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