1
"""Object-oriented filesystem paths.
3
This module provides classes to represent abstract paths and concrete
4
paths with operations that have semantics appropriate for different
15
from itertools import chain
17
S_IMODE, S_ISDIR, S_ISREG, S_ISLNK, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO,
19
from _collections_abc import Sequence
21
lazy from glob import _StringGlobber, _no_recurse_symlinks
32
from pathlib._os import (
34
ensure_different_files, ensure_distinct_paths,
35
copyfile2, copyfileobj,
40
"UnsupportedOperation",
41
"PurePath", "PurePosixPath", "PureWindowsPath",
42
"Path", "PosixPath", "WindowsPath",
46
class UnsupportedOperation(NotImplementedError):
47
"""An exception that is raised when an unsupported operation is attempted.
52
class _PathParents(Sequence):
53
"""This object provides sequence-like access to the logical ancestors
54
of a path. Don't try to construct it yourself."""
55
__slots__ = ('_path', '_drv', '_root', '_tail')
57
def __init__(self, path):
59
self._drv = path.drive
60
self._root = path.root
61
self._tail = path._tail
64
return len(self._tail)
66
def __getitem__(self, idx):
67
if isinstance(idx, slice):
68
return tuple(self[i] for i in range(*idx.indices(len(self))))
70
if idx >= len(self) or idx < -len(self):
74
return self._path._from_parsed_parts(self._drv, self._root,
75
self._tail[:-idx - 1])
78
return "<{}.parents>".format(type(self._path).__name__)
82
"""Base class for manipulating paths without I/O.
84
PurePath represents a filesystem path and offers operations which
85
don't imply any actual filesystem I/O. Depending on your system,
86
instantiating a PurePath will return either a PurePosixPath or a
87
PureWindowsPath object. You can also instantiate either of these classes
88
directly, regardless of your system.
92
# The `_raw_paths` slot stores unjoined string paths. This is set in
93
# the `__init__()` method.
96
# The `_drv`, `_root` and `_tail_cached` slots store parsed and
97
# normalized parts of the path. They are set when any of the `drive`,
98
# `root` or `_tail` properties are accessed for the first time. The
99
# three-part division corresponds to the result of
100
# `os.path.splitroot()`, except that the tail is further split on path
101
# separators (i.e. it is a list of strings), and that the root and
102
# tail are normalized.
103
'_drv', '_root', '_tail_cached',
105
# The `_str` slot stores the string representation of the path,
106
# computed from the drive, root and tail when `__str__()` is called
107
# for the first time. It's used to implement `_str_normcase`
110
# The `_str_normcase_cached` slot stores the string path with
111
# normalized case. It is set when the `_str_normcase` property is
112
# accessed for the first time. It's used to implement `__eq__()`
113
# `__hash__()`, and `_parts_normcase`
114
'_str_normcase_cached',
116
# The `_parts_normcase_cached` slot stores the case-normalized
117
# string path after splitting on path separators. It's set when the
118
# `_parts_normcase` property is accessed for the first time. It's used
119
# to implement comparison methods like `__lt__()`.
120
'_parts_normcase_cached',
122
# The `_hash` slot stores the hash of the case-normalized string
123
# path. It's set when `__hash__()` is called for the first time.
128
def __new__(cls, *args, **kwargs):
129
"""Construct a PurePath from one or several strings and or existing
130
PurePath objects. The strings and path objects are combined so as
131
to yield a canonicalized path, which is incorporated into the
135
cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
136
return object.__new__(cls)
138
def __init__(self, *args):
141
if isinstance(arg, PurePath):
142
if arg.parser is not self.parser:
143
# GH-103631: Convert separators for backwards compatibility.
144
paths.append(arg.as_posix())
146
paths.extend(arg._raw_paths)
149
path = os.fspath(arg)
152
if not isinstance(path, str):
154
"argument should be a str or an os.PathLike "
155
"object where __fspath__ returns a str, "
156
f"not {type(path).__name__!r}")
158
self._raw_paths = paths
160
def with_segments(self, *pathsegments):
161
"""Construct a new path object from any number of path-like objects.
162
Subclasses may override this method to customize how new path objects
163
are created from methods like `iterdir()`.
165
3
3
return type(self)(*pathsegments)
167
def joinpath(self, *pathsegments):
168
"""Combine this path with one or several arguments, and return a
169
new path representing either a subpath (if all arguments are relative
170
paths) or a totally different path (if one of the arguments is
173
return self.with_segments(self, *pathsegments)
175
def __truediv__(self, key):
177
return self.with_segments(self, key)
179
return NotImplemented
181
def __rtruediv__(self, key):
183
return self.with_segments(key, self)
185
return NotImplemented
187
def __reduce__(self):
188
return self.__class__, tuple(self._raw_paths)
191
return "{}({!r})".format(self.__class__.__name__, self.as_posix())
194
"""Return the bytes representation of the path. This is only
195
recommended to use under Unix."""
196
return os.fsencode(self)
199
def _str_normcase(self):
200
# String with normalized case, for hashing and equality checks
202
1
1
return self._str_normcase_cached
203
except AttributeError:
204
if self.parser is posixpath:
205
self._str_normcase_cached = str(self)
207
self._str_normcase_cached = str(self).lower()
208
return self._str_normcase_cached
213
except AttributeError:
214
self._hash = hash(self._str_normcase)
217
def __eq__(self, other):
218
if not isinstance(other, PurePath):
219
return NotImplemented
220
return self._str_normcase == other._str_normcase and self.parser is other.parser
223
def _parts_normcase(self):
224
# Cached parts with normalized case, for comparisons.
226
return self._parts_normcase_cached
227
except AttributeError:
228
self._parts_normcase_cached = self._str_normcase.split(self.parser.sep)
229
return self._parts_normcase_cached
231
def __lt__(self, other):
232
if not isinstance(other, PurePath) or self.parser is not other.parser:
233
return NotImplemented
234
return self._parts_normcase < other._parts_normcase
236
def __le__(self, other):
237
if not isinstance(other, PurePath) or self.parser is not other.parser:
238
return NotImplemented
239
return self._parts_normcase <= other._parts_normcase
241
def __gt__(self, other):
242
if not isinstance(other, PurePath) or self.parser is not other.parser:
243
return NotImplemented
244
return self._parts_normcase > other._parts_normcase
246
def __ge__(self, other):
247
if not isinstance(other, PurePath) or self.parser is not other.parser:
248
return NotImplemented
249
return self._parts_normcase >= other._parts_normcase
252
"""Return the string representation of the path, suitable for
253
passing to system calls."""
256
except AttributeError:
257
3
3
self._str = self._format_parsed_parts(self.drive, self.root,
262
__vfspath__ = __str__
265
def _format_parsed_parts(cls, drv, root, tail):
267
return drv + root + cls.parser.sep.join(tail)
268
elif tail and cls.parser.splitdrive(tail[0])[0]:
270
return cls.parser.sep.join(tail)
272
def _from_parsed_parts(self, drv, root, tail):
273
2
2
path = self._from_parsed_string(self._format_parsed_parts(drv, root, tail))
276
path._tail_cached = tail
279
def _from_parsed_string(self, path_str):
280
path = self.with_segments(path_str)
281
path._str = path_str or '.'
285
def _parse_path(cls, path):
289
altsep = cls.parser.altsep
291
path = path.replace(altsep, sep)
292
drv, root, rel = cls.parser.splitroot(path)
293
if not root and drv.startswith(sep) and not drv.endswith(sep):
294
drv_parts = drv.split(sep)
295
if len(drv_parts) == 4 and drv_parts[2] not in '?.':
296
# e.g. //server/share
298
elif len(drv_parts) == 6:
299
# e.g. //?/unc/server/share
301
1
1
return drv, root, [x for x in rel.split(sep) if x and x != '.']
304
def _parse_pattern(cls, pattern):
305
"""Parse a glob pattern to a list of parts. This is much like
308
- Rather than normalizing and returning the drive and root, we raise
309
NotImplementedError if either are present.
310
- If the path has no real parts, we raise ValueError.
311
- If the path ends in a slash, then a final empty part is added.
313
drv, root, rel = cls.parser.splitroot(pattern)
315
raise NotImplementedError("Non-relative patterns are unsupported")
317
altsep = cls.parser.altsep
319
rel = rel.replace(altsep, sep)
320
parts = [x for x in rel.split(sep) if x and x != '.']
322
raise ValueError(f"Unacceptable pattern: {str(pattern)!r}")
323
elif rel.endswith(sep):
324
# GH-65238: preserve trailing slash in glob patterns.
329
"""Return the string representation of the path with forward (/)
331
18
18
return str(self).replace(self.parser.sep, '/')
335
paths = self._raw_paths
339
# Join path segments from the initializer.
340
return self.parser.join(*paths)
346
"""The drive prefix (letter or UNC path), if any."""
349
except AttributeError:
350
self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path)
355
"""The root of the path, if any."""
358
except AttributeError:
359
self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path)
365
3
3
return self._tail_cached
366
except AttributeError:
367
6
7
self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path)
368
return self._tail_cached
372
"""The concatenation of the drive and root, or ''."""
373
return self.drive + self.root
377
"""An object providing sequence-like access to the
378
components in the filesystem path."""
379
1
1
if self.drive or self.root:
380
return (self.drive + self.root,) + tuple(self._tail)
382
return tuple(self._tail)
386
"""The logical parent of the path."""
392
return self._from_parsed_parts(drv, root, tail[:-1])
396
"""A sequence of this path's logical parents."""
397
# The value of this property should not be cached on the path object,
398
# as doing so would introduce a reference cycle.
399
2
2
return _PathParents(self)
403
"""The final path component, if any."""
404
33
37
tail = self._tail
409
def with_name(self, name):
410
"""Return a new path with the file name changed."""
412
if not name or p.sep in name or (p.altsep and p.altsep in name) or name == '.':
413
raise ValueError(f"Invalid name {name!r}")
414
tail = self._tail.copy()
416
raise ValueError(f"{self!r} has an empty name")
418
return self._from_parsed_parts(self.drive, self.root, tail)
420
def with_stem(self, stem):
421
"""Return a new path with the stem changed."""
424
return self.with_name(stem)
426
# If the suffix is non-empty, we can't make the stem empty.
427
raise ValueError(f"{self!r} has a non-empty suffix")
429
return self.with_name(stem + suffix)
431
def with_suffix(self, suffix):
432
"""Return a new path with the file suffix changed. If the path
433
has no suffix, add given suffix. If the given suffix is an empty
434
string, remove the suffix from the path.
438
# If the stem is empty, we can't make the suffix non-empty.
439
raise ValueError(f"{self!r} has an empty name")
440
elif suffix and not suffix.startswith('.'):
441
raise ValueError(f"Invalid suffix {suffix!r}")
443
return self.with_name(stem + suffix)
447
"""The final path component, minus its last suffix."""
452
# Stem must contain at least one non-dot character.
460
The final component's last suffix, if any.
462
This includes the leading period. For example: '.txt'
464
name = self.name.lstrip('.')
473
A list of the final component's suffixes, if any.
475
These include the leading periods. For example: ['.tar', '.gz']
477
return ['.' + ext for ext in self.name.lstrip('.').split('.')[1:]]
479
def relative_to(self, other, *, walk_up=False):
480
"""Return the relative path to another path identified by the passed
481
arguments. If the operation is not possible (because this is not
482
related to the other path), raise ValueError.
484
The *walk_up* parameter controls whether `..` may be used to resolve
487
1
1
if not hasattr(other, 'with_segments'):
488
other = self.with_segments(other)
490
5
5
for path in chain([other], other.parents):
491
332
341
if path == self or path in self.parents:
494
raise ValueError(f"{str(self)!r} is not in the subpath of {str(other)!r}")
495
elif path.name == '..':
496
raise ValueError(f"'..' segment in {str(other)!r} cannot be walked")
500
raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors")
501
19
19
parts.extend(self._tail[len(path._tail):])
502
88
90
return self._from_parsed_parts('', '', parts)
504
def is_relative_to(self, other):
505
"""Return True if the path is relative to another path or False.
507
if not hasattr(other, 'with_segments'):
508
other = self.with_segments(other)
509
2
2
return other == self or other in self.parents
511
def is_absolute(self):
512
"""True if the path is absolute (has both a root and, if applicable,
514
if self.parser is posixpath:
515
# Optimization: work with raw paths on POSIX.
516
for path in self._raw_paths:
517
if path.startswith('/'):
520
return self.parser.isabs(self)
523
"""Return the path as a URI."""
525
msg = ("pathlib.PurePath.as_uri() is deprecated and scheduled "
526
"for removal in Python 3.19. Use pathlib.Path.as_uri().")
527
warnings._deprecated("pathlib.PurePath.as_uri", msg, remove=(3, 19))
528
if not self.is_absolute():
529
raise ValueError("relative path can't be expressed as a file URI")
532
if len(drive) == 2 and drive[1] == ':':
533
# It's a path on a local drive => 'file:///c:/a/b'
534
prefix = 'file:///' + drive
535
path = self.as_posix()[2:]
537
# It's a path on a network drive => 'file://host/share/a/b'
539
path = self.as_posix()
541
# It's a posix path => 'file:///etc/hosts'
544
from urllib.parse import quote_from_bytes
545
return prefix + quote_from_bytes(os.fsencode(path))
547
def full_match(self, pattern, *, case_sensitive=None):
549
Return True if this path matches the given glob-style pattern. The
550
pattern is matched against the entire path.
552
if not hasattr(pattern, 'with_segments'):
553
pattern = self.with_segments(pattern)
554
if case_sensitive is None:
555
case_sensitive = self.parser is posixpath
557
# The string representation of an empty path is a single dot ('.'). Empty
558
# paths shouldn't match wildcards, so we change it to the empty string.
559
path = str(self) if self.parts else ''
560
pattern = str(pattern) if pattern.parts else ''
561
globber = _StringGlobber(self.parser.sep, case_sensitive, recursive=True)
562
return globber.compile(pattern)(path) is not None
564
def match(self, path_pattern, *, case_sensitive=None):
566
Return True if this path matches the given pattern. If the pattern is
567
relative, matching is done from the right; otherwise, the entire path
568
is matched. The recursive wildcard '**' is *not* supported by this
571
if not hasattr(path_pattern, 'with_segments'):
572
path_pattern = self.with_segments(path_pattern)
573
if case_sensitive is None:
574
case_sensitive = self.parser is posixpath
575
path_parts = self.parts[::-1]
576
pattern_parts = path_pattern.parts[::-1]
577
if not pattern_parts:
578
raise ValueError("empty pattern")
579
if len(path_parts) < len(pattern_parts):
581
if len(path_parts) > len(pattern_parts) and path_pattern.anchor:
583
globber = _StringGlobber(self.parser.sep, case_sensitive)
584
for path_part, pattern_part in zip(path_parts, pattern_parts):
585
match = globber.compile(pattern_part)
586
if match(path_part) is None:
590
# Subclassing os.PathLike makes isinstance() checks slower,
591
# which in turn makes Path construction slower. Register instead!
592
os.PathLike.register(PurePath)
595
class PurePosixPath(PurePath):
596
"""PurePath subclass for non-Windows systems.
598
On a POSIX system, instantiating a PurePath should return this object.
599
However, you can also instantiate it directly on any system.
605
class PureWindowsPath(PurePath):
606
"""PurePath subclass for Windows systems.
608
On a Windows system, instantiating a PurePath should return this object.
609
However, you can also instantiate it directly on any system.
615
_STAT_RESULT_ERROR = [] # falsy sentinel indicating stat() failed.
619
"""Implementation of pathlib.types.PathInfo that provides status
620
information by querying a wrapped os.stat_result object. Don't try to
621
construct it yourself."""
622
__slots__ = ('_path', '_entry', '_stat_result', '_lstat_result')
624
def __init__(self, path, entry=None):
627
self._stat_result = None
628
self._lstat_result = None
631
path_type = "WindowsPath" if os.name == "nt" else "PosixPath"
632
return f"<{path_type}.info>"
634
def _stat(self, *, follow_symlinks=True):
635
"""Return the status as an os.stat_result."""
637
return self._entry.stat(follow_symlinks=follow_symlinks)
639
if not self._stat_result:
641
self._stat_result = os.stat(self._path)
642
except (OSError, ValueError):
643
self._stat_result = _STAT_RESULT_ERROR
645
return self._stat_result
647
if not self._lstat_result:
649
self._lstat_result = os.lstat(self._path)
650
except (OSError, ValueError):
651
self._lstat_result = _STAT_RESULT_ERROR
653
return self._lstat_result
655
def exists(self, *, follow_symlinks=True):
656
"""Whether this path exists."""
658
if not follow_symlinks:
661
if self._stat_result is _STAT_RESULT_ERROR:
664
if self._lstat_result is _STAT_RESULT_ERROR:
667
self._stat(follow_symlinks=follow_symlinks)
668
except (OSError, ValueError):
672
def is_dir(self, *, follow_symlinks=True):
673
"""Whether this path is a directory."""
676
return self._entry.is_dir(follow_symlinks=follow_symlinks)
680
if self._stat_result is _STAT_RESULT_ERROR:
683
if self._lstat_result is _STAT_RESULT_ERROR:
686
st = self._stat(follow_symlinks=follow_symlinks)
687
except (OSError, ValueError):
689
return S_ISDIR(st.st_mode)
691
def is_file(self, *, follow_symlinks=True):
692
"""Whether this path is a regular file."""
695
return self._entry.is_file(follow_symlinks=follow_symlinks)
699
if self._stat_result is _STAT_RESULT_ERROR:
702
if self._lstat_result is _STAT_RESULT_ERROR:
705
st = self._stat(follow_symlinks=follow_symlinks)
706
except (OSError, ValueError):
708
return S_ISREG(st.st_mode)
710
def is_symlink(self):
711
"""Whether this path is a symbolic link."""
714
return self._entry.is_symlink()
717
if self._lstat_result is _STAT_RESULT_ERROR:
720
st = self._stat(follow_symlinks=False)
721
except (OSError, ValueError):
723
return S_ISLNK(st.st_mode)
725
def _posix_permissions(self, *, follow_symlinks=True):
726
"""Return the POSIX file permissions."""
727
return S_IMODE(self._stat(follow_symlinks=follow_symlinks).st_mode)
729
def _file_id(self, *, follow_symlinks=True):
730
"""Returns the identifier of the file."""
731
st = self._stat(follow_symlinks=follow_symlinks)
732
return st.st_dev, st.st_ino
734
def _access_time_ns(self, *, follow_symlinks=True):
735
"""Return the access time in nanoseconds."""
736
return self._stat(follow_symlinks=follow_symlinks).st_atime_ns
738
def _mod_time_ns(self, *, follow_symlinks=True):
739
"""Return the modify time in nanoseconds."""
740
return self._stat(follow_symlinks=follow_symlinks).st_mtime_ns
742
if hasattr(os.stat_result, 'st_flags'):
743
def _bsd_flags(self, *, follow_symlinks=True):
744
"""Return the flags."""
745
return self._stat(follow_symlinks=follow_symlinks).st_flags
747
if hasattr(os, 'listxattr'):
748
def _xattrs(self, *, follow_symlinks=True):
749
"""Return the xattrs as a list of (attr, value) pairs, or an empty
750
list if extended attributes aren't supported."""
753
(attr, os.getxattr(self._path, attr, follow_symlinks=follow_symlinks))
754
for attr in os.listxattr(self._path, follow_symlinks=follow_symlinks)]
755
except OSError as err:
756
if err.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES):
761
def _copy_info(info, target, follow_symlinks=True):
762
"""Copy metadata from the given PathInfo to the given local path."""
764
hasattr(info, '_access_time_ns') and
765
hasattr(info, '_mod_time_ns') and
766
(follow_symlinks or os.utime in os.supports_follow_symlinks))
768
t0 = info._access_time_ns(follow_symlinks=follow_symlinks)
769
t1 = info._mod_time_ns(follow_symlinks=follow_symlinks)
770
os.utime(target, ns=(t0, t1), follow_symlinks=follow_symlinks)
772
# We must copy extended attributes before the file is (potentially)
773
# chmod()'ed read-only, otherwise setxattr() will error with -EACCES.
775
hasattr(info, '_xattrs') and
776
hasattr(os, 'setxattr') and
777
(follow_symlinks or os.setxattr in os.supports_follow_symlinks))
779
xattrs = info._xattrs(follow_symlinks=follow_symlinks)
780
for attr, value in xattrs:
782
os.setxattr(target, attr, value, follow_symlinks=follow_symlinks)
784
if e.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES):
787
copy_posix_permissions = (
788
hasattr(info, '_posix_permissions') and
789
(follow_symlinks or os.chmod in os.supports_follow_symlinks))
790
if copy_posix_permissions:
791
posix_permissions = info._posix_permissions(follow_symlinks=follow_symlinks)
793
os.chmod(target, posix_permissions, follow_symlinks=follow_symlinks)
794
except NotImplementedError:
795
# if we got a NotImplementedError, it's because
796
# * follow_symlinks=False,
797
# * lchown() is unavailable, and
799
# * fchownat() is unavailable or
800
# * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW.
801
# (it returned ENOSUP.)
802
# therefore we're out of options--we simply cannot chown the
803
# symlink. give up, suppress the error.
804
# (which is what shutil always did in this circumstance.)
808
hasattr(info, '_bsd_flags') and
809
hasattr(os, 'chflags') and
810
(follow_symlinks or os.chflags in os.supports_follow_symlinks))
812
bsd_flags = info._bsd_flags(follow_symlinks=follow_symlinks)
814
os.chflags(target, bsd_flags, follow_symlinks=follow_symlinks)
815
except OSError as why:
816
if why.errno not in (EOPNOTSUPP, ENOTSUP):
820
class Path(PurePath):
821
"""PurePath subclass that can make system calls.
823
Path represents a filesystem path but unlike PurePath, also offers
824
methods to do system calls on path objects. Depending on your system,
825
instantiating a Path will return either a PosixPath or a WindowsPath
826
object. You can also instantiate a PosixPath or WindowsPath directly,
827
but cannot instantiate a WindowsPath on a POSIX system or vice versa.
829
__slots__ = ('_info',)
831
def __new__(cls, *args, **kwargs):
833
cls = WindowsPath if os.name == 'nt' else PosixPath
834
return object.__new__(cls)
839
A PathInfo object that exposes the file type and other file attributes
844
except AttributeError:
845
self._info = _Info(str(self))
848
def stat(self, *, follow_symlinks=True):
850
Return the result of the stat() system call on this path, like
853
return os.stat(self, follow_symlinks=follow_symlinks)
857
Like stat(), except if the path points to a symlink, the symlink's
858
status information is returned, rather than its target's.
860
193
194
return os.lstat(self)
862
def exists(self, *, follow_symlinks=True):
864
Whether this path exists.
866
This method normally follows symlinks; to check whether a symlink exists,
867
add the argument follow_symlinks=False.
870
1
return os.path.exists(self)
871
return os.path.lexists(self)
873
def is_dir(self, *, follow_symlinks=True):
875
Whether this path is a directory.
878
return os.path.isdir(self)
880
return S_ISDIR(self.stat(follow_symlinks=follow_symlinks).st_mode)
881
except (OSError, ValueError):
884
def is_file(self, *, follow_symlinks=True):
886
Whether this path is a regular file (also True for symlinks pointing
890
return os.path.isfile(self)
892
return S_ISREG(self.stat(follow_symlinks=follow_symlinks).st_mode)
893
except (OSError, ValueError):
898
Check if this path is a mount point
900
return os.path.ismount(self)
902
def is_symlink(self):
904
Whether this path is a symbolic link.
906
4
19
return os.path.islink(self)
908
def is_junction(self):
910
Whether this path is a junction.
912
return os.path.isjunction(self)
914
def is_block_device(self):
916
Whether this path is a block device.
919
return S_ISBLK(self.stat().st_mode)
920
except (OSError, ValueError):
923
def is_char_device(self):
925
Whether this path is a character device.
928
return S_ISCHR(self.stat().st_mode)
929
except (OSError, ValueError):
934
Whether this path is a FIFO.
937
return S_ISFIFO(self.stat().st_mode)
938
except (OSError, ValueError):
943
Whether this path is a socket.
946
return S_ISSOCK(self.stat().st_mode)
947
except (OSError, ValueError):
950
def samefile(self, other_path):
951
"""Return whether other_path is the same or not as this file
952
(as returned by os.path.samefile()).
956
other_st = other_path.stat()
957
except AttributeError:
958
other_st = self.with_segments(other_path).stat()
959
return (st.st_ino == other_st.st_ino and
960
st.st_dev == other_st.st_dev)
962
def open(self, mode='r', buffering=-1, encoding=None,
963
errors=None, newline=None):
965
Open the file pointed to by this path and return a file object, as
966
the built-in open() function does.
969
encoding = io.text_encoding(encoding)
970
27
27
return io.open(self, mode, buffering, encoding, errors, newline)
972
def read_bytes(self):
974
Open the file in bytes mode, read it, and close the file.
976
6
8
with self.open(mode='rb', buffering=0) as f:
977
5
5
return f.read()
979
def read_text(self, encoding=None, errors=None, newline=None):
981
Open the file in text mode, read it, and close the file.
983
# Call io.text_encoding() here to ensure any warning is raised at an
984
# appropriate stack level.
985
encoding = io.text_encoding(encoding)
986
with self.open(mode='r', encoding=encoding, errors=errors, newline=newline) as f:
989
def write_bytes(self, data):
991
Open the file in bytes mode, write to it, and close the file.
993
# type-check for the buffer interface before truncating the file
994
view = memoryview(data)
995
with self.open(mode='wb') as f:
998
def write_text(self, data, encoding=None, errors=None, newline=None):
1000
Open the file in text mode, write to it, and close the file.
1002
# Call io.text_encoding() here to ensure any warning is raised at an
1003
# appropriate stack level.
1004
encoding = io.text_encoding(encoding)
1005
if not isinstance(data, str):
1006
raise TypeError('data must be str, not %s' %
1007
data.__class__.__name__)
1008
with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
1009
return f.write(data)
1011
_remove_leading_dot = operator.itemgetter(slice(2, None))
1012
_remove_trailing_slash = operator.itemgetter(slice(-1))
1014
def _filter_trailing_slash(self, paths):
1015
sep = self.parser.sep
1016
anchor_len = len(self.anchor)
1017
for path_str in paths:
1018
if len(path_str) > anchor_len and path_str[-1] == sep:
1019
path_str = path_str[:-1]
1022
def _from_dir_entry(self, dir_entry, path_str):
1023
path = self.with_segments(path_str)
1024
path._str = path_str
1025
path._info = _Info(dir_entry.path, dir_entry)
1029
"""Yield path objects of the directory contents.
1031
The children are yielded in arbitrary order, and the
1032
special entries '.' and '..' are not included.
1034
root_dir = str(self)
1035
16
16
with os.scandir(root_dir) as scandir_it:
1036
75
75
entries = list(scandir_it)
1038
return (self._from_dir_entry(e, e.name) for e in entries)
1040
1
1
return (self._from_dir_entry(e, e.path) for e in entries)
1042
def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=False):
1043
"""Iterate over this subtree and yield all existing files (of any
1044
kind, including directories) matching the given relative pattern.
1046
sys.audit("pathlib.Path.glob", self, pattern)
1047
if case_sensitive is None:
1048
case_sensitive = self.parser is posixpath
1049
case_pedantic = False
1051
# The user has expressed a case sensitivity choice, but we don't
1052
# know the case sensitivity of the underlying filesystem, so we
1053
# must use scandir() for everything, including non-wildcard parts.
1054
case_pedantic = True
1055
parts = self._parse_pattern(pattern)
1056
recursive = True if recurse_symlinks else _no_recurse_symlinks
1057
globber = _StringGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive)
1058
select = globber.selector(parts[::-1])
1060
paths = select(self.parser.join(root, ''))
1064
paths = map(self._remove_leading_dot, paths)
1066
paths = map(self._remove_trailing_slash, paths)
1067
elif parts[-1] == '**':
1068
paths = self._filter_trailing_slash(paths)
1069
paths = map(self._from_parsed_string, paths)
1072
def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=False):
1073
"""Recursively yield all existing files (of any kind, including
1074
directories) matching the given relative pattern, anywhere in
1077
sys.audit("pathlib.Path.rglob", self, pattern)
1078
pattern = self.parser.join('**', pattern)
1079
return self.glob(pattern, case_sensitive=case_sensitive, recurse_symlinks=recurse_symlinks)
1081
def walk(self, top_down=True, on_error=None, follow_symlinks=False):
1082
"""Walk the directory tree from this directory, similar to os.walk()."""
1083
sys.audit("pathlib.Path.walk", self, on_error, follow_symlinks)
1084
root_dir = str(self)
1085
if not follow_symlinks:
1086
follow_symlinks = os._walk_symlinks_as_files
1087
results = os.walk(root_dir, top_down, on_error, follow_symlinks)
1088
for path_str, dirnames, filenames in results:
1090
path_str = path_str[2:]
1091
yield self._from_parsed_string(path_str), dirnames, filenames
1094
"""Return an absolute version of this path
1095
No normalization or symlink resolution is performed.
1097
Use resolve() to resolve symlinks and remove '..' segments.
1099
if self.is_absolute():
1102
drive = os.path.splitroot(os.getcwd())[0]
1103
return self._from_parsed_parts(drive, self.root, self._tail)
1105
# There is a CWD on each drive-letter drive.
1106
cwd = os.path.abspath(self.drive)
1110
# Fast path for "empty" paths, e.g. Path("."), Path("") or Path().
1111
# We pass only one argument to with_segments() to avoid the cost
1112
# of joining, and we exploit the fact that getcwd() returns a
1113
# fully-normalized string by storing it in _str. This is used to
1114
# implement Path.cwd().
1115
return self._from_parsed_string(cwd)
1116
drive, root, rel = os.path.splitroot(cwd)
1118
return self._from_parsed_parts(drive, root, self._tail)
1119
tail = rel.split(self.parser.sep)
1120
tail.extend(self._tail)
1121
return self._from_parsed_parts(drive, root, tail)
1125
"""Return a new path pointing to the current working directory."""
1128
path._str = cwd # getcwd() returns a normalized path
1131
def resolve(self, strict=False):
1133
Make the path absolute, resolving all symlinks on the way and also
1137
2
51
return self.with_segments(os.path.realpath(self, strict=strict))
1140
def owner(self, *, follow_symlinks=True):
1142
Return the login name of the file owner.
1144
uid = self.stat(follow_symlinks=follow_symlinks).st_uid
1145
return pwd.getpwuid(uid).pw_name
1147
def owner(self, *, follow_symlinks=True):
1149
Return the login name of the file owner.
1151
f = f"{type(self).__name__}.owner()"
1152
raise UnsupportedOperation(f"{f} is unsupported on this system")
1155
def group(self, *, follow_symlinks=True):
1157
Return the group name of the file gid.
1159
gid = self.stat(follow_symlinks=follow_symlinks).st_gid
1160
return grp.getgrgid(gid).gr_name
1162
def group(self, *, follow_symlinks=True):
1164
Return the group name of the file gid.
1166
f = f"{type(self).__name__}.group()"
1167
raise UnsupportedOperation(f"{f} is unsupported on this system")
1169
if hasattr(os, "readlink"):
1172
Return the path to which the symbolic link points.
1174
return self.with_segments(os.readlink(self))
1178
Return the path to which the symbolic link points.
1180
f = f"{type(self).__name__}.readlink()"
1181
raise UnsupportedOperation(f"{f} is unsupported on this system")
1183
def touch(self, mode=0o666, exist_ok=True):
1185
Create this file with the given access mode, if it doesn't exist.
1189
# First try to bump modification time
1190
# Implementation note: GNU touch uses the UTIME_NOW option of
1191
# the utimensat() / futimens() functions.
1193
os.utime(self, None)
1195
# Avoid exception chaining
1199
flags = os.O_CREAT | os.O_WRONLY
1202
fd = os.open(self, flags, mode)
1205
def mkdir(self, mode=0o777, parents=False, exist_ok=False, *, parent_mode=None):
1207
Create a new directory at this given path.
1210
os.mkdir(self, mode)
1211
except FileNotFoundError:
1212
if not parents or self.parent == self:
1214
if parent_mode is not None:
1215
self.parent.mkdir(mode=parent_mode, parents=True, exist_ok=True,
1216
parent_mode=parent_mode)
1218
self.parent.mkdir(parents=True, exist_ok=True)
1219
self.mkdir(mode, parents=False, exist_ok=exist_ok)
1221
# Cannot rely on checking for EEXIST, since the operating system
1222
# could give priority to other errors like EACCES or EROFS
1223
1
1
if not exist_ok or not self.is_dir():
1226
def chmod(self, mode, *, follow_symlinks=True):
1228
Change the permissions of the path, like os.chmod().
1230
os.chmod(self, mode, follow_symlinks=follow_symlinks)
1232
def lchmod(self, mode):
1234
Like chmod(), except if the path points to a symlink, the symlink's
1235
permissions are changed, rather than its target's.
1237
self.chmod(mode, follow_symlinks=False)
1239
def unlink(self, missing_ok=False):
1241
Remove this file or link.
1242
If the path is a directory, use rmdir() instead.
1245
16
16
os.unlink(self)
1246
except FileNotFoundError:
1252
Remove this directory. The directory must be empty.
1258
Delete this file or directory (including all sub-directories).
1260
if self.is_symlink() or self.is_junction():
1267
def rename(self, target):
1269
Rename this path to the target path.
1271
The target path may be absolute or relative. Relative paths are
1272
interpreted relative to the current working directory, *not* the
1273
directory of the Path object.
1275
Returns the new Path instance pointing to the target path.
1277
os.rename(self, target)
1278
if not hasattr(target, 'with_segments'):
1279
target = self.with_segments(target)
1282
def replace(self, target):
1284
Rename this path to the target path, overwriting if that path exists.
1286
The target path may be absolute or relative. Relative paths are
1287
interpreted relative to the current working directory, *not* the
1288
directory of the Path object.
1290
Returns the new Path instance pointing to the target path.
1292
os.replace(self, target)
1293
if not hasattr(target, 'with_segments'):
1294
target = self.with_segments(target)
1297
def copy(self, target, **kwargs):
1299
Recursively copy this file or directory tree to the given destination.
1301
if not hasattr(target, 'with_segments'):
1302
target = self.with_segments(target)
1303
ensure_distinct_paths(self, target)
1304
target._copy_from(self, **kwargs)
1305
return target.joinpath() # Empty join to ensure fresh metadata.
1307
def copy_into(self, target_dir, **kwargs):
1309
Copy this file or directory tree into the given existing directory.
1313
raise ValueError(f"{self!r} has an empty name")
1314
elif hasattr(target_dir, 'with_segments'):
1315
target = target_dir / name
1317
target = self.with_segments(target_dir, name)
1318
return self.copy(target, **kwargs)
1320
def _copy_from(self, source, follow_symlinks=True, preserve_metadata=False):
1322
Recursively copy the given path to this path.
1324
if not follow_symlinks and source.info.is_symlink():
1325
self._copy_from_symlink(source, preserve_metadata)
1326
elif source.info.is_dir():
1327
children = source.iterdir()
1329
for child in children:
1330
self.joinpath(child.name)._copy_from(
1331
child, follow_symlinks, preserve_metadata)
1332
if preserve_metadata:
1333
_copy_info(source.info, self)
1335
self._copy_from_file(source, preserve_metadata)
1337
def _copy_from_file(self, source, preserve_metadata=False):
1338
ensure_different_files(source, self)
1339
with vfsopen(source, 'rb') as source_f:
1340
with open(self, 'wb') as target_f:
1341
copyfileobj(source_f, target_f)
1342
if preserve_metadata:
1343
_copy_info(source.info, self)
1346
# Use fast OS routine for local file copying where available.
1347
_copy_from_file_fallback = _copy_from_file
1348
def _copy_from_file(self, source, preserve_metadata=False):
1350
source = os.fspath(source)
1354
copyfile2(source, str(self))
1356
self._copy_from_file_fallback(source, preserve_metadata)
1359
# If a directory-symlink is copied *before* its target, then
1360
# os.symlink() incorrectly creates a file-symlink on Windows. Avoid
1361
# this by passing *target_is_dir* to os.symlink() on Windows.
1362
def _copy_from_symlink(self, source, preserve_metadata=False):
1363
os.symlink(vfspath(source.readlink()), self, source.info.is_dir())
1364
if preserve_metadata:
1365
_copy_info(source.info, self, follow_symlinks=False)
1367
def _copy_from_symlink(self, source, preserve_metadata=False):
1368
os.symlink(vfspath(source.readlink()), self)
1369
if preserve_metadata:
1370
_copy_info(source.info, self, follow_symlinks=False)
1372
def move(self, target):
1374
Recursively move this file or directory tree to the given destination.
1376
# Use os.replace() if the target is os.PathLike and on the same FS.
1378
target = self.with_segments(target)
1382
ensure_different_files(self, target)
1384
os.replace(self, target)
1385
except OSError as err:
1386
if err.errno != EXDEV:
1389
return target.joinpath() # Empty join to ensure fresh metadata.
1390
# Fall back to copy+delete.
1391
target = self.copy(target, follow_symlinks=False, preserve_metadata=True)
1395
def move_into(self, target_dir):
1397
Move this file or directory tree into the given existing directory.
1401
raise ValueError(f"{self!r} has an empty name")
1402
elif hasattr(target_dir, 'with_segments'):
1403
target = target_dir / name
1405
target = self.with_segments(target_dir, name)
1406
return self.move(target)
1408
if hasattr(os, "symlink"):
1409
def symlink_to(self, target, target_is_directory=False):
1411
Make this path a symlink pointing to the target path.
1412
Note the order of arguments (link, target) is the reverse of os.symlink.
1414
os.symlink(target, self, target_is_directory)
1416
def symlink_to(self, target, target_is_directory=False):
1418
Make this path a symlink pointing to the target path.
1419
Note the order of arguments (link, target) is the reverse of os.symlink.
1421
f = f"{type(self).__name__}.symlink_to()"
1422
raise UnsupportedOperation(f"{f} is unsupported on this system")
1424
if hasattr(os, "link"):
1425
def hardlink_to(self, target):
1427
Make this path a hard link pointing to the same file as *target*.
1429
Note the order of arguments (self, target) is the reverse of os.link's.
1431
os.link(target, self)
1433
def hardlink_to(self, target):
1435
Make this path a hard link pointing to the same file as *target*.
1437
Note the order of arguments (self, target) is the reverse of os.link's.
1439
f = f"{type(self).__name__}.hardlink_to()"
1440
raise UnsupportedOperation(f"{f} is unsupported on this system")
1442
def expanduser(self):
1443
""" Return a new path with expanded ~ and ~user constructs
1444
(as returned by os.path.expanduser)
1446
if (not (self.drive or self.root) and
1447
self._tail and self._tail[0][:1] == '~'):
1448
homedir = os.path.expanduser(self._tail[0])
1449
if homedir[:1] == "~":
1450
raise RuntimeError("Could not determine home directory.")
1451
drv, root, tail = self._parse_path(homedir)
1452
return self._from_parsed_parts(drv, root, tail + self._tail[1:])
1458
"""Return a new path pointing to expanduser('~').
1460
homedir = os.path.expanduser("~")
1462
raise RuntimeError("Could not determine home directory.")
1466
"""Return the path as a URI."""
1467
if not self.is_absolute():
1468
raise ValueError("relative paths can't be expressed as file URIs")
1469
from urllib.request import pathname2url
1470
return pathname2url(str(self), add_scheme=True)
1473
def from_uri(cls, uri):
1474
"""Return a new path from the given 'file' URI."""
1475
from urllib.error import URLError
1476
from urllib.request import url2pathname
1478
path = cls(url2pathname(uri, require_scheme=True))
1479
except URLError as exc:
1480
raise ValueError(exc.reason) from None
1481
if not path.is_absolute():
1482
raise ValueError(f"URI is not absolute: {uri!r}")
1486
class PosixPath(Path, PurePosixPath):
1487
"""Path subclass for non-Windows systems.
1489
On a POSIX system, instantiating a Path should return this object.
1494
def __new__(cls, *args, **kwargs):
1495
raise UnsupportedOperation(
1496
f"cannot instantiate {cls.__name__!r} on your system")
1498
class WindowsPath(Path, PureWindowsPath):
1499
"""Path subclass for Windows systems.
1501
On a Windows system, instantiating a Path should return this object.
1506
def __new__(cls, *args, **kwargs):
1507
raise UnsupportedOperation(
1508
f"cannot instantiate {cls.__name__!r} on your system")