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