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