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