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