blob: 9db8ae2d8a389e70e63614882944017c054cd1fd [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
Serhiy Storchaka81108372017-09-26 00:55:55 +03009from _collections_abc import Sequence
Jörg Stucked5c120f2019-05-21 19:44:40 +020010from errno import EINVAL, ENOENT, ENOTDIR, EBADF, ELOOP
Antoine Pitrou31119e42013-11-22 17:38:12 +010011from operator import attrgetter
12from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
Antoine Pitrou069a5e12013-12-03 09:41:35 +010013from urllib.parse import quote_from_bytes as urlquote_from_bytes
Antoine Pitrou31119e42013-11-22 17:38:12 +010014
15
Antoine Pitroudb118f52014-11-19 00:32:08 +010016if os.name == 'nt':
Barney Galeb57e0452021-04-07 00:01:22 +010017 from nt import _getfinalpathname
Antoine Pitroudb118f52014-11-19 00:32:08 +010018else:
Barney Galeb57e0452021-04-07 00:01:22 +010019 _getfinalpathname = None
Antoine Pitrou31119e42013-11-22 17:38:12 +010020
21
22__all__ = [
23 "PurePath", "PurePosixPath", "PureWindowsPath",
24 "Path", "PosixPath", "WindowsPath",
25 ]
26
27#
28# Internals
29#
30
penguindustin96466302019-05-06 14:57:17 -040031# EBADF - guard against macOS `stat` throwing EBADF
Jörg Stucked5c120f2019-05-21 19:44:40 +020032_IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF, ELOOP)
Przemysław Spodymek216b7452018-08-27 23:33:45 +020033
Steve Dower2f6fae62019-02-03 23:08:18 -080034_IGNORED_WINERRORS = (
35 21, # ERROR_NOT_READY - drive exists but is not accessible
Jörg Stucked5c120f2019-05-21 19:44:40 +020036 1921, # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself
Steve Dower2f6fae62019-02-03 23:08:18 -080037)
38
39def _ignore_error(exception):
40 return (getattr(exception, 'errno', None) in _IGNORED_ERROS or
41 getattr(exception, 'winerror', None) in _IGNORED_WINERRORS)
42
43
Antoine Pitrou31119e42013-11-22 17:38:12 +010044def _is_wildcard_pattern(pat):
45 # Whether this pattern needs actual matching using fnmatch, or can
46 # be looked up directly as a file.
47 return "*" in pat or "?" in pat or "[" in pat
48
49
50class _Flavour(object):
51 """A flavour implements a particular (platform-specific) set of path
52 semantics."""
53
54 def __init__(self):
55 self.join = self.sep.join
56
57 def parse_parts(self, parts):
58 parsed = []
59 sep = self.sep
60 altsep = self.altsep
61 drv = root = ''
62 it = reversed(parts)
63 for part in it:
64 if not part:
65 continue
66 if altsep:
67 part = part.replace(altsep, sep)
68 drv, root, rel = self.splitroot(part)
69 if sep in rel:
70 for x in reversed(rel.split(sep)):
71 if x and x != '.':
72 parsed.append(sys.intern(x))
73 else:
74 if rel and rel != '.':
75 parsed.append(sys.intern(rel))
76 if drv or root:
77 if not drv:
78 # If no drive is present, try to find one in the previous
79 # parts. This makes the result of parsing e.g.
80 # ("C:", "/", "a") reasonably intuitive.
81 for part in it:
Antoine Pitrou57fffd62015-02-15 18:03:59 +010082 if not part:
83 continue
84 if altsep:
85 part = part.replace(altsep, sep)
Antoine Pitrou31119e42013-11-22 17:38:12 +010086 drv = self.splitroot(part)[0]
87 if drv:
88 break
89 break
90 if drv or root:
91 parsed.append(drv + root)
92 parsed.reverse()
93 return drv, root, parsed
94
95 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
96 """
97 Join the two paths represented by the respective
98 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple.
99 """
100 if root2:
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200101 if not drv2 and drv:
102 return drv, root2, [drv + root2] + parts2[1:]
103 elif drv2:
104 if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
105 # Same drive => second path is relative to the first
106 return drv, root, parts + parts2[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100107 else:
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200108 # Second path is non-anchored (common case)
109 return drv, root, parts + parts2
110 return drv2, root2, parts2
Antoine Pitrou31119e42013-11-22 17:38:12 +0100111
112
113class _WindowsFlavour(_Flavour):
114 # Reference for Windows paths can be found at
115 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
116
117 sep = '\\'
118 altsep = '/'
119 has_drv = True
120 pathmod = ntpath
121
Antoine Pitroudb118f52014-11-19 00:32:08 +0100122 is_supported = (os.name == 'nt')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100123
Jon Dufresne39726282017-05-18 07:35:54 -0700124 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100125 ext_namespace_prefix = '\\\\?\\'
126
127 reserved_names = (
128 {'CON', 'PRN', 'AUX', 'NUL'} |
129 {'COM%d' % i for i in range(1, 10)} |
130 {'LPT%d' % i for i in range(1, 10)}
131 )
132
133 # Interesting findings about extended paths:
134 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
135 # but '\\?\c:/a' is not
136 # - extended paths are always absolute; "relative" extended paths will
137 # fail.
138
139 def splitroot(self, part, sep=sep):
140 first = part[0:1]
141 second = part[1:2]
142 if (second == sep and first == sep):
143 # XXX extended paths should also disable the collapsing of "."
144 # components (according to MSDN docs).
145 prefix, part = self._split_extended_path(part)
146 first = part[0:1]
147 second = part[1:2]
148 else:
149 prefix = ''
150 third = part[2:3]
151 if (second == sep and first == sep and third != sep):
152 # is a UNC path:
153 # vvvvvvvvvvvvvvvvvvvvv root
154 # \\machine\mountpoint\directory\etc\...
155 # directory ^^^^^^^^^^^^^^
156 index = part.find(sep, 2)
157 if index != -1:
158 index2 = part.find(sep, index + 1)
159 # a UNC path can't have two slashes in a row
160 # (after the initial two)
161 if index2 != index + 1:
162 if index2 == -1:
163 index2 = len(part)
164 if prefix:
165 return prefix + part[1:index2], sep, part[index2+1:]
166 else:
167 return part[:index2], sep, part[index2+1:]
168 drv = root = ''
169 if second == ':' and first in self.drive_letters:
170 drv = part[:2]
171 part = part[2:]
172 first = third
173 if first == sep:
174 root = first
175 part = part.lstrip(sep)
176 return prefix + drv, root, part
177
178 def casefold(self, s):
179 return s.lower()
180
181 def casefold_parts(self, parts):
182 return [p.lower() for p in parts]
183
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300184 def compile_pattern(self, pattern):
185 return re.compile(fnmatch.translate(pattern), re.IGNORECASE).fullmatch
186
Steve Dower98eb3602016-11-09 12:58:17 -0800187 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100188 s = str(path)
189 if not s:
190 return os.getcwd()
Steve Dower98eb3602016-11-09 12:58:17 -0800191 previous_s = None
Antoine Pitrou31119e42013-11-22 17:38:12 +0100192 if _getfinalpathname is not None:
Steve Dower98eb3602016-11-09 12:58:17 -0800193 if strict:
194 return self._ext_to_normal(_getfinalpathname(s))
195 else:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200196 tail_parts = [] # End of the path after the first one not found
Steve Dower98eb3602016-11-09 12:58:17 -0800197 while True:
198 try:
199 s = self._ext_to_normal(_getfinalpathname(s))
200 except FileNotFoundError:
201 previous_s = s
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200202 s, tail = os.path.split(s)
203 tail_parts.append(tail)
Steve Dower4b1e98b2016-12-28 16:02:59 -0800204 if previous_s == s:
205 return path
Steve Dower98eb3602016-11-09 12:58:17 -0800206 else:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200207 return os.path.join(s, *reversed(tail_parts))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100208 # Means fallback on absolute
209 return None
210
211 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
212 prefix = ''
213 if s.startswith(ext_prefix):
214 prefix = s[:4]
215 s = s[4:]
216 if s.startswith('UNC\\'):
217 prefix += s[:3]
218 s = '\\' + s[3:]
219 return prefix, s
220
221 def _ext_to_normal(self, s):
222 # Turn back an extended path into a normal DOS-like path
223 return self._split_extended_path(s)[1]
224
225 def is_reserved(self, parts):
226 # NOTE: the rules for reserved names seem somewhat complicated
227 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
228 # We err on the side of caution and return True for paths which are
229 # not considered reserved by Windows.
230 if not parts:
231 return False
232 if parts[0].startswith('\\\\'):
233 # UNC paths are never reserved
234 return False
235 return parts[-1].partition('.')[0].upper() in self.reserved_names
236
237 def make_uri(self, path):
238 # Under Windows, file URIs use the UTF-8 encoding.
239 drive = path.drive
240 if len(drive) == 2 and drive[1] == ':':
241 # It's a path on a local drive => 'file:///c:/a/b'
242 rest = path.as_posix()[2:].lstrip('/')
243 return 'file:///%s/%s' % (
244 drive, urlquote_from_bytes(rest.encode('utf-8')))
245 else:
246 # It's a path on a network drive => 'file://host/share/a/b'
247 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
248
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100249 def gethomedir(self, username):
Christoph Reiterc45a2aa2020-01-28 10:41:50 +0100250 if 'USERPROFILE' in os.environ:
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100251 userhome = os.environ['USERPROFILE']
252 elif 'HOMEPATH' in os.environ:
Antoine Pitrou5d4e27e2014-12-30 22:09:42 +0100253 try:
254 drv = os.environ['HOMEDRIVE']
255 except KeyError:
256 drv = ''
257 userhome = drv + os.environ['HOMEPATH']
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100258 else:
259 raise RuntimeError("Can't determine home directory")
260
261 if username:
262 # Try to guess user home directory. By default all users
263 # directories are located in the same place and are named by
264 # corresponding usernames. If current user home directory points
265 # to nonstandard place, this guess is likely wrong.
266 if os.environ['USERNAME'] != username:
267 drv, root, parts = self.parse_parts((userhome,))
268 if parts[-1] != os.environ['USERNAME']:
269 raise RuntimeError("Can't determine home directory "
270 "for %r" % username)
271 parts[-1] = username
272 if drv or root:
273 userhome = drv + root + self.join(parts[1:])
274 else:
275 userhome = self.join(parts)
276 return userhome
Antoine Pitrou31119e42013-11-22 17:38:12 +0100277
278class _PosixFlavour(_Flavour):
279 sep = '/'
280 altsep = ''
281 has_drv = False
282 pathmod = posixpath
283
284 is_supported = (os.name != 'nt')
285
286 def splitroot(self, part, sep=sep):
287 if part and part[0] == sep:
288 stripped_part = part.lstrip(sep)
289 # According to POSIX path resolution:
290 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
291 # "A pathname that begins with two successive slashes may be
292 # interpreted in an implementation-defined manner, although more
293 # than two leading slashes shall be treated as a single slash".
294 if len(part) - len(stripped_part) == 2:
295 return '', sep * 2, stripped_part
296 else:
297 return '', sep, stripped_part
298 else:
299 return '', '', part
300
301 def casefold(self, s):
302 return s
303
304 def casefold_parts(self, parts):
305 return parts
306
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300307 def compile_pattern(self, pattern):
308 return re.compile(fnmatch.translate(pattern)).fullmatch
309
Steve Dower98eb3602016-11-09 12:58:17 -0800310 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100311 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100312 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100313 seen = {}
314 def _resolve(path, rest):
315 if rest.startswith(sep):
316 path = ''
317
318 for name in rest.split(sep):
319 if not name or name == '.':
320 # current dir
321 continue
322 if name == '..':
323 # parent dir
324 path, _, _ = path.rpartition(sep)
325 continue
Dong-hee Na94ad6c62018-06-12 23:30:45 +0900326 if path.endswith(sep):
327 newpath = path + name
328 else:
329 newpath = path + sep + name
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100330 if newpath in seen:
331 # Already seen this path
332 path = seen[newpath]
333 if path is not None:
334 # use cached value
335 continue
336 # The symlink is not resolved, so we must have a symlink loop.
337 raise RuntimeError("Symlink loop from %r" % newpath)
338 # Resolve the symbolic link
339 try:
340 target = accessor.readlink(newpath)
341 except OSError as e:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200342 if e.errno != EINVAL and strict:
343 raise
344 # Not a symlink, or non-strict mode. We just leave the path
345 # untouched.
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100346 path = newpath
347 else:
348 seen[newpath] = None # not resolved symlink
349 path = _resolve(path, target)
350 seen[newpath] = path # resolved symlink
351
352 return path
353 # NOTE: according to POSIX, getcwd() cannot contain path components
354 # which are symlinks.
355 base = '' if path.is_absolute() else os.getcwd()
356 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100357
358 def is_reserved(self, parts):
359 return False
360
361 def make_uri(self, path):
362 # We represent the path using the local filesystem encoding,
363 # for portability to other applications.
364 bpath = bytes(path)
365 return 'file://' + urlquote_from_bytes(bpath)
366
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100367 def gethomedir(self, username):
368 if not username:
369 try:
370 return os.environ['HOME']
371 except KeyError:
372 import pwd
373 return pwd.getpwuid(os.getuid()).pw_dir
374 else:
375 import pwd
376 try:
377 return pwd.getpwnam(username).pw_dir
378 except KeyError:
379 raise RuntimeError("Can't determine home directory "
380 "for %r" % username)
381
Antoine Pitrou31119e42013-11-22 17:38:12 +0100382
383_windows_flavour = _WindowsFlavour()
384_posix_flavour = _PosixFlavour()
385
386
387class _Accessor:
388 """An accessor implements a particular (system-specific or not) way of
389 accessing paths on the filesystem."""
390
391
392class _NormalAccessor(_Accessor):
393
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200394 stat = os.stat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100395
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200396 lstat = os.lstat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100397
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200398 open = os.open
Antoine Pitrou31119e42013-11-22 17:38:12 +0100399
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200400 listdir = os.listdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100401
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200402 scandir = os.scandir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100403
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200404 chmod = os.chmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100405
406 if hasattr(os, "lchmod"):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200407 lchmod = os.lchmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100408 else:
Barney Galeb57e0452021-04-07 00:01:22 +0100409 def lchmod(self, path, mode):
410 raise NotImplementedError("os.lchmod() not available on this system")
Antoine Pitrou31119e42013-11-22 17:38:12 +0100411
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200412 mkdir = os.mkdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100413
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200414 unlink = os.unlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100415
Toke Høiland-Jørgensen092435e2019-12-16 13:23:55 +0100416 if hasattr(os, "link"):
Barney Galeb57e0452021-04-07 00:01:22 +0100417 link = os.link
Toke Høiland-Jørgensen092435e2019-12-16 13:23:55 +0100418 else:
Barney Galeb57e0452021-04-07 00:01:22 +0100419 def link(self, src, dst):
Toke Høiland-Jørgensen092435e2019-12-16 13:23:55 +0100420 raise NotImplementedError("os.link() not available on this system")
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -0400421
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200422 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100423
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200424 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100425
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200426 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100427
Barney Galeb57e0452021-04-07 00:01:22 +0100428 if hasattr(os, "symlink"):
429 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100430 else:
Barney Galeb57e0452021-04-07 00:01:22 +0100431 def symlink(self, src, dst, target_is_directory=False):
432 raise NotImplementedError("os.symlink() not available on this system")
Antoine Pitrou31119e42013-11-22 17:38:12 +0100433
Barney Gale986da8e2021-04-07 01:25:37 +0100434 def touch(self, path, mode=0o666, exist_ok=True):
435 if exist_ok:
436 # First try to bump modification time
437 # Implementation note: GNU touch uses the UTIME_NOW option of
438 # the utimensat() / futimens() functions.
439 try:
440 os.utime(path, None)
441 except OSError:
442 # Avoid exception chaining
443 pass
444 else:
445 return
446 flags = os.O_CREAT | os.O_WRONLY
447 if not exist_ok:
448 flags |= os.O_EXCL
449 fd = os.open(path, flags, mode)
450 os.close(fd)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100451
Barney Galeb57e0452021-04-07 00:01:22 +0100452 if hasattr(os, "readlink"):
453 readlink = os.readlink
454 else:
455 def readlink(self, path):
456 raise NotImplementedError("os.readlink() not available on this system")
Antoine Pitrou31119e42013-11-22 17:38:12 +0100457
Barney Gale22386bb2020-04-17 17:41:07 +0100458 def owner(self, path):
459 try:
460 import pwd
461 return pwd.getpwuid(self.stat(path).st_uid).pw_name
462 except ImportError:
463 raise NotImplementedError("Path.owner() is unsupported on this system")
464
465 def group(self, path):
466 try:
467 import grp
468 return grp.getgrgid(self.stat(path).st_gid).gr_name
469 except ImportError:
470 raise NotImplementedError("Path.group() is unsupported on this system")
471
Antoine Pitrou31119e42013-11-22 17:38:12 +0100472
473_normal_accessor = _NormalAccessor()
474
475
476#
477# Globbing helpers
478#
479
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300480def _make_selector(pattern_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100481 pat = pattern_parts[0]
482 child_parts = pattern_parts[1:]
483 if pat == '**':
484 cls = _RecursiveWildcardSelector
485 elif '**' in pat:
486 raise ValueError("Invalid pattern: '**' can only be an entire path component")
487 elif _is_wildcard_pattern(pat):
488 cls = _WildcardSelector
489 else:
490 cls = _PreciseSelector
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300491 return cls(pat, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100492
493if hasattr(functools, "lru_cache"):
494 _make_selector = functools.lru_cache()(_make_selector)
495
496
497class _Selector:
498 """A selector matches a specific glob pattern part against the children
499 of a given path."""
500
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300501 def __init__(self, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100502 self.child_parts = child_parts
503 if child_parts:
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300504 self.successor = _make_selector(child_parts, flavour)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300505 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100506 else:
507 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300508 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100509
510 def select_from(self, parent_path):
511 """Iterate over all child paths of `parent_path` matched by this
512 selector. This can contain parent_path itself."""
513 path_cls = type(parent_path)
514 is_dir = path_cls.is_dir
515 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300516 scandir = parent_path._accessor.scandir
517 if not is_dir(parent_path):
518 return iter([])
519 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100520
521
522class _TerminatingSelector:
523
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300524 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100525 yield parent_path
526
527
528class _PreciseSelector(_Selector):
529
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300530 def __init__(self, name, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100531 self.name = name
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300532 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100533
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300534 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800535 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800536 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300537 if (is_dir if self.dironly else exists)(path):
538 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800539 yield p
540 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100541 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100542
543
544class _WildcardSelector(_Selector):
545
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300546 def __init__(self, pat, child_parts, flavour):
547 self.match = flavour.compile_pattern(pat)
548 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100549
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300550 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800551 try:
Serhiy Storchaka704e2062020-03-11 18:42:03 +0200552 with scandir(parent_path) as scandir_it:
553 entries = list(scandir_it)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300554 for entry in entries:
Pablo Galindoeb7560a2020-03-07 17:53:20 +0000555 if self.dironly:
556 try:
557 # "entry.is_dir()" can raise PermissionError
558 # in some cases (see bpo-38894), which is not
559 # among the errors ignored by _ignore_error()
560 if not entry.is_dir():
561 continue
562 except OSError as e:
563 if not _ignore_error(e):
564 raise
565 continue
566 name = entry.name
567 if self.match(name):
568 path = parent_path._make_child_relpath(name)
569 for p in self.successor._select_from(path, is_dir, exists, scandir):
570 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800571 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100572 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800573
Antoine Pitrou31119e42013-11-22 17:38:12 +0100574
Antoine Pitrou31119e42013-11-22 17:38:12 +0100575class _RecursiveWildcardSelector(_Selector):
576
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300577 def __init__(self, pat, child_parts, flavour):
578 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100579
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300580 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100581 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800582 try:
Serhiy Storchaka704e2062020-03-11 18:42:03 +0200583 with scandir(parent_path) as scandir_it:
584 entries = list(scandir_it)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300585 for entry in entries:
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200586 entry_is_dir = False
587 try:
588 entry_is_dir = entry.is_dir()
589 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -0800590 if not _ignore_error(e):
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200591 raise
592 if entry_is_dir and not entry.is_symlink():
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300593 path = parent_path._make_child_relpath(entry.name)
594 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800595 yield p
596 except PermissionError:
597 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100598
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300599 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800600 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300601 yielded = set()
602 try:
603 successor_select = self.successor._select_from
604 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
605 for p in successor_select(starting_point, is_dir, exists, scandir):
606 if p not in yielded:
607 yield p
608 yielded.add(p)
609 finally:
610 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800611 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100612 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100613
614
615#
616# Public API
617#
618
619class _PathParents(Sequence):
620 """This object provides sequence-like access to the logical ancestors
621 of a path. Don't try to construct it yourself."""
622 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
623
624 def __init__(self, path):
625 # We don't store the instance to avoid reference cycles
626 self._pathcls = type(path)
627 self._drv = path._drv
628 self._root = path._root
629 self._parts = path._parts
630
631 def __len__(self):
632 if self._drv or self._root:
633 return len(self._parts) - 1
634 else:
635 return len(self._parts)
636
637 def __getitem__(self, idx):
Joshua Cannon45205842020-11-20 09:40:39 -0600638 if isinstance(idx, slice):
639 return tuple(self[i] for i in range(*idx.indices(len(self))))
Yaroslav Pankovych79d2e622020-11-23 22:06:22 +0200640
641 if idx >= len(self) or idx < -len(self):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100642 raise IndexError(idx)
643 return self._pathcls._from_parsed_parts(self._drv, self._root,
644 self._parts[:-idx - 1])
645
646 def __repr__(self):
647 return "<{}.parents>".format(self._pathcls.__name__)
648
649
650class PurePath(object):
chasondfa015c2018-02-19 08:36:32 +0900651 """Base class for manipulating paths without I/O.
652
653 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100654 don't imply any actual filesystem I/O. Depending on your system,
655 instantiating a PurePath will return either a PurePosixPath or a
656 PureWindowsPath object. You can also instantiate either of these classes
657 directly, regardless of your system.
658 """
659 __slots__ = (
660 '_drv', '_root', '_parts',
661 '_str', '_hash', '_pparts', '_cached_cparts',
662 )
663
664 def __new__(cls, *args):
665 """Construct a PurePath from one or several strings and or existing
666 PurePath objects. The strings and path objects are combined so as
667 to yield a canonicalized path, which is incorporated into the
668 new PurePath object.
669 """
670 if cls is PurePath:
671 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
672 return cls._from_parts(args)
673
674 def __reduce__(self):
675 # Using the parts tuple helps share interned path parts
676 # when pickling related paths.
677 return (self.__class__, tuple(self._parts))
678
679 @classmethod
680 def _parse_args(cls, args):
681 # This is useful when you don't want to create an instance, just
682 # canonicalize some constructor arguments.
683 parts = []
684 for a in args:
685 if isinstance(a, PurePath):
686 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100687 else:
Brett Cannon568be632016-06-10 12:20:49 -0700688 a = os.fspath(a)
689 if isinstance(a, str):
690 # Force-cast str subclasses to str (issue #21127)
691 parts.append(str(a))
692 else:
693 raise TypeError(
694 "argument should be a str object or an os.PathLike "
695 "object returning str, not %r"
696 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100697 return cls._flavour.parse_parts(parts)
698
699 @classmethod
Barney Gale22191872021-04-07 01:26:37 +0100700 def _from_parts(cls, args):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100701 # We need to call _parse_args on the instance, so as to get the
702 # right flavour.
703 self = object.__new__(cls)
704 drv, root, parts = self._parse_args(args)
705 self._drv = drv
706 self._root = root
707 self._parts = parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100708 return self
709
710 @classmethod
Barney Gale22191872021-04-07 01:26:37 +0100711 def _from_parsed_parts(cls, drv, root, parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100712 self = object.__new__(cls)
713 self._drv = drv
714 self._root = root
715 self._parts = parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100716 return self
717
718 @classmethod
719 def _format_parsed_parts(cls, drv, root, parts):
720 if drv or root:
721 return drv + root + cls._flavour.join(parts[1:])
722 else:
723 return cls._flavour.join(parts)
724
Antoine Pitrou31119e42013-11-22 17:38:12 +0100725 def _make_child(self, args):
726 drv, root, parts = self._parse_args(args)
727 drv, root, parts = self._flavour.join_parsed_parts(
728 self._drv, self._root, self._parts, drv, root, parts)
729 return self._from_parsed_parts(drv, root, parts)
730
731 def __str__(self):
732 """Return the string representation of the path, suitable for
733 passing to system calls."""
734 try:
735 return self._str
736 except AttributeError:
737 self._str = self._format_parsed_parts(self._drv, self._root,
738 self._parts) or '.'
739 return self._str
740
Brett Cannon568be632016-06-10 12:20:49 -0700741 def __fspath__(self):
742 return str(self)
743
Antoine Pitrou31119e42013-11-22 17:38:12 +0100744 def as_posix(self):
745 """Return the string representation of the path with forward (/)
746 slashes."""
747 f = self._flavour
748 return str(self).replace(f.sep, '/')
749
750 def __bytes__(self):
751 """Return the bytes representation of the path. This is only
752 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200753 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100754
755 def __repr__(self):
756 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
757
758 def as_uri(self):
759 """Return the path as a 'file' URI."""
760 if not self.is_absolute():
761 raise ValueError("relative path can't be expressed as a file URI")
762 return self._flavour.make_uri(self)
763
764 @property
765 def _cparts(self):
766 # Cached casefolded parts, for hashing and comparison
767 try:
768 return self._cached_cparts
769 except AttributeError:
770 self._cached_cparts = self._flavour.casefold_parts(self._parts)
771 return self._cached_cparts
772
773 def __eq__(self, other):
774 if not isinstance(other, PurePath):
775 return NotImplemented
776 return self._cparts == other._cparts and self._flavour is other._flavour
777
Antoine Pitrou31119e42013-11-22 17:38:12 +0100778 def __hash__(self):
779 try:
780 return self._hash
781 except AttributeError:
782 self._hash = hash(tuple(self._cparts))
783 return self._hash
784
785 def __lt__(self, other):
786 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
787 return NotImplemented
788 return self._cparts < other._cparts
789
790 def __le__(self, other):
791 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
792 return NotImplemented
793 return self._cparts <= other._cparts
794
795 def __gt__(self, other):
796 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
797 return NotImplemented
798 return self._cparts > other._cparts
799
800 def __ge__(self, other):
801 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
802 return NotImplemented
803 return self._cparts >= other._cparts
804
Batuhan Taşkaya526606b2019-12-08 23:31:15 +0300805 def __class_getitem__(cls, type):
806 return cls
807
Antoine Pitrou31119e42013-11-22 17:38:12 +0100808 drive = property(attrgetter('_drv'),
809 doc="""The drive prefix (letter or UNC path), if any.""")
810
811 root = property(attrgetter('_root'),
812 doc="""The root of the path, if any.""")
813
814 @property
815 def anchor(self):
816 """The concatenation of the drive and root, or ''."""
817 anchor = self._drv + self._root
818 return anchor
819
820 @property
821 def name(self):
822 """The final path component, if any."""
823 parts = self._parts
824 if len(parts) == (1 if (self._drv or self._root) else 0):
825 return ''
826 return parts[-1]
827
828 @property
829 def suffix(self):
Ram Rachum8d4fef42019-11-02 18:46:24 +0200830 """
831 The final component's last suffix, if any.
832
833 This includes the leading period. For example: '.txt'
834 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100835 name = self.name
836 i = name.rfind('.')
837 if 0 < i < len(name) - 1:
838 return name[i:]
839 else:
840 return ''
841
842 @property
843 def suffixes(self):
Ram Rachum8d4fef42019-11-02 18:46:24 +0200844 """
845 A list of the final component's suffixes, if any.
846
847 These include the leading periods. For example: ['.tar', '.gz']
848 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100849 name = self.name
850 if name.endswith('.'):
851 return []
852 name = name.lstrip('.')
853 return ['.' + suffix for suffix in name.split('.')[1:]]
854
855 @property
856 def stem(self):
857 """The final path component, minus its last suffix."""
858 name = self.name
859 i = name.rfind('.')
860 if 0 < i < len(name) - 1:
861 return name[:i]
862 else:
863 return name
864
865 def with_name(self, name):
866 """Return a new path with the file name changed."""
867 if not self.name:
868 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400869 drv, root, parts = self._flavour.parse_parts((name,))
870 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
871 or drv or root or len(parts) != 1):
872 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100873 return self._from_parsed_parts(self._drv, self._root,
874 self._parts[:-1] + [name])
875
Tim Hoffmann8aea4b32020-04-19 17:29:49 +0200876 def with_stem(self, stem):
877 """Return a new path with the stem changed."""
878 return self.with_name(stem + self.suffix)
879
Antoine Pitrou31119e42013-11-22 17:38:12 +0100880 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200881 """Return a new path with the file suffix changed. If the path
882 has no suffix, add given suffix. If the given suffix is an empty
883 string, remove the suffix from the path.
884 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400885 f = self._flavour
886 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300887 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400888 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100889 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100890 name = self.name
891 if not name:
892 raise ValueError("%r has an empty name" % (self,))
893 old_suffix = self.suffix
894 if not old_suffix:
895 name = name + suffix
896 else:
897 name = name[:-len(old_suffix)] + suffix
898 return self._from_parsed_parts(self._drv, self._root,
899 self._parts[:-1] + [name])
900
901 def relative_to(self, *other):
902 """Return the relative path to another path identified by the passed
903 arguments. If the operation is not possible (because this is not
904 a subpath of the other path), raise ValueError.
905 """
906 # For the purpose of this method, drive and root are considered
907 # separate parts, i.e.:
908 # Path('c:/').relative_to('c:') gives Path('/')
909 # Path('c:/').relative_to('/') raise ValueError
910 if not other:
911 raise TypeError("need at least one argument")
912 parts = self._parts
913 drv = self._drv
914 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100915 if root:
916 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100917 else:
918 abs_parts = parts
919 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100920 if to_root:
921 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100922 else:
923 to_abs_parts = to_parts
924 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100925 cf = self._flavour.casefold_parts
926 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100927 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
Rotuna44832532020-05-25 21:42:28 +0200928 raise ValueError("{!r} is not in the subpath of {!r}"
929 " OR one path is relative and the other is absolute."
Antoine Pitrou31119e42013-11-22 17:38:12 +0100930 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100931 return self._from_parsed_parts('', root if n == 1 else '',
932 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100933
Hai Shi82642a02019-08-13 14:54:02 -0500934 def is_relative_to(self, *other):
935 """Return True if the path is relative to another path or False.
936 """
937 try:
938 self.relative_to(*other)
939 return True
940 except ValueError:
941 return False
942
Antoine Pitrou31119e42013-11-22 17:38:12 +0100943 @property
944 def parts(self):
945 """An object providing sequence-like access to the
946 components in the filesystem path."""
947 # We cache the tuple to avoid building a new one each time .parts
948 # is accessed. XXX is this necessary?
949 try:
950 return self._pparts
951 except AttributeError:
952 self._pparts = tuple(self._parts)
953 return self._pparts
954
955 def joinpath(self, *args):
956 """Combine this path with one or several arguments, and return a
957 new path representing either a subpath (if all arguments are relative
958 paths) or a totally different path (if one of the arguments is
959 anchored).
960 """
961 return self._make_child(args)
962
963 def __truediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400964 try:
965 return self._make_child((key,))
966 except TypeError:
967 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100968
969 def __rtruediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400970 try:
971 return self._from_parts([key] + self._parts)
972 except TypeError:
973 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100974
975 @property
976 def parent(self):
977 """The logical parent of the path."""
978 drv = self._drv
979 root = self._root
980 parts = self._parts
981 if len(parts) == 1 and (drv or root):
982 return self
983 return self._from_parsed_parts(drv, root, parts[:-1])
984
985 @property
986 def parents(self):
987 """A sequence of this path's logical parents."""
988 return _PathParents(self)
989
990 def is_absolute(self):
991 """True if the path is absolute (has both a root and, if applicable,
992 a drive)."""
993 if not self._root:
994 return False
995 return not self._flavour.has_drv or bool(self._drv)
996
997 def is_reserved(self):
998 """Return True if the path contains one of the special names reserved
999 by the system, if any."""
1000 return self._flavour.is_reserved(self._parts)
1001
1002 def match(self, path_pattern):
1003 """
1004 Return True if this path matches the given pattern.
1005 """
1006 cf = self._flavour.casefold
1007 path_pattern = cf(path_pattern)
1008 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
1009 if not pat_parts:
1010 raise ValueError("empty pattern")
1011 if drv and drv != cf(self._drv):
1012 return False
1013 if root and root != cf(self._root):
1014 return False
1015 parts = self._cparts
1016 if drv or root:
1017 if len(pat_parts) != len(parts):
1018 return False
1019 pat_parts = pat_parts[1:]
1020 elif len(pat_parts) > len(parts):
1021 return False
1022 for part, pat in zip(reversed(parts), reversed(pat_parts)):
1023 if not fnmatch.fnmatchcase(part, pat):
1024 return False
1025 return True
1026
Brett Cannon568be632016-06-10 12:20:49 -07001027# Can't subclass os.PathLike from PurePath and keep the constructor
1028# optimizations in PurePath._parse_args().
1029os.PathLike.register(PurePath)
1030
Antoine Pitrou31119e42013-11-22 17:38:12 +01001031
1032class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001033 """PurePath subclass for non-Windows systems.
1034
1035 On a POSIX system, instantiating a PurePath should return this object.
1036 However, you can also instantiate it directly on any system.
1037 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001038 _flavour = _posix_flavour
1039 __slots__ = ()
1040
1041
1042class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001043 """PurePath subclass for Windows systems.
1044
1045 On a Windows system, instantiating a PurePath should return this object.
1046 However, you can also instantiate it directly on any system.
1047 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001048 _flavour = _windows_flavour
1049 __slots__ = ()
1050
1051
1052# Filesystem-accessing classes
1053
1054
1055class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001056 """PurePath subclass that can make system calls.
1057
1058 Path represents a filesystem path but unlike PurePath, also offers
1059 methods to do system calls on path objects. Depending on your system,
1060 instantiating a Path will return either a PosixPath or a WindowsPath
1061 object. You can also instantiate a PosixPath or WindowsPath directly,
1062 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
1063 """
Barney Gale22191872021-04-07 01:26:37 +01001064 _accessor = _normal_accessor
1065 __slots__ = ()
Antoine Pitrou31119e42013-11-22 17:38:12 +01001066
1067 def __new__(cls, *args, **kwargs):
1068 if cls is Path:
1069 cls = WindowsPath if os.name == 'nt' else PosixPath
Barney Gale22191872021-04-07 01:26:37 +01001070 self = cls._from_parts(args)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001071 if not self._flavour.is_supported:
1072 raise NotImplementedError("cannot instantiate %r on your system"
1073 % (cls.__name__,))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001074 return self
1075
Antoine Pitrou31119e42013-11-22 17:38:12 +01001076 def _make_child_relpath(self, part):
1077 # This is an optimization used for dir walking. `part` must be
1078 # a single part relative to this path.
1079 parts = self._parts + [part]
1080 return self._from_parsed_parts(self._drv, self._root, parts)
1081
1082 def __enter__(self):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001083 return self
1084
1085 def __exit__(self, t, v, tb):
Barney Gale00002e62020-04-01 15:10:51 +01001086 # https://bugs.python.org/issue39682
1087 # In previous versions of pathlib, this method marked this path as
1088 # closed; subsequent attempts to perform I/O would raise an IOError.
1089 # This functionality was never documented, and had the effect of
1090 # making Path objects mutable, contrary to PEP 428. In Python 3.9 the
1091 # _closed attribute was removed, and this method made a no-op.
1092 # This method and __enter__()/__exit__() should be deprecated and
1093 # removed in the future.
1094 pass
Antoine Pitrou31119e42013-11-22 17:38:12 +01001095
1096 def _opener(self, name, flags, mode=0o666):
1097 # A stub for the opener argument to built-in open()
1098 return self._accessor.open(self, flags, mode)
1099
1100 # Public API
1101
1102 @classmethod
1103 def cwd(cls):
1104 """Return a new path pointing to the current working directory
1105 (as returned by os.getcwd()).
1106 """
1107 return cls(os.getcwd())
1108
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001109 @classmethod
1110 def home(cls):
1111 """Return a new path pointing to the user's home directory (as
1112 returned by os.path.expanduser('~')).
1113 """
1114 return cls(cls()._flavour.gethomedir(None))
1115
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001116 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001117 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001118 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001119 """
1120 st = self.stat()
1121 try:
1122 other_st = other_path.stat()
1123 except AttributeError:
Barney Gale5b1d9182020-04-17 18:47:27 +01001124 other_st = self._accessor.stat(other_path)
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001125 return os.path.samestat(st, other_st)
1126
Antoine Pitrou31119e42013-11-22 17:38:12 +01001127 def iterdir(self):
1128 """Iterate over the files in this directory. Does not yield any
1129 result for the special paths '.' and '..'.
1130 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001131 for name in self._accessor.listdir(self):
1132 if name in {'.', '..'}:
1133 # Yielding a path object for these makes little sense
1134 continue
1135 yield self._make_child_relpath(name)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001136
1137 def glob(self, pattern):
1138 """Iterate over this subtree and yield all existing files (of any
Eivind Teig537b6ca2019-02-11 11:47:09 +01001139 kind, including directories) matching the given relative pattern.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001140 """
Serhiy Storchakaf4f445b2020-02-12 12:11:34 +02001141 sys.audit("pathlib.Path.glob", self, pattern)
Berker Peksag4a208e42016-01-30 17:50:48 +02001142 if not pattern:
1143 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001144 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1145 if drv or root:
1146 raise NotImplementedError("Non-relative patterns are unsupported")
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +03001147 selector = _make_selector(tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001148 for p in selector.select_from(self):
1149 yield p
1150
1151 def rglob(self, pattern):
1152 """Recursively yield all existing files (of any kind, including
Eivind Teig537b6ca2019-02-11 11:47:09 +01001153 directories) matching the given relative pattern, anywhere in
1154 this subtree.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001155 """
Serhiy Storchakaf4f445b2020-02-12 12:11:34 +02001156 sys.audit("pathlib.Path.rglob", self, pattern)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001157 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1158 if drv or root:
1159 raise NotImplementedError("Non-relative patterns are unsupported")
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +03001160 selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001161 for p in selector.select_from(self):
1162 yield p
1163
1164 def absolute(self):
1165 """Return an absolute version of this path. This function works
1166 even if the path doesn't point to anything.
1167
1168 No normalization is done, i.e. all '.' and '..' will be kept along.
1169 Use resolve() to get the canonical path to a file.
1170 """
1171 # XXX untested yet!
Antoine Pitrou31119e42013-11-22 17:38:12 +01001172 if self.is_absolute():
1173 return self
1174 # FIXME this must defer to the specific flavour (and, under Windows,
1175 # use nt._getfullpathname())
Barney Gale22191872021-04-07 01:26:37 +01001176 return self._from_parts([os.getcwd()] + self._parts)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001177
Steve Dower98eb3602016-11-09 12:58:17 -08001178 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001179 """
1180 Make the path absolute, resolving all symlinks on the way and also
1181 normalizing it (for example turning slashes into backslashes under
1182 Windows).
1183 """
Steve Dower98eb3602016-11-09 12:58:17 -08001184 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001185 if s is None:
1186 # No symlink resolution => for consistency, raise an error if
1187 # the path doesn't exist or is forbidden
1188 self.stat()
1189 s = str(self.absolute())
1190 # Now we have no symlinks in the path, it's safe to normalize it.
1191 normed = self._flavour.pathmod.normpath(s)
Barney Gale22191872021-04-07 01:26:37 +01001192 return self._from_parts((normed,))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001193
1194 def stat(self):
1195 """
1196 Return the result of the stat() system call on this path, like
1197 os.stat() does.
1198 """
1199 return self._accessor.stat(self)
1200
1201 def owner(self):
1202 """
1203 Return the login name of the file owner.
1204 """
Barney Gale22386bb2020-04-17 17:41:07 +01001205 return self._accessor.owner(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001206
1207 def group(self):
1208 """
1209 Return the group name of the file gid.
1210 """
Barney Gale22386bb2020-04-17 17:41:07 +01001211 return self._accessor.group(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001212
Antoine Pitrou31119e42013-11-22 17:38:12 +01001213 def open(self, mode='r', buffering=-1, encoding=None,
1214 errors=None, newline=None):
1215 """
1216 Open the file pointed by this path and return a file object, as
1217 the built-in open() function does.
1218 """
Inada Naoki48274832021-03-29 12:28:14 +09001219 if "b" not in mode:
1220 encoding = io.text_encoding(encoding)
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001221 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001222 opener=self._opener)
1223
Georg Brandlea683982014-10-01 19:12:33 +02001224 def read_bytes(self):
1225 """
1226 Open the file in bytes mode, read it, and close the file.
1227 """
1228 with self.open(mode='rb') as f:
1229 return f.read()
1230
1231 def read_text(self, encoding=None, errors=None):
1232 """
1233 Open the file in text mode, read it, and close the file.
1234 """
Inada Naoki48274832021-03-29 12:28:14 +09001235 encoding = io.text_encoding(encoding)
Georg Brandlea683982014-10-01 19:12:33 +02001236 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1237 return f.read()
1238
1239 def write_bytes(self, data):
1240 """
1241 Open the file in bytes mode, write to it, and close the file.
1242 """
1243 # type-check for the buffer interface before truncating the file
1244 view = memoryview(data)
1245 with self.open(mode='wb') as f:
1246 return f.write(view)
1247
Максим5f227412020-10-21 05:08:19 +03001248 def write_text(self, data, encoding=None, errors=None, newline=None):
Georg Brandlea683982014-10-01 19:12:33 +02001249 """
1250 Open the file in text mode, write to it, and close the file.
1251 """
1252 if not isinstance(data, str):
1253 raise TypeError('data must be str, not %s' %
1254 data.__class__.__name__)
Inada Naoki48274832021-03-29 12:28:14 +09001255 encoding = io.text_encoding(encoding)
Максим5f227412020-10-21 05:08:19 +03001256 with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
Georg Brandlea683982014-10-01 19:12:33 +02001257 return f.write(data)
1258
Girtsa01ba332019-10-23 14:18:40 -07001259 def readlink(self):
1260 """
1261 Return the path to which the symbolic link points.
1262 """
1263 path = self._accessor.readlink(self)
Barney Gale22191872021-04-07 01:26:37 +01001264 return self._from_parts((path,))
Girtsa01ba332019-10-23 14:18:40 -07001265
Antoine Pitrou31119e42013-11-22 17:38:12 +01001266 def touch(self, mode=0o666, exist_ok=True):
1267 """
1268 Create this file with the given access mode, if it doesn't exist.
1269 """
Barney Gale986da8e2021-04-07 01:25:37 +01001270 self._accessor.touch(self, mode, exist_ok)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001271
Barry Warsaw7c549c42014-08-05 11:28:12 -04001272 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001273 """
1274 Create a new directory at this given path.
1275 """
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001276 try:
1277 self._accessor.mkdir(self, mode)
1278 except FileNotFoundError:
1279 if not parents or self.parent == self:
1280 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001281 self.parent.mkdir(parents=True, exist_ok=True)
1282 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001283 except OSError:
1284 # Cannot rely on checking for EEXIST, since the operating system
1285 # could give priority to other errors like EACCES or EROFS
1286 if not exist_ok or not self.is_dir():
1287 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001288
1289 def chmod(self, mode):
1290 """
1291 Change the permissions of the path, like os.chmod().
1292 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001293 self._accessor.chmod(self, mode)
1294
1295 def lchmod(self, mode):
1296 """
1297 Like chmod(), except if the path points to a symlink, the symlink's
1298 permissions are changed, rather than its target's.
1299 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001300 self._accessor.lchmod(self, mode)
1301
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001302 def unlink(self, missing_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001303 """
1304 Remove this file or link.
1305 If the path is a directory, use rmdir() instead.
1306 """
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001307 try:
1308 self._accessor.unlink(self)
1309 except FileNotFoundError:
1310 if not missing_ok:
1311 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001312
1313 def rmdir(self):
1314 """
1315 Remove this directory. The directory must be empty.
1316 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001317 self._accessor.rmdir(self)
1318
1319 def lstat(self):
1320 """
1321 Like stat(), except if the path points to a symlink, the symlink's
1322 status information is returned, rather than its target's.
1323 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001324 return self._accessor.lstat(self)
1325
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -04001326 def link_to(self, target):
1327 """
1328 Create a hard link pointing to a path named target.
1329 """
Barney Galeb57e0452021-04-07 00:01:22 +01001330 self._accessor.link(self, target)
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -04001331
Antoine Pitrou31119e42013-11-22 17:38:12 +01001332 def rename(self, target):
1333 """
Ram Rachumf97e42e2020-10-03 12:52:13 +03001334 Rename this path to the target path.
1335
1336 The target path may be absolute or relative. Relative paths are
1337 interpreted relative to the current working directory, *not* the
1338 directory of the Path object.
1339
1340 Returns the new Path instance pointing to the target path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001341 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001342 self._accessor.rename(self, target)
hui shang088a09a2019-09-11 21:26:49 +08001343 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001344
1345 def replace(self, target):
1346 """
Ram Rachumf97e42e2020-10-03 12:52:13 +03001347 Rename this path to the target path, overwriting if that path exists.
1348
1349 The target path may be absolute or relative. Relative paths are
1350 interpreted relative to the current working directory, *not* the
1351 directory of the Path object.
1352
1353 Returns the new Path instance pointing to the target path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001354 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001355 self._accessor.replace(self, target)
hui shang088a09a2019-09-11 21:26:49 +08001356 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001357
1358 def symlink_to(self, target, target_is_directory=False):
1359 """
1360 Make this path a symlink pointing to the given path.
1361 Note the order of arguments (self, target) is the reverse of os.symlink's.
1362 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001363 self._accessor.symlink(target, self, target_is_directory)
1364
1365 # Convenience functions for querying the stat results
1366
1367 def exists(self):
1368 """
1369 Whether this path exists.
1370 """
1371 try:
1372 self.stat()
1373 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001374 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001375 raise
1376 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001377 except ValueError:
1378 # Non-encodable path
1379 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001380 return True
1381
1382 def is_dir(self):
1383 """
1384 Whether this path is a directory.
1385 """
1386 try:
1387 return S_ISDIR(self.stat().st_mode)
1388 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001389 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001390 raise
1391 # Path doesn't exist or is a broken symlink
1392 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1393 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001394 except ValueError:
1395 # Non-encodable path
1396 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001397
1398 def is_file(self):
1399 """
1400 Whether this path is a regular file (also True for symlinks pointing
1401 to regular files).
1402 """
1403 try:
1404 return S_ISREG(self.stat().st_mode)
1405 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001406 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001407 raise
1408 # Path doesn't exist or is a broken symlink
1409 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1410 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001411 except ValueError:
1412 # Non-encodable path
1413 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001414
Cooper Lees173ff4a2017-08-01 15:35:45 -07001415 def is_mount(self):
1416 """
1417 Check if this path is a POSIX mount point
1418 """
1419 # Need to exist and be a dir
1420 if not self.exists() or not self.is_dir():
1421 return False
1422
Cooper Lees173ff4a2017-08-01 15:35:45 -07001423 try:
Barney Galec746c4f2020-04-17 18:42:06 +01001424 parent_dev = self.parent.stat().st_dev
Cooper Lees173ff4a2017-08-01 15:35:45 -07001425 except OSError:
1426 return False
1427
1428 dev = self.stat().st_dev
1429 if dev != parent_dev:
1430 return True
1431 ino = self.stat().st_ino
Barney Galec746c4f2020-04-17 18:42:06 +01001432 parent_ino = self.parent.stat().st_ino
Cooper Lees173ff4a2017-08-01 15:35:45 -07001433 return ino == parent_ino
1434
Antoine Pitrou31119e42013-11-22 17:38:12 +01001435 def is_symlink(self):
1436 """
1437 Whether this path is a symbolic link.
1438 """
1439 try:
1440 return S_ISLNK(self.lstat().st_mode)
1441 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001442 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001443 raise
1444 # Path doesn't exist
1445 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001446 except ValueError:
1447 # Non-encodable path
1448 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001449
1450 def is_block_device(self):
1451 """
1452 Whether this path is a block device.
1453 """
1454 try:
1455 return S_ISBLK(self.stat().st_mode)
1456 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001457 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001458 raise
1459 # Path doesn't exist or is a broken symlink
1460 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1461 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001462 except ValueError:
1463 # Non-encodable path
1464 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001465
1466 def is_char_device(self):
1467 """
1468 Whether this path is a character device.
1469 """
1470 try:
1471 return S_ISCHR(self.stat().st_mode)
1472 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001473 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001474 raise
1475 # Path doesn't exist or is a broken symlink
1476 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1477 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001478 except ValueError:
1479 # Non-encodable path
1480 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001481
1482 def is_fifo(self):
1483 """
1484 Whether this path is a FIFO.
1485 """
1486 try:
1487 return S_ISFIFO(self.stat().st_mode)
1488 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001489 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001490 raise
1491 # Path doesn't exist or is a broken symlink
1492 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1493 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001494 except ValueError:
1495 # Non-encodable path
1496 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001497
1498 def is_socket(self):
1499 """
1500 Whether this path is a socket.
1501 """
1502 try:
1503 return S_ISSOCK(self.stat().st_mode)
1504 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001505 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001506 raise
1507 # Path doesn't exist or is a broken symlink
1508 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1509 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001510 except ValueError:
1511 # Non-encodable path
1512 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001513
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001514 def expanduser(self):
1515 """ Return a new path with expanded ~ and ~user constructs
1516 (as returned by os.path.expanduser)
1517 """
1518 if (not (self._drv or self._root) and
1519 self._parts and self._parts[0][:1] == '~'):
1520 homedir = self._flavour.gethomedir(self._parts[0][1:])
1521 return self._from_parts([homedir] + self._parts[1:])
1522
1523 return self
1524
Antoine Pitrou31119e42013-11-22 17:38:12 +01001525
1526class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001527 """Path subclass for non-Windows systems.
1528
1529 On a POSIX system, instantiating a Path should return this object.
1530 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001531 __slots__ = ()
1532
1533class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001534 """Path subclass for Windows systems.
1535
1536 On a Windows system, instantiating a Path should return this object.
1537 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001538 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001539
Cooper Lees173ff4a2017-08-01 15:35:45 -07001540 def is_mount(self):
1541 raise NotImplementedError("Path.is_mount() is unsupported on this system")