eggs/py-1.4.0-py2.6.egg/py/_path/local.py
changeset 307 c6bca38c1cbf
equal deleted inserted replaced
306:5ff1fc726848 307:c6bca38c1cbf
       
     1 """
       
     2 local path implementation.
       
     3 """
       
     4 import sys, os, stat, re, atexit
       
     5 import py
       
     6 from py._path import common
       
     7 
       
     8 iswin32 = sys.platform == "win32" or (getattr(os, '_name', False) == 'nt')
       
     9 
       
    10 class Stat(object):
       
    11     def __getattr__(self, name):
       
    12         return getattr(self._osstatresult, "st_" + name)
       
    13 
       
    14     def __init__(self, path, osstatresult):
       
    15         self.path = path
       
    16         self._osstatresult = osstatresult
       
    17 
       
    18     def owner(self):
       
    19         if iswin32:
       
    20             raise NotImplementedError("XXX win32")
       
    21         import pwd
       
    22         entry = py.error.checked_call(pwd.getpwuid, self.uid)
       
    23         return entry[0]
       
    24     owner = property(owner, None, None, "owner of path")
       
    25 
       
    26     def group(self):
       
    27         """ return group name of file. """
       
    28         if iswin32:
       
    29             raise NotImplementedError("XXX win32")
       
    30         import grp
       
    31         entry = py.error.checked_call(grp.getgrgid, self.gid)
       
    32         return entry[0]
       
    33     group = property(group)
       
    34 
       
    35 class PosixPath(common.PathBase):
       
    36     def chown(self, user, group, rec=0):
       
    37         """ change ownership to the given user and group.
       
    38             user and group may be specified by a number or
       
    39             by a name.  if rec is True change ownership
       
    40             recursively.
       
    41         """
       
    42         uid = getuserid(user)
       
    43         gid = getgroupid(group)
       
    44         if rec:
       
    45             for x in self.visit(rec=lambda x: x.check(link=0)):
       
    46                 if x.check(link=0):
       
    47                     py.error.checked_call(os.chown, str(x), uid, gid)
       
    48         py.error.checked_call(os.chown, str(self), uid, gid)
       
    49 
       
    50     def readlink(self):
       
    51         """ return value of a symbolic link. """
       
    52         return py.error.checked_call(os.readlink, self.strpath)
       
    53 
       
    54     def mklinkto(self, oldname):
       
    55         """ posix style hard link to another name. """
       
    56         py.error.checked_call(os.link, str(oldname), str(self))
       
    57 
       
    58     def mksymlinkto(self, value, absolute=1):
       
    59         """ create a symbolic link with the given value (pointing to another name). """
       
    60         if absolute:
       
    61             py.error.checked_call(os.symlink, str(value), self.strpath)
       
    62         else:
       
    63             base = self.common(value)
       
    64             # with posix local paths '/' is always a common base
       
    65             relsource = self.__class__(value).relto(base)
       
    66             reldest = self.relto(base)
       
    67             n = reldest.count(self.sep)
       
    68             target = self.sep.join(('..', )*n + (relsource, ))
       
    69             py.error.checked_call(os.symlink, target, self.strpath)
       
    70 
       
    71 def getuserid(user):
       
    72     import pwd
       
    73     if not isinstance(user, int):
       
    74         user = pwd.getpwnam(user)[2]
       
    75     return user
       
    76 
       
    77 def getgroupid(group):
       
    78     import grp
       
    79     if not isinstance(group, int):
       
    80         group = grp.getgrnam(group)[2]
       
    81     return group
       
    82 
       
    83 FSBase = not iswin32 and PosixPath or common.PathBase
       
    84 
       
    85 class LocalPath(FSBase):
       
    86     """ object oriented interface to os.path and other local filesystem
       
    87         related information.
       
    88     """
       
    89     class ImportMismatchError(ImportError):
       
    90         """ raised on pyimport() if there is a mismatch of __file__'s"""
       
    91 
       
    92     sep = os.sep
       
    93     class Checkers(common.Checkers):
       
    94         def _stat(self):
       
    95             try:
       
    96                 return self._statcache
       
    97             except AttributeError:
       
    98                 try:
       
    99                     self._statcache = self.path.stat()
       
   100                 except py.error.ELOOP:
       
   101                     self._statcache = self.path.lstat()
       
   102                 return self._statcache
       
   103 
       
   104         def dir(self):
       
   105             return stat.S_ISDIR(self._stat().mode)
       
   106 
       
   107         def file(self):
       
   108             return stat.S_ISREG(self._stat().mode)
       
   109 
       
   110         def exists(self):
       
   111             return self._stat()
       
   112 
       
   113         def link(self):
       
   114             st = self.path.lstat()
       
   115             return stat.S_ISLNK(st.mode)
       
   116 
       
   117     def __new__(cls, path=None):
       
   118         """ Initialize and return a local Path instance.
       
   119 
       
   120         Path can be relative to the current directory.
       
   121         If it is None then the current working directory is taken.
       
   122         Note that Path instances always carry an absolute path.
       
   123         Note also that passing in a local path object will simply return
       
   124         the exact same path object. Use new() to get a new copy.
       
   125         """
       
   126         if isinstance(path, common.PathBase):
       
   127             if path.__class__ == cls:
       
   128                 return path
       
   129             path = path.strpath
       
   130         # initialize the path
       
   131         self = object.__new__(cls)
       
   132         if not path:
       
   133             self.strpath = os.getcwd()
       
   134         elif isinstance(path, py.builtin._basestring):
       
   135             self.strpath = os.path.abspath(os.path.normpath(str(path)))
       
   136         else:
       
   137             raise ValueError("can only pass None, Path instances "
       
   138                              "or non-empty strings to LocalPath")
       
   139         assert isinstance(self.strpath, str)
       
   140         return self
       
   141 
       
   142     def __hash__(self):
       
   143         return hash(self.strpath)
       
   144 
       
   145     def __eq__(self, other):
       
   146         s1 = str(self)
       
   147         s2 = str(other)
       
   148         if iswin32:
       
   149             s1 = s1.lower()
       
   150             s2 = s2.lower()
       
   151         return s1 == s2
       
   152 
       
   153     def __ne__(self, other):
       
   154         return not (self == other)
       
   155 
       
   156     def __lt__(self, other):
       
   157         return str(self) < str(other)
       
   158 
       
   159     def samefile(self, other):
       
   160         """ return True if 'other' references the same file as 'self'. """
       
   161         if self == other:
       
   162             return True
       
   163         if not iswin32:
       
   164             return py.error.checked_call(os.path.samefile, str(self), str(other))
       
   165         return False
       
   166 
       
   167     def remove(self, rec=1, ignore_errors=False):
       
   168         """ remove a file or directory (or a directory tree if rec=1).
       
   169         if ignore_errors is True, errors while removing directories will
       
   170         be ignored.
       
   171         """
       
   172         if self.check(dir=1, link=0):
       
   173             if rec:
       
   174                 # force remove of readonly files on windows
       
   175                 if iswin32:
       
   176                     self.chmod(448, rec=1) # octcal 0700
       
   177                 py.error.checked_call(py.std.shutil.rmtree, self.strpath,
       
   178                     ignore_errors=ignore_errors)
       
   179             else:
       
   180                 py.error.checked_call(os.rmdir, self.strpath)
       
   181         else:
       
   182             if iswin32:
       
   183                 self.chmod(448) # octcal 0700
       
   184             py.error.checked_call(os.remove, self.strpath)
       
   185 
       
   186     def computehash(self, hashtype="md5", chunksize=524288):
       
   187         """ return hexdigest of hashvalue for this file. """
       
   188         try:
       
   189             try:
       
   190                 import hashlib as mod
       
   191             except ImportError:
       
   192                 if hashtype == "sha1":
       
   193                     hashtype = "sha"
       
   194                 mod = __import__(hashtype)
       
   195             hash = getattr(mod, hashtype)()
       
   196         except (AttributeError, ImportError):
       
   197             raise ValueError("Don't know how to compute %r hash" %(hashtype,))
       
   198         f = self.open('rb')
       
   199         try:
       
   200             while 1:
       
   201                 buf = f.read(chunksize)
       
   202                 if not buf:
       
   203                     return hash.hexdigest()
       
   204                 hash.update(buf)
       
   205         finally:
       
   206             f.close()
       
   207 
       
   208     def new(self, **kw):
       
   209         """ create a modified version of this path.
       
   210             the following keyword arguments modify various path parts::
       
   211 
       
   212               a:/some/path/to/a/file.ext
       
   213               xx                           drive
       
   214               xxxxxxxxxxxxxxxxx            dirname
       
   215                                 xxxxxxxx   basename
       
   216                                 xxxx       purebasename
       
   217                                      xxx   ext
       
   218         """
       
   219         obj = object.__new__(self.__class__)
       
   220         drive, dirname, basename, purebasename,ext = self._getbyspec(
       
   221              "drive,dirname,basename,purebasename,ext")
       
   222         if 'basename' in kw:
       
   223             if 'purebasename' in kw or 'ext' in kw:
       
   224                 raise ValueError("invalid specification %r" % kw)
       
   225         else:
       
   226             pb = kw.setdefault('purebasename', purebasename)
       
   227             try:
       
   228                 ext = kw['ext']
       
   229             except KeyError:
       
   230                 pass
       
   231             else:
       
   232                 if ext and not ext.startswith('.'):
       
   233                     ext = '.' + ext
       
   234             kw['basename'] = pb + ext
       
   235 
       
   236         if ('dirname' in kw and not kw['dirname']):
       
   237             kw['dirname'] = drive
       
   238         else:
       
   239             kw.setdefault('dirname', dirname)
       
   240         kw.setdefault('sep', self.sep)
       
   241         obj.strpath = os.path.normpath(
       
   242             "%(dirname)s%(sep)s%(basename)s" % kw)
       
   243         return obj
       
   244 
       
   245     def _getbyspec(self, spec):
       
   246         """ see new for what 'spec' can be. """
       
   247         res = []
       
   248         parts = self.strpath.split(self.sep)
       
   249 
       
   250         args = filter(None, spec.split(',') )
       
   251         append = res.append
       
   252         for name in args:
       
   253             if name == 'drive':
       
   254                 append(parts[0])
       
   255             elif name == 'dirname':
       
   256                 append(self.sep.join(parts[:-1]))
       
   257             else:
       
   258                 basename = parts[-1]
       
   259                 if name == 'basename':
       
   260                     append(basename)
       
   261                 else:
       
   262                     i = basename.rfind('.')
       
   263                     if i == -1:
       
   264                         purebasename, ext = basename, ''
       
   265                     else:
       
   266                         purebasename, ext = basename[:i], basename[i:]
       
   267                     if name == 'purebasename':
       
   268                         append(purebasename)
       
   269                     elif name == 'ext':
       
   270                         append(ext)
       
   271                     else:
       
   272                         raise ValueError("invalid part specification %r" % name)
       
   273         return res
       
   274 
       
   275     def join(self, *args, **kwargs):
       
   276         """ return a new path by appending all 'args' as path
       
   277         components.  if abs=1 is used restart from root if any
       
   278         of the args is an absolute path.
       
   279         """
       
   280         if not args:
       
   281             return self
       
   282         strpath = self.strpath
       
   283         sep = self.sep
       
   284         strargs = [str(x) for x in args]
       
   285         if kwargs.get('abs', 0):
       
   286             for i in range(len(strargs)-1, -1, -1):
       
   287                 if os.path.isabs(strargs[i]):
       
   288                     strpath = strargs[i]
       
   289                     strargs = strargs[i+1:]
       
   290                     break
       
   291         for arg in strargs:
       
   292             arg = arg.strip(sep)
       
   293             if iswin32:
       
   294                 # allow unix style paths even on windows.
       
   295                 arg = arg.strip('/')
       
   296                 arg = arg.replace('/', sep)
       
   297             if arg:
       
   298                 if not strpath.endswith(sep):
       
   299                     strpath += sep
       
   300                 strpath += arg
       
   301         obj = self.new()
       
   302         obj.strpath = os.path.normpath(strpath)
       
   303         return obj
       
   304 
       
   305     def open(self, mode='r'):
       
   306         """ return an opened file with the given mode. """
       
   307         return py.error.checked_call(open, self.strpath, mode)
       
   308 
       
   309     def listdir(self, fil=None, sort=None):
       
   310         """ list directory contents, possibly filter by the given fil func
       
   311             and possibly sorted.
       
   312         """
       
   313         if isinstance(fil, str):
       
   314             fil = common.FNMatcher(fil)
       
   315         res = []
       
   316         for name in py.error.checked_call(os.listdir, self.strpath):
       
   317             childurl = self.join(name)
       
   318             if fil is None or fil(childurl):
       
   319                 res.append(childurl)
       
   320         self._sortlist(res, sort)
       
   321         return res
       
   322 
       
   323     def size(self):
       
   324         """ return size of the underlying file object """
       
   325         return self.stat().size
       
   326 
       
   327     def mtime(self):
       
   328         """ return last modification time of the path. """
       
   329         return self.stat().mtime
       
   330 
       
   331     def copy(self, target, archive=False):
       
   332         """ copy path to target."""
       
   333         assert not archive, "XXX archive-mode not supported"
       
   334         if self.check(file=1):
       
   335             if target.check(dir=1):
       
   336                 target = target.join(self.basename)
       
   337             assert self!=target
       
   338             copychunked(self, target)
       
   339         else:
       
   340             def rec(p):
       
   341                 return p.check(link=0)
       
   342             for x in self.visit(rec=rec):
       
   343                 relpath = x.relto(self)
       
   344                 newx = target.join(relpath)
       
   345                 newx.dirpath().ensure(dir=1)
       
   346                 if x.check(link=1):
       
   347                     newx.mksymlinkto(x.readlink())
       
   348                 elif x.check(file=1):
       
   349                     copychunked(x, newx)
       
   350                 elif x.check(dir=1):
       
   351                     newx.ensure(dir=1)
       
   352 
       
   353     def rename(self, target):
       
   354         """ rename this path to target. """
       
   355         return py.error.checked_call(os.rename, str(self), str(target))
       
   356 
       
   357     def dump(self, obj, bin=1):
       
   358         """ pickle object into path location"""
       
   359         f = self.open('wb')
       
   360         try:
       
   361             py.error.checked_call(py.std.pickle.dump, obj, f, bin)
       
   362         finally:
       
   363             f.close()
       
   364 
       
   365     def mkdir(self, *args):
       
   366         """ create & return the directory joined with args. """
       
   367         p = self.join(*args)
       
   368         py.error.checked_call(os.mkdir, str(p))
       
   369         return p
       
   370 
       
   371     def write(self, data, mode='w'):
       
   372         """ write data into path. """
       
   373         if 'b' in mode:
       
   374             if not py.builtin._isbytes(data):
       
   375                 raise ValueError("can only process bytes")
       
   376         else:
       
   377             if not py.builtin._istext(data):
       
   378                 if not py.builtin._isbytes(data):
       
   379                     data = str(data)
       
   380                 else:
       
   381                     data = py.builtin._totext(data, sys.getdefaultencoding())
       
   382         f = self.open(mode)
       
   383         try:
       
   384             f.write(data)
       
   385         finally:
       
   386             f.close()
       
   387 
       
   388     def _ensuredirs(self):
       
   389         parent = self.dirpath()
       
   390         if parent == self:
       
   391             return self
       
   392         if parent.check(dir=0):
       
   393             parent._ensuredirs()
       
   394         if self.check(dir=0):
       
   395             try:
       
   396                 self.mkdir()
       
   397             except py.error.EEXIST:
       
   398                 # race condition: file/dir created by another thread/process.
       
   399                 # complain if it is not a dir
       
   400                 if self.check(dir=0):
       
   401                     raise
       
   402         return self
       
   403 
       
   404     def ensure(self, *args, **kwargs):
       
   405         """ ensure that an args-joined path exists (by default as
       
   406             a file). if you specify a keyword argument 'dir=True'
       
   407             then the path is forced to be a directory path.
       
   408         """
       
   409         p = self.join(*args)
       
   410         if kwargs.get('dir', 0):
       
   411             return p._ensuredirs()
       
   412         else:
       
   413             p.dirpath()._ensuredirs()
       
   414             if not p.check(file=1):
       
   415                 p.open('w').close()
       
   416             return p
       
   417 
       
   418     def stat(self):
       
   419         """ Return an os.stat() tuple. """
       
   420         return Stat(self, py.error.checked_call(os.stat, self.strpath))
       
   421 
       
   422     def lstat(self):
       
   423         """ Return an os.lstat() tuple. """
       
   424         return Stat(self, py.error.checked_call(os.lstat, self.strpath))
       
   425 
       
   426     def setmtime(self, mtime=None):
       
   427         """ set modification time for the given path.  if 'mtime' is None
       
   428         (the default) then the file's mtime is set to current time.
       
   429 
       
   430         Note that the resolution for 'mtime' is platform dependent.
       
   431         """
       
   432         if mtime is None:
       
   433             return py.error.checked_call(os.utime, self.strpath, mtime)
       
   434         try:
       
   435             return py.error.checked_call(os.utime, self.strpath, (-1, mtime))
       
   436         except py.error.EINVAL:
       
   437             return py.error.checked_call(os.utime, self.strpath, (self.atime(), mtime))
       
   438 
       
   439     def chdir(self):
       
   440         """ change directory to self and return old current directory """
       
   441         old = self.__class__()
       
   442         py.error.checked_call(os.chdir, self.strpath)
       
   443         return old
       
   444 
       
   445     def realpath(self):
       
   446         """ return a new path which contains no symbolic links."""
       
   447         return self.__class__(os.path.realpath(self.strpath))
       
   448 
       
   449     def atime(self):
       
   450         """ return last access time of the path. """
       
   451         return self.stat().atime
       
   452 
       
   453     def __repr__(self):
       
   454         return 'local(%r)' % self.strpath
       
   455 
       
   456     def __str__(self):
       
   457         """ return string representation of the Path. """
       
   458         return self.strpath
       
   459 
       
   460     def pypkgpath(self, pkgname=None):
       
   461         """ return the Python package path by looking for a
       
   462             pkgname.  If pkgname is None look for the last
       
   463             directory upwards which still contains an __init__.py
       
   464             and whose basename is python-importable.
       
   465             Return None if a pkgpath can not be determined.
       
   466         """
       
   467         pkgpath = None
       
   468         for parent in self.parts(reverse=True):
       
   469             if pkgname is None:
       
   470                 if parent.check(file=1):
       
   471                     continue
       
   472                 if not isimportable(parent.basename):
       
   473                     break
       
   474                 if parent.join('__init__.py').check():
       
   475                     pkgpath = parent
       
   476                     continue
       
   477                 return pkgpath
       
   478             else:
       
   479                 if parent.basename == pkgname:
       
   480                     return parent
       
   481         return pkgpath
       
   482 
       
   483     def _prependsyspath(self, path):
       
   484         s = str(path)
       
   485         if s != sys.path[0]:
       
   486             #print "prepending to sys.path", s
       
   487             sys.path.insert(0, s)
       
   488 
       
   489     def chmod(self, mode, rec=0):
       
   490         """ change permissions to the given mode. If mode is an
       
   491             integer it directly encodes the os-specific modes.
       
   492             if rec is True perform recursively.
       
   493         """
       
   494         if not isinstance(mode, int):
       
   495             raise TypeError("mode %r must be an integer" % (mode,))
       
   496         if rec:
       
   497             for x in self.visit(rec=rec):
       
   498                 py.error.checked_call(os.chmod, str(x), mode)
       
   499         py.error.checked_call(os.chmod, str(self), mode)
       
   500 
       
   501     def pyimport(self, modname=None, ensuresyspath=True):
       
   502         """ return path as an imported python module.
       
   503             if modname is None, look for the containing package
       
   504             and construct an according module name.
       
   505             The module will be put/looked up in sys.modules.
       
   506         """
       
   507         if not self.check():
       
   508             raise py.error.ENOENT(self)
       
   509         #print "trying to import", self
       
   510         pkgpath = None
       
   511         if modname is None:
       
   512             pkgpath = self.pypkgpath()
       
   513             if pkgpath is not None:
       
   514                 if ensuresyspath:
       
   515                     self._prependsyspath(pkgpath.dirpath())
       
   516                 pkg = __import__(pkgpath.basename, None, None, [])
       
   517                 names = self.new(ext='').relto(pkgpath.dirpath())
       
   518                 names = names.split(self.sep)
       
   519                 if names and names[-1] == "__init__":
       
   520                     names.pop()
       
   521                 modname = ".".join(names)
       
   522             else:
       
   523                 # no package scope, still make it possible
       
   524                 if ensuresyspath:
       
   525                     self._prependsyspath(self.dirpath())
       
   526                 modname = self.purebasename
       
   527             mod = __import__(modname, None, None, ['__doc__'])
       
   528             if self.basename == "__init__.py":
       
   529                 return mod # we don't check anything as we might
       
   530                        # we in a namespace package ... too icky to check
       
   531             modfile = mod.__file__
       
   532             if modfile[-4:] in ('.pyc', '.pyo'):
       
   533                 modfile = modfile[:-1]
       
   534             elif modfile.endswith('$py.class'):
       
   535                 modfile = modfile[:-9] + '.py'
       
   536             if modfile.endswith(os.path.sep + "__init__.py"):
       
   537                 if self.basename != "__init__.py":
       
   538                     modfile = modfile[:-12]
       
   539 
       
   540             if not self.samefile(modfile):
       
   541                 raise self.ImportMismatchError(modname, modfile, self)
       
   542             return mod
       
   543         else:
       
   544             try:
       
   545                 return sys.modules[modname]
       
   546             except KeyError:
       
   547                 # we have a custom modname, do a pseudo-import
       
   548                 mod = py.std.types.ModuleType(modname)
       
   549                 mod.__file__ = str(self)
       
   550                 sys.modules[modname] = mod
       
   551                 try:
       
   552                     py.builtin.execfile(str(self), mod.__dict__)
       
   553                 except:
       
   554                     del sys.modules[modname]
       
   555                     raise
       
   556                 return mod
       
   557 
       
   558     def sysexec(self, *argv, **popen_opts):
       
   559         """ return stdout text from executing a system child process,
       
   560             where the 'self' path points to executable.
       
   561             The process is directly invoked and not through a system shell.
       
   562         """
       
   563         from subprocess import Popen, PIPE
       
   564         argv = map(str, argv)
       
   565         popen_opts['stdout'] = popen_opts['stderr'] = PIPE
       
   566         proc = Popen([str(self)] + list(argv), **popen_opts)
       
   567         stdout, stderr = proc.communicate()
       
   568         ret = proc.wait()
       
   569         if py.builtin._isbytes(stdout):
       
   570             stdout = py.builtin._totext(stdout, sys.getdefaultencoding())
       
   571         if ret != 0:
       
   572             if py.builtin._isbytes(stderr):
       
   573                 stderr = py.builtin._totext(stderr, sys.getdefaultencoding())
       
   574             raise py.process.cmdexec.Error(ret, ret, str(self),
       
   575                                            stdout, stderr,)
       
   576         return stdout
       
   577 
       
   578     def sysfind(cls, name, checker=None):
       
   579         """ return a path object found by looking at the systems
       
   580             underlying PATH specification. If the checker is not None
       
   581             it will be invoked to filter matching paths.  If a binary
       
   582             cannot be found, None is returned
       
   583             Note: This is probably not working on plain win32 systems
       
   584             but may work on cygwin.
       
   585         """
       
   586         if os.path.isabs(name):
       
   587             p = py.path.local(name)
       
   588             if p.check(file=1):
       
   589                 return p
       
   590         else:
       
   591             if iswin32:
       
   592                 paths = py.std.os.environ['Path'].split(';')
       
   593                 if '' not in paths and '.' not in paths:
       
   594                     paths.append('.')
       
   595                 try:
       
   596                     systemroot = os.environ['SYSTEMROOT']
       
   597                 except KeyError:
       
   598                     pass
       
   599                 else:
       
   600                     paths = [re.sub('%SystemRoot%', systemroot, path)
       
   601                              for path in paths]
       
   602                 tryadd = '', '.exe', '.com', '.bat' # XXX add more?
       
   603             else:
       
   604                 paths = py.std.os.environ['PATH'].split(':')
       
   605                 tryadd = ('',)
       
   606 
       
   607             for x in paths:
       
   608                 for addext in tryadd:
       
   609                     p = py.path.local(x).join(name, abs=True) + addext
       
   610                     try:
       
   611                         if p.check(file=1):
       
   612                             if checker:
       
   613                                 if not checker(p):
       
   614                                     continue
       
   615                             return p
       
   616                     except py.error.EACCES:
       
   617                         pass
       
   618         return None
       
   619     sysfind = classmethod(sysfind)
       
   620 
       
   621     def _gethomedir(cls):
       
   622         try:
       
   623             x = os.environ['HOME']
       
   624         except KeyError:
       
   625             x = os.environ["HOMEDRIVE"] + os.environ['HOMEPATH']
       
   626         return cls(x)
       
   627     _gethomedir = classmethod(_gethomedir)
       
   628 
       
   629     #"""
       
   630     #special class constructors for local filesystem paths
       
   631     #"""
       
   632     def get_temproot(cls):
       
   633         """ return the system's temporary directory
       
   634             (where tempfiles are usually created in)
       
   635         """
       
   636         return py.path.local(py.std.tempfile.gettempdir())
       
   637     get_temproot = classmethod(get_temproot)
       
   638 
       
   639     def mkdtemp(cls, rootdir=None):
       
   640         """ return a Path object pointing to a fresh new temporary directory
       
   641             (which we created ourself).
       
   642         """
       
   643         import tempfile
       
   644         if rootdir is None:
       
   645             rootdir = cls.get_temproot()
       
   646         return cls(py.error.checked_call(tempfile.mkdtemp, dir=str(rootdir)))
       
   647     mkdtemp = classmethod(mkdtemp)
       
   648 
       
   649     def make_numbered_dir(cls, prefix='session-', rootdir=None, keep=3,
       
   650                           lock_timeout = 172800):   # two days
       
   651         """ return unique directory with a number greater than the current
       
   652             maximum one.  The number is assumed to start directly after prefix.
       
   653             if keep is true directories with a number less than (maxnum-keep)
       
   654             will be removed.
       
   655         """
       
   656         if rootdir is None:
       
   657             rootdir = cls.get_temproot()
       
   658 
       
   659         def parse_num(path):
       
   660             """ parse the number out of a path (if it matches the prefix) """
       
   661             bn = path.basename
       
   662             if bn.startswith(prefix):
       
   663                 try:
       
   664                     return int(bn[len(prefix):])
       
   665                 except ValueError:
       
   666                     pass
       
   667 
       
   668         # compute the maximum number currently in use with the
       
   669         # prefix
       
   670         lastmax = None
       
   671         while True:
       
   672             maxnum = -1
       
   673             for path in rootdir.listdir():
       
   674                 num = parse_num(path)
       
   675                 if num is not None:
       
   676                     maxnum = max(maxnum, num)
       
   677 
       
   678             # make the new directory
       
   679             try:
       
   680                 udir = rootdir.mkdir(prefix + str(maxnum+1))
       
   681             except py.error.EEXIST:
       
   682                 # race condition: another thread/process created the dir
       
   683                 # in the meantime.  Try counting again
       
   684                 if lastmax == maxnum:
       
   685                     raise
       
   686                 lastmax = maxnum
       
   687                 continue
       
   688             break
       
   689 
       
   690         # put a .lock file in the new directory that will be removed at
       
   691         # process exit
       
   692         if lock_timeout:
       
   693             lockfile = udir.join('.lock')
       
   694             mypid = os.getpid()
       
   695             if hasattr(lockfile, 'mksymlinkto'):
       
   696                 lockfile.mksymlinkto(str(mypid))
       
   697             else:
       
   698                 lockfile.write(str(mypid))
       
   699             def try_remove_lockfile():
       
   700                 # in a fork() situation, only the last process should
       
   701                 # remove the .lock, otherwise the other processes run the
       
   702                 # risk of seeing their temporary dir disappear.  For now
       
   703                 # we remove the .lock in the parent only (i.e. we assume
       
   704                 # that the children finish before the parent).
       
   705                 if os.getpid() != mypid:
       
   706                     return
       
   707                 try:
       
   708                     lockfile.remove()
       
   709                 except py.error.Error:
       
   710                     pass
       
   711             atexit.register(try_remove_lockfile)
       
   712 
       
   713         # prune old directories
       
   714         if keep:
       
   715             for path in rootdir.listdir():
       
   716                 num = parse_num(path)
       
   717                 if num is not None and num <= (maxnum - keep):
       
   718                     lf = path.join('.lock')
       
   719                     try:
       
   720                         t1 = lf.lstat().mtime
       
   721                         t2 = lockfile.lstat().mtime
       
   722                         if not lock_timeout or abs(t2-t1) < lock_timeout:
       
   723                             continue   # skip directories still locked
       
   724                     except py.error.Error:
       
   725                         pass   # assume that it means that there is no 'lf'
       
   726                     try:
       
   727                         path.remove(rec=1)
       
   728                     except KeyboardInterrupt:
       
   729                         raise
       
   730                     except: # this might be py.error.Error, WindowsError ...
       
   731                         pass
       
   732 
       
   733         # make link...
       
   734         try:
       
   735             username = os.environ['USER']           #linux, et al
       
   736         except KeyError:
       
   737             try:
       
   738                 username = os.environ['USERNAME']   #windows
       
   739             except KeyError:
       
   740                 username = 'current'
       
   741 
       
   742         src  = str(udir)
       
   743         dest = src[:src.rfind('-')] + '-' + username
       
   744         try:
       
   745             os.unlink(dest)
       
   746         except OSError:
       
   747             pass
       
   748         try:
       
   749             os.symlink(src, dest)
       
   750         except (OSError, AttributeError): # AttributeError on win32
       
   751             pass
       
   752 
       
   753         return udir
       
   754     make_numbered_dir = classmethod(make_numbered_dir)
       
   755 
       
   756 def copychunked(src, dest):
       
   757     chunksize = 524288 # half a meg of bytes
       
   758     fsrc = src.open('rb')
       
   759     try:
       
   760         fdest = dest.open('wb')
       
   761         try:
       
   762             while 1:
       
   763                 buf = fsrc.read(chunksize)
       
   764                 if not buf:
       
   765                     break
       
   766                 fdest.write(buf)
       
   767         finally:
       
   768             fdest.close()
       
   769     finally:
       
   770         fsrc.close()
       
   771 
       
   772 def isimportable(name):
       
   773     if name:
       
   774         if not (name[0].isalpha() or name[0] == '_'):
       
   775             return False
       
   776         name= name.replace("_", '')
       
   777         return not name or name.isalnum()