eggs/py-1.4.0-py2.6.egg/py/_path/common.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 """
       
     2 """
       
     3 import os, sys
       
     4 import py
       
     5 
       
     6 class Checkers:
       
     7     _depend_on_existence = 'exists', 'link', 'dir', 'file'
       
     8 
       
     9     def __init__(self, path):
       
    10         self.path = path
       
    11 
       
    12     def dir(self):
       
    13         raise NotImplementedError
       
    14 
       
    15     def file(self):
       
    16         raise NotImplementedError
       
    17 
       
    18     def dotfile(self):
       
    19         return self.path.basename.startswith('.')
       
    20 
       
    21     def ext(self, arg):
       
    22         if not arg.startswith('.'):
       
    23             arg = '.' + arg
       
    24         return self.path.ext == arg
       
    25 
       
    26     def exists(self):
       
    27         raise NotImplementedError
       
    28 
       
    29     def basename(self, arg):
       
    30         return self.path.basename == arg
       
    31 
       
    32     def basestarts(self, arg):
       
    33         return self.path.basename.startswith(arg)
       
    34 
       
    35     def relto(self, arg):
       
    36         return self.path.relto(arg)
       
    37 
       
    38     def fnmatch(self, arg):
       
    39         return self.path.fnmatch(arg)
       
    40 
       
    41     def endswith(self, arg):
       
    42         return str(self.path).endswith(arg)
       
    43 
       
    44     def _evaluate(self, kw):
       
    45         for name, value in kw.items():
       
    46             invert = False
       
    47             meth = None
       
    48             try:
       
    49                 meth = getattr(self, name)
       
    50             except AttributeError:
       
    51                 if name[:3] == 'not':
       
    52                     invert = True
       
    53                     try:
       
    54                         meth = getattr(self, name[3:])
       
    55                     except AttributeError:
       
    56                         pass
       
    57             if meth is None:
       
    58                 raise TypeError(
       
    59                     "no %r checker available for %r" % (name, self.path))
       
    60             try:
       
    61                 if py.code.getrawcode(meth).co_argcount > 1:
       
    62                     if (not meth(value)) ^ invert:
       
    63                         return False
       
    64                 else:
       
    65                     if bool(value) ^ bool(meth()) ^ invert:
       
    66                         return False
       
    67             except (py.error.ENOENT, py.error.ENOTDIR):
       
    68                 for name in self._depend_on_existence:
       
    69                     if name in kw:
       
    70                         if kw.get(name):
       
    71                             return False
       
    72                     name = 'not' + name
       
    73                     if name in kw:
       
    74                         if not kw.get(name):
       
    75                             return False
       
    76         return True
       
    77 
       
    78 class NeverRaised(Exception):
       
    79     pass
       
    80 
       
    81 class PathBase(object):
       
    82     """ shared implementation for filesystem path objects."""
       
    83     Checkers = Checkers
       
    84 
       
    85     def __div__(self, other):
       
    86         return self.join(str(other))
       
    87     __truediv__ = __div__ # py3k
       
    88 
       
    89     def basename(self):
       
    90         """ basename part of path. """
       
    91         return self._getbyspec('basename')[0]
       
    92     basename = property(basename, None, None, basename.__doc__)
       
    93 
       
    94     def dirname(self):
       
    95         """ dirname part of path. """
       
    96         return self._getbyspec('dirname')[0]
       
    97     dirname = property(dirname, None, None, dirname.__doc__)
       
    98 
       
    99     def purebasename(self):
       
   100         """ pure base name of the path."""
       
   101         return self._getbyspec('purebasename')[0]
       
   102     purebasename = property(purebasename, None, None, purebasename.__doc__)
       
   103 
       
   104     def ext(self):
       
   105         """ extension of the path (including the '.')."""
       
   106         return self._getbyspec('ext')[0]
       
   107     ext = property(ext, None, None, ext.__doc__)
       
   108 
       
   109     def dirpath(self, *args, **kwargs):
       
   110         """ return the directory Path of the current Path joined
       
   111             with any given path arguments.
       
   112         """
       
   113         return self.new(basename='').join(*args, **kwargs)
       
   114 
       
   115     def read(self, mode='r'):
       
   116         """ read and return a bytestring from reading the path. """
       
   117         if sys.version_info < (2,3):
       
   118             for x in 'u', 'U':
       
   119                 if x in mode:
       
   120                     mode = mode.replace(x, '')
       
   121         f = self.open(mode)
       
   122         try:
       
   123             return f.read()
       
   124         finally:
       
   125             f.close()
       
   126 
       
   127     def readlines(self, cr=1):
       
   128         """ read and return a list of lines from the path. if cr is False, the
       
   129 newline will be removed from the end of each line. """
       
   130         if not cr:
       
   131             content = self.read('rU')
       
   132             return content.split('\n')
       
   133         else:
       
   134             f = self.open('rU')
       
   135             try:
       
   136                 return f.readlines()
       
   137             finally:
       
   138                 f.close()
       
   139 
       
   140     def load(self):
       
   141         """ (deprecated) return object unpickled from self.read() """
       
   142         f = self.open('rb')
       
   143         try:
       
   144             return py.error.checked_call(py.std.pickle.load, f)
       
   145         finally:
       
   146             f.close()
       
   147 
       
   148     def move(self, target):
       
   149         """ move this path to target. """
       
   150         if target.relto(self):
       
   151             raise py.error.EINVAL(target,
       
   152                 "cannot move path into a subdirectory of itself")
       
   153         try:
       
   154             self.rename(target)
       
   155         except py.error.EXDEV:  # invalid cross-device link
       
   156             self.copy(target)
       
   157             self.remove()
       
   158 
       
   159     def __repr__(self):
       
   160         """ return a string representation of this path. """
       
   161         return repr(str(self))
       
   162 
       
   163     def check(self, **kw):
       
   164         """ check a path for existence and properties.
       
   165 
       
   166             Without arguments, return True if the path exists, otherwise False.
       
   167 
       
   168             valid checkers::
       
   169 
       
   170                 file=1    # is a file
       
   171                 file=0    # is not a file (may not even exist)
       
   172                 dir=1     # is a dir
       
   173                 link=1    # is a link
       
   174                 exists=1  # exists
       
   175 
       
   176             You can specify multiple checker definitions, for example::
       
   177                 
       
   178                 path.check(file=1, link=1)  # a link pointing to a file
       
   179         """
       
   180         if not kw:
       
   181             kw = {'exists' : 1}
       
   182         return self.Checkers(self)._evaluate(kw)
       
   183 
       
   184     def fnmatch(self, pattern):
       
   185         """return true if the basename/fullname matches the glob-'pattern'.
       
   186 
       
   187         valid pattern characters::
       
   188 
       
   189             *       matches everything
       
   190             ?       matches any single character
       
   191             [seq]   matches any character in seq
       
   192             [!seq]  matches any char not in seq
       
   193 
       
   194         If the pattern contains a path-separator then the full path
       
   195         is used for pattern matching and a '*' is prepended to the
       
   196         pattern.
       
   197 
       
   198         if the pattern doesn't contain a path-separator the pattern
       
   199         is only matched against the basename.
       
   200         """
       
   201         return FNMatcher(pattern)(self)
       
   202 
       
   203     def relto(self, relpath):
       
   204         """ return a string which is the relative part of the path
       
   205         to the given 'relpath'.
       
   206         """
       
   207         if not isinstance(relpath, (str, PathBase)):
       
   208             raise TypeError("%r: not a string or path object" %(relpath,))
       
   209         strrelpath = str(relpath)
       
   210         if strrelpath and strrelpath[-1] != self.sep:
       
   211             strrelpath += self.sep
       
   212         #assert strrelpath[-1] == self.sep
       
   213         #assert strrelpath[-2] != self.sep
       
   214         strself = str(self)
       
   215         if sys.platform == "win32" or getattr(os, '_name', None) == 'nt':
       
   216             if os.path.normcase(strself).startswith(
       
   217                os.path.normcase(strrelpath)):
       
   218                 return strself[len(strrelpath):]
       
   219         elif strself.startswith(strrelpath):
       
   220             return strself[len(strrelpath):]
       
   221         return ""
       
   222 
       
   223     def bestrelpath(self, dest):
       
   224         """ return a string which is a relative path from self
       
   225             (assumed to be a directory) to dest such that
       
   226             self.join(bestrelpath) == dest and if not such
       
   227             path can be determined return dest.
       
   228         """
       
   229         try:
       
   230             if self == dest:
       
   231                 return os.curdir
       
   232             base = self.common(dest)
       
   233             if not base:  # can be the case on windows
       
   234                 return str(dest)
       
   235             self2base = self.relto(base)
       
   236             reldest = dest.relto(base)
       
   237             if self2base:
       
   238                 n = self2base.count(self.sep) + 1
       
   239             else:
       
   240                 n = 0
       
   241             l = [os.pardir] * n
       
   242             if reldest:
       
   243                 l.append(reldest)
       
   244             target = dest.sep.join(l)
       
   245             return target
       
   246         except AttributeError:
       
   247             return str(dest)
       
   248 
       
   249 
       
   250     def parts(self, reverse=False):
       
   251         """ return a root-first list of all ancestor directories
       
   252             plus the path itself.
       
   253         """
       
   254         current = self
       
   255         l = [self]
       
   256         while 1:
       
   257             last = current
       
   258             current = current.dirpath()
       
   259             if last == current:
       
   260                 break
       
   261             l.insert(0, current)
       
   262         if reverse:
       
   263             l.reverse()
       
   264         return l
       
   265 
       
   266     def common(self, other):
       
   267         """ return the common part shared with the other path
       
   268             or None if there is no common part.
       
   269         """
       
   270         last = None
       
   271         for x, y in zip(self.parts(), other.parts()):
       
   272             if x != y:
       
   273                 return last
       
   274             last = x
       
   275         return last
       
   276 
       
   277     def __add__(self, other):
       
   278         """ return new path object with 'other' added to the basename"""
       
   279         return self.new(basename=self.basename+str(other))
       
   280 
       
   281     def __cmp__(self, other):
       
   282         """ return sort value (-1, 0, +1). """
       
   283         try:
       
   284             return cmp(self.strpath, other.strpath)
       
   285         except AttributeError:
       
   286             return cmp(str(self), str(other)) # self.path, other.path)
       
   287 
       
   288     def __lt__(self, other):
       
   289         try:
       
   290             return self.strpath < other.strpath
       
   291         except AttributeError:
       
   292             return str(self) < str(other)
       
   293 
       
   294     def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False):
       
   295         """ yields all paths below the current one
       
   296 
       
   297             fil is a filter (glob pattern or callable), if not matching the
       
   298             path will not be yielded, defaulting to None (everything is
       
   299             returned)
       
   300 
       
   301             rec is a filter (glob pattern or callable) that controls whether
       
   302             a node is descended, defaulting to None
       
   303 
       
   304             ignore is an Exception class that is ignoredwhen calling dirlist()
       
   305             on any of the paths (by default, all exceptions are reported)
       
   306 
       
   307             bf if True will cause a breadthfirst search instead of the
       
   308             default depthfirst. Default: False
       
   309 
       
   310             sort if True will sort entries within each directory level.
       
   311         """
       
   312         for x in Visitor(fil, rec, ignore, bf, sort).gen(self):
       
   313             yield x
       
   314 
       
   315     def _sortlist(self, res, sort):
       
   316         if sort:
       
   317             if hasattr(sort, '__call__'):
       
   318                 res.sort(sort)
       
   319             else:
       
   320                 res.sort()
       
   321 
       
   322     def samefile(self, other):
       
   323         """ return True if other refers to the same stat object as self. """
       
   324         return self.strpath == str(other)
       
   325 
       
   326 class Visitor:
       
   327     def __init__(self, fil, rec, ignore, bf, sort):
       
   328         if isinstance(fil, str):
       
   329             fil = FNMatcher(fil)
       
   330         if isinstance(rec, str):
       
   331             self.rec = fnmatch(fil)
       
   332         elif not hasattr(rec, '__call__') and rec:
       
   333             self.rec = lambda path: True
       
   334         else:
       
   335             self.rec = rec
       
   336         self.fil = fil
       
   337         self.ignore = ignore
       
   338         self.breadthfirst = bf
       
   339         self.optsort = sort and sorted or (lambda x: x)
       
   340 
       
   341     def gen(self, path):
       
   342         try:
       
   343             entries = path.listdir()
       
   344         except self.ignore:
       
   345             return
       
   346         rec = self.rec
       
   347         dirs = self.optsort([p for p in entries
       
   348                     if p.check(dir=1) and (rec is None or rec(p))])
       
   349         if not self.breadthfirst:
       
   350             for subdir in dirs:
       
   351                 for p in self.gen(subdir):
       
   352                     yield p
       
   353         for p in self.optsort(entries):
       
   354             if self.fil is None or self.fil(p):
       
   355                 yield p
       
   356         if self.breadthfirst:
       
   357             for subdir in dirs:
       
   358                 for p in self.gen(subdir):
       
   359                     yield p
       
   360 
       
   361 class FNMatcher:
       
   362     def __init__(self, pattern):
       
   363         self.pattern = pattern
       
   364     def __call__(self, path):
       
   365         pattern = self.pattern
       
   366         if pattern.find(path.sep) == -1:
       
   367             name = path.basename
       
   368         else:
       
   369             name = str(path) # path.strpath # XXX svn?
       
   370             pattern = '*' + path.sep + pattern
       
   371         from fnmatch import fnmatch
       
   372         return fnmatch(name, pattern)
       
   373