blob: eff6ae3f0c9ff3f4485b9d074a5fcb5d9e528bed [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
Antoine Pitrou31119e42013-11-22 17:38:12 +0100257 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100258 seen = {}
259 def _resolve(path, rest):
260 if rest.startswith(sep):
261 path = ''
262
263 for name in rest.split(sep):
264 if not name or name == '.':
265 # current dir
266 continue
267 if name == '..':
268 # parent dir
269 path, _, _ = path.rpartition(sep)
270 continue
271 newpath = path + sep + name
272 if newpath in seen:
273 # Already seen this path
274 path = seen[newpath]
275 if path is not None:
276 # use cached value
277 continue
278 # The symlink is not resolved, so we must have a symlink loop.
279 raise RuntimeError("Symlink loop from %r" % newpath)
280 # Resolve the symbolic link
281 try:
282 target = accessor.readlink(newpath)
283 except OSError as e:
284 if e.errno != EINVAL:
285 raise
286 # Not a symlink
287 path = newpath
288 else:
289 seen[newpath] = None # not resolved symlink
290 path = _resolve(path, target)
291 seen[newpath] = path # resolved symlink
292
293 return path
294 # NOTE: according to POSIX, getcwd() cannot contain path components
295 # which are symlinks.
296 base = '' if path.is_absolute() else os.getcwd()
297 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100298
299 def is_reserved(self, parts):
300 return False
301
302 def make_uri(self, path):
303 # We represent the path using the local filesystem encoding,
304 # for portability to other applications.
305 bpath = bytes(path)
306 return 'file://' + urlquote_from_bytes(bpath)
307
308
309_windows_flavour = _WindowsFlavour()
310_posix_flavour = _PosixFlavour()
311
312
313class _Accessor:
314 """An accessor implements a particular (system-specific or not) way of
315 accessing paths on the filesystem."""
316
317
318class _NormalAccessor(_Accessor):
319
320 def _wrap_strfunc(strfunc):
321 @functools.wraps(strfunc)
322 def wrapped(pathobj, *args):
323 return strfunc(str(pathobj), *args)
324 return staticmethod(wrapped)
325
326 def _wrap_binary_strfunc(strfunc):
327 @functools.wraps(strfunc)
328 def wrapped(pathobjA, pathobjB, *args):
329 return strfunc(str(pathobjA), str(pathobjB), *args)
330 return staticmethod(wrapped)
331
332 stat = _wrap_strfunc(os.stat)
333
334 lstat = _wrap_strfunc(os.lstat)
335
336 open = _wrap_strfunc(os.open)
337
338 listdir = _wrap_strfunc(os.listdir)
339
340 chmod = _wrap_strfunc(os.chmod)
341
342 if hasattr(os, "lchmod"):
343 lchmod = _wrap_strfunc(os.lchmod)
344 else:
345 def lchmod(self, pathobj, mode):
346 raise NotImplementedError("lchmod() not available on this system")
347
348 mkdir = _wrap_strfunc(os.mkdir)
349
350 unlink = _wrap_strfunc(os.unlink)
351
352 rmdir = _wrap_strfunc(os.rmdir)
353
354 rename = _wrap_binary_strfunc(os.rename)
355
356 replace = _wrap_binary_strfunc(os.replace)
357
358 if nt:
359 if supports_symlinks:
360 symlink = _wrap_binary_strfunc(os.symlink)
361 else:
362 def symlink(a, b, target_is_directory):
363 raise NotImplementedError("symlink() not available on this system")
364 else:
365 # Under POSIX, os.symlink() takes two args
366 @staticmethod
367 def symlink(a, b, target_is_directory):
368 return os.symlink(str(a), str(b))
369
370 utime = _wrap_strfunc(os.utime)
371
372 # Helper for resolve()
373 def readlink(self, path):
374 return os.readlink(path)
375
376
377_normal_accessor = _NormalAccessor()
378
379
380#
381# Globbing helpers
382#
383
384@contextmanager
385def _cached(func):
386 try:
387 func.__cached__
388 yield func
389 except AttributeError:
390 cache = {}
391 def wrapper(*args):
392 try:
393 return cache[args]
394 except KeyError:
395 value = cache[args] = func(*args)
396 return value
397 wrapper.__cached__ = True
398 try:
399 yield wrapper
400 finally:
401 cache.clear()
402
403def _make_selector(pattern_parts):
404 pat = pattern_parts[0]
405 child_parts = pattern_parts[1:]
406 if pat == '**':
407 cls = _RecursiveWildcardSelector
408 elif '**' in pat:
409 raise ValueError("Invalid pattern: '**' can only be an entire path component")
410 elif _is_wildcard_pattern(pat):
411 cls = _WildcardSelector
412 else:
413 cls = _PreciseSelector
414 return cls(pat, child_parts)
415
416if hasattr(functools, "lru_cache"):
417 _make_selector = functools.lru_cache()(_make_selector)
418
419
420class _Selector:
421 """A selector matches a specific glob pattern part against the children
422 of a given path."""
423
424 def __init__(self, child_parts):
425 self.child_parts = child_parts
426 if child_parts:
427 self.successor = _make_selector(child_parts)
428 else:
429 self.successor = _TerminatingSelector()
430
431 def select_from(self, parent_path):
432 """Iterate over all child paths of `parent_path` matched by this
433 selector. This can contain parent_path itself."""
434 path_cls = type(parent_path)
435 is_dir = path_cls.is_dir
436 exists = path_cls.exists
437 listdir = parent_path._accessor.listdir
438 return self._select_from(parent_path, is_dir, exists, listdir)
439
440
441class _TerminatingSelector:
442
443 def _select_from(self, parent_path, is_dir, exists, listdir):
444 yield parent_path
445
446
447class _PreciseSelector(_Selector):
448
449 def __init__(self, name, child_parts):
450 self.name = name
451 _Selector.__init__(self, child_parts)
452
453 def _select_from(self, parent_path, is_dir, exists, listdir):
454 if not is_dir(parent_path):
455 return
456 path = parent_path._make_child_relpath(self.name)
457 if exists(path):
458 for p in self.successor._select_from(path, is_dir, exists, listdir):
459 yield p
460
461
462class _WildcardSelector(_Selector):
463
464 def __init__(self, pat, child_parts):
465 self.pat = re.compile(fnmatch.translate(pat))
466 _Selector.__init__(self, child_parts)
467
468 def _select_from(self, parent_path, is_dir, exists, listdir):
469 if not is_dir(parent_path):
470 return
471 cf = parent_path._flavour.casefold
472 for name in listdir(parent_path):
473 casefolded = cf(name)
474 if self.pat.match(casefolded):
475 path = parent_path._make_child_relpath(name)
476 for p in self.successor._select_from(path, is_dir, exists, listdir):
477 yield p
478
479
480class _RecursiveWildcardSelector(_Selector):
481
482 def __init__(self, pat, child_parts):
483 _Selector.__init__(self, child_parts)
484
485 def _iterate_directories(self, parent_path, is_dir, listdir):
486 yield parent_path
487 for name in listdir(parent_path):
488 path = parent_path._make_child_relpath(name)
489 if is_dir(path):
490 for p in self._iterate_directories(path, is_dir, listdir):
491 yield p
492
493 def _select_from(self, parent_path, is_dir, exists, listdir):
494 if not is_dir(parent_path):
495 return
496 with _cached(listdir) as listdir:
497 yielded = set()
498 try:
499 successor_select = self.successor._select_from
500 for starting_point in self._iterate_directories(parent_path, is_dir, listdir):
501 for p in successor_select(starting_point, is_dir, exists, listdir):
502 if p not in yielded:
503 yield p
504 yielded.add(p)
505 finally:
506 yielded.clear()
507
508
509#
510# Public API
511#
512
513class _PathParents(Sequence):
514 """This object provides sequence-like access to the logical ancestors
515 of a path. Don't try to construct it yourself."""
516 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
517
518 def __init__(self, path):
519 # We don't store the instance to avoid reference cycles
520 self._pathcls = type(path)
521 self._drv = path._drv
522 self._root = path._root
523 self._parts = path._parts
524
525 def __len__(self):
526 if self._drv or self._root:
527 return len(self._parts) - 1
528 else:
529 return len(self._parts)
530
531 def __getitem__(self, idx):
532 if idx < 0 or idx >= len(self):
533 raise IndexError(idx)
534 return self._pathcls._from_parsed_parts(self._drv, self._root,
535 self._parts[:-idx - 1])
536
537 def __repr__(self):
538 return "<{}.parents>".format(self._pathcls.__name__)
539
540
541class PurePath(object):
542 """PurePath represents a filesystem path and offers operations which
543 don't imply any actual filesystem I/O. Depending on your system,
544 instantiating a PurePath will return either a PurePosixPath or a
545 PureWindowsPath object. You can also instantiate either of these classes
546 directly, regardless of your system.
547 """
548 __slots__ = (
549 '_drv', '_root', '_parts',
550 '_str', '_hash', '_pparts', '_cached_cparts',
551 )
552
553 def __new__(cls, *args):
554 """Construct a PurePath from one or several strings and or existing
555 PurePath objects. The strings and path objects are combined so as
556 to yield a canonicalized path, which is incorporated into the
557 new PurePath object.
558 """
559 if cls is PurePath:
560 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
561 return cls._from_parts(args)
562
563 def __reduce__(self):
564 # Using the parts tuple helps share interned path parts
565 # when pickling related paths.
566 return (self.__class__, tuple(self._parts))
567
568 @classmethod
569 def _parse_args(cls, args):
570 # This is useful when you don't want to create an instance, just
571 # canonicalize some constructor arguments.
572 parts = []
573 for a in args:
574 if isinstance(a, PurePath):
575 parts += a._parts
576 elif isinstance(a, str):
Antoine Pitroucb5ec772014-04-23 00:34:15 +0200577 # Force-cast str subclasses to str (issue #21127)
578 parts.append(str(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100579 else:
580 raise TypeError(
581 "argument should be a path or str object, not %r"
582 % type(a))
583 return cls._flavour.parse_parts(parts)
584
585 @classmethod
586 def _from_parts(cls, args, init=True):
587 # We need to call _parse_args on the instance, so as to get the
588 # right flavour.
589 self = object.__new__(cls)
590 drv, root, parts = self._parse_args(args)
591 self._drv = drv
592 self._root = root
593 self._parts = parts
594 if init:
595 self._init()
596 return self
597
598 @classmethod
599 def _from_parsed_parts(cls, drv, root, parts, init=True):
600 self = object.__new__(cls)
601 self._drv = drv
602 self._root = root
603 self._parts = parts
604 if init:
605 self._init()
606 return self
607
608 @classmethod
609 def _format_parsed_parts(cls, drv, root, parts):
610 if drv or root:
611 return drv + root + cls._flavour.join(parts[1:])
612 else:
613 return cls._flavour.join(parts)
614
615 def _init(self):
616 # Overriden in concrete Path
617 pass
618
619 def _make_child(self, args):
620 drv, root, parts = self._parse_args(args)
621 drv, root, parts = self._flavour.join_parsed_parts(
622 self._drv, self._root, self._parts, drv, root, parts)
623 return self._from_parsed_parts(drv, root, parts)
624
625 def __str__(self):
626 """Return the string representation of the path, suitable for
627 passing to system calls."""
628 try:
629 return self._str
630 except AttributeError:
631 self._str = self._format_parsed_parts(self._drv, self._root,
632 self._parts) or '.'
633 return self._str
634
635 def as_posix(self):
636 """Return the string representation of the path with forward (/)
637 slashes."""
638 f = self._flavour
639 return str(self).replace(f.sep, '/')
640
641 def __bytes__(self):
642 """Return the bytes representation of the path. This is only
643 recommended to use under Unix."""
644 return os.fsencode(str(self))
645
646 def __repr__(self):
647 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
648
649 def as_uri(self):
650 """Return the path as a 'file' URI."""
651 if not self.is_absolute():
652 raise ValueError("relative path can't be expressed as a file URI")
653 return self._flavour.make_uri(self)
654
655 @property
656 def _cparts(self):
657 # Cached casefolded parts, for hashing and comparison
658 try:
659 return self._cached_cparts
660 except AttributeError:
661 self._cached_cparts = self._flavour.casefold_parts(self._parts)
662 return self._cached_cparts
663
664 def __eq__(self, other):
665 if not isinstance(other, PurePath):
666 return NotImplemented
667 return self._cparts == other._cparts and self._flavour is other._flavour
668
669 def __ne__(self, other):
670 return not self == other
671
672 def __hash__(self):
673 try:
674 return self._hash
675 except AttributeError:
676 self._hash = hash(tuple(self._cparts))
677 return self._hash
678
679 def __lt__(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 __le__(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 __gt__(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 def __ge__(self, other):
695 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
696 return NotImplemented
697 return self._cparts >= other._cparts
698
699 drive = property(attrgetter('_drv'),
700 doc="""The drive prefix (letter or UNC path), if any.""")
701
702 root = property(attrgetter('_root'),
703 doc="""The root of the path, if any.""")
704
705 @property
706 def anchor(self):
707 """The concatenation of the drive and root, or ''."""
708 anchor = self._drv + self._root
709 return anchor
710
711 @property
712 def name(self):
713 """The final path component, if any."""
714 parts = self._parts
715 if len(parts) == (1 if (self._drv or self._root) else 0):
716 return ''
717 return parts[-1]
718
719 @property
720 def suffix(self):
721 """The final component's last suffix, if any."""
722 name = self.name
723 i = name.rfind('.')
724 if 0 < i < len(name) - 1:
725 return name[i:]
726 else:
727 return ''
728
729 @property
730 def suffixes(self):
731 """A list of the final component's suffixes, if any."""
732 name = self.name
733 if name.endswith('.'):
734 return []
735 name = name.lstrip('.')
736 return ['.' + suffix for suffix in name.split('.')[1:]]
737
738 @property
739 def stem(self):
740 """The final path component, minus its last suffix."""
741 name = self.name
742 i = name.rfind('.')
743 if 0 < i < len(name) - 1:
744 return name[:i]
745 else:
746 return name
747
748 def with_name(self, name):
749 """Return a new path with the file name changed."""
750 if not self.name:
751 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400752 drv, root, parts = self._flavour.parse_parts((name,))
753 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
754 or drv or root or len(parts) != 1):
755 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100756 return self._from_parsed_parts(self._drv, self._root,
757 self._parts[:-1] + [name])
758
759 def with_suffix(self, suffix):
760 """Return a new path with the file suffix changed (or added, if none)."""
761 # XXX if suffix is None, should the current suffix be removed?
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400762 f = self._flavour
763 if f.sep in suffix or f.altsep and f.altsep in suffix:
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100764 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400765 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100766 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100767 name = self.name
768 if not name:
769 raise ValueError("%r has an empty name" % (self,))
770 old_suffix = self.suffix
771 if not old_suffix:
772 name = name + suffix
773 else:
774 name = name[:-len(old_suffix)] + suffix
775 return self._from_parsed_parts(self._drv, self._root,
776 self._parts[:-1] + [name])
777
778 def relative_to(self, *other):
779 """Return the relative path to another path identified by the passed
780 arguments. If the operation is not possible (because this is not
781 a subpath of the other path), raise ValueError.
782 """
783 # For the purpose of this method, drive and root are considered
784 # separate parts, i.e.:
785 # Path('c:/').relative_to('c:') gives Path('/')
786 # Path('c:/').relative_to('/') raise ValueError
787 if not other:
788 raise TypeError("need at least one argument")
789 parts = self._parts
790 drv = self._drv
791 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100792 if root:
793 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100794 else:
795 abs_parts = parts
796 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100797 if to_root:
798 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100799 else:
800 to_abs_parts = to_parts
801 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100802 cf = self._flavour.casefold_parts
803 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100804 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
805 raise ValueError("{!r} does not start with {!r}"
806 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100807 return self._from_parsed_parts('', root if n == 1 else '',
808 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100809
810 @property
811 def parts(self):
812 """An object providing sequence-like access to the
813 components in the filesystem path."""
814 # We cache the tuple to avoid building a new one each time .parts
815 # is accessed. XXX is this necessary?
816 try:
817 return self._pparts
818 except AttributeError:
819 self._pparts = tuple(self._parts)
820 return self._pparts
821
822 def joinpath(self, *args):
823 """Combine this path with one or several arguments, and return a
824 new path representing either a subpath (if all arguments are relative
825 paths) or a totally different path (if one of the arguments is
826 anchored).
827 """
828 return self._make_child(args)
829
830 def __truediv__(self, key):
831 return self._make_child((key,))
832
833 def __rtruediv__(self, key):
834 return self._from_parts([key] + self._parts)
835
836 @property
837 def parent(self):
838 """The logical parent of the path."""
839 drv = self._drv
840 root = self._root
841 parts = self._parts
842 if len(parts) == 1 and (drv or root):
843 return self
844 return self._from_parsed_parts(drv, root, parts[:-1])
845
846 @property
847 def parents(self):
848 """A sequence of this path's logical parents."""
849 return _PathParents(self)
850
851 def is_absolute(self):
852 """True if the path is absolute (has both a root and, if applicable,
853 a drive)."""
854 if not self._root:
855 return False
856 return not self._flavour.has_drv or bool(self._drv)
857
858 def is_reserved(self):
859 """Return True if the path contains one of the special names reserved
860 by the system, if any."""
861 return self._flavour.is_reserved(self._parts)
862
863 def match(self, path_pattern):
864 """
865 Return True if this path matches the given pattern.
866 """
867 cf = self._flavour.casefold
868 path_pattern = cf(path_pattern)
869 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
870 if not pat_parts:
871 raise ValueError("empty pattern")
872 if drv and drv != cf(self._drv):
873 return False
874 if root and root != cf(self._root):
875 return False
876 parts = self._cparts
877 if drv or root:
878 if len(pat_parts) != len(parts):
879 return False
880 pat_parts = pat_parts[1:]
881 elif len(pat_parts) > len(parts):
882 return False
883 for part, pat in zip(reversed(parts), reversed(pat_parts)):
884 if not fnmatch.fnmatchcase(part, pat):
885 return False
886 return True
887
888
889class PurePosixPath(PurePath):
890 _flavour = _posix_flavour
891 __slots__ = ()
892
893
894class PureWindowsPath(PurePath):
895 _flavour = _windows_flavour
896 __slots__ = ()
897
898
899# Filesystem-accessing classes
900
901
902class Path(PurePath):
903 __slots__ = (
904 '_accessor',
905 '_closed',
906 )
907
908 def __new__(cls, *args, **kwargs):
909 if cls is Path:
910 cls = WindowsPath if os.name == 'nt' else PosixPath
911 self = cls._from_parts(args, init=False)
912 if not self._flavour.is_supported:
913 raise NotImplementedError("cannot instantiate %r on your system"
914 % (cls.__name__,))
915 self._init()
916 return self
917
918 def _init(self,
919 # Private non-constructor arguments
920 template=None,
921 ):
922 self._closed = False
923 if template is not None:
924 self._accessor = template._accessor
925 else:
926 self._accessor = _normal_accessor
927
928 def _make_child_relpath(self, part):
929 # This is an optimization used for dir walking. `part` must be
930 # a single part relative to this path.
931 parts = self._parts + [part]
932 return self._from_parsed_parts(self._drv, self._root, parts)
933
934 def __enter__(self):
935 if self._closed:
936 self._raise_closed()
937 return self
938
939 def __exit__(self, t, v, tb):
940 self._closed = True
941
942 def _raise_closed(self):
943 raise ValueError("I/O operation on closed path")
944
945 def _opener(self, name, flags, mode=0o666):
946 # A stub for the opener argument to built-in open()
947 return self._accessor.open(self, flags, mode)
948
Antoine Pitrou4a60d422013-12-02 21:25:18 +0100949 def _raw_open(self, flags, mode=0o777):
950 """
951 Open the file pointed by this path and return a file descriptor,
952 as os.open() does.
953 """
954 if self._closed:
955 self._raise_closed()
956 return self._accessor.open(self, flags, mode)
957
Antoine Pitrou31119e42013-11-22 17:38:12 +0100958 # Public API
959
960 @classmethod
961 def cwd(cls):
962 """Return a new path pointing to the current working directory
963 (as returned by os.getcwd()).
964 """
965 return cls(os.getcwd())
966
Antoine Pitrou43e3d942014-05-13 10:50:15 +0200967 def samefile(self, other_path):
968 """Return whether `other_file` is the same or not as this file.
969 (as returned by os.path.samefile(file, other_file)).
970 """
971 st = self.stat()
972 try:
973 other_st = other_path.stat()
974 except AttributeError:
975 other_st = os.stat(other_path)
976 return os.path.samestat(st, other_st)
977
Antoine Pitrou31119e42013-11-22 17:38:12 +0100978 def iterdir(self):
979 """Iterate over the files in this directory. Does not yield any
980 result for the special paths '.' and '..'.
981 """
982 if self._closed:
983 self._raise_closed()
984 for name in self._accessor.listdir(self):
985 if name in {'.', '..'}:
986 # Yielding a path object for these makes little sense
987 continue
988 yield self._make_child_relpath(name)
989 if self._closed:
990 self._raise_closed()
991
992 def glob(self, pattern):
993 """Iterate over this subtree and yield all existing files (of any
994 kind, including directories) matching the given pattern.
995 """
996 pattern = self._flavour.casefold(pattern)
997 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
998 if drv or root:
999 raise NotImplementedError("Non-relative patterns are unsupported")
1000 selector = _make_selector(tuple(pattern_parts))
1001 for p in selector.select_from(self):
1002 yield p
1003
1004 def rglob(self, pattern):
1005 """Recursively yield all existing files (of any kind, including
1006 directories) matching the given pattern, anywhere in this subtree.
1007 """
1008 pattern = self._flavour.casefold(pattern)
1009 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1010 if drv or root:
1011 raise NotImplementedError("Non-relative patterns are unsupported")
1012 selector = _make_selector(("**",) + tuple(pattern_parts))
1013 for p in selector.select_from(self):
1014 yield p
1015
1016 def absolute(self):
1017 """Return an absolute version of this path. This function works
1018 even if the path doesn't point to anything.
1019
1020 No normalization is done, i.e. all '.' and '..' will be kept along.
1021 Use resolve() to get the canonical path to a file.
1022 """
1023 # XXX untested yet!
1024 if self._closed:
1025 self._raise_closed()
1026 if self.is_absolute():
1027 return self
1028 # FIXME this must defer to the specific flavour (and, under Windows,
1029 # use nt._getfullpathname())
1030 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1031 obj._init(template=self)
1032 return obj
1033
1034 def resolve(self):
1035 """
1036 Make the path absolute, resolving all symlinks on the way and also
1037 normalizing it (for example turning slashes into backslashes under
1038 Windows).
1039 """
1040 if self._closed:
1041 self._raise_closed()
1042 s = self._flavour.resolve(self)
1043 if s is None:
1044 # No symlink resolution => for consistency, raise an error if
1045 # the path doesn't exist or is forbidden
1046 self.stat()
1047 s = str(self.absolute())
1048 # Now we have no symlinks in the path, it's safe to normalize it.
1049 normed = self._flavour.pathmod.normpath(s)
1050 obj = self._from_parts((normed,), init=False)
1051 obj._init(template=self)
1052 return obj
1053
1054 def stat(self):
1055 """
1056 Return the result of the stat() system call on this path, like
1057 os.stat() does.
1058 """
1059 return self._accessor.stat(self)
1060
1061 def owner(self):
1062 """
1063 Return the login name of the file owner.
1064 """
1065 import pwd
1066 return pwd.getpwuid(self.stat().st_uid).pw_name
1067
1068 def group(self):
1069 """
1070 Return the group name of the file gid.
1071 """
1072 import grp
1073 return grp.getgrgid(self.stat().st_gid).gr_name
1074
Antoine Pitrou31119e42013-11-22 17:38:12 +01001075 def open(self, mode='r', buffering=-1, encoding=None,
1076 errors=None, newline=None):
1077 """
1078 Open the file pointed by this path and return a file object, as
1079 the built-in open() function does.
1080 """
1081 if self._closed:
1082 self._raise_closed()
1083 return io.open(str(self), mode, buffering, encoding, errors, newline,
1084 opener=self._opener)
1085
1086 def touch(self, mode=0o666, exist_ok=True):
1087 """
1088 Create this file with the given access mode, if it doesn't exist.
1089 """
1090 if self._closed:
1091 self._raise_closed()
1092 if exist_ok:
1093 # First try to bump modification time
1094 # Implementation note: GNU touch uses the UTIME_NOW option of
1095 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001096 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001097 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001098 except OSError:
1099 # Avoid exception chaining
1100 pass
1101 else:
1102 return
1103 flags = os.O_CREAT | os.O_WRONLY
1104 if not exist_ok:
1105 flags |= os.O_EXCL
1106 fd = self._raw_open(flags, mode)
1107 os.close(fd)
1108
Barry Warsaw7c549c42014-08-05 11:28:12 -04001109 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001110 if self._closed:
1111 self._raise_closed()
1112 if not parents:
Barry Warsaw7c549c42014-08-05 11:28:12 -04001113 try:
1114 self._accessor.mkdir(self, mode)
1115 except FileExistsError:
1116 if not exist_ok or not self.is_dir():
1117 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001118 else:
1119 try:
1120 self._accessor.mkdir(self, mode)
Barry Warsaw7c549c42014-08-05 11:28:12 -04001121 except FileExistsError:
1122 if not exist_ok or not self.is_dir():
1123 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001124 except OSError as e:
1125 if e.errno != ENOENT:
1126 raise
Antoine Pitrou0048c982013-12-16 20:22:37 +01001127 self.parent.mkdir(parents=True)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001128 self._accessor.mkdir(self, mode)
1129
1130 def chmod(self, mode):
1131 """
1132 Change the permissions of the path, like os.chmod().
1133 """
1134 if self._closed:
1135 self._raise_closed()
1136 self._accessor.chmod(self, mode)
1137
1138 def lchmod(self, mode):
1139 """
1140 Like chmod(), except if the path points to a symlink, the symlink's
1141 permissions are changed, rather than its target's.
1142 """
1143 if self._closed:
1144 self._raise_closed()
1145 self._accessor.lchmod(self, mode)
1146
1147 def unlink(self):
1148 """
1149 Remove this file or link.
1150 If the path is a directory, use rmdir() instead.
1151 """
1152 if self._closed:
1153 self._raise_closed()
1154 self._accessor.unlink(self)
1155
1156 def rmdir(self):
1157 """
1158 Remove this directory. The directory must be empty.
1159 """
1160 if self._closed:
1161 self._raise_closed()
1162 self._accessor.rmdir(self)
1163
1164 def lstat(self):
1165 """
1166 Like stat(), except if the path points to a symlink, the symlink's
1167 status information is returned, rather than its target's.
1168 """
1169 if self._closed:
1170 self._raise_closed()
1171 return self._accessor.lstat(self)
1172
1173 def rename(self, target):
1174 """
1175 Rename this path to the given path.
1176 """
1177 if self._closed:
1178 self._raise_closed()
1179 self._accessor.rename(self, target)
1180
1181 def replace(self, target):
1182 """
1183 Rename this path to the given path, clobbering the existing
1184 destination if it exists.
1185 """
1186 if self._closed:
1187 self._raise_closed()
1188 self._accessor.replace(self, target)
1189
1190 def symlink_to(self, target, target_is_directory=False):
1191 """
1192 Make this path a symlink pointing to the given path.
1193 Note the order of arguments (self, target) is the reverse of os.symlink's.
1194 """
1195 if self._closed:
1196 self._raise_closed()
1197 self._accessor.symlink(target, self, target_is_directory)
1198
1199 # Convenience functions for querying the stat results
1200
1201 def exists(self):
1202 """
1203 Whether this path exists.
1204 """
1205 try:
1206 self.stat()
1207 except OSError as e:
1208 if e.errno != ENOENT:
1209 raise
1210 return False
1211 return True
1212
1213 def is_dir(self):
1214 """
1215 Whether this path is a directory.
1216 """
1217 try:
1218 return S_ISDIR(self.stat().st_mode)
1219 except OSError as e:
1220 if e.errno != ENOENT:
1221 raise
1222 # Path doesn't exist or is a broken symlink
1223 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1224 return False
1225
1226 def is_file(self):
1227 """
1228 Whether this path is a regular file (also True for symlinks pointing
1229 to regular files).
1230 """
1231 try:
1232 return S_ISREG(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_symlink(self):
1241 """
1242 Whether this path is a symbolic link.
1243 """
1244 try:
1245 return S_ISLNK(self.lstat().st_mode)
1246 except OSError as e:
1247 if e.errno != ENOENT:
1248 raise
1249 # Path doesn't exist
1250 return False
1251
1252 def is_block_device(self):
1253 """
1254 Whether this path is a block device.
1255 """
1256 try:
1257 return S_ISBLK(self.stat().st_mode)
1258 except OSError as e:
1259 if e.errno != ENOENT:
1260 raise
1261 # Path doesn't exist or is a broken symlink
1262 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1263 return False
1264
1265 def is_char_device(self):
1266 """
1267 Whether this path is a character device.
1268 """
1269 try:
1270 return S_ISCHR(self.stat().st_mode)
1271 except OSError as e:
1272 if e.errno != ENOENT:
1273 raise
1274 # Path doesn't exist or is a broken symlink
1275 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1276 return False
1277
1278 def is_fifo(self):
1279 """
1280 Whether this path is a FIFO.
1281 """
1282 try:
1283 return S_ISFIFO(self.stat().st_mode)
1284 except OSError as e:
1285 if e.errno != ENOENT:
1286 raise
1287 # Path doesn't exist or is a broken symlink
1288 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1289 return False
1290
1291 def is_socket(self):
1292 """
1293 Whether this path is a socket.
1294 """
1295 try:
1296 return S_ISSOCK(self.stat().st_mode)
1297 except OSError as e:
1298 if e.errno != ENOENT:
1299 raise
1300 # Path doesn't exist or is a broken symlink
1301 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1302 return False
1303
1304
1305class PosixPath(Path, PurePosixPath):
1306 __slots__ = ()
1307
1308class WindowsPath(Path, PureWindowsPath):
1309 __slots__ = ()