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