AlternativePathModule - PythonInfo Wiki

#

#

# AlternativePathModule

Search:

       1 # -*- coding: iso-8859-1 -*-
       2 """ path.py - An object representing a path to a file or a directory.
       3
       4 Based on the path module by Jason Orendorff
       5 (http://www.jorendorff.com/articles/python/path)
       6
       7 Written by Noam Raphael to show the idea of using a tuple instead of
       8 a string, and to reduce the number of methods.
       9
      10 Currently only implements posix and nt paths - more can be added.
      11
      12 """
      13
      14 import os
      15 import stat
      16 import itertools
      17 import fnmatch
      18 import re
      19 import string
      20
      21 class StatWrapper(object):
      22     """ A wrapper around stat_result objects which gives additional properties.
      23
      24     This object is a wrapper around a stat_result object. It allows access
      25     to all the original object's attributes, and adds a few convinient
      26     properties, by using the stat module.
      27
      28     This object should have been a subclass posix.stat_result - it simply
      29     isn't possible currently. This functionality may also be integrated into
      30     the original type.
      31     """
      32
      33     __slots__ = ['_stat']
      34
      35     def __init__(self, stat):
      36         self._stat = stat
      37
      38     def __getattribute__(self, attr, *default):
      39         try:
      40             return object.__getattribute__(self, attr, *default)
      41         except AttributeError:
      42             return getattr(self._stat, attr, *default)
      43
      44     # Mode properties
      45
      46     @property
      47     def isdir(self):
      48         return stat.S_ISDIR(self.st_mode)
      49     @property
      50     def isfile(self):
      51         return stat.S_ISREG(self.st_mode)
      52     @property
      53     def islink(self):
      54         return stat.S_ISLNK(self.st_mode)
      55
      56     # Easier names properties
      57
      58     @property
      59     def size(self):
      60         return self.st_size
      61     @property
      62     def mtime(self):
      63         return self.st_mtime
      64     @property
      65     def atime(self):
      66         return self.st_atime
      67     @property
      68     def ctime(self):
      69         return self.st_ctime
      70
      71
      72 class BasePath(tuple):
      73     """ The base, abstract, path type.
      74
      75     The OS-specific path types inherit from it.
      76     """
      77
      78     # ----------------------------------------------------------------
      79     # We start with methods which don't use system calls - they just
      80     # manipulate paths.
      81
      82     class _BaseRoot(object):
      83         """ Represents a start location for a path.
      84
      85         A Root is an object which may be the first element of a path tuple,
      86         and represents from where to start the path.
      87
      88         On posix, there's only one: ROOT (singleton).
      89         On nt, there are a few:
      90           CURROOT - the root of the current drive (singleton)
      91           Drive(letter) - the root of a specific drive
      92           UnrootedDrive(letter) - the current working directory on a specific
      93                                   drive
      94           UNCRoot(host, mountpoint) - a UNC mount point
      95
      96         The class for each OS has its own root classes, which should inherit
      97         from _OSBaseRoot.
      98
      99         str(root) should return the string name of the root. The string should
     100         identify the root: two root elements with the same string should have
     101         the same meaning. To allow meaningful sorting of path objects, root
     102         objects can be compared to strings and other root objects. They are
     103         smaller than all strings, and are compared with other root objects
     104         according to their string name.
     105
     106         Every Root object should contain the "isabs" attribute, which is True
     107         if changes in the current working directory won't change the meaning
     108         of the root and False otherwise. (CURROOT and UnrootedDrive aren't
     109         absolute)
     110         If isabs is True, it should also implement the abspath() method, which
     111         should return an absolute path object, equivalent to the root when the
     112         call was made.
     113         """
     114         isabs = None
     115
     116         def abspath(self):
     117             if self.abspath:
     118                 raise NotImplementedError, 'This root is already absolute'
     119             else:
     120                 raise NotImplementedError, 'abspath is abstract'
     121
     122         def __str__(self):
     123             raise NotImplementedError, '__str__ is abstract'
     124
     125         def __cmp__(self, other):
     126             if isinstance(other, str):
     127                 return -1
     128             elif isinstance(other, BasePath._BaseRoot):
     129                 return cmp(str(self), str(other))
     130             else:
     131                 raise TypeError, 'Comparison not defined'
     132
     133         def __hash__(self):
     134             # This allows path objects to be hashable
     135             return hash(str(self))
     136
     137     # _OSBaseRoot should be the base of the OS-specific root classes, which
     138     # should inherit from _BaseRoot
     139     _OSBaseRoot = None
     140
     141     # These string constants should be filled by subclasses - they are real
     142     # directory names
     143     curdir = None
     144     pardir = None
     145
     146     # These string constants are used by default implementations of methods,
     147     # but are not part of the interface - the whole idea is for the interface
     148     # to hide those details.
     149     _sep = None
     150     _altsep = None
     151
     152     @staticmethod
     153     def _parse_str(pathstr):
     154         # Concrete path classes should implement _parse_str to get a path
     155         # string and return an iterable over path elements.
     156         raise NotImplementedError, '_parse_str is abstract'
     157
     158     @staticmethod
     159     def normcasestr(string):
     160         """ Normalize the case of one path element.
     161
     162         This default implementation returns string unchanged. On
     163         case-insensitive platforms, it returns the normalized string.
     164         """
     165         return string
     166
     167     # We make this method a property, to show that it doesn't use any
     168     # system calls.
     169     # Case-sensitive subclasses can redefine it to return self.
     170     @property
     171     def normcase(self):
     172         """ Return an equivalent path with case-normalized elements. """
     173         if self.isrel:
     174             return self.__class__(self.normcasestr(element)
     175                                   for element in self)
     176         else:
     177             def gen():
     178                 it = iter(self)
     179                 yield it.next()
     180                 for element in it:
     181                     yield self.normcasestr(element)
     182             return self.__class__(gen())
     183
     184     @classmethod
     185     def _normalize_elements(cls, elements):
     186         # This method gets an iterable over path elements.
     187         # It should return an iterator over normalized path elements -
     188         # that is, curdir elements should be ignored.
     189
     190         for i, element in enumerate(elements):
     191             if isinstance(element, str):
     192                 if element != cls.curdir:
     193                     if (not element or
     194                         cls._sep in element or
     195                         (cls._altsep and cls._altsep in element)):
     196                         # Those elements will cause path(str(x)) != x
     197                         raise ValueError, "Element %r is invalid" % element
     198                     yield element
     199             elif i == 0 and isinstance(element, cls._OSBaseRoot):
     200                 yield element
     201             else:
     202                 raise TypeError, "Element %r is of a wrong type" % element
     203
     204     def __new__(cls, arg=None):
     205         """ Create a new path object.
     206
     207         If arg isn't given, an empty path, which represents the current
     208         working directory, is returned.
     209         If arg is a string, it is parsed into a logical path.
     210         If arg is an iterable over path elements, a new path is created from
     211         them.
     212         """
     213         if arg is None:
     214             return tuple.__new__(cls)
     215         elif type(arg) is cls:
     216             return arg
     217         elif isinstance(arg, str):
     218             return tuple.__new__(cls, cls._parse_str(arg))
     219         else:
     220             return tuple.__new__(cls, cls._normalize_elements(arg))
     221
     222     def __init__(self, arg=None):
     223         # Since paths are immutable, we can cache the string representation
     224         self._cached_str = None
     225
     226     def _build_str(self):
     227         # Return a string representation of self.
     228         #
     229         # This is a default implementation, which may be overriden by
     230         # subclasses (form example, MacPath)
     231         if not self:
     232             return self.curdir
     233         elif isinstance(self[0], self._OSBaseRoot):
     234             return str(self[0]) + self._sep.join(self[1:])
     235         else:
     236             return self._sep.join(self)
     237
     238     def __str__(self):
     239         """ Return a string representation of self. """
     240         if self._cached_str is None:
     241             self._cached_str = self._build_str()
     242         return self._cached_str
     243
     244     def __repr__(self):
     245         # We want path, not the real class name.
     246         return 'path(%r)' % str(self)
     247
     248     @property
     249     def isabs(self):
     250         """ Return whether this path represent an absolute path.
     251
     252         An absolute path is a path whose meaning doesn't change when the
     253         the current working directory changes.
     254
     255         (Note that this is not the same as "not self.isrelative")
     256         """
     257         return len(self) > 0 and \
                   isinstance(self[0], self._OSBaseRoot) and \
                   self[0].isabs
     258
     259     @property
     260     def isrel(self):
     261         """ Return whether this path represents a relative path.
     262
     263         A relative path is a path without a root element, so it can be
     264         concatenated to other paths.
     265
     266         (Note that this is not the same as "not self.isabs")
     267         """
     268         return len(self) == 0 or \
                   not isinstance(self[0], self._OSBaseRoot)
     269
     270     # Wrap a few tuple methods to return path objects
     271
     272     def __add__(self, other):
     273         other = self.__class__(other)
     274         if not other.isrel:
     275             raise ValueError, "Right operand should be a relative path"
     276         return self.__class__(itertools.chain(self, other))
     277
     278     def __radd__(self, other):
     279         if not self.isrel:
     280             raise ValueError, "Right operand should be a relative path"
     281         other = self.__class__(other)
     282         return self.__class__(itertools.chain(other, self))
     283
     284     def __getslice__(self, *args):
     285         return self.__class__(tuple.__getslice__(self, *args))
     286
     287     def __mul__(self, *args):
     288         if not self.isrel:
     289             raise ValueError, "Only relative paths can be multiplied"
     290         return self.__class__(tuple.__mul__(self, *args))
     291
     292     def __rmul__(self, *args):
     293         if not self.isrel:
     294             raise ValueError, "Only relative paths can be multiplied"
     295         return self.__class__(tuple.__rmul__(self, *args))
     296
     297     def __eq__(self, other):
     298         return tuple.__eq__(self, self.__class__(other))
     299     def __ge__(self, other):
     300         return tuple.__ge__(self, self.__class__(other))
     301     def __gt__(self, other):
     302         return tuple.__gt__(self, self.__class__(other))
     303     def __le__(self, other):
     304         return tuple.__le__(self, self.__class__(other))
     305     def __lt__(self, other):
     306         return tuple.__lt__(self, self.__class__(other))
     307     def __ne__(self, other):
     308         return tuple.__ne__(self, self.__class__(other))
     309
     310
     311     # ----------------------------------------------------------------
     312     # Now come the methods which use system calls.
     313
     314     # --- Path transformation which use system calls
     315
     316     @classmethod
     317     def cwd(cls):
     318         return cls(os.getcwd())
     319
     320     def chdir(self):
     321         return os.chdir(str(self))
     322
     323     def abspath(self):
     324         if not self:
     325             return self.cwd()
     326         if isinstance(self[0], self._OSBaseRoot):
     327             if self[0].isabs:
     328                 return self
     329             else:
     330                 return self[0].abspath() + self[1:]
     331         else:
     332             return self.cwd() + self
     333
     334     def realpath(self):
     335         return self.__class__(os.path.realpath(str(self)))
     336
     337     def relpathto(self, dst):
     338         """ Return a relative path from self to dest.
     339
     340         This method examines self.realpath() and dest.realpath(). If
     341         they have the same root element, a path in the form
     342         path([path.pardir, path.pardir, ..., dir1, dir2, ...])
     343         is returned. If they have different root elements,
     344         dest.realpath() is returned.
     345         """
     346         src = self.realpath()
     347         dst = self.__class__(dst).realpath()
     348
     349         if src[0] == dst[0]:
     350             # They have the same root
     351
     352             # find the length of the equal prefix
     353             i = 1
     354             while i < len(src) and i < len(dst) and \
                      self.normcasestr(src[i]) == self.normcasestr(dst[i]):
     355                 i += 1
     356
     357             return [self.pardir] * (len(src) - i) + dst[i:]
     358
     359         else:
     360             # They don't have the same root
     361             return dst
     362
     363
     364
     365
     366     # --- Expand
     367
     368     def expanduser(self):
     369         return path(os.path.expanduser(str(self)))
     370
     371     def expandvars(self):
     372         return path(os.path.expandvars(str(self)))
     373
     374
     375     # --- Info about the path
     376
     377     def stat(self):
     378         return StatWrapper(os.stat(str(self)))
     379
     380     def exists(self):
     381         try:
     382             self.stat()
     383         except OSError:
     384             return False
     385         else:
     386             return True
     387
     388     def isdir(self):
     389         try:
     390             return self.stat().isdir
     391         except OSError:
     392             return False
     393
     394     def isfile(self):
     395         try:
     396             return self.stat().isfile
     397         except OSError:
     398             return False
     399
     400     def lstat(self):
     401         return StatWrapper(os.lstat(str(self)))
     402
     403     def lexists(self):
     404         try:
     405             self.lstat()
     406         except OSError:
     407             return False
     408         else:
     409             return True
     410
     411     def lisdir(self):
     412         try:
     413             return self.stat().lisdir
     414         except OSError:
     415             return False
     416
     417     def lisfile(self):
     418         try:
     419             return self.stat().lisfile
     420         except OSError:
     421             return False
     422
     423     def islink(self):
     424         try:
     425             return self.lstat().islink
     426         except OSError:
     427             return False
     428
     429     def ismount(self):
     430         return os.path.ismount(str(self))
     431
     432     def access(self, mode):
     433         """ Return true if current user has access to this path.
     434
     435         mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK
     436         """
     437         return os.access(str(self), mode)
     438
     439     # Additional methods in subclasses:
     440     # statvfs (PosixPath)
     441     # pathconf (PosixPath, XXX MacPath)
     442     # samefile (PosixPath, XXX MacPath)
     443
     444
     445     # --- Modifying operations on files and directories
     446
     447     def utime(self, times):
     448         """ Set the access and modified times of this file. """
     449         os.utime(str(self), times)
     450
     451     def chmod(self, mode):
     452         os.chmod(str(self), mode)
     453
     454     def rename(self, new):
     455         os.rename(str(self), str(new))
     456
     457     # Additional methods in subclasses:
     458     # chown (PosixPath, XXX MacPath)
     459     # lchown (PosixPath, XXX MacPath)
     460
     461
     462     # --- Create/delete operations on directories
     463
     464     def mkdir(self, mode=0777):
     465         os.mkdir(str(self), mode)
     466
     467     def makedirs(self, mode=0777):
     468         os.makedirs(str(self), mode)
     469
     470     def rmdir(self):
     471         os.rmdir(str(self))
     472
     473     def removedirs(self, base=None):
     474         """ Remove empty directories recursively.
     475
     476         If the directory is empty, remove it. If the parent directory becomes
     477         empty, remove it too. Continue until a directory can't be removed,
     478         because it's not empty or for other reasons.
     479         If base is given, it should be a prefix of self. base won't be removed
     480         even if it becomes empty.
     481         Note: only directories explicitly listed in the path will be removed.
     482         This means that if self is a relative path, predecesors of the
     483         current working directory won't be removed.
     484         """
     485         if not self.stat().isdir:
     486             raise OSError, 'removedirs only works on directories.'
     487         base = self.__class__(base)
     488         if base:
     489             if not self[:len(base)] == base:
     490                 raise ValueError, 'base should be a prefix of self.'
     491             stopat = len(base)
     492         else:
     493             stopat = 0
     494         for i in xrange(len(self), stopat, -1):
     495             try:
     496                 self[:i].rmdir()
     497             except OSError:
     498                 break
     499
     500     def rmtree(self, *args):
     501         return shutil.rmtree(str(self), *args)
     502
     503
     504     # --- Modifying operations on files
     505
     506     def touch(self):
     507         """ Set the access/modified times of this file to the current time.
     508         Create the file if it does not exist.
     509         """
     510         fd = os.open(str(self), os.O_WRONLY | os.O_CREAT, 0666)
     511         os.close(fd)
     512         os.utime(str(self), None)
     513
     514     def remove(self):
     515         os.remove(str(self))
     516
     517     def copy(self, dst, copystat=False):
     518         """ Copy file from self to dst.
     519
     520         If copystat is False, copy data and mode bits ("cp self dst").
     521         If copystat is True, copy data and all stat info ("cp -p self dst").
     522
     523         The destination may be a directory. If so, a file with the same base
     524         name as self will be created in that directory.
     525         """
     526         dst = self.__class__(dst)
     527         if dst.stat().isdir:
     528             dst += self[-1]
     529         shutil.copyfile(str(self), str(dst))
     530         if copystat:
     531             shutil.copystat(str(self), str(dst))
     532         else:
     533             shutil.copymode(str(self), str(dst))
     534
     535     def move(self, dst):
     536         dst = self.__class__(dst)
     537         return shutil.move(str(self), str(dst))
     538
     539
     540     # --- Links
     541
     542     # In subclasses:
     543     # link (PosixPath, XXX MacPath)
     544     # writelink (PosixPath) - what about MacPath?
     545     # readlink (PosixPath, XXX MacPath)
     546     # readlinkpath (PosixPath, XXXMacPath)
     547
     548
     549     # --- Extra
     550
     551     # In subclasses:
     552     # mkfifo (PosixPath, XXX MacPath)
     553     # mknod (PosixPath, XXX MacPath)
     554     # chroot (PosixPath, XXX MacPath)
     555     #
     556     # startfile (NTPath)
     557
     558
     559     # --- Globbing
     560
     561     # If the OS supports it, _id should be a function that gets a stat object
     562     # and returns a unique id of a file.
     563     # It the OS doesn't support it, it should be None.
     564     _id = None
     565
     566     @staticmethod
     567     def _match_element(comp_element, element):
     568         # Does a filename match a compiled pattern element?
     569         # The filename should be normcased.
     570         if comp_element is None:
     571             return True
     572         elif isinstance(comp_element, str):
     573             return comp_element == element
     574         else:
     575             return comp_element.match(element)
     576
     577     def _glob(cls, pth, comp_pattern, topdown, onlydirs, onlyfiles,
     578               positions, on_path, stat):
     579         """ The recursive function used by glob.
     580
     581         This version treats symbolic links as files. Broken symlinks won't be
     582         listed.
     583
     584         pth is a dir in which we search.
     585
     586         comp_pattern is the compiled pattern. It's a sequence which should
     587         consist of three kinds of elements:
     588         * None - matches any number of subdirectories, including 0.
     589         * a string - a normalized name, when exactly one name can be matched.
     590         * a regexp - for testing if normalized names match.
     591
     592         positions is a sequence of positions on comp_pattern that children of
     593         path may match. On the first call, if will be [0].
     594
     595         on_path is a set of inode identifiers on path, or None if circles
     596         shouldn't be checked.
     597
     598         stat is the appropriate stat function - cls.stat or cls.lstat.
     599         """
     600
     601         if len(positions) == 1 and isinstance(comp_pattern[positions[0]], str):
     602             # We don't have to listdir if exactly one file name can match.
     603             # Since we always stat the files, it's ok if the file doesn't exist.
     604             listdir = [comp_pattern[positions[0]]]
     605         else:
     606             listdir = os.listdir(str(pth))
     607             listdir.sort()
     608
     609         for subfile in listdir:
     610             newpth = pth + subfile
     611             # We don't strictly need to stat a file if we don't follow symlinks
     612             # AND positions == [len(comp_pattern)-1] AND
     613             # not isinstance(comp_pattern[-1], str), but do me a favour...
     614             try:
     615                 st = stat(newpth)
     616             except OSError:
     617                 continue
     618             newpositions = []
     619             subfilenorm = cls.normcasestr(subfile)
     620
     621             if topdown:
     622                 # If not topdown, it happens after we handle subdirs
     623                 if positions[-1] == len(comp_pattern) - 1:
     624                     if cls._match_element(comp_pattern[-1], subfilenorm):
     625                         if not ((onlydirs and not st.isdir) or
     626                                 (onlyfiles and not st.isfile)):
     627                             yield newpth
     628
     629             for pos in reversed(positions):
     630                 if st.isdir:
     631                     comp_element = comp_pattern[pos]
     632                     if pos + 1 < len(comp_pattern):
     633                         if cls._match_element(comp_element, subfilenorm):
     634                             newpositions.append(pos + 1)
     635                             if comp_pattern[pos + 1] is None:
     636                                 # We should stop at '..'
     637                                 break
     638                     if comp_element is None:
     639                         newpositions.append(pos)
     640                         # We don't have to break - there are not supposed
     641                         # to be any positions before '..'.
     642
     643             if newpositions:
     644                 newpositions.reverse()
     645
     646                 if on_path is not None:
     647                     newpath_id = cls._id(st)
     648                     if newpath_id in on_path:
     649                         raise OSError, "Circular path encountered"
     650                     on_path.add(newpath_id)
     651
     652                 for x in cls._glob(newpth,
     653                                    comp_pattern, topdown, onlydirs, onlyfiles,
     654                                    newpositions, on_path, stat):
     655                     yield x
     656
     657                 if on_path is not None:
     658                     on_path.remove(newpath_id)
     659
     660             if not topdown:
     661                 # If topdown, it happens after we handle subdirs
     662                 if positions[-1] == len(comp_pattern) - 1:
     663                     if cls._match_element(comp_pattern[-1], subfilenorm):
     664                         if not ((onlydirs and not st.isdir) or
     665                                 (onlyfiles and not st.isfile)):
     666                             yield newpth
     667
     668     _magic_check = re.compile('[*?[]')
     669
     670     @classmethod
     671     def _has_magic(cls, s):
     672         return cls._magic_check.search(s) is not None
     673
     674     _cache = {}
     675
     676     @classmethod
     677     def _compile_pattern(cls, pattern):
     678         # Get a pattern, return the list of compiled pattern elements
     679         # and the list of initial positions.
     680         pattern = cls(pattern)
     681         if not pattern.isrel:
     682             raise ValueError, "pattern should be a relative path."
     683
     684         comp_pattern = []
     685         last_was_none = False
     686         for element in pattern:
     687             element = cls.normcasestr(element)
     688             if element == '**':
     689                 if not last_was_none:
     690                     comp_pattern.append(None)
     691             else:
     692                 last_was_none = False
     693                 if not cls._has_magic(element):
     694                     comp_pattern.append(element)
     695                 else:
     696                     try:
     697                         r = cls._cache[element]
     698                     except KeyError:
     699                         r = re.compile(fnmatch.translate(element))
     700                         cls._cache[element] = r
     701                     comp_pattern.append(r)
     702
     703         if comp_pattern[0] is None and len(comp_pattern) > 1:
     704             positions = [0, 1]
     705         else:
     706             positions = [0]
     707
     708         return comp_pattern, positions
     709
     710     def match(self, pattern):
     711         """ Return whether self matches the given pattern.
     712
     713         pattern has the same meaning as in the glob method.
     714         self should be relative.
     715
     716         This method doesn't use any system calls.
     717         """
     718         if not self.isrel:
     719             raise ValueError, "self must be a relative path"
     720         comp_pattern, positions = self._compile_pattern(pattern)
     721
     722         for element in self.normcase:
     723             newpositions = []
     724             for pos in reversed(positions):
     725                 if pos == len(comp_pattern):
     726                     # We matched the pattern but the path isn't finished -
     727                     # too bad
     728                     continue
     729                 comp_element = comp_pattern[pos]
     730                 if self._match_element(comp_element, element):
     731                     newpositions.append(pos + 1)
     732                 if comp_element is None:
     733                     newpositions.append(pos)
     734                     # No need to continue after a '**'
     735                     break
     736             newpositions.reverse()
     737             positions = newpositions
     738             if not positions:
     739                 # No point in carrying on
     740                 break
     741
     742         return (len(comp_pattern) in positions)
     743
     744     def glob(self, pattern='*', topdown=True, onlydirs=False, onlyfiles=False):
     745         """ Return an iterator over all files in self matching pattern.
     746
     747         pattern should be a relative path, which may include wildcards.
     748         In addition to the regular shell wildcards, you can use '**', which
     749         matches any number of directories, including 0.
     750
     751         If topdown is True (the default), a directory is yielded before its
     752         descendents. If it's False, a directory is yielded after its
     753         descendents.
     754
     755         If onlydirs is True, only directories will be yielded. If onlyfiles
     756         is True, only regular files will be yielded.
     757
     758         This method treats symbolic links as regular files. Broken symlinks
     759         won't be yielded.
     760         """
     761
     762         if onlydirs and onlyfiles:
     763             raise ValueError, \
                      "Only one of onlydirs and onlyfiles can be specified."
     764
     765         comp_pattern, positions = self._compile_pattern(pattern)
     766
     767         if self._id is not None and None in comp_pattern:
     768             on_path = set([self._id(self.stat())])
     769         else:
     770             on_path = None
     771
     772         for x in self._glob(self, comp_pattern, topdown, onlydirs, onlyfiles,
     773                             positions, on_path, self.__class__.stat):
     774             yield x
     775
     776     def lglob(self, pattern='*', topdown=True, onlydirs=False, onlyfiles=False):
     777         """ Return an iterator over all files in self matching pattern.
     778
     779         pattern should be a relative path, which may include wildcards.
     780         In addition to the regular shell wildcards, you can use '**', which
     781         matches any number of directories, including 0.
     782
     783         If topdown is True (the default), a directory is yielded before its
     784         descendents. If it's False, a directory is yielded after its
     785         descendents.
     786
     787         If onlydirs is True, only directories will be yielded. If onlyfiles
     788         is True, only regular files will be yielded.
     789
     790         This method treats symbolic links as special files - they won't be
     791         followed, and they will be yielded even if they're broken.
     792         """
     793
     794         if onlydirs and onlyfiles:
     795             raise ValueError, \
                      "Only one of onlydirs and onlyfiles can be specified."
     796
     797         comp_pattern, positions = self._compile_pattern(pattern)
     798
     799         for x in self._glob(self, comp_pattern, topdown, onlydirs, onlyfiles,
     800                             positions, None, self.__class__.lstat):
     801             yield x
     802
     803
     804 class PosixPath(BasePath):
     805     """ Represents POSIX paths. """
     806
     807     class _PosixRoot(BasePath._BaseRoot):
     808         """ Represents the filesystem root (/).
     809
     810         There's only one root on posix systems, so this is a singleton.
     811         """
     812         instance = None
     813         def __new__(cls):
     814             if cls.instance is None:
     815                 instance = object.__new__(cls)
     816                 cls.instance = instance
     817             return cls.instance
     818
     819         def __str__(self):
     820             return '/'
     821
     822         def __repr__(self):
     823             return 'path.ROOT'
     824
     825         isabs = True
     826
     827     _OSBaseRoot = _PosixRoot
     828
     829     ROOT = _PosixRoot()
     830
     831     # Public constants
     832     curdir = '.'
     833     pardir = '..'
     834
     835     # Private constants
     836     _sep = '/'
     837     _altsep = None
     838
     839     @classmethod
     840     def _parse_str(cls, pathstr):
     841         # get a path string and return an iterable over path elements.
     842         if pathstr.startswith('/'):
     843             if pathstr.startswith('//') and not pathstr.startswith('///'):
     844                 # Two initial slashes have application-specific meaning
     845                 # in POSIX, and it's not supported currently.
     846                 raise NotImplementedError, \
                          "Paths with two leading slashes aren't supported."
     847             yield cls.ROOT
     848         for element in pathstr.split('/'):
     849             if element == '' or element == cls.curdir:
     850                 continue
     851             # '..' aren't specially treated, since popping the last
     852             # element isn't correct if the last element was a symbolic
     853             # link.
     854             yield element
     855
     856
     857     # POSIX-specific methods
     858
     859
     860     # --- Info about the path
     861
     862     def statvfs(self):
     863         """ Perform a statvfs() system call on this path. """
     864         return os.statvfs(str(self))
     865
     866     def pathconf(self, name):
     867         return os.pathconf(str(self), name)
     868
     869     def samefile(self, other):
     870         other = self.__class__(other)
     871         s1 = self.stat()
     872         s2 = other.stat()
     873         return s1.st_ino == s2.st_ino and \
                   s1.st_dev == s2.st_dev
     874
     875
     876     # --- Modifying operations on files and directories
     877
     878     def chown(self, uid=None, gid=None):
     879         if uid is None:
     880             uid = -1
     881         if gid is None:
     882             gid = -1
     883         return os.chown(str(self), uid, gid)
     884
     885     def lchown(self, uid=None, gid=None):
     886         if uid is None:
     887             uid = -1
     888         if gid is None:
     889             gid = -1
     890         return os.lchown(str(self), uid, gid)
     891
     892
     893     # --- Links
     894
     895     def link(self, newpath):
     896         """ Create a hard link at 'newpath', pointing to this file. """
     897         os.link(str(self), str(newpath))
     898
     899     def writelink(self, src):
     900         """ Create a symbolic link at self, pointing to src.
     901
     902         src may be any string. Note that if it's a relative path, it
     903         will be interpreted relative to self, not relative to the current
     904         working directory.
     905         """
     906         os.symlink(str(src), str(self))
     907
     908     def readlink(self):
     909         """ Return the path to which this symbolic link points.
     910
     911         The result is a string, which may be an absolute path, a
     912         relative path (which should be interpreted relative to self[:-1]),
     913         or any arbitrary string.
     914         """
     915         return os.readlink(str(self))
     916
     917     def readlinkpath(self):
     918         """ Return the path to which this symbolic link points. """
     919         linkpath = self.__class__(self.readlink())
     920         if linkpath.isrel:
     921             return self + linkpath
     922         else:
     923             return linkpath
     924
     925
     926     # --- Extra
     927
     928     def mkfifo(self, *args):
     929         return os.mkfifo(str(self), *args)
     930
     931     def mknod(self, *args):
     932         return os.mknod(str(self), *args)
     933
     934     def chroot(self):
     935         return os.chroot(str(self))
     936
     937
     938     # --- Globbing
     939
     940     @staticmethod
     941     def _id(stat):
     942         return (stat.st_ino, stat.st_dev)
     943
     944
     945 class NTPath(BasePath):
     946     """ Represents paths on Windows operating systems. """
     947
     948     class _NTBaseRoot(BasePath._BaseRoot):
     949         """ The base class of all Windows root classes. """
     950         pass
     951
     952     _OSBaseRoot = _NTBaseRoot
     953
     954     class _CurRootType(_NTBaseRoot):
     955         """ Represents the root of the current working drive.
     956
     957         This class is a singleton. It represents the root of the current
     958         working drive - paths starting with '\'.
     959         """
     960         instance = None
     961         def __new__(cls):
     962             if cls.instance is None:
     963                 instance = object.__new__(cls)
     964                 cls.instance = instance
     965             return cls.instance
     966
     967         def __str__(self):
     968             return '\\'
     969
     970         def __repr__(self):
     971             return 'path.CURROOT'
     972
     973         isabs = False
     974
     975         def abspath(self):
     976             from nt import _getfullpathname
     977             return NTPath(_getfullpathname(str(self)))
     978
     979     CURROOT = _CurRootType()
     980
     981     class Drive(_NTBaseRoot):
     982         """ Represents the root of a specific drive. """
     983         def __init__(self, letter):
     984             # Drive letter is normalized - we don't lose any information
     985             if len(letter) != 1 or letter not in string.letters:
     986                 raise ValueError, 'Should get one letter'
     987             self._letter = letter.lower()
     988
     989         @property
     990         def letter(self):
     991             # We use a property because we want the object to be immutable.
     992             return self._letter
     993
     994         def __str__(self):
     995             return '%s:\\' % self.letter
     996
     997         def __repr__(self):
     998             return 'path.Drive(%r)' % self.letter
     999
    1000         isabs = True
    1001
    1002     class UnrootedDrive(_NTBaseRoot):
    1003         """ Represents the current working directory on a specific drive. """
    1004         def __init__(self, letter):
    1005             # Drive letter is normalized - we don't lose any information
    1006             if len(letter) != 1 or letter not in string.letters:
    1007                 raise ValueError, 'Should get one letter'
    1008             self._letter = letter.lower()
    1009
    1010         @property
    1011         def letter(self):
    1012             # We use a property because we want the object to be immutable.
    1013             return self._letter
    1014
    1015         def __str__(self):
    1016             return '%s:' % self.letter
    1017
    1018         def __repr__(self):
    1019             return 'path.UnrootedDrive(%r)' % self.letter
    1020
    1021         isabs = False
    1022
    1023         def abspath(self):
    1024             from nt import _getfullpathname
    1025             return NTPath(_getfullpathname(str(self)))
    1026
    1027     class UNCRoot(_NTBaseRoot):
    1028         """ Represents a UNC mount point. """
    1029         def __init__(self, host, mountpoint):
    1030             # Host and mountpoint are normalized - we don't lose any information
    1031             self._host = host.lower()
    1032             self._mountpoint = mountpoint.lower()
    1033
    1034         @property
    1035         def host(self):
    1036             # We use a property because we want the object to be immutable.
    1037             return self._host
    1038
    1039         @property
    1040         def mountpoint(self):
    1041             # We use a property because we want the object to be immutable.
    1042             return self._mountpoint
    1043
    1044         def __str__(self):
    1045             return '\\\\%s\\%s\\' % (self.host, self.mountpoint)
    1046
    1047         def __repr__(self):
    1048             return 'path.UNCRoot(%r, %r)' % (self.host, self.mountpoint)
    1049
    1050         isabs = True
    1051
    1052
    1053     # Public constants
    1054     curdir = '.'
    1055     pardir = '..'
    1056
    1057     # Private constants
    1058     _sep = '\\'
    1059     _altsep = '/'
    1060
    1061     @staticmethod
    1062     def normcasestr(string):
    1063         """ Normalize the case of one path element.
    1064
    1065         On Windows, this returns string.lower()
    1066         """
    1067         return string.lower()
    1068
    1069     @classmethod
    1070     def _parse_str(cls, pathstr):
    1071         # get a path string and return an iterable over path elements.
    1072
    1073         # First, replace all backslashes with slashes.
    1074         # I know that it should have been the other way round, but I can't
    1075         # stand all those escapes.
    1076
    1077         pathstr = pathstr.replace('\\', '/')
    1078
    1079         # Handle the root element
    1080
    1081         if pathstr.startswith('/'):
    1082             if pathstr.startswith('//'):
    1083                 # UNC Path
    1084                 if pathstr.startswith('///'):
    1085                     raise ValueError, \
                              "Paths can't start with more than two slashes"
    1086                 index = pathstr.find('/', 2)
    1087                 if index == -1:
    1088                     raise ValueError, \
                              "UNC host name should end with a slash"
    1089                 index2 = index+1
    1090                 while pathstr[index2:index2+1] == '/':
    1091                     index2 += 1
    1092                 if index2 == len(pathstr):
    1093                     raise ValueError, \
                              "UNC mount point is empty"
    1094                 index3 = pathstr.find('/', index2)
    1095                 if index3 == -1:
    1096                     index3 = len(pathstr)
    1097                 yield cls.UNCRoot(pathstr[2:index], pathstr[index2:index3])
    1098                 pathstr = pathstr[index3:]
    1099             else:
    1100                 # CURROOT
    1101                 yield cls.CURROOT
    1102         else:
    1103             if pathstr[1:2] == ':':
    1104                 if pathstr[2:3] == '/':
    1105                     # Rooted drive
    1106                     yield cls.Drive(pathstr[0])
    1107                     pathstr = pathstr[3:]
    1108                 else:
    1109                     # Unrooted drive
    1110                     yield cls.UnrootedDrive(pathstr[0])
    1111                     pathstr = pathstr[2:]
    1112
    1113         # Handle all other elements
    1114
    1115         for element in pathstr.split('/'):
    1116             if element == '' or element == cls.curdir:
    1117                 continue
    1118             # We don't treat pardir specially, since in the presence of
    1119             # links there's nothing to do about them.
    1120             # Windows doesn't have links, but why not keep path handling
    1121             # similiar?
    1122             yield element
    1123
    1124
    1125     # NT-specific methods
    1126
    1127     # --- Extra
    1128
    1129     def startfile(self):
    1130         return os.startfile(str(self))
    1131
    1132 if os.name == 'posix':
    1133     path = PosixPath
    1134 elif os.name == 'nt':
    1135     path = NTPath
    1136 else:
    1137     raise NotImplementedError, \
              "The path object is currently not implemented for OS %r" % os.name

EditText (last edited 2006-05-02 14:01:48 by NoamRaphael)