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