1# Copyright (c) 2001-2022 Python Software Foundation; All Rights Reserved
2# Copyright (c) 2014-2022 Matthias C. M. Troffaes and contributors
3# Copyright (c) 2012-2014 Antoine Pitrou and contributors
4#
5# Distributed under the terms of the MIT License.
6
7import fnmatch
8import functools
9import io
10import ntpath
11import os
12import posixpath
13import re
14import sys
15import warnings
16from _collections_abc import Sequence
17from errno import ENOENT, ENOTDIR, EBADF, ELOOP
18from operator import attrgetter
19from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
20from urllib.parse import quote_from_bytes as urlquote_from_bytes
21
22
23__all__ = [
24 "PurePath", "PurePosixPath", "PureWindowsPath",
25 "Path", "PosixPath", "WindowsPath",
26 ]
27
28#
29# Internals
30#
31
32_WINERROR_NOT_READY = 21 # drive exists but is not accessible
33_WINERROR_INVALID_NAME = 123 # fix for bpo-35306
34_WINERROR_CANT_RESOLVE_FILENAME = 1921 # broken symlink pointing to itself
35
36# EBADF - guard against macOS `stat` throwing EBADF
37_IGNORED_ERRNOS = (ENOENT, ENOTDIR, EBADF, ELOOP)
38
39_IGNORED_WINERRORS = (
40 _WINERROR_NOT_READY,
41 _WINERROR_INVALID_NAME,
42 _WINERROR_CANT_RESOLVE_FILENAME)
43
44def _ignore_error(exception):
45 return (getattr(exception, 'errno', None) in _IGNORED_ERRNOS or
46 getattr(exception, 'winerror', None) in _IGNORED_WINERRORS)
47
48
49def _is_wildcard_pattern(pat):
50 # Whether this pattern needs actual matching using fnmatch, or can
51 # be looked up directly as a file.
52 return "*" in pat or "?" in pat or "[" in pat
53
54
55if sys.version_info >= (3, 10):
56 io_text_encoding = io.text_encoding
57else:
58 def io_text_encoding(encoding, stacklevel=2):
59 return encoding
60
61
62class _Flavour(object):
63 """A flavour implements a particular (platform-specific) set of path
64 semantics."""
65
66 sep: str
67 altsep: str
68
69 def __init__(self):
70 self.join = self.sep.join
71
72 def parse_parts(self, parts):
73 parsed = []
74 sep = self.sep
75 altsep = self.altsep
76 drv = root = ''
77 it = reversed(parts)
78 for part in it:
79 if not part:
80 continue
81 if altsep:
82 part = part.replace(altsep, sep)
83 drv, root, rel = self.splitroot(part)
84 if sep in rel:
85 for x in reversed(rel.split(sep)):
86 if x and x != '.':
87 parsed.append(sys.intern(x))
88 else:
89 if rel and rel != '.':
90 parsed.append(sys.intern(rel))
91 if drv or root:
92 if not drv:
93 # If no drive is present, try to find one in the previous
94 # parts. This makes the result of parsing e.g.
95 # ("C:", "/", "a") reasonably intuitive.
96 for part in it:
97 if not part:
98 continue
99 if altsep:
100 part = part.replace(altsep, sep)
101 drv = self.splitroot(part)[0]
102 if drv:
103 break
104 break
105 if drv or root:
106 parsed.append(drv + root)
107 parsed.reverse()
108 return drv, root, parsed
109
110 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
111 """
112 Join the two paths represented by the respective
113 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple.
114 """
115 if root2:
116 if not drv2 and drv:
117 return drv, root2, [drv + root2] + parts2[1:]
118 elif drv2:
119 if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
120 # Same drive => second path is relative to the first
121 return drv, root, parts + parts2[1:]
122 else:
123 # Second path is non-anchored (common case)
124 return drv, root, parts + parts2
125 return drv2, root2, parts2
126
127
128class _WindowsFlavour(_Flavour):
129 # Reference for Windows paths can be found at
130 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
131
132 sep = '\\'
133 altsep = '/'
134 has_drv = True
135 pathmod = ntpath
136
137 is_supported = (os.name == 'nt')
138
139 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
140 ext_namespace_prefix = '\\\\?\\'
141
142 reserved_names = (
143 {'CON', 'PRN', 'AUX', 'NUL', 'CONIN$', 'CONOUT$'} |
144 {'COM%s' % c for c in '123456789\xb9\xb2\xb3'} |
145 {'LPT%s' % c for c in '123456789\xb9\xb2\xb3'}
146 )
147
148 # Interesting findings about extended paths:
149 # * '\\?\c:\a' is an extended path, which bypasses normal Windows API
150 # path processing. Thus relative paths are not resolved and slash is not
151 # translated to backslash. It has the native NT path limit of 32767
152 # characters, but a bit less after resolving device symbolic links,
153 # such as '\??\C:' => '\Device\HarddiskVolume2'.
154 # * '\\?\c:/a' looks for a device named 'C:/a' because slash is a
155 # regular name character in the object namespace.
156 # * '\\?\c:\foo/bar' is invalid because '/' is illegal in NT filesystems.
157 # The only path separator at the filesystem level is backslash.
158 # * '//?/c:\a' and '//?/c:/a' are effectively equivalent to '\\.\c:\a' and
159 # thus limited to MAX_PATH.
160 # * Prior to Windows 8, ANSI API bytes paths are limited to MAX_PATH,
161 # even with the '\\?\' prefix.
162
163 def splitroot(self, part, sep=sep):
164 first = part[0:1]
165 second = part[1:2]
166 if (second == sep and first == sep):
167 # XXX extended paths should also disable the collapsing of "."
168 # components (according to MSDN docs).
169 prefix, part = self._split_extended_path(part)
170 first = part[0:1]
171 second = part[1:2]
172 else:
173 prefix = ''
174 third = part[2:3]
175 if (second == sep and first == sep and third != sep):
176 # is a UNC path:
177 # vvvvvvvvvvvvvvvvvvvvv root
178 # \\machine\mountpoint\directory\etc\...
179 # directory ^^^^^^^^^^^^^^
180 index = part.find(sep, 2)
181 if index != -1:
182 index2 = part.find(sep, index + 1)
183 # a UNC path can't have two slashes in a row
184 # (after the initial two)
185 if index2 != index + 1:
186 if index2 == -1:
187 index2 = len(part)
188 if prefix:
189 return prefix + part[1:index2], sep, part[index2+1:]
190 else:
191 return part[:index2], sep, part[index2+1:]
192 drv = root = ''
193 if second == ':' and first in self.drive_letters:
194 drv = part[:2]
195 part = part[2:]
196 first = third
197 if first == sep:
198 root = first
199 part = part.lstrip(sep)
200 return prefix + drv, root, part
201
202 def casefold(self, s):
203 return s.lower()
204
205 def casefold_parts(self, parts):
206 return [p.lower() for p in parts]
207
208 def compile_pattern(self, pattern):
209 return re.compile(fnmatch.translate(pattern), re.IGNORECASE).fullmatch
210
211 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
212 prefix = ''
213 if s.startswith(ext_prefix):
214 prefix = s[:4]
215 s = s[4:]
216 if s.startswith('UNC\\'):
217 prefix += s[:3]
218 s = '\\' + s[3:]
219 return prefix, s
220
221 def is_reserved(self, parts):
222 # NOTE: the rules for reserved names seem somewhat complicated
223 # (e.g. r"..\NUL" is reserved but not r"foo\NUL" if "foo" does not
224 # exist). We err on the side of caution and return True for paths
225 # which are not considered reserved by Windows.
226 if not parts:
227 return False
228 if parts[0].startswith('\\\\'):
229 # UNC paths are never reserved
230 return False
231 name = parts[-1].partition('.')[0].partition(':')[0].rstrip(' ')
232 return name.upper() in self.reserved_names
233
234 def make_uri(self, path):
235 # Under Windows, file URIs use the UTF-8 encoding.
236 drive = path.drive
237 if len(drive) == 2 and drive[1] == ':':
238 # It's a path on a local drive => 'file:///c:/a/b'
239 rest = path.as_posix()[2:].lstrip('/')
240 return 'file:///%s/%s' % (
241 drive, urlquote_from_bytes(rest.encode('utf-8')))
242 else:
243 # It's a path on a network drive => 'file://host/share/a/b'
244 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
245
246
247class _PosixFlavour(_Flavour):
248 sep = '/'
249 altsep = ''
250 has_drv = False
251 pathmod = posixpath
252
253 is_supported = (os.name != 'nt')
254
255 def splitroot(self, part, sep=sep):
256 if part and part[0] == sep:
257 stripped_part = part.lstrip(sep)
258 # According to POSIX path resolution:
259 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
260 # "A pathname that begins with two successive slashes may be
261 # interpreted in an implementation-defined manner, although more
262 # than two leading slashes shall be treated as a single slash".
263 if len(part) - len(stripped_part) == 2:
264 return '', sep * 2, stripped_part
265 else:
266 return '', sep, stripped_part
267 else:
268 return '', '', part
269
270 def casefold(self, s):
271 return s
272
273 def casefold_parts(self, parts):
274 return parts
275
276 def compile_pattern(self, pattern):
277 return re.compile(fnmatch.translate(pattern)).fullmatch
278
279 def is_reserved(self, parts):
280 return False
281
282 def make_uri(self, path):
283 # We represent the path using the local filesystem encoding,
284 # for portability to other applications.
285 bpath = bytes(path)
286 return 'file://' + urlquote_from_bytes(bpath)
287
288
289_windows_flavour = _WindowsFlavour()
290_posix_flavour = _PosixFlavour()
291
292
293if sys.version_info >= (3, 10):
294 from os.path import realpath as os_path_realpath
295elif os.name == "posix":
296 from pathlib2._posixpath import realpath as os_path_realpath
297else:
298 from pathlib2._ntpath import realpath as os_path_realpath
299
300
301#
302# Globbing helpers
303#
304
305def _make_selector(pattern_parts, flavour):
306 pat = pattern_parts[0]
307 child_parts = pattern_parts[1:]
308 if pat == '**':
309 cls = _RecursiveWildcardSelector
310 elif '**' in pat:
311 raise ValueError("Invalid pattern: '**' can only be an entire path component")
312 elif _is_wildcard_pattern(pat):
313 cls = _WildcardSelector
314 else:
315 cls = _PreciseSelector
316 return cls(pat, child_parts, flavour)
317
318if hasattr(functools, "lru_cache"):
319 _make_selector = functools.lru_cache()(_make_selector)
320
321
322class _Selector:
323 """A selector matches a specific glob pattern part against the children
324 of a given path."""
325
326 def __init__(self, child_parts, flavour):
327 self.child_parts = child_parts
328 if child_parts:
329 self.successor = _make_selector(child_parts, flavour)
330 self.dironly = True
331 else:
332 self.successor = _TerminatingSelector()
333 self.dironly = False
334
335 def select_from(self, parent_path):
336 """Iterate over all child paths of `parent_path` matched by this
337 selector. This can contain parent_path itself."""
338 path_cls = type(parent_path)
339 is_dir = path_cls.is_dir
340 exists = path_cls.exists
341 scandir = path_cls._scandir
342 if not is_dir(parent_path):
343 return iter([])
344 return self._select_from(parent_path, is_dir, exists, scandir)
345
346
347class _TerminatingSelector:
348
349 def _select_from(self, parent_path, is_dir, exists, scandir):
350 yield parent_path
351
352
353class _PreciseSelector(_Selector):
354
355 def __init__(self, name, child_parts, flavour):
356 self.name = name
357 _Selector.__init__(self, child_parts, flavour)
358
359 def _select_from(self, parent_path, is_dir, exists, scandir):
360 try:
361 path = parent_path._make_child_relpath(self.name)
362 if (is_dir if self.dironly else exists)(path):
363 for p in self.successor._select_from(path, is_dir, exists, scandir):
364 yield p
365 except PermissionError:
366 return
367
368
369class _WildcardSelector(_Selector):
370
371 def __init__(self, pat, child_parts, flavour):
372 self.match = flavour.compile_pattern(pat)
373 _Selector.__init__(self, child_parts, flavour)
374
375 def _select_from(self, parent_path, is_dir, exists, scandir):
376 try:
377 with scandir(parent_path) as scandir_it:
378 entries = list(scandir_it)
379 for entry in entries:
380 if self.dironly:
381 try:
382 # "entry.is_dir()" can raise PermissionError
383 # in some cases (see bpo-38894), which is not
384 # among the errors ignored by _ignore_error()
385 if not entry.is_dir():
386 continue
387 except OSError as e:
388 if not _ignore_error(e):
389 raise
390 continue
391 name = entry.name
392 if self.match(name):
393 path = parent_path._make_child_relpath(name)
394 for p in self.successor._select_from(path, is_dir, exists, scandir):
395 yield p
396 except PermissionError:
397 return
398
399
400class _RecursiveWildcardSelector(_Selector):
401
402 def __init__(self, pat, child_parts, flavour):
403 _Selector.__init__(self, child_parts, flavour)
404
405 def _iterate_directories(self, parent_path, is_dir, scandir):
406 yield parent_path
407 try:
408 with scandir(parent_path) as scandir_it:
409 entries = list(scandir_it)
410 for entry in entries:
411 entry_is_dir = False
412 try:
413 entry_is_dir = entry.is_dir()
414 except OSError as e:
415 if not _ignore_error(e):
416 raise
417 if entry_is_dir and not entry.is_symlink():
418 path = parent_path._make_child_relpath(entry.name)
419 for p in self._iterate_directories(path, is_dir, scandir):
420 yield p
421 except PermissionError:
422 return
423
424 def _select_from(self, parent_path, is_dir, exists, scandir):
425 try:
426 yielded = set()
427 try:
428 successor_select = self.successor._select_from
429 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
430 for p in successor_select(starting_point, is_dir, exists, scandir):
431 if p not in yielded:
432 yield p
433 yielded.add(p)
434 finally:
435 yielded.clear()
436 except PermissionError:
437 return
438
439
440#
441# Public API
442#
443
444class _PathParents(Sequence):
445 """This object provides sequence-like access to the logical ancestors
446 of a path. Don't try to construct it yourself."""
447 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
448
449 def __init__(self, path):
450 # We don't store the instance to avoid reference cycles
451 self._pathcls = type(path)
452 self._drv = path._drv
453 self._root = path._root
454 self._parts = path._parts
455
456 def __len__(self):
457 if self._drv or self._root:
458 return len(self._parts) - 1
459 else:
460 return len(self._parts)
461
462 def __getitem__(self, idx):
463 if isinstance(idx, slice):
464 return tuple(self[i] for i in range(*idx.indices(len(self))))
465
466 if idx >= len(self) or idx < -len(self):
467 raise IndexError(idx)
468 return self._pathcls._from_parsed_parts(self._drv, self._root,
469 self._parts[:-idx - 1])
470
471 def __repr__(self):
472 return "<{}.parents>".format(self._pathcls.__name__)
473
474
475class PurePath(object):
476 """Base class for manipulating paths without I/O.
477
478 PurePath represents a filesystem path and offers operations which
479 don't imply any actual filesystem I/O. Depending on your system,
480 instantiating a PurePath will return either a PurePosixPath or a
481 PureWindowsPath object. You can also instantiate either of these classes
482 directly, regardless of your system.
483 """
484 __slots__ = (
485 '_drv', '_root', '_parts',
486 '_str', '_hash', '_pparts', '_cached_cparts',
487 )
488
489 def __new__(cls, *args):
490 """Construct a PurePath from one or several strings and or existing
491 PurePath objects. The strings and path objects are combined so as
492 to yield a canonicalized path, which is incorporated into the
493 new PurePath object.
494 """
495 if cls is PurePath:
496 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
497 return cls._from_parts(args)
498
499 def __reduce__(self):
500 # Using the parts tuple helps share interned path parts
501 # when pickling related paths.
502 return (self.__class__, tuple(self._parts))
503
504 @classmethod
505 def _parse_args(cls, args):
506 # This is useful when you don't want to create an instance, just
507 # canonicalize some constructor arguments.
508 parts = []
509 for a in args:
510 if isinstance(a, PurePath):
511 parts += a._parts
512 else:
513 a = os.fspath(a)
514 if isinstance(a, str):
515 # Force-cast str subclasses to str (issue #21127)
516 parts.append(str(a))
517 else:
518 raise TypeError(
519 "argument should be a str object or an os.PathLike "
520 "object returning str, not %r"
521 % type(a))
522 return cls._flavour.parse_parts(parts)
523
524 @classmethod
525 def _from_parts(cls, args):
526 # We need to call _parse_args on the instance, so as to get the
527 # right flavour.
528 self = object.__new__(cls)
529 drv, root, parts = self._parse_args(args)
530 self._drv = drv
531 self._root = root
532 self._parts = parts
533 return self
534
535 @classmethod
536 def _from_parsed_parts(cls, drv, root, parts):
537 self = object.__new__(cls)
538 self._drv = drv
539 self._root = root
540 self._parts = parts
541 return self
542
543 @classmethod
544 def _format_parsed_parts(cls, drv, root, parts):
545 if drv or root:
546 return drv + root + cls._flavour.join(parts[1:])
547 else:
548 return cls._flavour.join(parts)
549
550 def _make_child(self, args):
551 drv, root, parts = self._parse_args(args)
552 drv, root, parts = self._flavour.join_parsed_parts(
553 self._drv, self._root, self._parts, drv, root, parts)
554 return self._from_parsed_parts(drv, root, parts)
555
556 def __str__(self):
557 """Return the string representation of the path, suitable for
558 passing to system calls."""
559 try:
560 return self._str
561 except AttributeError:
562 self._str = self._format_parsed_parts(self._drv, self._root,
563 self._parts) or '.'
564 return self._str
565
566 def __fspath__(self):
567 return str(self)
568
569 def as_posix(self):
570 """Return the string representation of the path with forward (/)
571 slashes."""
572 f = self._flavour
573 return str(self).replace(f.sep, '/')
574
575 def __bytes__(self):
576 """Return the bytes representation of the path. This is only
577 recommended to use under Unix."""
578 return os.fsencode(self)
579
580 def __repr__(self):
581 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
582
583 def as_uri(self):
584 """Return the path as a 'file' URI."""
585 if not self.is_absolute():
586 raise ValueError("relative path can't be expressed as a file URI")
587 return self._flavour.make_uri(self)
588
589 @property
590 def _cparts(self):
591 # Cached casefolded parts, for hashing and comparison
592 try:
593 return self._cached_cparts
594 except AttributeError:
595 self._cached_cparts = self._flavour.casefold_parts(self._parts)
596 return self._cached_cparts
597
598 def __eq__(self, other):
599 if not isinstance(other, PurePath):
600 return NotImplemented
601 return self._cparts == other._cparts and self._flavour is other._flavour
602
603 def __hash__(self):
604 try:
605 return self._hash
606 except AttributeError:
607 self._hash = hash(tuple(self._cparts))
608 return self._hash
609
610 def __lt__(self, other):
611 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
612 return NotImplemented
613 return self._cparts < other._cparts
614
615 def __le__(self, other):
616 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
617 return NotImplemented
618 return self._cparts <= other._cparts
619
620 def __gt__(self, other):
621 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
622 return NotImplemented
623 return self._cparts > other._cparts
624
625 def __ge__(self, other):
626 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
627 return NotImplemented
628 return self._cparts >= other._cparts
629
630 drive = property(attrgetter('_drv'),
631 doc="""The drive prefix (letter or UNC path), if any.""")
632
633 root = property(attrgetter('_root'),
634 doc="""The root of the path, if any.""")
635
636 @property
637 def anchor(self):
638 """The concatenation of the drive and root, or ''."""
639 anchor = self._drv + self._root
640 return anchor
641
642 @property
643 def name(self):
644 """The final path component, if any."""
645 parts = self._parts
646 if len(parts) == (1 if (self._drv or self._root) else 0):
647 return ''
648 return parts[-1]
649
650 @property
651 def suffix(self):
652 """
653 The final component's last suffix, if any.
654
655 This includes the leading period. For example: '.txt'
656 """
657 name = self.name
658 i = name.rfind('.')
659 if 0 < i < len(name) - 1:
660 return name[i:]
661 else:
662 return ''
663
664 @property
665 def suffixes(self):
666 """
667 A list of the final component's suffixes, if any.
668
669 These include the leading periods. For example: ['.tar', '.gz']
670 """
671 name = self.name
672 if name.endswith('.'):
673 return []
674 name = name.lstrip('.')
675 return ['.' + suffix for suffix in name.split('.')[1:]]
676
677 @property
678 def stem(self):
679 """The final path component, minus its last suffix."""
680 name = self.name
681 i = name.rfind('.')
682 if 0 < i < len(name) - 1:
683 return name[:i]
684 else:
685 return name
686
687 def with_name(self, name):
688 """Return a new path with the file name changed."""
689 if not self.name:
690 raise ValueError("%r has an empty name" % (self,))
691 drv, root, parts = self._flavour.parse_parts((name,))
692 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
693 or drv or root or len(parts) != 1):
694 raise ValueError("Invalid name %r" % (name))
695 return self._from_parsed_parts(self._drv, self._root,
696 self._parts[:-1] + [name])
697
698 def with_stem(self, stem):
699 """Return a new path with the stem changed."""
700 return self.with_name(stem + self.suffix)
701
702 def with_suffix(self, suffix):
703 """Return a new path with the file suffix changed. If the path
704 has no suffix, add given suffix. If the given suffix is an empty
705 string, remove the suffix from the path.
706 """
707 f = self._flavour
708 if f.sep in suffix or f.altsep and f.altsep in suffix:
709 raise ValueError("Invalid suffix %r" % (suffix,))
710 if suffix and not suffix.startswith('.') or suffix == '.':
711 raise ValueError("Invalid suffix %r" % (suffix))
712 name = self.name
713 if not name:
714 raise ValueError("%r has an empty name" % (self,))
715 old_suffix = self.suffix
716 if not old_suffix:
717 name = name + suffix
718 else:
719 name = name[:-len(old_suffix)] + suffix
720 return self._from_parsed_parts(self._drv, self._root,
721 self._parts[:-1] + [name])
722
723 def relative_to(self, *other):
724 """Return the relative path to another path identified by the passed
725 arguments. If the operation is not possible (because this is not
726 a subpath of the other path), raise ValueError.
727 """
728 # For the purpose of this method, drive and root are considered
729 # separate parts, i.e.:
730 # Path('c:/').relative_to('c:') gives Path('/')
731 # Path('c:/').relative_to('/') raise ValueError
732 if not other:
733 raise TypeError("need at least one argument")
734 parts = self._parts
735 drv = self._drv
736 root = self._root
737 if root:
738 abs_parts = [drv, root] + parts[1:]
739 else:
740 abs_parts = parts
741 to_drv, to_root, to_parts = self._parse_args(other)
742 if to_root:
743 to_abs_parts = [to_drv, to_root] + to_parts[1:]
744 else:
745 to_abs_parts = to_parts
746 n = len(to_abs_parts)
747 cf = self._flavour.casefold_parts
748 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
749 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
750 raise ValueError("{!r} is not in the subpath of {!r}"
751 " OR one path is relative and the other is absolute."
752 .format(str(self), str(formatted)))
753 return self._from_parsed_parts('', root if n == 1 else '',
754 abs_parts[n:])
755
756 def is_relative_to(self, *other):
757 """Return True if the path is relative to another path or False.
758 """
759 try:
760 self.relative_to(*other)
761 return True
762 except ValueError:
763 return False
764
765 @property
766 def parts(self):
767 """An object providing sequence-like access to the
768 components in the filesystem path."""
769 # We cache the tuple to avoid building a new one each time .parts
770 # is accessed. XXX is this necessary?
771 try:
772 return self._pparts
773 except AttributeError:
774 self._pparts = tuple(self._parts)
775 return self._pparts
776
777 def joinpath(self, *args):
778 """Combine this path with one or several arguments, and return a
779 new path representing either a subpath (if all arguments are relative
780 paths) or a totally different path (if one of the arguments is
781 anchored).
782 """
783 return self._make_child(args)
784
785 def __truediv__(self, key):
786 try:
787 return self._make_child((key,))
788 except TypeError:
789 return NotImplemented
790
791 def __rtruediv__(self, key):
792 try:
793 return self._from_parts([key] + self._parts)
794 except TypeError:
795 return NotImplemented
796
797 @property
798 def parent(self):
799 """The logical parent of the path."""
800 drv = self._drv
801 root = self._root
802 parts = self._parts
803 if len(parts) == 1 and (drv or root):
804 return self
805 return self._from_parsed_parts(drv, root, parts[:-1])
806
807 @property
808 def parents(self):
809 """A sequence of this path's logical parents."""
810 return _PathParents(self)
811
812 def is_absolute(self):
813 """True if the path is absolute (has both a root and, if applicable,
814 a drive)."""
815 if not self._root:
816 return False
817 return not self._flavour.has_drv or bool(self._drv)
818
819 def is_reserved(self):
820 """Return True if the path contains one of the special names reserved
821 by the system, if any."""
822 return self._flavour.is_reserved(self._parts)
823
824 def match(self, path_pattern):
825 """
826 Return True if this path matches the given pattern.
827 """
828 cf = self._flavour.casefold
829 path_pattern = cf(path_pattern)
830 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
831 if not pat_parts:
832 raise ValueError("empty pattern")
833 if drv and drv != cf(self._drv):
834 return False
835 if root and root != cf(self._root):
836 return False
837 parts = self._cparts
838 if drv or root:
839 if len(pat_parts) != len(parts):
840 return False
841 pat_parts = pat_parts[1:]
842 elif len(pat_parts) > len(parts):
843 return False
844 for part, pat in zip(reversed(parts), reversed(pat_parts)):
845 if not fnmatch.fnmatchcase(part, pat):
846 return False
847 return True
848
849# Can't subclass os.PathLike from PurePath and keep the constructor
850# optimizations in PurePath._parse_args().
851os.PathLike.register(PurePath)
852
853
854class PurePosixPath(PurePath):
855 """PurePath subclass for non-Windows systems.
856
857 On a POSIX system, instantiating a PurePath should return this object.
858 However, you can also instantiate it directly on any system.
859 """
860 _flavour = _posix_flavour
861 __slots__ = ()
862
863
864class PureWindowsPath(PurePath):
865 """PurePath subclass for Windows systems.
866
867 On a Windows system, instantiating a PurePath should return this object.
868 However, you can also instantiate it directly on any system.
869 """
870 _flavour = _windows_flavour
871 __slots__ = ()
872
873
874# Filesystem-accessing classes
875
876
877class Path(PurePath):
878 """PurePath subclass that can make system calls.
879
880 Path represents a filesystem path but unlike PurePath, also offers
881 methods to do system calls on path objects. Depending on your system,
882 instantiating a Path will return either a PosixPath or a WindowsPath
883 object. You can also instantiate a PosixPath or WindowsPath directly,
884 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
885 """
886 __slots__ = ()
887
888 def __new__(cls, *args, **kwargs):
889 if cls is Path:
890 cls = WindowsPath if os.name == 'nt' else PosixPath
891 self = cls._from_parts(args)
892 if not self._flavour.is_supported:
893 raise NotImplementedError("cannot instantiate %r on your system"
894 % (cls.__name__,))
895 return self
896
897 def _make_child_relpath(self, part):
898 # This is an optimization used for dir walking. `part` must be
899 # a single part relative to this path.
900 parts = self._parts + [part]
901 return self._from_parsed_parts(self._drv, self._root, parts)
902
903 def __enter__(self):
904 return self
905
906 def __exit__(self, t, v, tb):
907 # https://bugs.python.org/issue39682
908 # In previous versions of pathlib, this method marked this path as
909 # closed; subsequent attempts to perform I/O would raise an IOError.
910 # This functionality was never documented, and had the effect of
911 # making Path objects mutable, contrary to PEP 428. In Python 3.9 the
912 # _closed attribute was removed, and this method made a no-op.
913 # This method and __enter__()/__exit__() should be deprecated and
914 # removed in the future.
915 pass
916
917 # Public API
918
919 @classmethod
920 def cwd(cls):
921 """Return a new path pointing to the current working directory
922 (as returned by os.getcwd()).
923 """
924 return cls(os.getcwd())
925
926 @classmethod
927 def home(cls):
928 """Return a new path pointing to the user's home directory (as
929 returned by os.path.expanduser('~')).
930 """
931 return cls("~").expanduser()
932
933 def samefile(self, other_path):
934 """Return whether other_path is the same or not as this file
935 (as returned by os.path.samefile()).
936 """
937 st = self.stat()
938 try:
939 other_st = other_path.stat()
940 except AttributeError:
941 other_st = self.__class__(other_path).stat()
942 return os.path.samestat(st, other_st)
943
944 def iterdir(self):
945 """Iterate over the files in this directory. Does not yield any
946 result for the special paths '.' and '..'.
947 """
948 for name in os.listdir(self):
949 yield self._make_child_relpath(name)
950
951 def _scandir(self):
952 # bpo-24132: a future version of pathlib will support subclassing of
953 # pathlib.Path to customize how the filesystem is accessed. This
954 # includes scandir(), which is used to implement glob().
955 return os.scandir(self)
956
957 def glob(self, pattern):
958 """Iterate over this subtree and yield all existing files (of any
959 kind, including directories) matching the given relative pattern.
960 """
961 sys.audit("pathlib.Path.glob", self, pattern)
962 if not pattern:
963 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
964 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
965 if drv or root:
966 raise NotImplementedError("Non-relative patterns are unsupported")
967 selector = _make_selector(tuple(pattern_parts), self._flavour)
968 for p in selector.select_from(self):
969 yield p
970
971 def rglob(self, pattern):
972 """Recursively yield all existing files (of any kind, including
973 directories) matching the given relative pattern, anywhere in
974 this subtree.
975 """
976 sys.audit("pathlib.Path.rglob", self, pattern)
977 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
978 if drv or root:
979 raise NotImplementedError("Non-relative patterns are unsupported")
980 selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour)
981 for p in selector.select_from(self):
982 yield p
983
984 def absolute(self):
985 """Return an absolute version of this path by prepending the current
986 working directory. No normalization or symlink resolution is performed.
987
988 Use resolve() to get the canonical path to a file.
989 """
990 if self.is_absolute():
991 return self
992 return self._from_parts([self.cwd()] + self._parts)
993
994 def resolve(self, strict=False):
995 """
996 Make the path absolute, resolving all symlinks on the way and also
997 normalizing it.
998 """
999
1000 def check_eloop(e):
1001 winerror = getattr(e, 'winerror', 0)
1002 if e.errno == ELOOP or winerror == _WINERROR_CANT_RESOLVE_FILENAME:
1003 raise RuntimeError("Symlink loop from %r" % e.filename)
1004
1005 try:
1006 s = os_path_realpath(self, strict=strict)
1007 except OSError as e:
1008 check_eloop(e)
1009 raise
1010 p = self._from_parts((s,))
1011
1012 # In non-strict mode, realpath() doesn't raise on symlink loops.
1013 # Ensure we get an exception by calling stat()
1014 if not strict:
1015 try:
1016 p.stat()
1017 except OSError as e:
1018 check_eloop(e)
1019 return p
1020
1021 def stat(self, *, follow_symlinks=True):
1022 """
1023 Return the result of the stat() system call on this path, like
1024 os.stat() does.
1025 """
1026 return os.stat(self, follow_symlinks=follow_symlinks)
1027
1028 def owner(self):
1029 """
1030 Return the login name of the file owner.
1031 """
1032 try:
1033 import pwd
1034 return pwd.getpwuid(self.stat().st_uid).pw_name
1035 except ImportError:
1036 raise NotImplementedError("Path.owner() is unsupported on this system")
1037
1038 def group(self):
1039 """
1040 Return the group name of the file gid.
1041 """
1042
1043 try:
1044 import grp
1045 return grp.getgrgid(self.stat().st_gid).gr_name
1046 except ImportError:
1047 raise NotImplementedError("Path.group() is unsupported on this system")
1048
1049 def open(self, mode='r', buffering=-1, encoding=None,
1050 errors=None, newline=None):
1051 """
1052 Open the file pointed by this path and return a file object, as
1053 the built-in open() function does.
1054 """
1055 if "b" not in mode:
1056 encoding = io_text_encoding(encoding)
1057 return io.open(self, mode, buffering, encoding, errors, newline)
1058
1059 def read_bytes(self):
1060 """
1061 Open the file in bytes mode, read it, and close the file.
1062 """
1063 with self.open(mode='rb') as f:
1064 return f.read()
1065
1066 def read_text(self, encoding=None, errors=None):
1067 """
1068 Open the file in text mode, read it, and close the file.
1069 """
1070 encoding = io_text_encoding(encoding)
1071 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1072 return f.read()
1073
1074 def write_bytes(self, data):
1075 """
1076 Open the file in bytes mode, write to it, and close the file.
1077 """
1078 # type-check for the buffer interface before truncating the file
1079 view = memoryview(data)
1080 with self.open(mode='wb') as f:
1081 return f.write(view)
1082
1083 def write_text(self, data, encoding=None, errors=None, newline=None):
1084 """
1085 Open the file in text mode, write to it, and close the file.
1086 """
1087 if not isinstance(data, str):
1088 raise TypeError('data must be str, not %s' %
1089 data.__class__.__name__)
1090 encoding = io_text_encoding(encoding)
1091 with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
1092 return f.write(data)
1093
1094 def readlink(self):
1095 """
1096 Return the path to which the symbolic link points.
1097 """
1098 if not hasattr(os, "readlink"):
1099 raise NotImplementedError("os.readlink() not available on this system")
1100 return self._from_parts((os.readlink(self),))
1101
1102 def touch(self, mode=0o666, exist_ok=True):
1103 """
1104 Create this file with the given access mode, if it doesn't exist.
1105 """
1106
1107 if exist_ok:
1108 # First try to bump modification time
1109 # Implementation note: GNU touch uses the UTIME_NOW option of
1110 # the utimensat() / futimens() functions.
1111 try:
1112 os.utime(self, None)
1113 except OSError:
1114 # Avoid exception chaining
1115 pass
1116 else:
1117 return
1118 flags = os.O_CREAT | os.O_WRONLY
1119 if not exist_ok:
1120 flags |= os.O_EXCL
1121 fd = os.open(self, flags, mode)
1122 os.close(fd)
1123
1124 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
1125 """
1126 Create a new directory at this given path.
1127 """
1128 try:
1129 os.mkdir(self, mode)
1130 except FileNotFoundError:
1131 if not parents or self.parent == self:
1132 raise
1133 self.parent.mkdir(parents=True, exist_ok=True)
1134 self.mkdir(mode, parents=False, exist_ok=exist_ok)
1135 except OSError:
1136 # Cannot rely on checking for EEXIST, since the operating system
1137 # could give priority to other errors like EACCES or EROFS
1138 if not exist_ok or not self.is_dir():
1139 raise
1140
1141 def chmod(self, mode, *, follow_symlinks=True):
1142 """
1143 Change the permissions of the path, like os.chmod().
1144 """
1145 os.chmod(self, mode, follow_symlinks=follow_symlinks)
1146
1147 def lchmod(self, mode):
1148 """
1149 Like chmod(), except if the path points to a symlink, the symlink's
1150 permissions are changed, rather than its target's.
1151 """
1152 self.chmod(mode, follow_symlinks=False)
1153
1154 def unlink(self, missing_ok=False):
1155 """
1156 Remove this file or link.
1157 If the path is a directory, use rmdir() instead.
1158 """
1159 try:
1160 os.unlink(self)
1161 except FileNotFoundError:
1162 if not missing_ok:
1163 raise
1164
1165 def rmdir(self):
1166 """
1167 Remove this directory. The directory must be empty.
1168 """
1169 os.rmdir(self)
1170
1171 def lstat(self):
1172 """
1173 Like stat(), except if the path points to a symlink, the symlink's
1174 status information is returned, rather than its target's.
1175 """
1176 return self.stat(follow_symlinks=False)
1177
1178 def rename(self, target):
1179 """
1180 Rename this path to the target path.
1181
1182 The target path may be absolute or relative. Relative paths are
1183 interpreted relative to the current working directory, *not* the
1184 directory of the Path object.
1185
1186 Returns the new Path instance pointing to the target path.
1187 """
1188 os.rename(self, target)
1189 return self.__class__(target)
1190
1191 def replace(self, target):
1192 """
1193 Rename this path to the target path, overwriting if that path exists.
1194
1195 The target path may be absolute or relative. Relative paths are
1196 interpreted relative to the current working directory, *not* the
1197 directory of the Path object.
1198
1199 Returns the new Path instance pointing to the target path.
1200 """
1201 os.replace(self, target)
1202 return self.__class__(target)
1203
1204 def symlink_to(self, target, target_is_directory=False):
1205 """
1206 Make this path a symlink pointing to the target path.
1207 Note the order of arguments (link, target) is the reverse of os.symlink.
1208 """
1209 if not hasattr(os, "symlink"):
1210 raise NotImplementedError("os.symlink() not available on this system")
1211 os.symlink(target, self, target_is_directory)
1212
1213 def hardlink_to(self, target):
1214 """
1215 Make this path a hard link pointing to the same file as *target*.
1216
1217 Note the order of arguments (self, target) is the reverse of os.link's.
1218 """
1219 if not hasattr(os, "link"):
1220 raise NotImplementedError("os.link() not available on this system")
1221 os.link(target, self)
1222
1223 def link_to(self, target):
1224 """
1225 Make the target path a hard link pointing to this path.
1226
1227 Note this function does not make this path a hard link to *target*,
1228 despite the implication of the function and argument names. The order
1229 of arguments (target, link) is the reverse of Path.symlink_to, but
1230 matches that of os.link.
1231
1232 Deprecated since Python 3.10 and scheduled for removal in Python 3.12.
1233 Use `hardlink_to()` instead.
1234 """
1235 warnings.warn("pathlib.Path.link_to() is deprecated and is scheduled "
1236 "for removal in Python 3.12. "
1237 "Use pathlib.Path.hardlink_to() instead.",
1238 DeprecationWarning, stacklevel=2)
1239 self.__class__(target).hardlink_to(self)
1240
1241 # Convenience functions for querying the stat results
1242
1243 def exists(self):
1244 """
1245 Whether this path exists.
1246 """
1247 try:
1248 self.stat()
1249 except OSError as e:
1250 if not _ignore_error(e):
1251 raise
1252 return False
1253 except ValueError:
1254 # Non-encodable path
1255 return False
1256 return True
1257
1258 def is_dir(self):
1259 """
1260 Whether this path is a directory.
1261 """
1262 try:
1263 return S_ISDIR(self.stat().st_mode)
1264 except OSError as e:
1265 if not _ignore_error(e):
1266 raise
1267 # Path doesn't exist or is a broken symlink
1268 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
1269 return False
1270 except ValueError:
1271 # Non-encodable path
1272 return False
1273
1274 def is_file(self):
1275 """
1276 Whether this path is a regular file (also True for symlinks pointing
1277 to regular files).
1278 """
1279 try:
1280 return S_ISREG(self.stat().st_mode)
1281 except OSError as e:
1282 if not _ignore_error(e):
1283 raise
1284 # Path doesn't exist or is a broken symlink
1285 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
1286 return False
1287 except ValueError:
1288 # Non-encodable path
1289 return False
1290
1291 def is_mount(self):
1292 """
1293 Check if this path is a POSIX mount point
1294 """
1295 # Need to exist and be a dir
1296 if not self.exists() or not self.is_dir():
1297 return False
1298
1299 try:
1300 parent_dev = self.parent.stat().st_dev
1301 except OSError:
1302 return False
1303
1304 dev = self.stat().st_dev
1305 if dev != parent_dev:
1306 return True
1307 ino = self.stat().st_ino
1308 parent_ino = self.parent.stat().st_ino
1309 return ino == parent_ino
1310
1311 def is_symlink(self):
1312 """
1313 Whether this path is a symbolic link.
1314 """
1315 try:
1316 return S_ISLNK(self.lstat().st_mode)
1317 except OSError as e:
1318 if not _ignore_error(e):
1319 raise
1320 # Path doesn't exist
1321 return False
1322 except ValueError:
1323 # Non-encodable path
1324 return False
1325
1326 def is_block_device(self):
1327 """
1328 Whether this path is a block device.
1329 """
1330 try:
1331 return S_ISBLK(self.stat().st_mode)
1332 except OSError as e:
1333 if not _ignore_error(e):
1334 raise
1335 # Path doesn't exist or is a broken symlink
1336 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
1337 return False
1338 except ValueError:
1339 # Non-encodable path
1340 return False
1341
1342 def is_char_device(self):
1343 """
1344 Whether this path is a character device.
1345 """
1346 try:
1347 return S_ISCHR(self.stat().st_mode)
1348 except OSError as e:
1349 if not _ignore_error(e):
1350 raise
1351 # Path doesn't exist or is a broken symlink
1352 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
1353 return False
1354 except ValueError:
1355 # Non-encodable path
1356 return False
1357
1358 def is_fifo(self):
1359 """
1360 Whether this path is a FIFO.
1361 """
1362 try:
1363 return S_ISFIFO(self.stat().st_mode)
1364 except OSError as e:
1365 if not _ignore_error(e):
1366 raise
1367 # Path doesn't exist or is a broken symlink
1368 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
1369 return False
1370 except ValueError:
1371 # Non-encodable path
1372 return False
1373
1374 def is_socket(self):
1375 """
1376 Whether this path is a socket.
1377 """
1378 try:
1379 return S_ISSOCK(self.stat().st_mode)
1380 except OSError as e:
1381 if not _ignore_error(e):
1382 raise
1383 # Path doesn't exist or is a broken symlink
1384 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
1385 return False
1386 except ValueError:
1387 # Non-encodable path
1388 return False
1389
1390 def expanduser(self):
1391 """ Return a new path with expanded ~ and ~user constructs
1392 (as returned by os.path.expanduser)
1393 """
1394 if (not (self._drv or self._root) and
1395 self._parts and self._parts[0][:1] == '~'):
1396 homedir = os.path.expanduser(self._parts[0])
1397 if homedir[:1] == "~":
1398 raise RuntimeError("Could not determine home directory.")
1399 return self._from_parts([homedir] + self._parts[1:])
1400
1401 return self
1402
1403
1404class PosixPath(Path, PurePosixPath):
1405 """Path subclass for non-Windows systems.
1406
1407 On a POSIX system, instantiating a Path should return this object.
1408 """
1409 __slots__ = ()
1410
1411class WindowsPath(Path, PureWindowsPath):
1412 """Path subclass for Windows systems.
1413
1414 On a Windows system, instantiating a Path should return this object.
1415 """
1416 __slots__ = ()
1417
1418 def is_mount(self):
1419 raise NotImplementedError("Path.is_mount() is unsupported on this system")