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