Home | History | Annotate | Download | only in Lib
      1 import fnmatch
      2 import functools
      3 import io
      4 import ntpath
      5 import os
      6 import posixpath
      7 import re
      8 import sys
      9 from collections import Sequence
     10 from contextlib import contextmanager
     11 from errno import EINVAL, ENOENT, ENOTDIR
     12 from operator import attrgetter
     13 from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
     14 from urllib.parse import quote_from_bytes as urlquote_from_bytes
     15 
     16 
     17 supports_symlinks = True
     18 if os.name == 'nt':
     19     import nt
     20     if sys.getwindowsversion()[:2] >= (6, 0):
     21         from nt import _getfinalpathname
     22     else:
     23         supports_symlinks = False
     24         _getfinalpathname = None
     25 else:
     26     nt = None
     27 
     28 
     29 __all__ = [
     30     "PurePath", "PurePosixPath", "PureWindowsPath",
     31     "Path", "PosixPath", "WindowsPath",
     32     ]
     33 
     34 #
     35 # Internals
     36 #
     37 
     38 def _is_wildcard_pattern(pat):
     39     # Whether this pattern needs actual matching using fnmatch, or can
     40     # be looked up directly as a file.
     41     return "*" in pat or "?" in pat or "[" in pat
     42 
     43 
     44 class _Flavour(object):
     45     """A flavour implements a particular (platform-specific) set of path
     46     semantics."""
     47 
     48     def __init__(self):
     49         self.join = self.sep.join
     50 
     51     def parse_parts(self, parts):
     52         parsed = []
     53         sep = self.sep
     54         altsep = self.altsep
     55         drv = root = ''
     56         it = reversed(parts)
     57         for part in it:
     58             if not part:
     59                 continue
     60             if altsep:
     61                 part = part.replace(altsep, sep)
     62             drv, root, rel = self.splitroot(part)
     63             if sep in rel:
     64                 for x in reversed(rel.split(sep)):
     65                     if x and x != '.':
     66                         parsed.append(sys.intern(x))
     67             else:
     68                 if rel and rel != '.':
     69                     parsed.append(sys.intern(rel))
     70             if drv or root:
     71                 if not drv:
     72                     # If no drive is present, try to find one in the previous
     73                     # parts. This makes the result of parsing e.g.
     74                     # ("C:", "/", "a") reasonably intuitive.
     75                     for part in it:
     76                         if not part:
     77                             continue
     78                         if altsep:
     79                             part = part.replace(altsep, sep)
     80                         drv = self.splitroot(part)[0]
     81                         if drv:
     82                             break
     83                 break
     84         if drv or root:
     85             parsed.append(drv + root)
     86         parsed.reverse()
     87         return drv, root, parsed
     88 
     89     def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
     90         """
     91         Join the two paths represented by the respective
     92         (drive, root, parts) tuples.  Return a new (drive, root, parts) tuple.
     93         """
     94         if root2:
     95             if not drv2 and drv:
     96                 return drv, root2, [drv + root2] + parts2[1:]
     97         elif drv2:
     98             if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
     99                 # Same drive => second path is relative to the first
    100                 return drv, root, parts + parts2[1:]
    101         else:
    102             # Second path is non-anchored (common case)
    103             return drv, root, parts + parts2
    104         return drv2, root2, parts2
    105 
    106 
    107 class _WindowsFlavour(_Flavour):
    108     # Reference for Windows paths can be found at
    109     # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
    110 
    111     sep = '\\'
    112     altsep = '/'
    113     has_drv = True
    114     pathmod = ntpath
    115 
    116     is_supported = (os.name == 'nt')
    117 
    118     drive_letters = (
    119         set(chr(x) for x in range(ord('a'), ord('z') + 1)) |
    120         set(chr(x) for x in range(ord('A'), ord('Z') + 1))
    121     )
    122     ext_namespace_prefix = '\\\\?\\'
    123 
    124     reserved_names = (
    125         {'CON', 'PRN', 'AUX', 'NUL'} |
    126         {'COM%d' % i for i in range(1, 10)} |
    127         {'LPT%d' % i for i in range(1, 10)}
    128         )
    129 
    130     # Interesting findings about extended paths:
    131     # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
    132     #   but '\\?\c:/a' is not
    133     # - extended paths are always absolute; "relative" extended paths will
    134     #   fail.
    135 
    136     def splitroot(self, part, sep=sep):
    137         first = part[0:1]
    138         second = part[1:2]
    139         if (second == sep and first == sep):
    140             # XXX extended paths should also disable the collapsing of "."
    141             # components (according to MSDN docs).
    142             prefix, part = self._split_extended_path(part)
    143             first = part[0:1]
    144             second = part[1:2]
    145         else:
    146             prefix = ''
    147         third = part[2:3]
    148         if (second == sep and first == sep and third != sep):
    149             # is a UNC path:
    150             # vvvvvvvvvvvvvvvvvvvvv root
    151             # \\machine\mountpoint\directory\etc\...
    152             #            directory ^^^^^^^^^^^^^^
    153             index = part.find(sep, 2)
    154             if index != -1:
    155                 index2 = part.find(sep, index + 1)
    156                 # a UNC path can't have two slashes in a row
    157                 # (after the initial two)
    158                 if index2 != index + 1:
    159                     if index2 == -1:
    160                         index2 = len(part)
    161                     if prefix:
    162                         return prefix + part[1:index2], sep, part[index2+1:]
    163                     else:
    164                         return part[:index2], sep, part[index2+1:]
    165         drv = root = ''
    166         if second == ':' and first in self.drive_letters:
    167             drv = part[:2]
    168             part = part[2:]
    169             first = third
    170         if first == sep:
    171             root = first
    172             part = part.lstrip(sep)
    173         return prefix + drv, root, part
    174 
    175     def casefold(self, s):
    176         return s.lower()
    177 
    178     def casefold_parts(self, parts):
    179         return [p.lower() for p in parts]
    180 
    181     def resolve(self, path, strict=False):
    182         s = str(path)
    183         if not s:
    184             return os.getcwd()
    185         previous_s = None
    186         if _getfinalpathname is not None:
    187             if strict:
    188                 return self._ext_to_normal(_getfinalpathname(s))
    189             else:
    190                 while True:
    191                     try:
    192                         s = self._ext_to_normal(_getfinalpathname(s))
    193                     except FileNotFoundError:
    194                         previous_s = s
    195                         s = os.path.dirname(s)
    196                         if previous_s == s:
    197                             return path
    198                     else:
    199                         if previous_s is None:
    200                             return s
    201                         else:
    202                             return s + os.path.sep + os.path.basename(previous_s)
    203         # Means fallback on absolute
    204         return None
    205 
    206     def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
    207         prefix = ''
    208         if s.startswith(ext_prefix):
    209             prefix = s[:4]
    210             s = s[4:]
    211             if s.startswith('UNC\\'):
    212                 prefix += s[:3]
    213                 s = '\\' + s[3:]
    214         return prefix, s
    215 
    216     def _ext_to_normal(self, s):
    217         # Turn back an extended path into a normal DOS-like path
    218         return self._split_extended_path(s)[1]
    219 
    220     def is_reserved(self, parts):
    221         # NOTE: the rules for reserved names seem somewhat complicated
    222         # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
    223         # We err on the side of caution and return True for paths which are
    224         # not considered reserved by Windows.
    225         if not parts:
    226             return False
    227         if parts[0].startswith('\\\\'):
    228             # UNC paths are never reserved
    229             return False
    230         return parts[-1].partition('.')[0].upper() in self.reserved_names
    231 
    232     def make_uri(self, path):
    233         # Under Windows, file URIs use the UTF-8 encoding.
    234         drive = path.drive
    235         if len(drive) == 2 and drive[1] == ':':
    236             # It's a path on a local drive => 'file:///c:/a/b'
    237             rest = path.as_posix()[2:].lstrip('/')
    238             return 'file:///%s/%s' % (
    239                 drive, urlquote_from_bytes(rest.encode('utf-8')))
    240         else:
    241             # It's a path on a network drive => 'file://host/share/a/b'
    242             return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
    243 
    244     def gethomedir(self, username):
    245         if 'HOME' in os.environ:
    246             userhome = os.environ['HOME']
    247         elif 'USERPROFILE' in os.environ:
    248             userhome = os.environ['USERPROFILE']
    249         elif 'HOMEPATH' in os.environ:
    250             try:
    251                 drv = os.environ['HOMEDRIVE']
    252             except KeyError:
    253                 drv = ''
    254             userhome = drv + os.environ['HOMEPATH']
    255         else:
    256             raise RuntimeError("Can't determine home directory")
    257 
    258         if username:
    259             # Try to guess user home directory.  By default all users
    260             # directories are located in the same place and are named by
    261             # corresponding usernames.  If current user home directory points
    262             # to nonstandard place, this guess is likely wrong.
    263             if os.environ['USERNAME'] != username:
    264                 drv, root, parts = self.parse_parts((userhome,))
    265                 if parts[-1] != os.environ['USERNAME']:
    266                     raise RuntimeError("Can't determine home directory "
    267                                        "for %r" % username)
    268                 parts[-1] = username
    269                 if drv or root:
    270                     userhome = drv + root + self.join(parts[1:])
    271                 else:
    272                     userhome = self.join(parts)
    273         return userhome
    274 
    275 class _PosixFlavour(_Flavour):
    276     sep = '/'
    277     altsep = ''
    278     has_drv = False
    279     pathmod = posixpath
    280 
    281     is_supported = (os.name != 'nt')
    282 
    283     def splitroot(self, part, sep=sep):
    284         if part and part[0] == sep:
    285             stripped_part = part.lstrip(sep)
    286             # According to POSIX path resolution:
    287             # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
    288             # "A pathname that begins with two successive slashes may be
    289             # interpreted in an implementation-defined manner, although more
    290             # than two leading slashes shall be treated as a single slash".
    291             if len(part) - len(stripped_part) == 2:
    292                 return '', sep * 2, stripped_part
    293             else:
    294                 return '', sep, stripped_part
    295         else:
    296             return '', '', part
    297 
    298     def casefold(self, s):
    299         return s
    300 
    301     def casefold_parts(self, parts):
    302         return parts
    303 
    304     def resolve(self, path, strict=False):
    305         sep = self.sep
    306         accessor = path._accessor
    307         seen = {}
    308         def _resolve(path, rest):
    309             if rest.startswith(sep):
    310                 path = ''
    311 
    312             for name in rest.split(sep):
    313                 if not name or name == '.':
    314                     # current dir
    315                     continue
    316                 if name == '..':
    317                     # parent dir
    318                     path, _, _ = path.rpartition(sep)
    319                     continue
    320                 newpath = path + sep + name
    321                 if newpath in seen:
    322                     # Already seen this path
    323                     path = seen[newpath]
    324                     if path is not None:
    325                         # use cached value
    326                         continue
    327                     # The symlink is not resolved, so we must have a symlink loop.
    328                     raise RuntimeError("Symlink loop from %r" % newpath)
    329                 # Resolve the symbolic link
    330                 try:
    331                     target = accessor.readlink(newpath)
    332                 except OSError as e:
    333                     if e.errno != EINVAL:
    334                         if strict:
    335                             raise
    336                         else:
    337                             return newpath
    338                     # Not a symlink
    339                     path = newpath
    340                 else:
    341                     seen[newpath] = None # not resolved symlink
    342                     path = _resolve(path, target)
    343                     seen[newpath] = path # resolved symlink
    344 
    345             return path
    346         # NOTE: according to POSIX, getcwd() cannot contain path components
    347         # which are symlinks.
    348         base = '' if path.is_absolute() else os.getcwd()
    349         return _resolve(base, str(path)) or sep
    350 
    351     def is_reserved(self, parts):
    352         return False
    353 
    354     def make_uri(self, path):
    355         # We represent the path using the local filesystem encoding,
    356         # for portability to other applications.
    357         bpath = bytes(path)
    358         return 'file://' + urlquote_from_bytes(bpath)
    359 
    360     def gethomedir(self, username):
    361         if not username:
    362             try:
    363                 return os.environ['HOME']
    364             except KeyError:
    365                 import pwd
    366                 return pwd.getpwuid(os.getuid()).pw_dir
    367         else:
    368             import pwd
    369             try:
    370                 return pwd.getpwnam(username).pw_dir
    371             except KeyError:
    372                 raise RuntimeError("Can't determine home directory "
    373                                    "for %r" % username)
    374 
    375 
    376 _windows_flavour = _WindowsFlavour()
    377 _posix_flavour = _PosixFlavour()
    378 
    379 
    380 class _Accessor:
    381     """An accessor implements a particular (system-specific or not) way of
    382     accessing paths on the filesystem."""
    383 
    384 
    385 class _NormalAccessor(_Accessor):
    386 
    387     def _wrap_strfunc(strfunc):
    388         @functools.wraps(strfunc)
    389         def wrapped(pathobj, *args):
    390             return strfunc(str(pathobj), *args)
    391         return staticmethod(wrapped)
    392 
    393     def _wrap_binary_strfunc(strfunc):
    394         @functools.wraps(strfunc)
    395         def wrapped(pathobjA, pathobjB, *args):
    396             return strfunc(str(pathobjA), str(pathobjB), *args)
    397         return staticmethod(wrapped)
    398 
    399     stat = _wrap_strfunc(os.stat)
    400 
    401     lstat = _wrap_strfunc(os.lstat)
    402 
    403     open = _wrap_strfunc(os.open)
    404 
    405     listdir = _wrap_strfunc(os.listdir)
    406 
    407     scandir = _wrap_strfunc(os.scandir)
    408 
    409     chmod = _wrap_strfunc(os.chmod)
    410 
    411     if hasattr(os, "lchmod"):
    412         lchmod = _wrap_strfunc(os.lchmod)
    413     else:
    414         def lchmod(self, pathobj, mode):
    415             raise NotImplementedError("lchmod() not available on this system")
    416 
    417     mkdir = _wrap_strfunc(os.mkdir)
    418 
    419     unlink = _wrap_strfunc(os.unlink)
    420 
    421     rmdir = _wrap_strfunc(os.rmdir)
    422 
    423     rename = _wrap_binary_strfunc(os.rename)
    424 
    425     replace = _wrap_binary_strfunc(os.replace)
    426 
    427     if nt:
    428         if supports_symlinks:
    429             symlink = _wrap_binary_strfunc(os.symlink)
    430         else:
    431             def symlink(a, b, target_is_directory):
    432                 raise NotImplementedError("symlink() not available on this system")
    433     else:
    434         # Under POSIX, os.symlink() takes two args
    435         @staticmethod
    436         def symlink(a, b, target_is_directory):
    437             return os.symlink(str(a), str(b))
    438 
    439     utime = _wrap_strfunc(os.utime)
    440 
    441     # Helper for resolve()
    442     def readlink(self, path):
    443         return os.readlink(path)
    444 
    445 
    446 _normal_accessor = _NormalAccessor()
    447 
    448 
    449 #
    450 # Globbing helpers
    451 #
    452 
    453 def _make_selector(pattern_parts):
    454     pat = pattern_parts[0]
    455     child_parts = pattern_parts[1:]
    456     if pat == '**':
    457         cls = _RecursiveWildcardSelector
    458     elif '**' in pat:
    459         raise ValueError("Invalid pattern: '**' can only be an entire path component")
    460     elif _is_wildcard_pattern(pat):
    461         cls = _WildcardSelector
    462     else:
    463         cls = _PreciseSelector
    464     return cls(pat, child_parts)
    465 
    466 if hasattr(functools, "lru_cache"):
    467     _make_selector = functools.lru_cache()(_make_selector)
    468 
    469 
    470 class _Selector:
    471     """A selector matches a specific glob pattern part against the children
    472     of a given path."""
    473 
    474     def __init__(self, child_parts):
    475         self.child_parts = child_parts
    476         if child_parts:
    477             self.successor = _make_selector(child_parts)
    478             self.dironly = True
    479         else:
    480             self.successor = _TerminatingSelector()
    481             self.dironly = False
    482 
    483     def select_from(self, parent_path):
    484         """Iterate over all child paths of `parent_path` matched by this
    485         selector.  This can contain parent_path itself."""
    486         path_cls = type(parent_path)
    487         is_dir = path_cls.is_dir
    488         exists = path_cls.exists
    489         scandir = parent_path._accessor.scandir
    490         if not is_dir(parent_path):
    491             return iter([])
    492         return self._select_from(parent_path, is_dir, exists, scandir)
    493 
    494 
    495 class _TerminatingSelector:
    496 
    497     def _select_from(self, parent_path, is_dir, exists, scandir):
    498         yield parent_path
    499 
    500 
    501 class _PreciseSelector(_Selector):
    502 
    503     def __init__(self, name, child_parts):
    504         self.name = name
    505         _Selector.__init__(self, child_parts)
    506 
    507     def _select_from(self, parent_path, is_dir, exists, scandir):
    508         try:
    509             path = parent_path._make_child_relpath(self.name)
    510             if (is_dir if self.dironly else exists)(path):
    511                 for p in self.successor._select_from(path, is_dir, exists, scandir):
    512                     yield p
    513         except PermissionError:
    514             return
    515 
    516 
    517 class _WildcardSelector(_Selector):
    518 
    519     def __init__(self, pat, child_parts):
    520         self.pat = re.compile(fnmatch.translate(pat))
    521         _Selector.__init__(self, child_parts)
    522 
    523     def _select_from(self, parent_path, is_dir, exists, scandir):
    524         try:
    525             cf = parent_path._flavour.casefold
    526             entries = list(scandir(parent_path))
    527             for entry in entries:
    528                 if not self.dironly or entry.is_dir():
    529                     name = entry.name
    530                     casefolded = cf(name)
    531                     if self.pat.match(casefolded):
    532                         path = parent_path._make_child_relpath(name)
    533                         for p in self.successor._select_from(path, is_dir, exists, scandir):
    534                             yield p
    535         except PermissionError:
    536             return
    537 
    538 
    539 
    540 class _RecursiveWildcardSelector(_Selector):
    541 
    542     def __init__(self, pat, child_parts):
    543         _Selector.__init__(self, child_parts)
    544 
    545     def _iterate_directories(self, parent_path, is_dir, scandir):
    546         yield parent_path
    547         try:
    548             entries = list(scandir(parent_path))
    549             for entry in entries:
    550                 if entry.is_dir() and not entry.is_symlink():
    551                     path = parent_path._make_child_relpath(entry.name)
    552                     for p in self._iterate_directories(path, is_dir, scandir):
    553                         yield p
    554         except PermissionError:
    555             return
    556 
    557     def _select_from(self, parent_path, is_dir, exists, scandir):
    558         try:
    559             yielded = set()
    560             try:
    561                 successor_select = self.successor._select_from
    562                 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
    563                     for p in successor_select(starting_point, is_dir, exists, scandir):
    564                         if p not in yielded:
    565                             yield p
    566                             yielded.add(p)
    567             finally:
    568                 yielded.clear()
    569         except PermissionError:
    570             return
    571 
    572 
    573 #
    574 # Public API
    575 #
    576 
    577 class _PathParents(Sequence):
    578     """This object provides sequence-like access to the logical ancestors
    579     of a path.  Don't try to construct it yourself."""
    580     __slots__ = ('_pathcls', '_drv', '_root', '_parts')
    581 
    582     def __init__(self, path):
    583         # We don't store the instance to avoid reference cycles
    584         self._pathcls = type(path)
    585         self._drv = path._drv
    586         self._root = path._root
    587         self._parts = path._parts
    588 
    589     def __len__(self):
    590         if self._drv or self._root:
    591             return len(self._parts) - 1
    592         else:
    593             return len(self._parts)
    594 
    595     def __getitem__(self, idx):
    596         if idx < 0 or idx >= len(self):
    597             raise IndexError(idx)
    598         return self._pathcls._from_parsed_parts(self._drv, self._root,
    599                                                 self._parts[:-idx - 1])
    600 
    601     def __repr__(self):
    602         return "<{}.parents>".format(self._pathcls.__name__)
    603 
    604 
    605 class PurePath(object):
    606     """PurePath represents a filesystem path and offers operations which
    607     don't imply any actual filesystem I/O.  Depending on your system,
    608     instantiating a PurePath will return either a PurePosixPath or a
    609     PureWindowsPath object.  You can also instantiate either of these classes
    610     directly, regardless of your system.
    611     """
    612     __slots__ = (
    613         '_drv', '_root', '_parts',
    614         '_str', '_hash', '_pparts', '_cached_cparts',
    615     )
    616 
    617     def __new__(cls, *args):
    618         """Construct a PurePath from one or several strings and or existing
    619         PurePath objects.  The strings and path objects are combined so as
    620         to yield a canonicalized path, which is incorporated into the
    621         new PurePath object.
    622         """
    623         if cls is PurePath:
    624             cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
    625         return cls._from_parts(args)
    626 
    627     def __reduce__(self):
    628         # Using the parts tuple helps share interned path parts
    629         # when pickling related paths.
    630         return (self.__class__, tuple(self._parts))
    631 
    632     @classmethod
    633     def _parse_args(cls, args):
    634         # This is useful when you don't want to create an instance, just
    635         # canonicalize some constructor arguments.
    636         parts = []
    637         for a in args:
    638             if isinstance(a, PurePath):
    639                 parts += a._parts
    640             else:
    641                 a = os.fspath(a)
    642                 if isinstance(a, str):
    643                     # Force-cast str subclasses to str (issue #21127)
    644                     parts.append(str(a))
    645                 else:
    646                     raise TypeError(
    647                         "argument should be a str object or an os.PathLike "
    648                         "object returning str, not %r"
    649                         % type(a))
    650         return cls._flavour.parse_parts(parts)
    651 
    652     @classmethod
    653     def _from_parts(cls, args, init=True):
    654         # We need to call _parse_args on the instance, so as to get the
    655         # right flavour.
    656         self = object.__new__(cls)
    657         drv, root, parts = self._parse_args(args)
    658         self._drv = drv
    659         self._root = root
    660         self._parts = parts
    661         if init:
    662             self._init()
    663         return self
    664 
    665     @classmethod
    666     def _from_parsed_parts(cls, drv, root, parts, init=True):
    667         self = object.__new__(cls)
    668         self._drv = drv
    669         self._root = root
    670         self._parts = parts
    671         if init:
    672             self._init()
    673         return self
    674 
    675     @classmethod
    676     def _format_parsed_parts(cls, drv, root, parts):
    677         if drv or root:
    678             return drv + root + cls._flavour.join(parts[1:])
    679         else:
    680             return cls._flavour.join(parts)
    681 
    682     def _init(self):
    683         # Overridden in concrete Path
    684         pass
    685 
    686     def _make_child(self, args):
    687         drv, root, parts = self._parse_args(args)
    688         drv, root, parts = self._flavour.join_parsed_parts(
    689             self._drv, self._root, self._parts, drv, root, parts)
    690         return self._from_parsed_parts(drv, root, parts)
    691 
    692     def __str__(self):
    693         """Return the string representation of the path, suitable for
    694         passing to system calls."""
    695         try:
    696             return self._str
    697         except AttributeError:
    698             self._str = self._format_parsed_parts(self._drv, self._root,
    699                                                   self._parts) or '.'
    700             return self._str
    701 
    702     def __fspath__(self):
    703         return str(self)
    704 
    705     def as_posix(self):
    706         """Return the string representation of the path with forward (/)
    707         slashes."""
    708         f = self._flavour
    709         return str(self).replace(f.sep, '/')
    710 
    711     def __bytes__(self):
    712         """Return the bytes representation of the path.  This is only
    713         recommended to use under Unix."""
    714         return os.fsencode(str(self))
    715 
    716     def __repr__(self):
    717         return "{}({!r})".format(self.__class__.__name__, self.as_posix())
    718 
    719     def as_uri(self):
    720         """Return the path as a 'file' URI."""
    721         if not self.is_absolute():
    722             raise ValueError("relative path can't be expressed as a file URI")
    723         return self._flavour.make_uri(self)
    724 
    725     @property
    726     def _cparts(self):
    727         # Cached casefolded parts, for hashing and comparison
    728         try:
    729             return self._cached_cparts
    730         except AttributeError:
    731             self._cached_cparts = self._flavour.casefold_parts(self._parts)
    732             return self._cached_cparts
    733 
    734     def __eq__(self, other):
    735         if not isinstance(other, PurePath):
    736             return NotImplemented
    737         return self._cparts == other._cparts and self._flavour is other._flavour
    738 
    739     def __hash__(self):
    740         try:
    741             return self._hash
    742         except AttributeError:
    743             self._hash = hash(tuple(self._cparts))
    744             return self._hash
    745 
    746     def __lt__(self, other):
    747         if not isinstance(other, PurePath) or self._flavour is not other._flavour:
    748             return NotImplemented
    749         return self._cparts < other._cparts
    750 
    751     def __le__(self, other):
    752         if not isinstance(other, PurePath) or self._flavour is not other._flavour:
    753             return NotImplemented
    754         return self._cparts <= other._cparts
    755 
    756     def __gt__(self, other):
    757         if not isinstance(other, PurePath) or self._flavour is not other._flavour:
    758             return NotImplemented
    759         return self._cparts > other._cparts
    760 
    761     def __ge__(self, other):
    762         if not isinstance(other, PurePath) or self._flavour is not other._flavour:
    763             return NotImplemented
    764         return self._cparts >= other._cparts
    765 
    766     drive = property(attrgetter('_drv'),
    767                      doc="""The drive prefix (letter or UNC path), if any.""")
    768 
    769     root = property(attrgetter('_root'),
    770                     doc="""The root of the path, if any.""")
    771 
    772     @property
    773     def anchor(self):
    774         """The concatenation of the drive and root, or ''."""
    775         anchor = self._drv + self._root
    776         return anchor
    777 
    778     @property
    779     def name(self):
    780         """The final path component, if any."""
    781         parts = self._parts
    782         if len(parts) == (1 if (self._drv or self._root) else 0):
    783             return ''
    784         return parts[-1]
    785 
    786     @property
    787     def suffix(self):
    788         """The final component's last suffix, if any."""
    789         name = self.name
    790         i = name.rfind('.')
    791         if 0 < i < len(name) - 1:
    792             return name[i:]
    793         else:
    794             return ''
    795 
    796     @property
    797     def suffixes(self):
    798         """A list of the final component's suffixes, if any."""
    799         name = self.name
    800         if name.endswith('.'):
    801             return []
    802         name = name.lstrip('.')
    803         return ['.' + suffix for suffix in name.split('.')[1:]]
    804 
    805     @property
    806     def stem(self):
    807         """The final path component, minus its last suffix."""
    808         name = self.name
    809         i = name.rfind('.')
    810         if 0 < i < len(name) - 1:
    811             return name[:i]
    812         else:
    813             return name
    814 
    815     def with_name(self, name):
    816         """Return a new path with the file name changed."""
    817         if not self.name:
    818             raise ValueError("%r has an empty name" % (self,))
    819         drv, root, parts = self._flavour.parse_parts((name,))
    820         if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
    821             or drv or root or len(parts) != 1):
    822             raise ValueError("Invalid name %r" % (name))
    823         return self._from_parsed_parts(self._drv, self._root,
    824                                        self._parts[:-1] + [name])
    825 
    826     def with_suffix(self, suffix):
    827         """Return a new path with the file suffix changed (or added, if none)."""
    828         # XXX if suffix is None, should the current suffix be removed?
    829         f = self._flavour
    830         if f.sep in suffix or f.altsep and f.altsep in suffix:
    831             raise ValueError("Invalid suffix %r" % (suffix))
    832         if suffix and not suffix.startswith('.') or suffix == '.':
    833             raise ValueError("Invalid suffix %r" % (suffix))
    834         name = self.name
    835         if not name:
    836             raise ValueError("%r has an empty name" % (self,))
    837         old_suffix = self.suffix
    838         if not old_suffix:
    839             name = name + suffix
    840         else:
    841             name = name[:-len(old_suffix)] + suffix
    842         return self._from_parsed_parts(self._drv, self._root,
    843                                        self._parts[:-1] + [name])
    844 
    845     def relative_to(self, *other):
    846         """Return the relative path to another path identified by the passed
    847         arguments.  If the operation is not possible (because this is not
    848         a subpath of the other path), raise ValueError.
    849         """
    850         # For the purpose of this method, drive and root are considered
    851         # separate parts, i.e.:
    852         #   Path('c:/').relative_to('c:')  gives Path('/')
    853         #   Path('c:/').relative_to('/')   raise ValueError
    854         if not other:
    855             raise TypeError("need at least one argument")
    856         parts = self._parts
    857         drv = self._drv
    858         root = self._root
    859         if root:
    860             abs_parts = [drv, root] + parts[1:]
    861         else:
    862             abs_parts = parts
    863         to_drv, to_root, to_parts = self._parse_args(other)
    864         if to_root:
    865             to_abs_parts = [to_drv, to_root] + to_parts[1:]
    866         else:
    867             to_abs_parts = to_parts
    868         n = len(to_abs_parts)
    869         cf = self._flavour.casefold_parts
    870         if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
    871             formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
    872             raise ValueError("{!r} does not start with {!r}"
    873                              .format(str(self), str(formatted)))
    874         return self._from_parsed_parts('', root if n == 1 else '',
    875                                        abs_parts[n:])
    876 
    877     @property
    878     def parts(self):
    879         """An object providing sequence-like access to the
    880         components in the filesystem path."""
    881         # We cache the tuple to avoid building a new one each time .parts
    882         # is accessed.  XXX is this necessary?
    883         try:
    884             return self._pparts
    885         except AttributeError:
    886             self._pparts = tuple(self._parts)
    887             return self._pparts
    888 
    889     def joinpath(self, *args):
    890         """Combine this path with one or several arguments, and return a
    891         new path representing either a subpath (if all arguments are relative
    892         paths) or a totally different path (if one of the arguments is
    893         anchored).
    894         """
    895         return self._make_child(args)
    896 
    897     def __truediv__(self, key):
    898         return self._make_child((key,))
    899 
    900     def __rtruediv__(self, key):
    901         return self._from_parts([key] + self._parts)
    902 
    903     @property
    904     def parent(self):
    905         """The logical parent of the path."""
    906         drv = self._drv
    907         root = self._root
    908         parts = self._parts
    909         if len(parts) == 1 and (drv or root):
    910             return self
    911         return self._from_parsed_parts(drv, root, parts[:-1])
    912 
    913     @property
    914     def parents(self):
    915         """A sequence of this path's logical parents."""
    916         return _PathParents(self)
    917 
    918     def is_absolute(self):
    919         """True if the path is absolute (has both a root and, if applicable,
    920         a drive)."""
    921         if not self._root:
    922             return False
    923         return not self._flavour.has_drv or bool(self._drv)
    924 
    925     def is_reserved(self):
    926         """Return True if the path contains one of the special names reserved
    927         by the system, if any."""
    928         return self._flavour.is_reserved(self._parts)
    929 
    930     def match(self, path_pattern):
    931         """
    932         Return True if this path matches the given pattern.
    933         """
    934         cf = self._flavour.casefold
    935         path_pattern = cf(path_pattern)
    936         drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
    937         if not pat_parts:
    938             raise ValueError("empty pattern")
    939         if drv and drv != cf(self._drv):
    940             return False
    941         if root and root != cf(self._root):
    942             return False
    943         parts = self._cparts
    944         if drv or root:
    945             if len(pat_parts) != len(parts):
    946                 return False
    947             pat_parts = pat_parts[1:]
    948         elif len(pat_parts) > len(parts):
    949             return False
    950         for part, pat in zip(reversed(parts), reversed(pat_parts)):
    951             if not fnmatch.fnmatchcase(part, pat):
    952                 return False
    953         return True
    954 
    955 # Can't subclass os.PathLike from PurePath and keep the constructor
    956 # optimizations in PurePath._parse_args().
    957 os.PathLike.register(PurePath)
    958 
    959 
    960 class PurePosixPath(PurePath):
    961     _flavour = _posix_flavour
    962     __slots__ = ()
    963 
    964 
    965 class PureWindowsPath(PurePath):
    966     _flavour = _windows_flavour
    967     __slots__ = ()
    968 
    969 
    970 # Filesystem-accessing classes
    971 
    972 
    973 class Path(PurePath):
    974     __slots__ = (
    975         '_accessor',
    976         '_closed',
    977     )
    978 
    979     def __new__(cls, *args, **kwargs):
    980         if cls is Path:
    981             cls = WindowsPath if os.name == 'nt' else PosixPath
    982         self = cls._from_parts(args, init=False)
    983         if not self._flavour.is_supported:
    984             raise NotImplementedError("cannot instantiate %r on your system"
    985                                       % (cls.__name__,))
    986         self._init()
    987         return self
    988 
    989     def _init(self,
    990               # Private non-constructor arguments
    991               template=None,
    992               ):
    993         self._closed = False
    994         if template is not None:
    995             self._accessor = template._accessor
    996         else:
    997             self._accessor = _normal_accessor
    998 
    999     def _make_child_relpath(self, part):
   1000         # This is an optimization used for dir walking.  `part` must be
   1001         # a single part relative to this path.
   1002         parts = self._parts + [part]
   1003         return self._from_parsed_parts(self._drv, self._root, parts)
   1004 
   1005     def __enter__(self):
   1006         if self._closed:
   1007             self._raise_closed()
   1008         return self
   1009 
   1010     def __exit__(self, t, v, tb):
   1011         self._closed = True
   1012 
   1013     def _raise_closed(self):
   1014         raise ValueError("I/O operation on closed path")
   1015 
   1016     def _opener(self, name, flags, mode=0o666):
   1017         # A stub for the opener argument to built-in open()
   1018         return self._accessor.open(self, flags, mode)
   1019 
   1020     def _raw_open(self, flags, mode=0o777):
   1021         """
   1022         Open the file pointed by this path and return a file descriptor,
   1023         as os.open() does.
   1024         """
   1025         if self._closed:
   1026             self._raise_closed()
   1027         return self._accessor.open(self, flags, mode)
   1028 
   1029     # Public API
   1030 
   1031     @classmethod
   1032     def cwd(cls):
   1033         """Return a new path pointing to the current working directory
   1034         (as returned by os.getcwd()).
   1035         """
   1036         return cls(os.getcwd())
   1037 
   1038     @classmethod
   1039     def home(cls):
   1040         """Return a new path pointing to the user's home directory (as
   1041         returned by os.path.expanduser('~')).
   1042         """
   1043         return cls(cls()._flavour.gethomedir(None))
   1044 
   1045     def samefile(self, other_path):
   1046         """Return whether other_path is the same or not as this file
   1047         (as returned by os.path.samefile()).
   1048         """
   1049         st = self.stat()
   1050         try:
   1051             other_st = other_path.stat()
   1052         except AttributeError:
   1053             other_st = os.stat(other_path)
   1054         return os.path.samestat(st, other_st)
   1055 
   1056     def iterdir(self):
   1057         """Iterate over the files in this directory.  Does not yield any
   1058         result for the special paths '.' and '..'.
   1059         """
   1060         if self._closed:
   1061             self._raise_closed()
   1062         for name in self._accessor.listdir(self):
   1063             if name in {'.', '..'}:
   1064                 # Yielding a path object for these makes little sense
   1065                 continue
   1066             yield self._make_child_relpath(name)
   1067             if self._closed:
   1068                 self._raise_closed()
   1069 
   1070     def glob(self, pattern):
   1071         """Iterate over this subtree and yield all existing files (of any
   1072         kind, including directories) matching the given pattern.
   1073         """
   1074         if not pattern:
   1075             raise ValueError("Unacceptable pattern: {!r}".format(pattern))
   1076         pattern = self._flavour.casefold(pattern)
   1077         drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
   1078         if drv or root:
   1079             raise NotImplementedError("Non-relative patterns are unsupported")
   1080         selector = _make_selector(tuple(pattern_parts))
   1081         for p in selector.select_from(self):
   1082             yield p
   1083 
   1084     def rglob(self, pattern):
   1085         """Recursively yield all existing files (of any kind, including
   1086         directories) matching the given pattern, anywhere in this subtree.
   1087         """
   1088         pattern = self._flavour.casefold(pattern)
   1089         drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
   1090         if drv or root:
   1091             raise NotImplementedError("Non-relative patterns are unsupported")
   1092         selector = _make_selector(("**",) + tuple(pattern_parts))
   1093         for p in selector.select_from(self):
   1094             yield p
   1095 
   1096     def absolute(self):
   1097         """Return an absolute version of this path.  This function works
   1098         even if the path doesn't point to anything.
   1099 
   1100         No normalization is done, i.e. all '.' and '..' will be kept along.
   1101         Use resolve() to get the canonical path to a file.
   1102         """
   1103         # XXX untested yet!
   1104         if self._closed:
   1105             self._raise_closed()
   1106         if self.is_absolute():
   1107             return self
   1108         # FIXME this must defer to the specific flavour (and, under Windows,
   1109         # use nt._getfullpathname())
   1110         obj = self._from_parts([os.getcwd()] + self._parts, init=False)
   1111         obj._init(template=self)
   1112         return obj
   1113 
   1114     def resolve(self, strict=False):
   1115         """
   1116         Make the path absolute, resolving all symlinks on the way and also
   1117         normalizing it (for example turning slashes into backslashes under
   1118         Windows).
   1119         """
   1120         if self._closed:
   1121             self._raise_closed()
   1122         s = self._flavour.resolve(self, strict=strict)
   1123         if s is None:
   1124             # No symlink resolution => for consistency, raise an error if
   1125             # the path doesn't exist or is forbidden
   1126             self.stat()
   1127             s = str(self.absolute())
   1128         # Now we have no symlinks in the path, it's safe to normalize it.
   1129         normed = self._flavour.pathmod.normpath(s)
   1130         obj = self._from_parts((normed,), init=False)
   1131         obj._init(template=self)
   1132         return obj
   1133 
   1134     def stat(self):
   1135         """
   1136         Return the result of the stat() system call on this path, like
   1137         os.stat() does.
   1138         """
   1139         return self._accessor.stat(self)
   1140 
   1141     def owner(self):
   1142         """
   1143         Return the login name of the file owner.
   1144         """
   1145         import pwd
   1146         return pwd.getpwuid(self.stat().st_uid).pw_name
   1147 
   1148     def group(self):
   1149         """
   1150         Return the group name of the file gid.
   1151         """
   1152         import grp
   1153         return grp.getgrgid(self.stat().st_gid).gr_name
   1154 
   1155     def open(self, mode='r', buffering=-1, encoding=None,
   1156              errors=None, newline=None):
   1157         """
   1158         Open the file pointed by this path and return a file object, as
   1159         the built-in open() function does.
   1160         """
   1161         if self._closed:
   1162             self._raise_closed()
   1163         return io.open(str(self), mode, buffering, encoding, errors, newline,
   1164                        opener=self._opener)
   1165 
   1166     def read_bytes(self):
   1167         """
   1168         Open the file in bytes mode, read it, and close the file.
   1169         """
   1170         with self.open(mode='rb') as f:
   1171             return f.read()
   1172 
   1173     def read_text(self, encoding=None, errors=None):
   1174         """
   1175         Open the file in text mode, read it, and close the file.
   1176         """
   1177         with self.open(mode='r', encoding=encoding, errors=errors) as f:
   1178             return f.read()
   1179 
   1180     def write_bytes(self, data):
   1181         """
   1182         Open the file in bytes mode, write to it, and close the file.
   1183         """
   1184         # type-check for the buffer interface before truncating the file
   1185         view = memoryview(data)
   1186         with self.open(mode='wb') as f:
   1187             return f.write(view)
   1188 
   1189     def write_text(self, data, encoding=None, errors=None):
   1190         """
   1191         Open the file in text mode, write to it, and close the file.
   1192         """
   1193         if not isinstance(data, str):
   1194             raise TypeError('data must be str, not %s' %
   1195                             data.__class__.__name__)
   1196         with self.open(mode='w', encoding=encoding, errors=errors) as f:
   1197             return f.write(data)
   1198 
   1199     def touch(self, mode=0o666, exist_ok=True):
   1200         """
   1201         Create this file with the given access mode, if it doesn't exist.
   1202         """
   1203         if self._closed:
   1204             self._raise_closed()
   1205         if exist_ok:
   1206             # First try to bump modification time
   1207             # Implementation note: GNU touch uses the UTIME_NOW option of
   1208             # the utimensat() / futimens() functions.
   1209             try:
   1210                 self._accessor.utime(self, None)
   1211             except OSError:
   1212                 # Avoid exception chaining
   1213                 pass
   1214             else:
   1215                 return
   1216         flags = os.O_CREAT | os.O_WRONLY
   1217         if not exist_ok:
   1218             flags |= os.O_EXCL
   1219         fd = self._raw_open(flags, mode)
   1220         os.close(fd)
   1221 
   1222     def mkdir(self, mode=0o777, parents=False, exist_ok=False):
   1223         if self._closed:
   1224             self._raise_closed()
   1225         if not parents:
   1226             try:
   1227                 self._accessor.mkdir(self, mode)
   1228             except FileExistsError:
   1229                 if not exist_ok or not self.is_dir():
   1230                     raise
   1231         else:
   1232             try:
   1233                 self._accessor.mkdir(self, mode)
   1234             except FileExistsError:
   1235                 if not exist_ok or not self.is_dir():
   1236                     raise
   1237             except OSError as e:
   1238                 if e.errno != ENOENT or self.parent == self:
   1239                     raise
   1240                 self.parent.mkdir(parents=True)
   1241                 self._accessor.mkdir(self, mode)
   1242 
   1243     def chmod(self, mode):
   1244         """
   1245         Change the permissions of the path, like os.chmod().
   1246         """
   1247         if self._closed:
   1248             self._raise_closed()
   1249         self._accessor.chmod(self, mode)
   1250 
   1251     def lchmod(self, mode):
   1252         """
   1253         Like chmod(), except if the path points to a symlink, the symlink's
   1254         permissions are changed, rather than its target's.
   1255         """
   1256         if self._closed:
   1257             self._raise_closed()
   1258         self._accessor.lchmod(self, mode)
   1259 
   1260     def unlink(self):
   1261         """
   1262         Remove this file or link.
   1263         If the path is a directory, use rmdir() instead.
   1264         """
   1265         if self._closed:
   1266             self._raise_closed()
   1267         self._accessor.unlink(self)
   1268 
   1269     def rmdir(self):
   1270         """
   1271         Remove this directory.  The directory must be empty.
   1272         """
   1273         if self._closed:
   1274             self._raise_closed()
   1275         self._accessor.rmdir(self)
   1276 
   1277     def lstat(self):
   1278         """
   1279         Like stat(), except if the path points to a symlink, the symlink's
   1280         status information is returned, rather than its target's.
   1281         """
   1282         if self._closed:
   1283             self._raise_closed()
   1284         return self._accessor.lstat(self)
   1285 
   1286     def rename(self, target):
   1287         """
   1288         Rename this path to the given path.
   1289         """
   1290         if self._closed:
   1291             self._raise_closed()
   1292         self._accessor.rename(self, target)
   1293 
   1294     def replace(self, target):
   1295         """
   1296         Rename this path to the given path, clobbering the existing
   1297         destination if it exists.
   1298         """
   1299         if self._closed:
   1300             self._raise_closed()
   1301         self._accessor.replace(self, target)
   1302 
   1303     def symlink_to(self, target, target_is_directory=False):
   1304         """
   1305         Make this path a symlink pointing to the given path.
   1306         Note the order of arguments (self, target) is the reverse of os.symlink's.
   1307         """
   1308         if self._closed:
   1309             self._raise_closed()
   1310         self._accessor.symlink(target, self, target_is_directory)
   1311 
   1312     # Convenience functions for querying the stat results
   1313 
   1314     def exists(self):
   1315         """
   1316         Whether this path exists.
   1317         """
   1318         try:
   1319             self.stat()
   1320         except OSError as e:
   1321             if e.errno not in (ENOENT, ENOTDIR):
   1322                 raise
   1323             return False
   1324         return True
   1325 
   1326     def is_dir(self):
   1327         """
   1328         Whether this path is a directory.
   1329         """
   1330         try:
   1331             return S_ISDIR(self.stat().st_mode)
   1332         except OSError as e:
   1333             if e.errno not in (ENOENT, ENOTDIR):
   1334                 raise
   1335             # Path doesn't exist or is a broken symlink
   1336             # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
   1337             return False
   1338 
   1339     def is_file(self):
   1340         """
   1341         Whether this path is a regular file (also True for symlinks pointing
   1342         to regular files).
   1343         """
   1344         try:
   1345             return S_ISREG(self.stat().st_mode)
   1346         except OSError as e:
   1347             if e.errno not in (ENOENT, ENOTDIR):
   1348                 raise
   1349             # Path doesn't exist or is a broken symlink
   1350             # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
   1351             return False
   1352 
   1353     def is_symlink(self):
   1354         """
   1355         Whether this path is a symbolic link.
   1356         """
   1357         try:
   1358             return S_ISLNK(self.lstat().st_mode)
   1359         except OSError as e:
   1360             if e.errno not in (ENOENT, ENOTDIR):
   1361                 raise
   1362             # Path doesn't exist
   1363             return False
   1364 
   1365     def is_block_device(self):
   1366         """
   1367         Whether this path is a block device.
   1368         """
   1369         try:
   1370             return S_ISBLK(self.stat().st_mode)
   1371         except OSError as e:
   1372             if e.errno not in (ENOENT, ENOTDIR):
   1373                 raise
   1374             # Path doesn't exist or is a broken symlink
   1375             # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
   1376             return False
   1377 
   1378     def is_char_device(self):
   1379         """
   1380         Whether this path is a character device.
   1381         """
   1382         try:
   1383             return S_ISCHR(self.stat().st_mode)
   1384         except OSError as e:
   1385             if e.errno not in (ENOENT, ENOTDIR):
   1386                 raise
   1387             # Path doesn't exist or is a broken symlink
   1388             # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
   1389             return False
   1390 
   1391     def is_fifo(self):
   1392         """
   1393         Whether this path is a FIFO.
   1394         """
   1395         try:
   1396             return S_ISFIFO(self.stat().st_mode)
   1397         except OSError as e:
   1398             if e.errno not in (ENOENT, ENOTDIR):
   1399                 raise
   1400             # Path doesn't exist or is a broken symlink
   1401             # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
   1402             return False
   1403 
   1404     def is_socket(self):
   1405         """
   1406         Whether this path is a socket.
   1407         """
   1408         try:
   1409             return S_ISSOCK(self.stat().st_mode)
   1410         except OSError as e:
   1411             if e.errno not in (ENOENT, ENOTDIR):
   1412                 raise
   1413             # Path doesn't exist or is a broken symlink
   1414             # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
   1415             return False
   1416 
   1417     def expanduser(self):
   1418         """ Return a new path with expanded ~ and ~user constructs
   1419         (as returned by os.path.expanduser)
   1420         """
   1421         if (not (self._drv or self._root) and
   1422             self._parts and self._parts[0][:1] == '~'):
   1423             homedir = self._flavour.gethomedir(self._parts[0][1:])
   1424             return self._from_parts([homedir] + self._parts[1:])
   1425 
   1426         return self
   1427 
   1428 
   1429 class PosixPath(Path, PurePosixPath):
   1430     __slots__ = ()
   1431 
   1432 class WindowsPath(Path, PureWindowsPath):
   1433     __slots__ = ()
   1434 
   1435     def owner(self):
   1436         raise NotImplementedError("Path.owner() is unsupported on this system")
   1437 
   1438     def group(self):
   1439         raise NotImplementedError("Path.group() is unsupported on this system")
   1440