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