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