blob: 531a699a40df4941b16c8886a8169f3bf246d5c1 [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
16supports_symlinks = True
Antoine Pitroudb118f52014-11-19 00:32:08 +010017if os.name == 'nt':
Antoine Pitrou31119e42013-11-22 17:38:12 +010018 import nt
Antoine Pitrou31119e42013-11-22 17:38:12 +010019 if sys.getwindowsversion()[:2] >= (6, 0):
20 from nt import _getfinalpathname
21 else:
22 supports_symlinks = False
23 _getfinalpathname = None
Antoine Pitroudb118f52014-11-19 00:32:08 +010024else:
25 nt = None
Antoine Pitrou31119e42013-11-22 17:38:12 +010026
27
28__all__ = [
29 "PurePath", "PurePosixPath", "PureWindowsPath",
30 "Path", "PosixPath", "WindowsPath",
31 ]
32
33#
34# Internals
35#
36
penguindustin96466302019-05-06 14:57:17 -040037# EBADF - guard against macOS `stat` throwing EBADF
Jörg Stucked5c120f2019-05-21 19:44:40 +020038_IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF, ELOOP)
Przemysław Spodymek216b7452018-08-27 23:33:45 +020039
Steve Dower2f6fae62019-02-03 23:08:18 -080040_IGNORED_WINERRORS = (
41 21, # ERROR_NOT_READY - drive exists but is not accessible
Jörg Stucked5c120f2019-05-21 19:44:40 +020042 1921, # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself
Steve Dower2f6fae62019-02-03 23:08:18 -080043)
44
45def _ignore_error(exception):
46 return (getattr(exception, 'errno', None) in _IGNORED_ERROS or
47 getattr(exception, 'winerror', None) in _IGNORED_WINERRORS)
48
49
Antoine Pitrou31119e42013-11-22 17:38:12 +010050def _is_wildcard_pattern(pat):
51 # Whether this pattern needs actual matching using fnmatch, or can
52 # be looked up directly as a file.
53 return "*" in pat or "?" in pat or "[" in pat
54
55
56class _Flavour(object):
57 """A flavour implements a particular (platform-specific) set of path
58 semantics."""
59
60 def __init__(self):
61 self.join = self.sep.join
62
63 def parse_parts(self, parts):
64 parsed = []
65 sep = self.sep
66 altsep = self.altsep
67 drv = root = ''
68 it = reversed(parts)
69 for part in it:
70 if not part:
71 continue
72 if altsep:
73 part = part.replace(altsep, sep)
74 drv, root, rel = self.splitroot(part)
75 if sep in rel:
76 for x in reversed(rel.split(sep)):
77 if x and x != '.':
78 parsed.append(sys.intern(x))
79 else:
80 if rel and rel != '.':
81 parsed.append(sys.intern(rel))
82 if drv or root:
83 if not drv:
84 # If no drive is present, try to find one in the previous
85 # parts. This makes the result of parsing e.g.
86 # ("C:", "/", "a") reasonably intuitive.
87 for part in it:
Antoine Pitrou57fffd62015-02-15 18:03:59 +010088 if not part:
89 continue
90 if altsep:
91 part = part.replace(altsep, sep)
Antoine Pitrou31119e42013-11-22 17:38:12 +010092 drv = self.splitroot(part)[0]
93 if drv:
94 break
95 break
96 if drv or root:
97 parsed.append(drv + root)
98 parsed.reverse()
99 return drv, root, parsed
100
101 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
102 """
103 Join the two paths represented by the respective
104 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple.
105 """
106 if root2:
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200107 if not drv2 and drv:
108 return drv, root2, [drv + root2] + parts2[1:]
109 elif drv2:
110 if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
111 # Same drive => second path is relative to the first
112 return drv, root, parts + parts2[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100113 else:
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200114 # Second path is non-anchored (common case)
115 return drv, root, parts + parts2
116 return drv2, root2, parts2
Antoine Pitrou31119e42013-11-22 17:38:12 +0100117
118
119class _WindowsFlavour(_Flavour):
120 # Reference for Windows paths can be found at
121 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
122
123 sep = '\\'
124 altsep = '/'
125 has_drv = True
126 pathmod = ntpath
127
Antoine Pitroudb118f52014-11-19 00:32:08 +0100128 is_supported = (os.name == 'nt')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100129
Jon Dufresne39726282017-05-18 07:35:54 -0700130 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100131 ext_namespace_prefix = '\\\\?\\'
132
133 reserved_names = (
134 {'CON', 'PRN', 'AUX', 'NUL'} |
135 {'COM%d' % i for i in range(1, 10)} |
136 {'LPT%d' % i for i in range(1, 10)}
137 )
138
139 # Interesting findings about extended paths:
140 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
141 # but '\\?\c:/a' is not
142 # - extended paths are always absolute; "relative" extended paths will
143 # fail.
144
145 def splitroot(self, part, sep=sep):
146 first = part[0:1]
147 second = part[1:2]
148 if (second == sep and first == sep):
149 # XXX extended paths should also disable the collapsing of "."
150 # components (according to MSDN docs).
151 prefix, part = self._split_extended_path(part)
152 first = part[0:1]
153 second = part[1:2]
154 else:
155 prefix = ''
156 third = part[2:3]
157 if (second == sep and first == sep and third != sep):
158 # is a UNC path:
159 # vvvvvvvvvvvvvvvvvvvvv root
160 # \\machine\mountpoint\directory\etc\...
161 # directory ^^^^^^^^^^^^^^
162 index = part.find(sep, 2)
163 if index != -1:
164 index2 = part.find(sep, index + 1)
165 # a UNC path can't have two slashes in a row
166 # (after the initial two)
167 if index2 != index + 1:
168 if index2 == -1:
169 index2 = len(part)
170 if prefix:
171 return prefix + part[1:index2], sep, part[index2+1:]
172 else:
173 return part[:index2], sep, part[index2+1:]
174 drv = root = ''
175 if second == ':' and first in self.drive_letters:
176 drv = part[:2]
177 part = part[2:]
178 first = third
179 if first == sep:
180 root = first
181 part = part.lstrip(sep)
182 return prefix + drv, root, part
183
184 def casefold(self, s):
185 return s.lower()
186
187 def casefold_parts(self, parts):
188 return [p.lower() for p in parts]
189
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300190 def compile_pattern(self, pattern):
191 return re.compile(fnmatch.translate(pattern), re.IGNORECASE).fullmatch
192
Steve Dower98eb3602016-11-09 12:58:17 -0800193 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100194 s = str(path)
195 if not s:
196 return os.getcwd()
Steve Dower98eb3602016-11-09 12:58:17 -0800197 previous_s = None
Antoine Pitrou31119e42013-11-22 17:38:12 +0100198 if _getfinalpathname is not None:
Steve Dower98eb3602016-11-09 12:58:17 -0800199 if strict:
200 return self._ext_to_normal(_getfinalpathname(s))
201 else:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200202 tail_parts = [] # End of the path after the first one not found
Steve Dower98eb3602016-11-09 12:58:17 -0800203 while True:
204 try:
205 s = self._ext_to_normal(_getfinalpathname(s))
206 except FileNotFoundError:
207 previous_s = s
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200208 s, tail = os.path.split(s)
209 tail_parts.append(tail)
Steve Dower4b1e98b2016-12-28 16:02:59 -0800210 if previous_s == s:
211 return path
Steve Dower98eb3602016-11-09 12:58:17 -0800212 else:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200213 return os.path.join(s, *reversed(tail_parts))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100214 # Means fallback on absolute
215 return None
216
217 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
218 prefix = ''
219 if s.startswith(ext_prefix):
220 prefix = s[:4]
221 s = s[4:]
222 if s.startswith('UNC\\'):
223 prefix += s[:3]
224 s = '\\' + s[3:]
225 return prefix, s
226
227 def _ext_to_normal(self, s):
228 # Turn back an extended path into a normal DOS-like path
229 return self._split_extended_path(s)[1]
230
231 def is_reserved(self, parts):
232 # NOTE: the rules for reserved names seem somewhat complicated
233 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
234 # We err on the side of caution and return True for paths which are
235 # not considered reserved by Windows.
236 if not parts:
237 return False
238 if parts[0].startswith('\\\\'):
239 # UNC paths are never reserved
240 return False
241 return parts[-1].partition('.')[0].upper() in self.reserved_names
242
243 def make_uri(self, path):
244 # Under Windows, file URIs use the UTF-8 encoding.
245 drive = path.drive
246 if len(drive) == 2 and drive[1] == ':':
247 # It's a path on a local drive => 'file:///c:/a/b'
248 rest = path.as_posix()[2:].lstrip('/')
249 return 'file:///%s/%s' % (
250 drive, urlquote_from_bytes(rest.encode('utf-8')))
251 else:
252 # It's a path on a network drive => 'file://host/share/a/b'
253 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
254
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100255 def gethomedir(self, username):
Christoph Reiterc45a2aa2020-01-28 10:41:50 +0100256 if 'USERPROFILE' in os.environ:
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100257 userhome = os.environ['USERPROFILE']
258 elif 'HOMEPATH' in os.environ:
Antoine Pitrou5d4e27e2014-12-30 22:09:42 +0100259 try:
260 drv = os.environ['HOMEDRIVE']
261 except KeyError:
262 drv = ''
263 userhome = drv + os.environ['HOMEPATH']
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100264 else:
265 raise RuntimeError("Can't determine home directory")
266
267 if username:
268 # Try to guess user home directory. By default all users
269 # directories are located in the same place and are named by
270 # corresponding usernames. If current user home directory points
271 # to nonstandard place, this guess is likely wrong.
272 if os.environ['USERNAME'] != username:
273 drv, root, parts = self.parse_parts((userhome,))
274 if parts[-1] != os.environ['USERNAME']:
275 raise RuntimeError("Can't determine home directory "
276 "for %r" % username)
277 parts[-1] = username
278 if drv or root:
279 userhome = drv + root + self.join(parts[1:])
280 else:
281 userhome = self.join(parts)
282 return userhome
Antoine Pitrou31119e42013-11-22 17:38:12 +0100283
284class _PosixFlavour(_Flavour):
285 sep = '/'
286 altsep = ''
287 has_drv = False
288 pathmod = posixpath
289
290 is_supported = (os.name != 'nt')
291
292 def splitroot(self, part, sep=sep):
293 if part and part[0] == sep:
294 stripped_part = part.lstrip(sep)
295 # According to POSIX path resolution:
296 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
297 # "A pathname that begins with two successive slashes may be
298 # interpreted in an implementation-defined manner, although more
299 # than two leading slashes shall be treated as a single slash".
300 if len(part) - len(stripped_part) == 2:
301 return '', sep * 2, stripped_part
302 else:
303 return '', sep, stripped_part
304 else:
305 return '', '', part
306
307 def casefold(self, s):
308 return s
309
310 def casefold_parts(self, parts):
311 return parts
312
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300313 def compile_pattern(self, pattern):
314 return re.compile(fnmatch.translate(pattern)).fullmatch
315
Steve Dower98eb3602016-11-09 12:58:17 -0800316 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100317 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100318 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100319 seen = {}
320 def _resolve(path, rest):
321 if rest.startswith(sep):
322 path = ''
323
324 for name in rest.split(sep):
325 if not name or name == '.':
326 # current dir
327 continue
328 if name == '..':
329 # parent dir
330 path, _, _ = path.rpartition(sep)
331 continue
Dong-hee Na94ad6c62018-06-12 23:30:45 +0900332 if path.endswith(sep):
333 newpath = path + name
334 else:
335 newpath = path + sep + name
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100336 if newpath in seen:
337 # Already seen this path
338 path = seen[newpath]
339 if path is not None:
340 # use cached value
341 continue
342 # The symlink is not resolved, so we must have a symlink loop.
343 raise RuntimeError("Symlink loop from %r" % newpath)
344 # Resolve the symbolic link
345 try:
346 target = accessor.readlink(newpath)
347 except OSError as e:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200348 if e.errno != EINVAL and strict:
349 raise
350 # Not a symlink, or non-strict mode. We just leave the path
351 # untouched.
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100352 path = newpath
353 else:
354 seen[newpath] = None # not resolved symlink
355 path = _resolve(path, target)
356 seen[newpath] = path # resolved symlink
357
358 return path
359 # NOTE: according to POSIX, getcwd() cannot contain path components
360 # which are symlinks.
361 base = '' if path.is_absolute() else os.getcwd()
362 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100363
364 def is_reserved(self, parts):
365 return False
366
367 def make_uri(self, path):
368 # We represent the path using the local filesystem encoding,
369 # for portability to other applications.
370 bpath = bytes(path)
371 return 'file://' + urlquote_from_bytes(bpath)
372
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100373 def gethomedir(self, username):
374 if not username:
375 try:
376 return os.environ['HOME']
377 except KeyError:
378 import pwd
379 return pwd.getpwuid(os.getuid()).pw_dir
380 else:
381 import pwd
382 try:
383 return pwd.getpwnam(username).pw_dir
384 except KeyError:
385 raise RuntimeError("Can't determine home directory "
386 "for %r" % username)
387
Antoine Pitrou31119e42013-11-22 17:38:12 +0100388
389_windows_flavour = _WindowsFlavour()
390_posix_flavour = _PosixFlavour()
391
392
393class _Accessor:
394 """An accessor implements a particular (system-specific or not) way of
395 accessing paths on the filesystem."""
396
397
398class _NormalAccessor(_Accessor):
399
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200400 stat = os.stat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100401
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200402 lstat = os.lstat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100403
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200404 open = os.open
Antoine Pitrou31119e42013-11-22 17:38:12 +0100405
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200406 listdir = os.listdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100407
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200408 scandir = os.scandir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100409
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200410 chmod = os.chmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100411
412 if hasattr(os, "lchmod"):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200413 lchmod = os.lchmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100414 else:
415 def lchmod(self, pathobj, mode):
416 raise NotImplementedError("lchmod() not available on this system")
417
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200418 mkdir = os.mkdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100419
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200420 unlink = os.unlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100421
Toke Høiland-Jørgensen092435e2019-12-16 13:23:55 +0100422 if hasattr(os, "link"):
423 link_to = os.link
424 else:
425 @staticmethod
426 def link_to(self, target):
427 raise NotImplementedError("os.link() not available on this system")
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -0400428
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200429 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100430
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200431 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100432
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200433 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100434
435 if nt:
436 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200437 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100438 else:
439 def symlink(a, b, target_is_directory):
440 raise NotImplementedError("symlink() not available on this system")
441 else:
442 # Under POSIX, os.symlink() takes two args
443 @staticmethod
444 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200445 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100446
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200447 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100448
449 # Helper for resolve()
450 def readlink(self, path):
451 return os.readlink(path)
452
Barney Gale22386bb2020-04-17 17:41:07 +0100453 def owner(self, path):
454 try:
455 import pwd
456 return pwd.getpwuid(self.stat(path).st_uid).pw_name
457 except ImportError:
458 raise NotImplementedError("Path.owner() is unsupported on this system")
459
460 def group(self, path):
461 try:
462 import grp
463 return grp.getgrgid(self.stat(path).st_gid).gr_name
464 except ImportError:
465 raise NotImplementedError("Path.group() is unsupported on this system")
466
Antoine Pitrou31119e42013-11-22 17:38:12 +0100467
468_normal_accessor = _NormalAccessor()
469
470
471#
472# Globbing helpers
473#
474
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300475def _make_selector(pattern_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100476 pat = pattern_parts[0]
477 child_parts = pattern_parts[1:]
478 if pat == '**':
479 cls = _RecursiveWildcardSelector
480 elif '**' in pat:
481 raise ValueError("Invalid pattern: '**' can only be an entire path component")
482 elif _is_wildcard_pattern(pat):
483 cls = _WildcardSelector
484 else:
485 cls = _PreciseSelector
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300486 return cls(pat, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100487
488if hasattr(functools, "lru_cache"):
489 _make_selector = functools.lru_cache()(_make_selector)
490
491
492class _Selector:
493 """A selector matches a specific glob pattern part against the children
494 of a given path."""
495
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300496 def __init__(self, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100497 self.child_parts = child_parts
498 if child_parts:
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300499 self.successor = _make_selector(child_parts, flavour)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300500 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100501 else:
502 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300503 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100504
505 def select_from(self, parent_path):
506 """Iterate over all child paths of `parent_path` matched by this
507 selector. This can contain parent_path itself."""
508 path_cls = type(parent_path)
509 is_dir = path_cls.is_dir
510 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300511 scandir = parent_path._accessor.scandir
512 if not is_dir(parent_path):
513 return iter([])
514 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100515
516
517class _TerminatingSelector:
518
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300519 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100520 yield parent_path
521
522
523class _PreciseSelector(_Selector):
524
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300525 def __init__(self, name, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100526 self.name = name
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300527 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100528
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300529 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800530 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800531 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300532 if (is_dir if self.dironly else exists)(path):
533 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800534 yield p
535 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100536 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100537
538
539class _WildcardSelector(_Selector):
540
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300541 def __init__(self, pat, child_parts, flavour):
542 self.match = flavour.compile_pattern(pat)
543 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100544
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300545 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800546 try:
Serhiy Storchaka704e2062020-03-11 18:42:03 +0200547 with scandir(parent_path) as scandir_it:
548 entries = list(scandir_it)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300549 for entry in entries:
Pablo Galindoeb7560a2020-03-07 17:53:20 +0000550 if self.dironly:
551 try:
552 # "entry.is_dir()" can raise PermissionError
553 # in some cases (see bpo-38894), which is not
554 # among the errors ignored by _ignore_error()
555 if not entry.is_dir():
556 continue
557 except OSError as e:
558 if not _ignore_error(e):
559 raise
560 continue
561 name = entry.name
562 if self.match(name):
563 path = parent_path._make_child_relpath(name)
564 for p in self.successor._select_from(path, is_dir, exists, scandir):
565 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800566 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100567 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800568
Antoine Pitrou31119e42013-11-22 17:38:12 +0100569
Antoine Pitrou31119e42013-11-22 17:38:12 +0100570class _RecursiveWildcardSelector(_Selector):
571
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300572 def __init__(self, pat, child_parts, flavour):
573 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100574
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300575 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100576 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800577 try:
Serhiy Storchaka704e2062020-03-11 18:42:03 +0200578 with scandir(parent_path) as scandir_it:
579 entries = list(scandir_it)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300580 for entry in entries:
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200581 entry_is_dir = False
582 try:
583 entry_is_dir = entry.is_dir()
584 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -0800585 if not _ignore_error(e):
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200586 raise
587 if entry_is_dir and not entry.is_symlink():
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300588 path = parent_path._make_child_relpath(entry.name)
589 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800590 yield p
591 except PermissionError:
592 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100593
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300594 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800595 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300596 yielded = set()
597 try:
598 successor_select = self.successor._select_from
599 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
600 for p in successor_select(starting_point, is_dir, exists, scandir):
601 if p not in yielded:
602 yield p
603 yielded.add(p)
604 finally:
605 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800606 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100607 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100608
609
610#
611# Public API
612#
613
614class _PathParents(Sequence):
615 """This object provides sequence-like access to the logical ancestors
616 of a path. Don't try to construct it yourself."""
617 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
618
619 def __init__(self, path):
620 # We don't store the instance to avoid reference cycles
621 self._pathcls = type(path)
622 self._drv = path._drv
623 self._root = path._root
624 self._parts = path._parts
625
626 def __len__(self):
627 if self._drv or self._root:
628 return len(self._parts) - 1
629 else:
630 return len(self._parts)
631
632 def __getitem__(self, idx):
Joshua Cannon45205842020-11-20 09:40:39 -0600633 if isinstance(idx, slice):
634 return tuple(self[i] for i in range(*idx.indices(len(self))))
Yaroslav Pankovych79d2e622020-11-23 22:06:22 +0200635
636 if idx >= len(self) or idx < -len(self):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100637 raise IndexError(idx)
638 return self._pathcls._from_parsed_parts(self._drv, self._root,
639 self._parts[:-idx - 1])
640
641 def __repr__(self):
642 return "<{}.parents>".format(self._pathcls.__name__)
643
644
645class PurePath(object):
chasondfa015c2018-02-19 08:36:32 +0900646 """Base class for manipulating paths without I/O.
647
648 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100649 don't imply any actual filesystem I/O. Depending on your system,
650 instantiating a PurePath will return either a PurePosixPath or a
651 PureWindowsPath object. You can also instantiate either of these classes
652 directly, regardless of your system.
653 """
654 __slots__ = (
655 '_drv', '_root', '_parts',
656 '_str', '_hash', '_pparts', '_cached_cparts',
657 )
658
659 def __new__(cls, *args):
660 """Construct a PurePath from one or several strings and or existing
661 PurePath objects. The strings and path objects are combined so as
662 to yield a canonicalized path, which is incorporated into the
663 new PurePath object.
664 """
665 if cls is PurePath:
666 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
667 return cls._from_parts(args)
668
669 def __reduce__(self):
670 # Using the parts tuple helps share interned path parts
671 # when pickling related paths.
672 return (self.__class__, tuple(self._parts))
673
674 @classmethod
675 def _parse_args(cls, args):
676 # This is useful when you don't want to create an instance, just
677 # canonicalize some constructor arguments.
678 parts = []
679 for a in args:
680 if isinstance(a, PurePath):
681 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100682 else:
Brett Cannon568be632016-06-10 12:20:49 -0700683 a = os.fspath(a)
684 if isinstance(a, str):
685 # Force-cast str subclasses to str (issue #21127)
686 parts.append(str(a))
687 else:
688 raise TypeError(
689 "argument should be a str object or an os.PathLike "
690 "object returning str, not %r"
691 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100692 return cls._flavour.parse_parts(parts)
693
694 @classmethod
695 def _from_parts(cls, args, init=True):
696 # We need to call _parse_args on the instance, so as to get the
697 # right flavour.
698 self = object.__new__(cls)
699 drv, root, parts = self._parse_args(args)
700 self._drv = drv
701 self._root = root
702 self._parts = parts
703 if init:
704 self._init()
705 return self
706
707 @classmethod
708 def _from_parsed_parts(cls, drv, root, parts, init=True):
709 self = object.__new__(cls)
710 self._drv = drv
711 self._root = root
712 self._parts = parts
713 if init:
714 self._init()
715 return self
716
717 @classmethod
718 def _format_parsed_parts(cls, drv, root, parts):
719 if drv or root:
720 return drv + root + cls._flavour.join(parts[1:])
721 else:
722 return cls._flavour.join(parts)
723
724 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000725 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100726 pass
727
728 def _make_child(self, args):
729 drv, root, parts = self._parse_args(args)
730 drv, root, parts = self._flavour.join_parsed_parts(
731 self._drv, self._root, self._parts, drv, root, parts)
732 return self._from_parsed_parts(drv, root, parts)
733
734 def __str__(self):
735 """Return the string representation of the path, suitable for
736 passing to system calls."""
737 try:
738 return self._str
739 except AttributeError:
740 self._str = self._format_parsed_parts(self._drv, self._root,
741 self._parts) or '.'
742 return self._str
743
Brett Cannon568be632016-06-10 12:20:49 -0700744 def __fspath__(self):
745 return str(self)
746
Antoine Pitrou31119e42013-11-22 17:38:12 +0100747 def as_posix(self):
748 """Return the string representation of the path with forward (/)
749 slashes."""
750 f = self._flavour
751 return str(self).replace(f.sep, '/')
752
753 def __bytes__(self):
754 """Return the bytes representation of the path. This is only
755 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200756 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100757
758 def __repr__(self):
759 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
760
761 def as_uri(self):
762 """Return the path as a 'file' URI."""
763 if not self.is_absolute():
764 raise ValueError("relative path can't be expressed as a file URI")
765 return self._flavour.make_uri(self)
766
767 @property
768 def _cparts(self):
769 # Cached casefolded parts, for hashing and comparison
770 try:
771 return self._cached_cparts
772 except AttributeError:
773 self._cached_cparts = self._flavour.casefold_parts(self._parts)
774 return self._cached_cparts
775
776 def __eq__(self, other):
777 if not isinstance(other, PurePath):
778 return NotImplemented
779 return self._cparts == other._cparts and self._flavour is other._flavour
780
Antoine Pitrou31119e42013-11-22 17:38:12 +0100781 def __hash__(self):
782 try:
783 return self._hash
784 except AttributeError:
785 self._hash = hash(tuple(self._cparts))
786 return self._hash
787
788 def __lt__(self, other):
789 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
790 return NotImplemented
791 return self._cparts < other._cparts
792
793 def __le__(self, other):
794 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
795 return NotImplemented
796 return self._cparts <= other._cparts
797
798 def __gt__(self, other):
799 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
800 return NotImplemented
801 return self._cparts > other._cparts
802
803 def __ge__(self, other):
804 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
805 return NotImplemented
806 return self._cparts >= other._cparts
807
Batuhan Taşkaya526606b2019-12-08 23:31:15 +0300808 def __class_getitem__(cls, type):
809 return cls
810
Antoine Pitrou31119e42013-11-22 17:38:12 +0100811 drive = property(attrgetter('_drv'),
812 doc="""The drive prefix (letter or UNC path), if any.""")
813
814 root = property(attrgetter('_root'),
815 doc="""The root of the path, if any.""")
816
817 @property
818 def anchor(self):
819 """The concatenation of the drive and root, or ''."""
820 anchor = self._drv + self._root
821 return anchor
822
823 @property
824 def name(self):
825 """The final path component, if any."""
826 parts = self._parts
827 if len(parts) == (1 if (self._drv or self._root) else 0):
828 return ''
829 return parts[-1]
830
831 @property
832 def suffix(self):
Ram Rachum8d4fef42019-11-02 18:46:24 +0200833 """
834 The final component's last suffix, if any.
835
836 This includes the leading period. For example: '.txt'
837 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100838 name = self.name
839 i = name.rfind('.')
840 if 0 < i < len(name) - 1:
841 return name[i:]
842 else:
843 return ''
844
845 @property
846 def suffixes(self):
Ram Rachum8d4fef42019-11-02 18:46:24 +0200847 """
848 A list of the final component's suffixes, if any.
849
850 These include the leading periods. For example: ['.tar', '.gz']
851 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100852 name = self.name
853 if name.endswith('.'):
854 return []
855 name = name.lstrip('.')
856 return ['.' + suffix for suffix in name.split('.')[1:]]
857
858 @property
859 def stem(self):
860 """The final path component, minus its last suffix."""
861 name = self.name
862 i = name.rfind('.')
863 if 0 < i < len(name) - 1:
864 return name[:i]
865 else:
866 return name
867
868 def with_name(self, name):
869 """Return a new path with the file name changed."""
870 if not self.name:
871 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400872 drv, root, parts = self._flavour.parse_parts((name,))
873 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
874 or drv or root or len(parts) != 1):
875 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100876 return self._from_parsed_parts(self._drv, self._root,
877 self._parts[:-1] + [name])
878
Tim Hoffmann8aea4b32020-04-19 17:29:49 +0200879 def with_stem(self, stem):
880 """Return a new path with the stem changed."""
881 return self.with_name(stem + self.suffix)
882
Antoine Pitrou31119e42013-11-22 17:38:12 +0100883 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200884 """Return a new path with the file suffix changed. If the path
885 has no suffix, add given suffix. If the given suffix is an empty
886 string, remove the suffix from the path.
887 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400888 f = self._flavour
889 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300890 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400891 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100892 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100893 name = self.name
894 if not name:
895 raise ValueError("%r has an empty name" % (self,))
896 old_suffix = self.suffix
897 if not old_suffix:
898 name = name + suffix
899 else:
900 name = name[:-len(old_suffix)] + suffix
901 return self._from_parsed_parts(self._drv, self._root,
902 self._parts[:-1] + [name])
903
904 def relative_to(self, *other):
905 """Return the relative path to another path identified by the passed
906 arguments. If the operation is not possible (because this is not
907 a subpath of the other path), raise ValueError.
908 """
909 # For the purpose of this method, drive and root are considered
910 # separate parts, i.e.:
911 # Path('c:/').relative_to('c:') gives Path('/')
912 # Path('c:/').relative_to('/') raise ValueError
913 if not other:
914 raise TypeError("need at least one argument")
915 parts = self._parts
916 drv = self._drv
917 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100918 if root:
919 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100920 else:
921 abs_parts = parts
922 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100923 if to_root:
924 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100925 else:
926 to_abs_parts = to_parts
927 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100928 cf = self._flavour.casefold_parts
929 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100930 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
Rotuna44832532020-05-25 21:42:28 +0200931 raise ValueError("{!r} is not in the subpath of {!r}"
932 " OR one path is relative and the other is absolute."
Antoine Pitrou31119e42013-11-22 17:38:12 +0100933 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100934 return self._from_parsed_parts('', root if n == 1 else '',
935 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100936
Hai Shi82642a02019-08-13 14:54:02 -0500937 def is_relative_to(self, *other):
938 """Return True if the path is relative to another path or False.
939 """
940 try:
941 self.relative_to(*other)
942 return True
943 except ValueError:
944 return False
945
Antoine Pitrou31119e42013-11-22 17:38:12 +0100946 @property
947 def parts(self):
948 """An object providing sequence-like access to the
949 components in the filesystem path."""
950 # We cache the tuple to avoid building a new one each time .parts
951 # is accessed. XXX is this necessary?
952 try:
953 return self._pparts
954 except AttributeError:
955 self._pparts = tuple(self._parts)
956 return self._pparts
957
958 def joinpath(self, *args):
959 """Combine this path with one or several arguments, and return a
960 new path representing either a subpath (if all arguments are relative
961 paths) or a totally different path (if one of the arguments is
962 anchored).
963 """
964 return self._make_child(args)
965
966 def __truediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400967 try:
968 return self._make_child((key,))
969 except TypeError:
970 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100971
972 def __rtruediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400973 try:
974 return self._from_parts([key] + self._parts)
975 except TypeError:
976 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100977
978 @property
979 def parent(self):
980 """The logical parent of the path."""
981 drv = self._drv
982 root = self._root
983 parts = self._parts
984 if len(parts) == 1 and (drv or root):
985 return self
986 return self._from_parsed_parts(drv, root, parts[:-1])
987
988 @property
989 def parents(self):
990 """A sequence of this path's logical parents."""
991 return _PathParents(self)
992
993 def is_absolute(self):
994 """True if the path is absolute (has both a root and, if applicable,
995 a drive)."""
996 if not self._root:
997 return False
998 return not self._flavour.has_drv or bool(self._drv)
999
1000 def is_reserved(self):
1001 """Return True if the path contains one of the special names reserved
1002 by the system, if any."""
1003 return self._flavour.is_reserved(self._parts)
1004
1005 def match(self, path_pattern):
1006 """
1007 Return True if this path matches the given pattern.
1008 """
1009 cf = self._flavour.casefold
1010 path_pattern = cf(path_pattern)
1011 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
1012 if not pat_parts:
1013 raise ValueError("empty pattern")
1014 if drv and drv != cf(self._drv):
1015 return False
1016 if root and root != cf(self._root):
1017 return False
1018 parts = self._cparts
1019 if drv or root:
1020 if len(pat_parts) != len(parts):
1021 return False
1022 pat_parts = pat_parts[1:]
1023 elif len(pat_parts) > len(parts):
1024 return False
1025 for part, pat in zip(reversed(parts), reversed(pat_parts)):
1026 if not fnmatch.fnmatchcase(part, pat):
1027 return False
1028 return True
1029
Brett Cannon568be632016-06-10 12:20:49 -07001030# Can't subclass os.PathLike from PurePath and keep the constructor
1031# optimizations in PurePath._parse_args().
1032os.PathLike.register(PurePath)
1033
Antoine Pitrou31119e42013-11-22 17:38:12 +01001034
1035class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001036 """PurePath subclass for non-Windows systems.
1037
1038 On a POSIX system, instantiating a PurePath should return this object.
1039 However, you can also instantiate it directly on any system.
1040 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001041 _flavour = _posix_flavour
1042 __slots__ = ()
1043
1044
1045class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001046 """PurePath subclass for Windows systems.
1047
1048 On a Windows system, instantiating a PurePath should return this object.
1049 However, you can also instantiate it directly on any system.
1050 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001051 _flavour = _windows_flavour
1052 __slots__ = ()
1053
1054
1055# Filesystem-accessing classes
1056
1057
1058class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001059 """PurePath subclass that can make system calls.
1060
1061 Path represents a filesystem path but unlike PurePath, also offers
1062 methods to do system calls on path objects. Depending on your system,
1063 instantiating a Path will return either a PosixPath or a WindowsPath
1064 object. You can also instantiate a PosixPath or WindowsPath directly,
1065 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
1066 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001067 __slots__ = (
1068 '_accessor',
Antoine Pitrou31119e42013-11-22 17:38:12 +01001069 )
1070
1071 def __new__(cls, *args, **kwargs):
1072 if cls is Path:
1073 cls = WindowsPath if os.name == 'nt' else PosixPath
1074 self = cls._from_parts(args, init=False)
1075 if not self._flavour.is_supported:
1076 raise NotImplementedError("cannot instantiate %r on your system"
1077 % (cls.__name__,))
1078 self._init()
1079 return self
1080
1081 def _init(self,
1082 # Private non-constructor arguments
1083 template=None,
1084 ):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001085 if template is not None:
1086 self._accessor = template._accessor
1087 else:
1088 self._accessor = _normal_accessor
1089
1090 def _make_child_relpath(self, part):
1091 # This is an optimization used for dir walking. `part` must be
1092 # a single part relative to this path.
1093 parts = self._parts + [part]
1094 return self._from_parsed_parts(self._drv, self._root, parts)
1095
1096 def __enter__(self):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001097 return self
1098
1099 def __exit__(self, t, v, tb):
Barney Gale00002e62020-04-01 15:10:51 +01001100 # https://bugs.python.org/issue39682
1101 # In previous versions of pathlib, this method marked this path as
1102 # closed; subsequent attempts to perform I/O would raise an IOError.
1103 # This functionality was never documented, and had the effect of
1104 # making Path objects mutable, contrary to PEP 428. In Python 3.9 the
1105 # _closed attribute was removed, and this method made a no-op.
1106 # This method and __enter__()/__exit__() should be deprecated and
1107 # removed in the future.
1108 pass
Antoine Pitrou31119e42013-11-22 17:38:12 +01001109
1110 def _opener(self, name, flags, mode=0o666):
1111 # A stub for the opener argument to built-in open()
1112 return self._accessor.open(self, flags, mode)
1113
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001114 def _raw_open(self, flags, mode=0o777):
1115 """
1116 Open the file pointed by this path and return a file descriptor,
1117 as os.open() does.
1118 """
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001119 return self._accessor.open(self, flags, mode)
1120
Antoine Pitrou31119e42013-11-22 17:38:12 +01001121 # Public API
1122
1123 @classmethod
1124 def cwd(cls):
1125 """Return a new path pointing to the current working directory
1126 (as returned by os.getcwd()).
1127 """
1128 return cls(os.getcwd())
1129
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001130 @classmethod
1131 def home(cls):
1132 """Return a new path pointing to the user's home directory (as
1133 returned by os.path.expanduser('~')).
1134 """
1135 return cls(cls()._flavour.gethomedir(None))
1136
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001137 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001138 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001139 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001140 """
1141 st = self.stat()
1142 try:
1143 other_st = other_path.stat()
1144 except AttributeError:
Barney Gale5b1d9182020-04-17 18:47:27 +01001145 other_st = self._accessor.stat(other_path)
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001146 return os.path.samestat(st, other_st)
1147
Antoine Pitrou31119e42013-11-22 17:38:12 +01001148 def iterdir(self):
1149 """Iterate over the files in this directory. Does not yield any
1150 result for the special paths '.' and '..'.
1151 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001152 for name in self._accessor.listdir(self):
1153 if name in {'.', '..'}:
1154 # Yielding a path object for these makes little sense
1155 continue
1156 yield self._make_child_relpath(name)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001157
1158 def glob(self, pattern):
1159 """Iterate over this subtree and yield all existing files (of any
Eivind Teig537b6ca2019-02-11 11:47:09 +01001160 kind, including directories) matching the given relative pattern.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001161 """
Serhiy Storchakaf4f445b2020-02-12 12:11:34 +02001162 sys.audit("pathlib.Path.glob", self, pattern)
Berker Peksag4a208e42016-01-30 17:50:48 +02001163 if not pattern:
1164 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001165 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1166 if drv or root:
1167 raise NotImplementedError("Non-relative patterns are unsupported")
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +03001168 selector = _make_selector(tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001169 for p in selector.select_from(self):
1170 yield p
1171
1172 def rglob(self, pattern):
1173 """Recursively yield all existing files (of any kind, including
Eivind Teig537b6ca2019-02-11 11:47:09 +01001174 directories) matching the given relative pattern, anywhere in
1175 this subtree.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001176 """
Serhiy Storchakaf4f445b2020-02-12 12:11:34 +02001177 sys.audit("pathlib.Path.rglob", self, pattern)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001178 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1179 if drv or root:
1180 raise NotImplementedError("Non-relative patterns are unsupported")
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +03001181 selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001182 for p in selector.select_from(self):
1183 yield p
1184
1185 def absolute(self):
1186 """Return an absolute version of this path. This function works
1187 even if the path doesn't point to anything.
1188
1189 No normalization is done, i.e. all '.' and '..' will be kept along.
1190 Use resolve() to get the canonical path to a file.
1191 """
1192 # XXX untested yet!
Antoine Pitrou31119e42013-11-22 17:38:12 +01001193 if self.is_absolute():
1194 return self
1195 # FIXME this must defer to the specific flavour (and, under Windows,
1196 # use nt._getfullpathname())
1197 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1198 obj._init(template=self)
1199 return obj
1200
Steve Dower98eb3602016-11-09 12:58:17 -08001201 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001202 """
1203 Make the path absolute, resolving all symlinks on the way and also
1204 normalizing it (for example turning slashes into backslashes under
1205 Windows).
1206 """
Steve Dower98eb3602016-11-09 12:58:17 -08001207 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001208 if s is None:
1209 # No symlink resolution => for consistency, raise an error if
1210 # the path doesn't exist or is forbidden
1211 self.stat()
1212 s = str(self.absolute())
1213 # Now we have no symlinks in the path, it's safe to normalize it.
1214 normed = self._flavour.pathmod.normpath(s)
1215 obj = self._from_parts((normed,), init=False)
1216 obj._init(template=self)
1217 return obj
1218
1219 def stat(self):
1220 """
1221 Return the result of the stat() system call on this path, like
1222 os.stat() does.
1223 """
1224 return self._accessor.stat(self)
1225
1226 def owner(self):
1227 """
1228 Return the login name of the file owner.
1229 """
Barney Gale22386bb2020-04-17 17:41:07 +01001230 return self._accessor.owner(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001231
1232 def group(self):
1233 """
1234 Return the group name of the file gid.
1235 """
Barney Gale22386bb2020-04-17 17:41:07 +01001236 return self._accessor.group(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001237
Antoine Pitrou31119e42013-11-22 17:38:12 +01001238 def open(self, mode='r', buffering=-1, encoding=None,
1239 errors=None, newline=None):
1240 """
1241 Open the file pointed by this path and return a file object, as
1242 the built-in open() function does.
1243 """
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001244 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001245 opener=self._opener)
1246
Georg Brandlea683982014-10-01 19:12:33 +02001247 def read_bytes(self):
1248 """
1249 Open the file in bytes mode, read it, and close the file.
1250 """
1251 with self.open(mode='rb') as f:
1252 return f.read()
1253
1254 def read_text(self, encoding=None, errors=None):
1255 """
1256 Open the file in text mode, read it, and close the file.
1257 """
1258 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1259 return f.read()
1260
1261 def write_bytes(self, data):
1262 """
1263 Open the file in bytes mode, write to it, and close the file.
1264 """
1265 # type-check for the buffer interface before truncating the file
1266 view = memoryview(data)
1267 with self.open(mode='wb') as f:
1268 return f.write(view)
1269
Максим5f227412020-10-21 05:08:19 +03001270 def write_text(self, data, encoding=None, errors=None, newline=None):
Georg Brandlea683982014-10-01 19:12:33 +02001271 """
1272 Open the file in text mode, write to it, and close the file.
1273 """
1274 if not isinstance(data, str):
1275 raise TypeError('data must be str, not %s' %
1276 data.__class__.__name__)
Максим5f227412020-10-21 05:08:19 +03001277 with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
Georg Brandlea683982014-10-01 19:12:33 +02001278 return f.write(data)
1279
Girtsa01ba332019-10-23 14:18:40 -07001280 def readlink(self):
1281 """
1282 Return the path to which the symbolic link points.
1283 """
1284 path = self._accessor.readlink(self)
1285 obj = self._from_parts((path,), init=False)
1286 obj._init(template=self)
1287 return obj
1288
Antoine Pitrou31119e42013-11-22 17:38:12 +01001289 def touch(self, mode=0o666, exist_ok=True):
1290 """
1291 Create this file with the given access mode, if it doesn't exist.
1292 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001293 if exist_ok:
1294 # First try to bump modification time
1295 # Implementation note: GNU touch uses the UTIME_NOW option of
1296 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001297 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001298 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001299 except OSError:
1300 # Avoid exception chaining
1301 pass
1302 else:
1303 return
1304 flags = os.O_CREAT | os.O_WRONLY
1305 if not exist_ok:
1306 flags |= os.O_EXCL
1307 fd = self._raw_open(flags, mode)
1308 os.close(fd)
1309
Barry Warsaw7c549c42014-08-05 11:28:12 -04001310 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001311 """
1312 Create a new directory at this given path.
1313 """
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001314 try:
1315 self._accessor.mkdir(self, mode)
1316 except FileNotFoundError:
1317 if not parents or self.parent == self:
1318 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001319 self.parent.mkdir(parents=True, exist_ok=True)
1320 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001321 except OSError:
1322 # Cannot rely on checking for EEXIST, since the operating system
1323 # could give priority to other errors like EACCES or EROFS
1324 if not exist_ok or not self.is_dir():
1325 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001326
1327 def chmod(self, mode):
1328 """
1329 Change the permissions of the path, like os.chmod().
1330 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001331 self._accessor.chmod(self, mode)
1332
1333 def lchmod(self, mode):
1334 """
1335 Like chmod(), except if the path points to a symlink, the symlink's
1336 permissions are changed, rather than its target's.
1337 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001338 self._accessor.lchmod(self, mode)
1339
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001340 def unlink(self, missing_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001341 """
1342 Remove this file or link.
1343 If the path is a directory, use rmdir() instead.
1344 """
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001345 try:
1346 self._accessor.unlink(self)
1347 except FileNotFoundError:
1348 if not missing_ok:
1349 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001350
1351 def rmdir(self):
1352 """
1353 Remove this directory. The directory must be empty.
1354 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001355 self._accessor.rmdir(self)
1356
1357 def lstat(self):
1358 """
1359 Like stat(), except if the path points to a symlink, the symlink's
1360 status information is returned, rather than its target's.
1361 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001362 return self._accessor.lstat(self)
1363
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -04001364 def link_to(self, target):
1365 """
1366 Create a hard link pointing to a path named target.
1367 """
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -04001368 self._accessor.link_to(self, target)
1369
Antoine Pitrou31119e42013-11-22 17:38:12 +01001370 def rename(self, target):
1371 """
Ram Rachumf97e42e2020-10-03 12:52:13 +03001372 Rename this path to the target path.
1373
1374 The target path may be absolute or relative. Relative paths are
1375 interpreted relative to the current working directory, *not* the
1376 directory of the Path object.
1377
1378 Returns the new Path instance pointing to the target path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001379 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001380 self._accessor.rename(self, target)
hui shang088a09a2019-09-11 21:26:49 +08001381 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001382
1383 def replace(self, target):
1384 """
Ram Rachumf97e42e2020-10-03 12:52:13 +03001385 Rename this path to the target path, overwriting if that path exists.
1386
1387 The target path may be absolute or relative. Relative paths are
1388 interpreted relative to the current working directory, *not* the
1389 directory of the Path object.
1390
1391 Returns the new Path instance pointing to the target path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001392 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001393 self._accessor.replace(self, target)
hui shang088a09a2019-09-11 21:26:49 +08001394 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001395
1396 def symlink_to(self, target, target_is_directory=False):
1397 """
1398 Make this path a symlink pointing to the given path.
1399 Note the order of arguments (self, target) is the reverse of os.symlink's.
1400 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001401 self._accessor.symlink(target, self, target_is_directory)
1402
1403 # Convenience functions for querying the stat results
1404
1405 def exists(self):
1406 """
1407 Whether this path exists.
1408 """
1409 try:
1410 self.stat()
1411 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001412 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001413 raise
1414 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001415 except ValueError:
1416 # Non-encodable path
1417 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001418 return True
1419
1420 def is_dir(self):
1421 """
1422 Whether this path is a directory.
1423 """
1424 try:
1425 return S_ISDIR(self.stat().st_mode)
1426 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001427 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001428 raise
1429 # Path doesn't exist or is a broken symlink
1430 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1431 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001432 except ValueError:
1433 # Non-encodable path
1434 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001435
1436 def is_file(self):
1437 """
1438 Whether this path is a regular file (also True for symlinks pointing
1439 to regular files).
1440 """
1441 try:
1442 return S_ISREG(self.stat().st_mode)
1443 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001444 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001445 raise
1446 # Path doesn't exist or is a broken symlink
1447 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1448 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001449 except ValueError:
1450 # Non-encodable path
1451 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001452
Cooper Lees173ff4a2017-08-01 15:35:45 -07001453 def is_mount(self):
1454 """
1455 Check if this path is a POSIX mount point
1456 """
1457 # Need to exist and be a dir
1458 if not self.exists() or not self.is_dir():
1459 return False
1460
Cooper Lees173ff4a2017-08-01 15:35:45 -07001461 try:
Barney Galec746c4f2020-04-17 18:42:06 +01001462 parent_dev = self.parent.stat().st_dev
Cooper Lees173ff4a2017-08-01 15:35:45 -07001463 except OSError:
1464 return False
1465
1466 dev = self.stat().st_dev
1467 if dev != parent_dev:
1468 return True
1469 ino = self.stat().st_ino
Barney Galec746c4f2020-04-17 18:42:06 +01001470 parent_ino = self.parent.stat().st_ino
Cooper Lees173ff4a2017-08-01 15:35:45 -07001471 return ino == parent_ino
1472
Antoine Pitrou31119e42013-11-22 17:38:12 +01001473 def is_symlink(self):
1474 """
1475 Whether this path is a symbolic link.
1476 """
1477 try:
1478 return S_ISLNK(self.lstat().st_mode)
1479 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001480 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001481 raise
1482 # Path doesn't exist
1483 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001484 except ValueError:
1485 # Non-encodable path
1486 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001487
1488 def is_block_device(self):
1489 """
1490 Whether this path is a block device.
1491 """
1492 try:
1493 return S_ISBLK(self.stat().st_mode)
1494 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001495 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001496 raise
1497 # Path doesn't exist or is a broken symlink
1498 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1499 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001500 except ValueError:
1501 # Non-encodable path
1502 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001503
1504 def is_char_device(self):
1505 """
1506 Whether this path is a character device.
1507 """
1508 try:
1509 return S_ISCHR(self.stat().st_mode)
1510 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001511 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001512 raise
1513 # Path doesn't exist or is a broken symlink
1514 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1515 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001516 except ValueError:
1517 # Non-encodable path
1518 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001519
1520 def is_fifo(self):
1521 """
1522 Whether this path is a FIFO.
1523 """
1524 try:
1525 return S_ISFIFO(self.stat().st_mode)
1526 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001527 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001528 raise
1529 # Path doesn't exist or is a broken symlink
1530 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1531 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001532 except ValueError:
1533 # Non-encodable path
1534 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001535
1536 def is_socket(self):
1537 """
1538 Whether this path is a socket.
1539 """
1540 try:
1541 return S_ISSOCK(self.stat().st_mode)
1542 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001543 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001544 raise
1545 # Path doesn't exist or is a broken symlink
1546 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1547 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001548 except ValueError:
1549 # Non-encodable path
1550 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001551
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001552 def expanduser(self):
1553 """ Return a new path with expanded ~ and ~user constructs
1554 (as returned by os.path.expanduser)
1555 """
1556 if (not (self._drv or self._root) and
1557 self._parts and self._parts[0][:1] == '~'):
1558 homedir = self._flavour.gethomedir(self._parts[0][1:])
1559 return self._from_parts([homedir] + self._parts[1:])
1560
1561 return self
1562
Antoine Pitrou31119e42013-11-22 17:38:12 +01001563
1564class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001565 """Path subclass for non-Windows systems.
1566
1567 On a POSIX system, instantiating a Path should return this object.
1568 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001569 __slots__ = ()
1570
1571class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001572 """Path subclass for Windows systems.
1573
1574 On a Windows system, instantiating a Path should return this object.
1575 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001576 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001577
Cooper Lees173ff4a2017-08-01 15:35:45 -07001578 def is_mount(self):
1579 raise NotImplementedError("Path.is_mount() is unsupported on this system")