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