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