blob: 8e6eb48b9767cabc1d03a35cbea46ec51bbd1f29 [file] [log] [blame]
Antoine Pitrou31119e42013-11-22 17:38:12 +01001import fnmatch
2import functools
3import io
4import ntpath
5import os
6import posixpath
7import re
8import sys
Barney Galef24e2e52021-04-23 21:48:52 +01009import warnings
Serhiy Storchaka81108372017-09-26 00:55:55 +030010from _collections_abc import Sequence
Jörg Stucked5c120f2019-05-21 19:44:40 +020011from errno import EINVAL, ENOENT, ENOTDIR, EBADF, ELOOP
Antoine Pitrou31119e42013-11-22 17:38:12 +010012from operator import attrgetter
13from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
Antoine Pitrou069a5e12013-12-03 09:41:35 +010014from urllib.parse import quote_from_bytes as urlquote_from_bytes
Antoine Pitrou31119e42013-11-22 17:38:12 +010015
16
Antoine Pitrou31119e42013-11-22 17:38:12 +010017__all__ = [
18 "PurePath", "PurePosixPath", "PureWindowsPath",
19 "Path", "PosixPath", "WindowsPath",
20 ]
21
22#
23# Internals
24#
25
Barney Galebaecfbd2021-04-28 16:50:17 +010026_WINERROR_NOT_READY = 21 # drive exists but is not accessible
27_WINERROR_INVALID_NAME = 123 # fix for bpo-35306
28_WINERROR_CANT_RESOLVE_FILENAME = 1921 # broken symlink pointing to itself
29
penguindustin96466302019-05-06 14:57:17 -040030# EBADF - guard against macOS `stat` throwing EBADF
Jörg Stucked5c120f2019-05-21 19:44:40 +020031_IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF, ELOOP)
Przemysław Spodymek216b7452018-08-27 23:33:45 +020032
Steve Dower2f6fae62019-02-03 23:08:18 -080033_IGNORED_WINERRORS = (
Barney Galebaecfbd2021-04-28 16:50:17 +010034 _WINERROR_NOT_READY,
35 _WINERROR_INVALID_NAME,
36 _WINERROR_CANT_RESOLVE_FILENAME)
Steve Dower2f6fae62019-02-03 23:08:18 -080037
38def _ignore_error(exception):
39 return (getattr(exception, 'errno', None) in _IGNORED_ERROS or
40 getattr(exception, 'winerror', None) in _IGNORED_WINERRORS)
41
42
Antoine Pitrou31119e42013-11-22 17:38:12 +010043def _is_wildcard_pattern(pat):
44 # Whether this pattern needs actual matching using fnmatch, or can
45 # be looked up directly as a file.
46 return "*" in pat or "?" in pat or "[" in pat
47
48
49class _Flavour(object):
50 """A flavour implements a particular (platform-specific) set of path
51 semantics."""
52
53 def __init__(self):
54 self.join = self.sep.join
55
56 def parse_parts(self, parts):
57 parsed = []
58 sep = self.sep
59 altsep = self.altsep
60 drv = root = ''
61 it = reversed(parts)
62 for part in it:
63 if not part:
64 continue
65 if altsep:
66 part = part.replace(altsep, sep)
67 drv, root, rel = self.splitroot(part)
68 if sep in rel:
69 for x in reversed(rel.split(sep)):
70 if x and x != '.':
71 parsed.append(sys.intern(x))
72 else:
73 if rel and rel != '.':
74 parsed.append(sys.intern(rel))
75 if drv or root:
76 if not drv:
77 # If no drive is present, try to find one in the previous
78 # parts. This makes the result of parsing e.g.
79 # ("C:", "/", "a") reasonably intuitive.
80 for part in it:
Antoine Pitrou57fffd62015-02-15 18:03:59 +010081 if not part:
82 continue
83 if altsep:
84 part = part.replace(altsep, sep)
Antoine Pitrou31119e42013-11-22 17:38:12 +010085 drv = self.splitroot(part)[0]
86 if drv:
87 break
88 break
89 if drv or root:
90 parsed.append(drv + root)
91 parsed.reverse()
92 return drv, root, parsed
93
94 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
95 """
96 Join the two paths represented by the respective
97 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple.
98 """
99 if root2:
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200100 if not drv2 and drv:
101 return drv, root2, [drv + root2] + parts2[1:]
102 elif drv2:
103 if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
104 # Same drive => second path is relative to the first
105 return drv, root, parts + parts2[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100106 else:
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200107 # Second path is non-anchored (common case)
108 return drv, root, parts + parts2
109 return drv2, root2, parts2
Antoine Pitrou31119e42013-11-22 17:38:12 +0100110
111
112class _WindowsFlavour(_Flavour):
113 # Reference for Windows paths can be found at
114 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
115
116 sep = '\\'
117 altsep = '/'
118 has_drv = True
119 pathmod = ntpath
120
Antoine Pitroudb118f52014-11-19 00:32:08 +0100121 is_supported = (os.name == 'nt')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100122
Jon Dufresne39726282017-05-18 07:35:54 -0700123 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100124 ext_namespace_prefix = '\\\\?\\'
125
126 reserved_names = (
127 {'CON', 'PRN', 'AUX', 'NUL'} |
128 {'COM%d' % i for i in range(1, 10)} |
129 {'LPT%d' % i for i in range(1, 10)}
130 )
131
132 # Interesting findings about extended paths:
133 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
134 # but '\\?\c:/a' is not
135 # - extended paths are always absolute; "relative" extended paths will
136 # fail.
137
138 def splitroot(self, part, sep=sep):
139 first = part[0:1]
140 second = part[1:2]
141 if (second == sep and first == sep):
142 # XXX extended paths should also disable the collapsing of "."
143 # components (according to MSDN docs).
144 prefix, part = self._split_extended_path(part)
145 first = part[0:1]
146 second = part[1:2]
147 else:
148 prefix = ''
149 third = part[2:3]
150 if (second == sep and first == sep and third != sep):
151 # is a UNC path:
152 # vvvvvvvvvvvvvvvvvvvvv root
153 # \\machine\mountpoint\directory\etc\...
154 # directory ^^^^^^^^^^^^^^
155 index = part.find(sep, 2)
156 if index != -1:
157 index2 = part.find(sep, index + 1)
158 # a UNC path can't have two slashes in a row
159 # (after the initial two)
160 if index2 != index + 1:
161 if index2 == -1:
162 index2 = len(part)
163 if prefix:
164 return prefix + part[1:index2], sep, part[index2+1:]
165 else:
166 return part[:index2], sep, part[index2+1:]
167 drv = root = ''
168 if second == ':' and first in self.drive_letters:
169 drv = part[:2]
170 part = part[2:]
171 first = third
172 if first == sep:
173 root = first
174 part = part.lstrip(sep)
175 return prefix + drv, root, part
176
177 def casefold(self, s):
178 return s.lower()
179
180 def casefold_parts(self, parts):
181 return [p.lower() for p in parts]
182
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300183 def compile_pattern(self, pattern):
184 return re.compile(fnmatch.translate(pattern), re.IGNORECASE).fullmatch
185
Antoine Pitrou31119e42013-11-22 17:38:12 +0100186 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
187 prefix = ''
188 if s.startswith(ext_prefix):
189 prefix = s[:4]
190 s = s[4:]
191 if s.startswith('UNC\\'):
192 prefix += s[:3]
193 s = '\\' + s[3:]
194 return prefix, s
195
Antoine Pitrou31119e42013-11-22 17:38:12 +0100196 def is_reserved(self, parts):
197 # NOTE: the rules for reserved names seem somewhat complicated
198 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
199 # We err on the side of caution and return True for paths which are
200 # not considered reserved by Windows.
201 if not parts:
202 return False
203 if parts[0].startswith('\\\\'):
204 # UNC paths are never reserved
205 return False
206 return parts[-1].partition('.')[0].upper() in self.reserved_names
207
208 def make_uri(self, path):
209 # Under Windows, file URIs use the UTF-8 encoding.
210 drive = path.drive
211 if len(drive) == 2 and drive[1] == ':':
212 # It's a path on a local drive => 'file:///c:/a/b'
213 rest = path.as_posix()[2:].lstrip('/')
214 return 'file:///%s/%s' % (
215 drive, urlquote_from_bytes(rest.encode('utf-8')))
216 else:
217 # It's a path on a network drive => 'file://host/share/a/b'
218 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
219
220
221class _PosixFlavour(_Flavour):
222 sep = '/'
223 altsep = ''
224 has_drv = False
225 pathmod = posixpath
226
227 is_supported = (os.name != 'nt')
228
229 def splitroot(self, part, sep=sep):
230 if part and part[0] == sep:
231 stripped_part = part.lstrip(sep)
232 # According to POSIX path resolution:
233 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
234 # "A pathname that begins with two successive slashes may be
235 # interpreted in an implementation-defined manner, although more
236 # than two leading slashes shall be treated as a single slash".
237 if len(part) - len(stripped_part) == 2:
238 return '', sep * 2, stripped_part
239 else:
240 return '', sep, stripped_part
241 else:
242 return '', '', part
243
244 def casefold(self, s):
245 return s
246
247 def casefold_parts(self, parts):
248 return parts
249
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300250 def compile_pattern(self, pattern):
251 return re.compile(fnmatch.translate(pattern)).fullmatch
252
Antoine Pitrou31119e42013-11-22 17:38:12 +0100253 def is_reserved(self, parts):
254 return False
255
256 def make_uri(self, path):
257 # We represent the path using the local filesystem encoding,
258 # for portability to other applications.
259 bpath = bytes(path)
260 return 'file://' + urlquote_from_bytes(bpath)
261
262
263_windows_flavour = _WindowsFlavour()
264_posix_flavour = _PosixFlavour()
265
266
267class _Accessor:
268 """An accessor implements a particular (system-specific or not) way of
269 accessing paths on the filesystem."""
270
271
272class _NormalAccessor(_Accessor):
273
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200274 stat = os.stat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100275
Barney Gale11c3bd32021-04-09 21:52:49 +0100276 open = io.open
Antoine Pitrou31119e42013-11-22 17:38:12 +0100277
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200278 listdir = os.listdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100279
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200280 scandir = os.scandir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100281
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200282 chmod = os.chmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100283
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200284 mkdir = os.mkdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100285
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200286 unlink = os.unlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100287
Toke Høiland-Jørgensen092435e2019-12-16 13:23:55 +0100288 if hasattr(os, "link"):
Barney Galeb57e0452021-04-07 00:01:22 +0100289 link = os.link
Toke Høiland-Jørgensen092435e2019-12-16 13:23:55 +0100290 else:
Barney Galeb57e0452021-04-07 00:01:22 +0100291 def link(self, src, dst):
Toke Høiland-Jørgensen092435e2019-12-16 13:23:55 +0100292 raise NotImplementedError("os.link() not available on this system")
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -0400293
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200294 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100295
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200296 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100297
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200298 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100299
Barney Galeb57e0452021-04-07 00:01:22 +0100300 if hasattr(os, "symlink"):
301 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100302 else:
Barney Galeb57e0452021-04-07 00:01:22 +0100303 def symlink(self, src, dst, target_is_directory=False):
304 raise NotImplementedError("os.symlink() not available on this system")
Antoine Pitrou31119e42013-11-22 17:38:12 +0100305
Barney Gale986da8e2021-04-07 01:25:37 +0100306 def touch(self, path, mode=0o666, exist_ok=True):
307 if exist_ok:
308 # First try to bump modification time
309 # Implementation note: GNU touch uses the UTIME_NOW option of
310 # the utimensat() / futimens() functions.
311 try:
312 os.utime(path, None)
313 except OSError:
314 # Avoid exception chaining
315 pass
316 else:
317 return
318 flags = os.O_CREAT | os.O_WRONLY
319 if not exist_ok:
320 flags |= os.O_EXCL
321 fd = os.open(path, flags, mode)
322 os.close(fd)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100323
Barney Galeb57e0452021-04-07 00:01:22 +0100324 if hasattr(os, "readlink"):
325 readlink = os.readlink
326 else:
327 def readlink(self, path):
328 raise NotImplementedError("os.readlink() not available on this system")
Antoine Pitrou31119e42013-11-22 17:38:12 +0100329
Barney Gale22386bb2020-04-17 17:41:07 +0100330 def owner(self, path):
331 try:
332 import pwd
333 return pwd.getpwuid(self.stat(path).st_uid).pw_name
334 except ImportError:
335 raise NotImplementedError("Path.owner() is unsupported on this system")
336
337 def group(self, path):
338 try:
339 import grp
340 return grp.getgrgid(self.stat(path).st_gid).gr_name
341 except ImportError:
342 raise NotImplementedError("Path.group() is unsupported on this system")
343
Barney Galeb05440c2021-04-07 17:31:49 +0100344 getcwd = os.getcwd
345
Barney Gale3f3d82b2021-04-07 23:50:13 +0100346 expanduser = staticmethod(os.path.expanduser)
347
Barney Galebaecfbd2021-04-28 16:50:17 +0100348 realpath = staticmethod(os.path.realpath)
349
Antoine Pitrou31119e42013-11-22 17:38:12 +0100350
351_normal_accessor = _NormalAccessor()
352
353
354#
355# Globbing helpers
356#
357
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300358def _make_selector(pattern_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100359 pat = pattern_parts[0]
360 child_parts = pattern_parts[1:]
361 if pat == '**':
362 cls = _RecursiveWildcardSelector
363 elif '**' in pat:
364 raise ValueError("Invalid pattern: '**' can only be an entire path component")
365 elif _is_wildcard_pattern(pat):
366 cls = _WildcardSelector
367 else:
368 cls = _PreciseSelector
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300369 return cls(pat, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100370
371if hasattr(functools, "lru_cache"):
372 _make_selector = functools.lru_cache()(_make_selector)
373
374
375class _Selector:
376 """A selector matches a specific glob pattern part against the children
377 of a given path."""
378
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300379 def __init__(self, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100380 self.child_parts = child_parts
381 if child_parts:
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300382 self.successor = _make_selector(child_parts, flavour)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300383 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100384 else:
385 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300386 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100387
388 def select_from(self, parent_path):
389 """Iterate over all child paths of `parent_path` matched by this
390 selector. This can contain parent_path itself."""
391 path_cls = type(parent_path)
392 is_dir = path_cls.is_dir
393 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300394 scandir = parent_path._accessor.scandir
395 if not is_dir(parent_path):
396 return iter([])
397 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100398
399
400class _TerminatingSelector:
401
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300402 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100403 yield parent_path
404
405
406class _PreciseSelector(_Selector):
407
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300408 def __init__(self, name, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100409 self.name = name
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300410 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100411
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300412 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800413 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800414 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300415 if (is_dir if self.dironly else exists)(path):
416 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800417 yield p
418 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100419 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100420
421
422class _WildcardSelector(_Selector):
423
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300424 def __init__(self, pat, child_parts, flavour):
425 self.match = flavour.compile_pattern(pat)
426 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100427
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300428 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800429 try:
Serhiy Storchaka704e2062020-03-11 18:42:03 +0200430 with scandir(parent_path) as scandir_it:
431 entries = list(scandir_it)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300432 for entry in entries:
Pablo Galindoeb7560a2020-03-07 17:53:20 +0000433 if self.dironly:
434 try:
435 # "entry.is_dir()" can raise PermissionError
436 # in some cases (see bpo-38894), which is not
437 # among the errors ignored by _ignore_error()
438 if not entry.is_dir():
439 continue
440 except OSError as e:
441 if not _ignore_error(e):
442 raise
443 continue
444 name = entry.name
445 if self.match(name):
446 path = parent_path._make_child_relpath(name)
447 for p in self.successor._select_from(path, is_dir, exists, scandir):
448 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800449 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100450 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800451
Antoine Pitrou31119e42013-11-22 17:38:12 +0100452
Antoine Pitrou31119e42013-11-22 17:38:12 +0100453class _RecursiveWildcardSelector(_Selector):
454
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300455 def __init__(self, pat, child_parts, flavour):
456 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100457
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300458 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100459 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800460 try:
Serhiy Storchaka704e2062020-03-11 18:42:03 +0200461 with scandir(parent_path) as scandir_it:
462 entries = list(scandir_it)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300463 for entry in entries:
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200464 entry_is_dir = False
465 try:
466 entry_is_dir = entry.is_dir()
467 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -0800468 if not _ignore_error(e):
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200469 raise
470 if entry_is_dir and not entry.is_symlink():
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300471 path = parent_path._make_child_relpath(entry.name)
472 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800473 yield p
474 except PermissionError:
475 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100476
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300477 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800478 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300479 yielded = set()
480 try:
481 successor_select = self.successor._select_from
482 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
483 for p in successor_select(starting_point, is_dir, exists, scandir):
484 if p not in yielded:
485 yield p
486 yielded.add(p)
487 finally:
488 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800489 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100490 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100491
492
493#
494# Public API
495#
496
497class _PathParents(Sequence):
498 """This object provides sequence-like access to the logical ancestors
499 of a path. Don't try to construct it yourself."""
500 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
501
502 def __init__(self, path):
503 # We don't store the instance to avoid reference cycles
504 self._pathcls = type(path)
505 self._drv = path._drv
506 self._root = path._root
507 self._parts = path._parts
508
509 def __len__(self):
510 if self._drv or self._root:
511 return len(self._parts) - 1
512 else:
513 return len(self._parts)
514
515 def __getitem__(self, idx):
Joshua Cannon45205842020-11-20 09:40:39 -0600516 if isinstance(idx, slice):
517 return tuple(self[i] for i in range(*idx.indices(len(self))))
Yaroslav Pankovych79d2e622020-11-23 22:06:22 +0200518
519 if idx >= len(self) or idx < -len(self):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100520 raise IndexError(idx)
521 return self._pathcls._from_parsed_parts(self._drv, self._root,
522 self._parts[:-idx - 1])
523
524 def __repr__(self):
525 return "<{}.parents>".format(self._pathcls.__name__)
526
527
528class PurePath(object):
chasondfa015c2018-02-19 08:36:32 +0900529 """Base class for manipulating paths without I/O.
530
531 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100532 don't imply any actual filesystem I/O. Depending on your system,
533 instantiating a PurePath will return either a PurePosixPath or a
534 PureWindowsPath object. You can also instantiate either of these classes
535 directly, regardless of your system.
536 """
537 __slots__ = (
538 '_drv', '_root', '_parts',
539 '_str', '_hash', '_pparts', '_cached_cparts',
540 )
541
542 def __new__(cls, *args):
543 """Construct a PurePath from one or several strings and or existing
544 PurePath objects. The strings and path objects are combined so as
545 to yield a canonicalized path, which is incorporated into the
546 new PurePath object.
547 """
548 if cls is PurePath:
549 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
550 return cls._from_parts(args)
551
552 def __reduce__(self):
553 # Using the parts tuple helps share interned path parts
554 # when pickling related paths.
555 return (self.__class__, tuple(self._parts))
556
557 @classmethod
558 def _parse_args(cls, args):
559 # This is useful when you don't want to create an instance, just
560 # canonicalize some constructor arguments.
561 parts = []
562 for a in args:
563 if isinstance(a, PurePath):
564 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100565 else:
Brett Cannon568be632016-06-10 12:20:49 -0700566 a = os.fspath(a)
567 if isinstance(a, str):
568 # Force-cast str subclasses to str (issue #21127)
569 parts.append(str(a))
570 else:
571 raise TypeError(
572 "argument should be a str object or an os.PathLike "
573 "object returning str, not %r"
574 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100575 return cls._flavour.parse_parts(parts)
576
577 @classmethod
Barney Gale22191872021-04-07 01:26:37 +0100578 def _from_parts(cls, args):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100579 # We need to call _parse_args on the instance, so as to get the
580 # right flavour.
581 self = object.__new__(cls)
582 drv, root, parts = self._parse_args(args)
583 self._drv = drv
584 self._root = root
585 self._parts = parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100586 return self
587
588 @classmethod
Barney Gale22191872021-04-07 01:26:37 +0100589 def _from_parsed_parts(cls, drv, root, parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100590 self = object.__new__(cls)
591 self._drv = drv
592 self._root = root
593 self._parts = parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100594 return self
595
596 @classmethod
597 def _format_parsed_parts(cls, drv, root, parts):
598 if drv or root:
599 return drv + root + cls._flavour.join(parts[1:])
600 else:
601 return cls._flavour.join(parts)
602
Antoine Pitrou31119e42013-11-22 17:38:12 +0100603 def _make_child(self, args):
604 drv, root, parts = self._parse_args(args)
605 drv, root, parts = self._flavour.join_parsed_parts(
606 self._drv, self._root, self._parts, drv, root, parts)
607 return self._from_parsed_parts(drv, root, parts)
608
609 def __str__(self):
610 """Return the string representation of the path, suitable for
611 passing to system calls."""
612 try:
613 return self._str
614 except AttributeError:
615 self._str = self._format_parsed_parts(self._drv, self._root,
616 self._parts) or '.'
617 return self._str
618
Brett Cannon568be632016-06-10 12:20:49 -0700619 def __fspath__(self):
620 return str(self)
621
Antoine Pitrou31119e42013-11-22 17:38:12 +0100622 def as_posix(self):
623 """Return the string representation of the path with forward (/)
624 slashes."""
625 f = self._flavour
626 return str(self).replace(f.sep, '/')
627
628 def __bytes__(self):
629 """Return the bytes representation of the path. This is only
630 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200631 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100632
633 def __repr__(self):
634 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
635
636 def as_uri(self):
637 """Return the path as a 'file' URI."""
638 if not self.is_absolute():
639 raise ValueError("relative path can't be expressed as a file URI")
640 return self._flavour.make_uri(self)
641
642 @property
643 def _cparts(self):
644 # Cached casefolded parts, for hashing and comparison
645 try:
646 return self._cached_cparts
647 except AttributeError:
648 self._cached_cparts = self._flavour.casefold_parts(self._parts)
649 return self._cached_cparts
650
651 def __eq__(self, other):
652 if not isinstance(other, PurePath):
653 return NotImplemented
654 return self._cparts == other._cparts and self._flavour is other._flavour
655
Antoine Pitrou31119e42013-11-22 17:38:12 +0100656 def __hash__(self):
657 try:
658 return self._hash
659 except AttributeError:
660 self._hash = hash(tuple(self._cparts))
661 return self._hash
662
663 def __lt__(self, other):
664 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
665 return NotImplemented
666 return self._cparts < other._cparts
667
668 def __le__(self, other):
669 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
670 return NotImplemented
671 return self._cparts <= other._cparts
672
673 def __gt__(self, other):
674 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
675 return NotImplemented
676 return self._cparts > other._cparts
677
678 def __ge__(self, other):
679 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
680 return NotImplemented
681 return self._cparts >= other._cparts
682
Batuhan Taşkaya526606b2019-12-08 23:31:15 +0300683 def __class_getitem__(cls, type):
684 return cls
685
Antoine Pitrou31119e42013-11-22 17:38:12 +0100686 drive = property(attrgetter('_drv'),
687 doc="""The drive prefix (letter or UNC path), if any.""")
688
689 root = property(attrgetter('_root'),
690 doc="""The root of the path, if any.""")
691
692 @property
693 def anchor(self):
694 """The concatenation of the drive and root, or ''."""
695 anchor = self._drv + self._root
696 return anchor
697
698 @property
699 def name(self):
700 """The final path component, if any."""
701 parts = self._parts
702 if len(parts) == (1 if (self._drv or self._root) else 0):
703 return ''
704 return parts[-1]
705
706 @property
707 def suffix(self):
Ram Rachum8d4fef42019-11-02 18:46:24 +0200708 """
709 The final component's last suffix, if any.
710
711 This includes the leading period. For example: '.txt'
712 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100713 name = self.name
714 i = name.rfind('.')
715 if 0 < i < len(name) - 1:
716 return name[i:]
717 else:
718 return ''
719
720 @property
721 def suffixes(self):
Ram Rachum8d4fef42019-11-02 18:46:24 +0200722 """
723 A list of the final component's suffixes, if any.
724
725 These include the leading periods. For example: ['.tar', '.gz']
726 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100727 name = self.name
728 if name.endswith('.'):
729 return []
730 name = name.lstrip('.')
731 return ['.' + suffix for suffix in name.split('.')[1:]]
732
733 @property
734 def stem(self):
735 """The final path component, minus its last suffix."""
736 name = self.name
737 i = name.rfind('.')
738 if 0 < i < len(name) - 1:
739 return name[:i]
740 else:
741 return name
742
743 def with_name(self, name):
744 """Return a new path with the file name changed."""
745 if not self.name:
746 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400747 drv, root, parts = self._flavour.parse_parts((name,))
748 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
749 or drv or root or len(parts) != 1):
750 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100751 return self._from_parsed_parts(self._drv, self._root,
752 self._parts[:-1] + [name])
753
Tim Hoffmann8aea4b32020-04-19 17:29:49 +0200754 def with_stem(self, stem):
755 """Return a new path with the stem changed."""
756 return self.with_name(stem + self.suffix)
757
Antoine Pitrou31119e42013-11-22 17:38:12 +0100758 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200759 """Return a new path with the file suffix changed. If the path
760 has no suffix, add given suffix. If the given suffix is an empty
761 string, remove the suffix from the path.
762 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400763 f = self._flavour
764 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300765 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400766 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100767 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100768 name = self.name
769 if not name:
770 raise ValueError("%r has an empty name" % (self,))
771 old_suffix = self.suffix
772 if not old_suffix:
773 name = name + suffix
774 else:
775 name = name[:-len(old_suffix)] + suffix
776 return self._from_parsed_parts(self._drv, self._root,
777 self._parts[:-1] + [name])
778
779 def relative_to(self, *other):
780 """Return the relative path to another path identified by the passed
781 arguments. If the operation is not possible (because this is not
782 a subpath of the other path), raise ValueError.
783 """
784 # For the purpose of this method, drive and root are considered
785 # separate parts, i.e.:
786 # Path('c:/').relative_to('c:') gives Path('/')
787 # Path('c:/').relative_to('/') raise ValueError
788 if not other:
789 raise TypeError("need at least one argument")
790 parts = self._parts
791 drv = self._drv
792 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100793 if root:
794 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100795 else:
796 abs_parts = parts
797 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100798 if to_root:
799 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100800 else:
801 to_abs_parts = to_parts
802 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100803 cf = self._flavour.casefold_parts
804 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100805 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
Rotuna44832532020-05-25 21:42:28 +0200806 raise ValueError("{!r} is not in the subpath of {!r}"
807 " OR one path is relative and the other is absolute."
Antoine Pitrou31119e42013-11-22 17:38:12 +0100808 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100809 return self._from_parsed_parts('', root if n == 1 else '',
810 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100811
Hai Shi82642a02019-08-13 14:54:02 -0500812 def is_relative_to(self, *other):
813 """Return True if the path is relative to another path or False.
814 """
815 try:
816 self.relative_to(*other)
817 return True
818 except ValueError:
819 return False
820
Antoine Pitrou31119e42013-11-22 17:38:12 +0100821 @property
822 def parts(self):
823 """An object providing sequence-like access to the
824 components in the filesystem path."""
825 # We cache the tuple to avoid building a new one each time .parts
826 # is accessed. XXX is this necessary?
827 try:
828 return self._pparts
829 except AttributeError:
830 self._pparts = tuple(self._parts)
831 return self._pparts
832
833 def joinpath(self, *args):
834 """Combine this path with one or several arguments, and return a
835 new path representing either a subpath (if all arguments are relative
836 paths) or a totally different path (if one of the arguments is
837 anchored).
838 """
839 return self._make_child(args)
840
841 def __truediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400842 try:
843 return self._make_child((key,))
844 except TypeError:
845 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100846
847 def __rtruediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400848 try:
849 return self._from_parts([key] + self._parts)
850 except TypeError:
851 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100852
853 @property
854 def parent(self):
855 """The logical parent of the path."""
856 drv = self._drv
857 root = self._root
858 parts = self._parts
859 if len(parts) == 1 and (drv or root):
860 return self
861 return self._from_parsed_parts(drv, root, parts[:-1])
862
863 @property
864 def parents(self):
865 """A sequence of this path's logical parents."""
866 return _PathParents(self)
867
868 def is_absolute(self):
869 """True if the path is absolute (has both a root and, if applicable,
870 a drive)."""
871 if not self._root:
872 return False
873 return not self._flavour.has_drv or bool(self._drv)
874
875 def is_reserved(self):
876 """Return True if the path contains one of the special names reserved
877 by the system, if any."""
878 return self._flavour.is_reserved(self._parts)
879
880 def match(self, path_pattern):
881 """
882 Return True if this path matches the given pattern.
883 """
884 cf = self._flavour.casefold
885 path_pattern = cf(path_pattern)
886 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
887 if not pat_parts:
888 raise ValueError("empty pattern")
889 if drv and drv != cf(self._drv):
890 return False
891 if root and root != cf(self._root):
892 return False
893 parts = self._cparts
894 if drv or root:
895 if len(pat_parts) != len(parts):
896 return False
897 pat_parts = pat_parts[1:]
898 elif len(pat_parts) > len(parts):
899 return False
900 for part, pat in zip(reversed(parts), reversed(pat_parts)):
901 if not fnmatch.fnmatchcase(part, pat):
902 return False
903 return True
904
Brett Cannon568be632016-06-10 12:20:49 -0700905# Can't subclass os.PathLike from PurePath and keep the constructor
906# optimizations in PurePath._parse_args().
907os.PathLike.register(PurePath)
908
Antoine Pitrou31119e42013-11-22 17:38:12 +0100909
910class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900911 """PurePath subclass for non-Windows systems.
912
913 On a POSIX system, instantiating a PurePath should return this object.
914 However, you can also instantiate it directly on any system.
915 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100916 _flavour = _posix_flavour
917 __slots__ = ()
918
919
920class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900921 """PurePath subclass for Windows systems.
922
923 On a Windows system, instantiating a PurePath should return this object.
924 However, you can also instantiate it directly on any system.
925 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100926 _flavour = _windows_flavour
927 __slots__ = ()
928
929
930# Filesystem-accessing classes
931
932
933class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900934 """PurePath subclass that can make system calls.
935
936 Path represents a filesystem path but unlike PurePath, also offers
937 methods to do system calls on path objects. Depending on your system,
938 instantiating a Path will return either a PosixPath or a WindowsPath
939 object. You can also instantiate a PosixPath or WindowsPath directly,
940 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
941 """
Barney Gale22191872021-04-07 01:26:37 +0100942 _accessor = _normal_accessor
943 __slots__ = ()
Antoine Pitrou31119e42013-11-22 17:38:12 +0100944
945 def __new__(cls, *args, **kwargs):
946 if cls is Path:
947 cls = WindowsPath if os.name == 'nt' else PosixPath
Barney Gale22191872021-04-07 01:26:37 +0100948 self = cls._from_parts(args)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100949 if not self._flavour.is_supported:
950 raise NotImplementedError("cannot instantiate %r on your system"
951 % (cls.__name__,))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100952 return self
953
Antoine Pitrou31119e42013-11-22 17:38:12 +0100954 def _make_child_relpath(self, part):
955 # This is an optimization used for dir walking. `part` must be
956 # a single part relative to this path.
957 parts = self._parts + [part]
958 return self._from_parsed_parts(self._drv, self._root, parts)
959
960 def __enter__(self):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100961 return self
962
963 def __exit__(self, t, v, tb):
Barney Gale00002e62020-04-01 15:10:51 +0100964 # https://bugs.python.org/issue39682
965 # In previous versions of pathlib, this method marked this path as
966 # closed; subsequent attempts to perform I/O would raise an IOError.
967 # This functionality was never documented, and had the effect of
968 # making Path objects mutable, contrary to PEP 428. In Python 3.9 the
969 # _closed attribute was removed, and this method made a no-op.
970 # This method and __enter__()/__exit__() should be deprecated and
971 # removed in the future.
972 pass
Antoine Pitrou31119e42013-11-22 17:38:12 +0100973
Antoine Pitrou31119e42013-11-22 17:38:12 +0100974 # Public API
975
976 @classmethod
977 def cwd(cls):
978 """Return a new path pointing to the current working directory
979 (as returned by os.getcwd()).
980 """
kfollstad4a857182021-04-28 16:01:51 -0700981 return cls(cls._accessor.getcwd())
Antoine Pitrou31119e42013-11-22 17:38:12 +0100982
Antoine Pitrou17cba7d2015-01-12 21:03:41 +0100983 @classmethod
984 def home(cls):
985 """Return a new path pointing to the user's home directory (as
986 returned by os.path.expanduser('~')).
987 """
Barney Gale3f3d82b2021-04-07 23:50:13 +0100988 return cls("~").expanduser()
Antoine Pitrou17cba7d2015-01-12 21:03:41 +0100989
Antoine Pitrou43e3d942014-05-13 10:50:15 +0200990 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +0300991 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +0300992 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +0200993 """
994 st = self.stat()
995 try:
996 other_st = other_path.stat()
997 except AttributeError:
Barney Gale5b1d9182020-04-17 18:47:27 +0100998 other_st = self._accessor.stat(other_path)
Antoine Pitrou43e3d942014-05-13 10:50:15 +0200999 return os.path.samestat(st, other_st)
1000
Antoine Pitrou31119e42013-11-22 17:38:12 +01001001 def iterdir(self):
1002 """Iterate over the files in this directory. Does not yield any
1003 result for the special paths '.' and '..'.
1004 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001005 for name in self._accessor.listdir(self):
1006 if name in {'.', '..'}:
1007 # Yielding a path object for these makes little sense
1008 continue
1009 yield self._make_child_relpath(name)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001010
1011 def glob(self, pattern):
1012 """Iterate over this subtree and yield all existing files (of any
Eivind Teig537b6ca2019-02-11 11:47:09 +01001013 kind, including directories) matching the given relative pattern.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001014 """
Serhiy Storchakaf4f445b2020-02-12 12:11:34 +02001015 sys.audit("pathlib.Path.glob", self, pattern)
Berker Peksag4a208e42016-01-30 17:50:48 +02001016 if not pattern:
1017 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001018 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1019 if drv or root:
1020 raise NotImplementedError("Non-relative patterns are unsupported")
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +03001021 selector = _make_selector(tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001022 for p in selector.select_from(self):
1023 yield p
1024
1025 def rglob(self, pattern):
1026 """Recursively yield all existing files (of any kind, including
Eivind Teig537b6ca2019-02-11 11:47:09 +01001027 directories) matching the given relative pattern, anywhere in
1028 this subtree.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001029 """
Serhiy Storchakaf4f445b2020-02-12 12:11:34 +02001030 sys.audit("pathlib.Path.rglob", self, pattern)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001031 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1032 if drv or root:
1033 raise NotImplementedError("Non-relative patterns are unsupported")
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +03001034 selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001035 for p in selector.select_from(self):
1036 yield p
1037
1038 def absolute(self):
1039 """Return an absolute version of this path. This function works
1040 even if the path doesn't point to anything.
1041
1042 No normalization is done, i.e. all '.' and '..' will be kept along.
1043 Use resolve() to get the canonical path to a file.
1044 """
1045 # XXX untested yet!
Antoine Pitrou31119e42013-11-22 17:38:12 +01001046 if self.is_absolute():
1047 return self
1048 # FIXME this must defer to the specific flavour (and, under Windows,
1049 # use nt._getfullpathname())
Barney Galeb05440c2021-04-07 17:31:49 +01001050 return self._from_parts([self._accessor.getcwd()] + self._parts)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001051
Steve Dower98eb3602016-11-09 12:58:17 -08001052 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001053 """
1054 Make the path absolute, resolving all symlinks on the way and also
1055 normalizing it (for example turning slashes into backslashes under
1056 Windows).
1057 """
Barney Galebaecfbd2021-04-28 16:50:17 +01001058
1059 def check_eloop(e):
1060 winerror = getattr(e, 'winerror', 0)
1061 if e.errno == ELOOP or winerror == _WINERROR_CANT_RESOLVE_FILENAME:
1062 raise RuntimeError("Symlink loop from %r" % e.filename)
1063
1064 try:
1065 s = self._accessor.realpath(self, strict=strict)
1066 except OSError as e:
1067 check_eloop(e)
1068 raise
1069 p = self._from_parts((s,))
1070
1071 # In non-strict mode, realpath() doesn't raise on symlink loops.
1072 # Ensure we get an exception by calling stat()
1073 if not strict:
1074 try:
1075 p.stat()
1076 except OSError as e:
1077 check_eloop(e)
1078 return p
Antoine Pitrou31119e42013-11-22 17:38:12 +01001079
Barney Galeabf96492021-04-07 16:53:39 +01001080 def stat(self, *, follow_symlinks=True):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001081 """
1082 Return the result of the stat() system call on this path, like
1083 os.stat() does.
1084 """
Barney Galeabf96492021-04-07 16:53:39 +01001085 return self._accessor.stat(self, follow_symlinks=follow_symlinks)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001086
1087 def owner(self):
1088 """
1089 Return the login name of the file owner.
1090 """
Barney Gale22386bb2020-04-17 17:41:07 +01001091 return self._accessor.owner(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001092
1093 def group(self):
1094 """
1095 Return the group name of the file gid.
1096 """
Barney Gale22386bb2020-04-17 17:41:07 +01001097 return self._accessor.group(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001098
Antoine Pitrou31119e42013-11-22 17:38:12 +01001099 def open(self, mode='r', buffering=-1, encoding=None,
1100 errors=None, newline=None):
1101 """
1102 Open the file pointed by this path and return a file object, as
1103 the built-in open() function does.
1104 """
Inada Naoki48274832021-03-29 12:28:14 +09001105 if "b" not in mode:
1106 encoding = io.text_encoding(encoding)
Barney Gale11c3bd32021-04-09 21:52:49 +01001107 return self._accessor.open(self, mode, buffering, encoding, errors,
1108 newline)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001109
Georg Brandlea683982014-10-01 19:12:33 +02001110 def read_bytes(self):
1111 """
1112 Open the file in bytes mode, read it, and close the file.
1113 """
1114 with self.open(mode='rb') as f:
1115 return f.read()
1116
1117 def read_text(self, encoding=None, errors=None):
1118 """
1119 Open the file in text mode, read it, and close the file.
1120 """
Inada Naoki48274832021-03-29 12:28:14 +09001121 encoding = io.text_encoding(encoding)
Georg Brandlea683982014-10-01 19:12:33 +02001122 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1123 return f.read()
1124
1125 def write_bytes(self, data):
1126 """
1127 Open the file in bytes mode, write to it, and close the file.
1128 """
1129 # type-check for the buffer interface before truncating the file
1130 view = memoryview(data)
1131 with self.open(mode='wb') as f:
1132 return f.write(view)
1133
Максим5f227412020-10-21 05:08:19 +03001134 def write_text(self, data, encoding=None, errors=None, newline=None):
Georg Brandlea683982014-10-01 19:12:33 +02001135 """
1136 Open the file in text mode, write to it, and close the file.
1137 """
1138 if not isinstance(data, str):
1139 raise TypeError('data must be str, not %s' %
1140 data.__class__.__name__)
Inada Naoki48274832021-03-29 12:28:14 +09001141 encoding = io.text_encoding(encoding)
Максим5f227412020-10-21 05:08:19 +03001142 with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
Georg Brandlea683982014-10-01 19:12:33 +02001143 return f.write(data)
1144
Girtsa01ba332019-10-23 14:18:40 -07001145 def readlink(self):
1146 """
1147 Return the path to which the symbolic link points.
1148 """
1149 path = self._accessor.readlink(self)
Barney Gale22191872021-04-07 01:26:37 +01001150 return self._from_parts((path,))
Girtsa01ba332019-10-23 14:18:40 -07001151
Antoine Pitrou31119e42013-11-22 17:38:12 +01001152 def touch(self, mode=0o666, exist_ok=True):
1153 """
1154 Create this file with the given access mode, if it doesn't exist.
1155 """
Barney Gale986da8e2021-04-07 01:25:37 +01001156 self._accessor.touch(self, mode, exist_ok)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001157
Barry Warsaw7c549c42014-08-05 11:28:12 -04001158 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001159 """
1160 Create a new directory at this given path.
1161 """
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001162 try:
1163 self._accessor.mkdir(self, mode)
1164 except FileNotFoundError:
1165 if not parents or self.parent == self:
1166 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001167 self.parent.mkdir(parents=True, exist_ok=True)
1168 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001169 except OSError:
1170 # Cannot rely on checking for EEXIST, since the operating system
1171 # could give priority to other errors like EACCES or EROFS
1172 if not exist_ok or not self.is_dir():
1173 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001174
Barney Galeabf96492021-04-07 16:53:39 +01001175 def chmod(self, mode, *, follow_symlinks=True):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001176 """
1177 Change the permissions of the path, like os.chmod().
1178 """
Barney Galeabf96492021-04-07 16:53:39 +01001179 self._accessor.chmod(self, mode, follow_symlinks=follow_symlinks)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001180
1181 def lchmod(self, mode):
1182 """
1183 Like chmod(), except if the path points to a symlink, the symlink's
1184 permissions are changed, rather than its target's.
1185 """
Barney Galeabf96492021-04-07 16:53:39 +01001186 self.chmod(mode, follow_symlinks=False)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001187
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001188 def unlink(self, missing_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001189 """
1190 Remove this file or link.
1191 If the path is a directory, use rmdir() instead.
1192 """
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001193 try:
1194 self._accessor.unlink(self)
1195 except FileNotFoundError:
1196 if not missing_ok:
1197 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001198
1199 def rmdir(self):
1200 """
1201 Remove this directory. The directory must be empty.
1202 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001203 self._accessor.rmdir(self)
1204
1205 def lstat(self):
1206 """
1207 Like stat(), except if the path points to a symlink, the symlink's
1208 status information is returned, rather than its target's.
1209 """
Barney Galeabf96492021-04-07 16:53:39 +01001210 return self.stat(follow_symlinks=False)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001211
1212 def rename(self, target):
1213 """
Ram Rachumf97e42e2020-10-03 12:52:13 +03001214 Rename this path to the target path.
1215
1216 The target path may be absolute or relative. Relative paths are
1217 interpreted relative to the current working directory, *not* the
1218 directory of the Path object.
1219
1220 Returns the new Path instance pointing to the target path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001221 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001222 self._accessor.rename(self, target)
hui shang088a09a2019-09-11 21:26:49 +08001223 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001224
1225 def replace(self, target):
1226 """
Ram Rachumf97e42e2020-10-03 12:52:13 +03001227 Rename this path to the target path, overwriting if that path exists.
1228
1229 The target path may be absolute or relative. Relative paths are
1230 interpreted relative to the current working directory, *not* the
1231 directory of the Path object.
1232
1233 Returns the new Path instance pointing to the target path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001234 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001235 self._accessor.replace(self, target)
hui shang088a09a2019-09-11 21:26:49 +08001236 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001237
1238 def symlink_to(self, target, target_is_directory=False):
1239 """
Barney Gale8aac1be2021-04-07 16:56:32 +01001240 Make this path a symlink pointing to the target path.
1241 Note the order of arguments (link, target) is the reverse of os.symlink.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001242 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001243 self._accessor.symlink(target, self, target_is_directory)
1244
Barney Galef24e2e52021-04-23 21:48:52 +01001245 def hardlink_to(self, target):
1246 """
1247 Make this path a hard link pointing to the same file as *target*.
1248
1249 Note the order of arguments (self, target) is the reverse of os.link's.
1250 """
1251 self._accessor.link(target, self)
1252
Barney Gale8aac1be2021-04-07 16:56:32 +01001253 def link_to(self, target):
1254 """
1255 Make the target path a hard link pointing to this path.
1256
1257 Note this function does not make this path a hard link to *target*,
1258 despite the implication of the function and argument names. The order
1259 of arguments (target, link) is the reverse of Path.symlink_to, but
1260 matches that of os.link.
1261
Barney Galef24e2e52021-04-23 21:48:52 +01001262 Deprecated since Python 3.10 and scheduled for removal in Python 3.12.
1263 Use `hardlink_to()` instead.
Barney Gale8aac1be2021-04-07 16:56:32 +01001264 """
Barney Galef24e2e52021-04-23 21:48:52 +01001265 warnings.warn("pathlib.Path.link_to() is deprecated and is scheduled "
1266 "for removal in Python 3.12. "
1267 "Use pathlib.Path.hardlink_to() instead.",
Miss Islington (bot)b913f472021-05-16 15:35:44 -07001268 DeprecationWarning, stacklevel=2)
Barney Gale8aac1be2021-04-07 16:56:32 +01001269 self._accessor.link(self, target)
1270
Antoine Pitrou31119e42013-11-22 17:38:12 +01001271 # Convenience functions for querying the stat results
1272
1273 def exists(self):
1274 """
1275 Whether this path exists.
1276 """
1277 try:
1278 self.stat()
1279 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001280 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001281 raise
1282 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001283 except ValueError:
1284 # Non-encodable path
1285 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001286 return True
1287
1288 def is_dir(self):
1289 """
1290 Whether this path is a directory.
1291 """
1292 try:
1293 return S_ISDIR(self.stat().st_mode)
1294 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001295 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001296 raise
1297 # Path doesn't exist or is a broken symlink
Miss Islington (bot)ce4fee22021-05-05 01:54:05 -07001298 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
Antoine Pitrou31119e42013-11-22 17:38:12 +01001299 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001300 except ValueError:
1301 # Non-encodable path
1302 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001303
1304 def is_file(self):
1305 """
1306 Whether this path is a regular file (also True for symlinks pointing
1307 to regular files).
1308 """
1309 try:
1310 return S_ISREG(self.stat().st_mode)
1311 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001312 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001313 raise
1314 # Path doesn't exist or is a broken symlink
Miss Islington (bot)ce4fee22021-05-05 01:54:05 -07001315 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
Antoine Pitrou31119e42013-11-22 17:38:12 +01001316 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001317 except ValueError:
1318 # Non-encodable path
1319 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001320
Cooper Lees173ff4a2017-08-01 15:35:45 -07001321 def is_mount(self):
1322 """
1323 Check if this path is a POSIX mount point
1324 """
1325 # Need to exist and be a dir
1326 if not self.exists() or not self.is_dir():
1327 return False
1328
Cooper Lees173ff4a2017-08-01 15:35:45 -07001329 try:
Barney Galec746c4f2020-04-17 18:42:06 +01001330 parent_dev = self.parent.stat().st_dev
Cooper Lees173ff4a2017-08-01 15:35:45 -07001331 except OSError:
1332 return False
1333
1334 dev = self.stat().st_dev
1335 if dev != parent_dev:
1336 return True
1337 ino = self.stat().st_ino
Barney Galec746c4f2020-04-17 18:42:06 +01001338 parent_ino = self.parent.stat().st_ino
Cooper Lees173ff4a2017-08-01 15:35:45 -07001339 return ino == parent_ino
1340
Antoine Pitrou31119e42013-11-22 17:38:12 +01001341 def is_symlink(self):
1342 """
1343 Whether this path is a symbolic link.
1344 """
1345 try:
1346 return S_ISLNK(self.lstat().st_mode)
1347 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001348 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001349 raise
1350 # Path doesn't exist
1351 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001352 except ValueError:
1353 # Non-encodable path
1354 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001355
1356 def is_block_device(self):
1357 """
1358 Whether this path is a block device.
1359 """
1360 try:
1361 return S_ISBLK(self.stat().st_mode)
1362 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001363 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001364 raise
1365 # Path doesn't exist or is a broken symlink
Miss Islington (bot)ce4fee22021-05-05 01:54:05 -07001366 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
Antoine Pitrou31119e42013-11-22 17:38:12 +01001367 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001368 except ValueError:
1369 # Non-encodable path
1370 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001371
1372 def is_char_device(self):
1373 """
1374 Whether this path is a character device.
1375 """
1376 try:
1377 return S_ISCHR(self.stat().st_mode)
1378 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001379 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001380 raise
1381 # Path doesn't exist or is a broken symlink
Miss Islington (bot)ce4fee22021-05-05 01:54:05 -07001382 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
Antoine Pitrou31119e42013-11-22 17:38:12 +01001383 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001384 except ValueError:
1385 # Non-encodable path
1386 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001387
1388 def is_fifo(self):
1389 """
1390 Whether this path is a FIFO.
1391 """
1392 try:
1393 return S_ISFIFO(self.stat().st_mode)
1394 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001395 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001396 raise
1397 # Path doesn't exist or is a broken symlink
Miss Islington (bot)ce4fee22021-05-05 01:54:05 -07001398 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
Antoine Pitrou31119e42013-11-22 17:38:12 +01001399 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001400 except ValueError:
1401 # Non-encodable path
1402 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001403
1404 def is_socket(self):
1405 """
1406 Whether this path is a socket.
1407 """
1408 try:
1409 return S_ISSOCK(self.stat().st_mode)
1410 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001411 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001412 raise
1413 # Path doesn't exist or is a broken symlink
Miss Islington (bot)ce4fee22021-05-05 01:54:05 -07001414 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ )
Antoine Pitrou31119e42013-11-22 17:38:12 +01001415 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001416 except ValueError:
1417 # Non-encodable path
1418 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001419
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001420 def expanduser(self):
1421 """ Return a new path with expanded ~ and ~user constructs
1422 (as returned by os.path.expanduser)
1423 """
1424 if (not (self._drv or self._root) and
1425 self._parts and self._parts[0][:1] == '~'):
Barney Gale3f3d82b2021-04-07 23:50:13 +01001426 homedir = self._accessor.expanduser(self._parts[0])
1427 if homedir[:1] == "~":
1428 raise RuntimeError("Could not determine home directory.")
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001429 return self._from_parts([homedir] + self._parts[1:])
1430
1431 return self
1432
Antoine Pitrou31119e42013-11-22 17:38:12 +01001433
1434class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001435 """Path subclass for non-Windows systems.
1436
1437 On a POSIX system, instantiating a Path should return this object.
1438 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001439 __slots__ = ()
1440
1441class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001442 """Path subclass for Windows systems.
1443
1444 On a Windows system, instantiating a Path should return this object.
1445 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001446 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001447
Cooper Lees173ff4a2017-08-01 15:35:45 -07001448 def is_mount(self):
1449 raise NotImplementedError("Path.is_mount() is unsupported on this system")