blob: 9f5e27b91178e68e5e39ab4d3bde8549b5703a73 [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
332 newpath = path + sep + name
333 if newpath in seen:
334 # Already seen this path
335 path = seen[newpath]
336 if path is not None:
337 # use cached value
338 continue
339 # The symlink is not resolved, so we must have a symlink loop.
340 raise RuntimeError("Symlink loop from %r" % newpath)
341 # Resolve the symbolic link
342 try:
343 target = accessor.readlink(newpath)
344 except OSError as e:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200345 if e.errno != EINVAL and strict:
346 raise
347 # Not a symlink, or non-strict mode. We just leave the path
348 # untouched.
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100349 path = newpath
350 else:
351 seen[newpath] = None # not resolved symlink
352 path = _resolve(path, target)
353 seen[newpath] = path # resolved symlink
354
355 return path
356 # NOTE: according to POSIX, getcwd() cannot contain path components
357 # which are symlinks.
358 base = '' if path.is_absolute() else os.getcwd()
359 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100360
361 def is_reserved(self, parts):
362 return False
363
364 def make_uri(self, path):
365 # We represent the path using the local filesystem encoding,
366 # for portability to other applications.
367 bpath = bytes(path)
368 return 'file://' + urlquote_from_bytes(bpath)
369
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100370 def gethomedir(self, username):
371 if not username:
372 try:
373 return os.environ['HOME']
374 except KeyError:
375 import pwd
376 return pwd.getpwuid(os.getuid()).pw_dir
377 else:
378 import pwd
379 try:
380 return pwd.getpwnam(username).pw_dir
381 except KeyError:
382 raise RuntimeError("Can't determine home directory "
383 "for %r" % username)
384
Antoine Pitrou31119e42013-11-22 17:38:12 +0100385
386_windows_flavour = _WindowsFlavour()
387_posix_flavour = _PosixFlavour()
388
389
390class _Accessor:
391 """An accessor implements a particular (system-specific or not) way of
392 accessing paths on the filesystem."""
393
394
395class _NormalAccessor(_Accessor):
396
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200397 stat = os.stat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100398
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200399 lstat = os.lstat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100400
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200401 open = os.open
Antoine Pitrou31119e42013-11-22 17:38:12 +0100402
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200403 listdir = os.listdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100404
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200405 scandir = os.scandir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100406
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200407 chmod = os.chmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100408
409 if hasattr(os, "lchmod"):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200410 lchmod = os.lchmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100411 else:
412 def lchmod(self, pathobj, mode):
413 raise NotImplementedError("lchmod() not available on this system")
414
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200415 mkdir = os.mkdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100416
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200417 unlink = os.unlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100418
Toke Høiland-Jørgensen092435e2019-12-16 13:23:55 +0100419 if hasattr(os, "link"):
420 link_to = os.link
421 else:
422 @staticmethod
423 def link_to(self, target):
424 raise NotImplementedError("os.link() not available on this system")
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -0400425
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200426 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100427
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200428 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100429
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200430 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100431
432 if nt:
433 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200434 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100435 else:
436 def symlink(a, b, target_is_directory):
437 raise NotImplementedError("symlink() not available on this system")
438 else:
439 # Under POSIX, os.symlink() takes two args
440 @staticmethod
441 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200442 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100443
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200444 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100445
446 # Helper for resolve()
447 def readlink(self, path):
448 return os.readlink(path)
449
Barney Gale22386bb2020-04-17 17:41:07 +0100450 def owner(self, path):
451 try:
452 import pwd
453 return pwd.getpwuid(self.stat(path).st_uid).pw_name
454 except ImportError:
455 raise NotImplementedError("Path.owner() is unsupported on this system")
456
457 def group(self, path):
458 try:
459 import grp
460 return grp.getgrgid(self.stat(path).st_gid).gr_name
461 except ImportError:
462 raise NotImplementedError("Path.group() is unsupported on this system")
463
Antoine Pitrou31119e42013-11-22 17:38:12 +0100464
465_normal_accessor = _NormalAccessor()
466
467
468#
469# Globbing helpers
470#
471
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300472def _make_selector(pattern_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100473 pat = pattern_parts[0]
474 child_parts = pattern_parts[1:]
475 if pat == '**':
476 cls = _RecursiveWildcardSelector
477 elif '**' in pat:
478 raise ValueError("Invalid pattern: '**' can only be an entire path component")
479 elif _is_wildcard_pattern(pat):
480 cls = _WildcardSelector
481 else:
482 cls = _PreciseSelector
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300483 return cls(pat, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100484
485if hasattr(functools, "lru_cache"):
486 _make_selector = functools.lru_cache()(_make_selector)
487
488
489class _Selector:
490 """A selector matches a specific glob pattern part against the children
491 of a given path."""
492
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300493 def __init__(self, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100494 self.child_parts = child_parts
495 if child_parts:
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300496 self.successor = _make_selector(child_parts, flavour)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300497 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100498 else:
499 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300500 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100501
502 def select_from(self, parent_path):
503 """Iterate over all child paths of `parent_path` matched by this
504 selector. This can contain parent_path itself."""
505 path_cls = type(parent_path)
506 is_dir = path_cls.is_dir
507 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300508 scandir = parent_path._accessor.scandir
509 if not is_dir(parent_path):
510 return iter([])
511 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100512
513
514class _TerminatingSelector:
515
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300516 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100517 yield parent_path
518
519
520class _PreciseSelector(_Selector):
521
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300522 def __init__(self, name, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100523 self.name = name
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300524 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100525
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300526 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800527 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800528 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300529 if (is_dir if self.dironly else exists)(path):
530 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800531 yield p
532 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100533 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100534
535
536class _WildcardSelector(_Selector):
537
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300538 def __init__(self, pat, child_parts, flavour):
539 self.match = flavour.compile_pattern(pat)
540 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100541
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300542 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800543 try:
Serhiy Storchaka704e2062020-03-11 18:42:03 +0200544 with scandir(parent_path) as scandir_it:
545 entries = list(scandir_it)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300546 for entry in entries:
Pablo Galindoeb7560a2020-03-07 17:53:20 +0000547 if self.dironly:
548 try:
549 # "entry.is_dir()" can raise PermissionError
550 # in some cases (see bpo-38894), which is not
551 # among the errors ignored by _ignore_error()
552 if not entry.is_dir():
553 continue
554 except OSError as e:
555 if not _ignore_error(e):
556 raise
557 continue
558 name = entry.name
559 if self.match(name):
560 path = parent_path._make_child_relpath(name)
561 for p in self.successor._select_from(path, is_dir, exists, scandir):
562 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800563 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100564 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800565
Antoine Pitrou31119e42013-11-22 17:38:12 +0100566
Antoine Pitrou31119e42013-11-22 17:38:12 +0100567class _RecursiveWildcardSelector(_Selector):
568
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300569 def __init__(self, pat, child_parts, flavour):
570 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100571
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300572 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100573 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800574 try:
Serhiy Storchaka704e2062020-03-11 18:42:03 +0200575 with scandir(parent_path) as scandir_it:
576 entries = list(scandir_it)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300577 for entry in entries:
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200578 entry_is_dir = False
579 try:
580 entry_is_dir = entry.is_dir()
581 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -0800582 if not _ignore_error(e):
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200583 raise
584 if entry_is_dir and not entry.is_symlink():
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300585 path = parent_path._make_child_relpath(entry.name)
586 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800587 yield p
588 except PermissionError:
589 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100590
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300591 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800592 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300593 yielded = set()
594 try:
595 successor_select = self.successor._select_from
596 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
597 for p in successor_select(starting_point, is_dir, exists, scandir):
598 if p not in yielded:
599 yield p
600 yielded.add(p)
601 finally:
602 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800603 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100604 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100605
606
607#
608# Public API
609#
610
611class _PathParents(Sequence):
612 """This object provides sequence-like access to the logical ancestors
613 of a path. Don't try to construct it yourself."""
614 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
615
616 def __init__(self, path):
617 # We don't store the instance to avoid reference cycles
618 self._pathcls = type(path)
619 self._drv = path._drv
620 self._root = path._root
621 self._parts = path._parts
622
623 def __len__(self):
624 if self._drv or self._root:
625 return len(self._parts) - 1
626 else:
627 return len(self._parts)
628
629 def __getitem__(self, idx):
630 if idx < 0 or idx >= len(self):
631 raise IndexError(idx)
632 return self._pathcls._from_parsed_parts(self._drv, self._root,
633 self._parts[:-idx - 1])
634
635 def __repr__(self):
636 return "<{}.parents>".format(self._pathcls.__name__)
637
638
639class PurePath(object):
chasondfa015c2018-02-19 08:36:32 +0900640 """Base class for manipulating paths without I/O.
641
642 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100643 don't imply any actual filesystem I/O. Depending on your system,
644 instantiating a PurePath will return either a PurePosixPath or a
645 PureWindowsPath object. You can also instantiate either of these classes
646 directly, regardless of your system.
647 """
648 __slots__ = (
649 '_drv', '_root', '_parts',
650 '_str', '_hash', '_pparts', '_cached_cparts',
651 )
652
653 def __new__(cls, *args):
654 """Construct a PurePath from one or several strings and or existing
655 PurePath objects. The strings and path objects are combined so as
656 to yield a canonicalized path, which is incorporated into the
657 new PurePath object.
658 """
659 if cls is PurePath:
660 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
661 return cls._from_parts(args)
662
663 def __reduce__(self):
664 # Using the parts tuple helps share interned path parts
665 # when pickling related paths.
666 return (self.__class__, tuple(self._parts))
667
668 @classmethod
669 def _parse_args(cls, args):
670 # This is useful when you don't want to create an instance, just
671 # canonicalize some constructor arguments.
672 parts = []
673 for a in args:
674 if isinstance(a, PurePath):
675 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100676 else:
Brett Cannon568be632016-06-10 12:20:49 -0700677 a = os.fspath(a)
678 if isinstance(a, str):
679 # Force-cast str subclasses to str (issue #21127)
680 parts.append(str(a))
681 else:
682 raise TypeError(
683 "argument should be a str object or an os.PathLike "
684 "object returning str, not %r"
685 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100686 return cls._flavour.parse_parts(parts)
687
688 @classmethod
689 def _from_parts(cls, args, init=True):
690 # We need to call _parse_args on the instance, so as to get the
691 # right flavour.
692 self = object.__new__(cls)
693 drv, root, parts = self._parse_args(args)
694 self._drv = drv
695 self._root = root
696 self._parts = parts
697 if init:
698 self._init()
699 return self
700
701 @classmethod
702 def _from_parsed_parts(cls, drv, root, parts, init=True):
703 self = object.__new__(cls)
704 self._drv = drv
705 self._root = root
706 self._parts = parts
707 if init:
708 self._init()
709 return self
710
711 @classmethod
712 def _format_parsed_parts(cls, drv, root, parts):
713 if drv or root:
714 return drv + root + cls._flavour.join(parts[1:])
715 else:
716 return cls._flavour.join(parts)
717
718 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000719 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100720 pass
721
722 def _make_child(self, args):
723 drv, root, parts = self._parse_args(args)
724 drv, root, parts = self._flavour.join_parsed_parts(
725 self._drv, self._root, self._parts, drv, root, parts)
726 return self._from_parsed_parts(drv, root, parts)
727
728 def __str__(self):
729 """Return the string representation of the path, suitable for
730 passing to system calls."""
731 try:
732 return self._str
733 except AttributeError:
734 self._str = self._format_parsed_parts(self._drv, self._root,
735 self._parts) or '.'
736 return self._str
737
Brett Cannon568be632016-06-10 12:20:49 -0700738 def __fspath__(self):
739 return str(self)
740
Antoine Pitrou31119e42013-11-22 17:38:12 +0100741 def as_posix(self):
742 """Return the string representation of the path with forward (/)
743 slashes."""
744 f = self._flavour
745 return str(self).replace(f.sep, '/')
746
747 def __bytes__(self):
748 """Return the bytes representation of the path. This is only
749 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200750 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100751
752 def __repr__(self):
753 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
754
755 def as_uri(self):
756 """Return the path as a 'file' URI."""
757 if not self.is_absolute():
758 raise ValueError("relative path can't be expressed as a file URI")
759 return self._flavour.make_uri(self)
760
761 @property
762 def _cparts(self):
763 # Cached casefolded parts, for hashing and comparison
764 try:
765 return self._cached_cparts
766 except AttributeError:
767 self._cached_cparts = self._flavour.casefold_parts(self._parts)
768 return self._cached_cparts
769
770 def __eq__(self, other):
771 if not isinstance(other, PurePath):
772 return NotImplemented
773 return self._cparts == other._cparts and self._flavour is other._flavour
774
Antoine Pitrou31119e42013-11-22 17:38:12 +0100775 def __hash__(self):
776 try:
777 return self._hash
778 except AttributeError:
779 self._hash = hash(tuple(self._cparts))
780 return self._hash
781
782 def __lt__(self, other):
783 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
784 return NotImplemented
785 return self._cparts < other._cparts
786
787 def __le__(self, other):
788 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
789 return NotImplemented
790 return self._cparts <= other._cparts
791
792 def __gt__(self, other):
793 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
794 return NotImplemented
795 return self._cparts > other._cparts
796
797 def __ge__(self, other):
798 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
799 return NotImplemented
800 return self._cparts >= other._cparts
801
Batuhan Taşkaya526606b2019-12-08 23:31:15 +0300802 def __class_getitem__(cls, type):
803 return cls
804
Antoine Pitrou31119e42013-11-22 17:38:12 +0100805 drive = property(attrgetter('_drv'),
806 doc="""The drive prefix (letter or UNC path), if any.""")
807
808 root = property(attrgetter('_root'),
809 doc="""The root of the path, if any.""")
810
811 @property
812 def anchor(self):
813 """The concatenation of the drive and root, or ''."""
814 anchor = self._drv + self._root
815 return anchor
816
817 @property
818 def name(self):
819 """The final path component, if any."""
820 parts = self._parts
821 if len(parts) == (1 if (self._drv or self._root) else 0):
822 return ''
823 return parts[-1]
824
825 @property
826 def suffix(self):
Ram Rachum8d4fef42019-11-02 18:46:24 +0200827 """
828 The final component's last suffix, if any.
829
830 This includes the leading period. For example: '.txt'
831 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100832 name = self.name
833 i = name.rfind('.')
834 if 0 < i < len(name) - 1:
835 return name[i:]
836 else:
837 return ''
838
839 @property
840 def suffixes(self):
Ram Rachum8d4fef42019-11-02 18:46:24 +0200841 """
842 A list of the final component's suffixes, if any.
843
844 These include the leading periods. For example: ['.tar', '.gz']
845 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100846 name = self.name
847 if name.endswith('.'):
848 return []
849 name = name.lstrip('.')
850 return ['.' + suffix for suffix in name.split('.')[1:]]
851
852 @property
853 def stem(self):
854 """The final path component, minus its last suffix."""
855 name = self.name
856 i = name.rfind('.')
857 if 0 < i < len(name) - 1:
858 return name[:i]
859 else:
860 return name
861
862 def with_name(self, name):
863 """Return a new path with the file name changed."""
864 if not self.name:
865 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400866 drv, root, parts = self._flavour.parse_parts((name,))
867 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
868 or drv or root or len(parts) != 1):
869 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100870 return self._from_parsed_parts(self._drv, self._root,
871 self._parts[:-1] + [name])
872
Tim Hoffmann8aea4b32020-04-19 17:29:49 +0200873 def with_stem(self, stem):
874 """Return a new path with the stem changed."""
875 return self.with_name(stem + self.suffix)
876
Antoine Pitrou31119e42013-11-22 17:38:12 +0100877 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200878 """Return a new path with the file suffix changed. If the path
879 has no suffix, add given suffix. If the given suffix is an empty
880 string, remove the suffix from the path.
881 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400882 f = self._flavour
883 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300884 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400885 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100886 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100887 name = self.name
888 if not name:
889 raise ValueError("%r has an empty name" % (self,))
890 old_suffix = self.suffix
891 if not old_suffix:
892 name = name + suffix
893 else:
894 name = name[:-len(old_suffix)] + suffix
895 return self._from_parsed_parts(self._drv, self._root,
896 self._parts[:-1] + [name])
897
898 def relative_to(self, *other):
899 """Return the relative path to another path identified by the passed
900 arguments. If the operation is not possible (because this is not
901 a subpath of the other path), raise ValueError.
902 """
903 # For the purpose of this method, drive and root are considered
904 # separate parts, i.e.:
905 # Path('c:/').relative_to('c:') gives Path('/')
906 # Path('c:/').relative_to('/') raise ValueError
907 if not other:
908 raise TypeError("need at least one argument")
909 parts = self._parts
910 drv = self._drv
911 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100912 if root:
913 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100914 else:
915 abs_parts = parts
916 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100917 if to_root:
918 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100919 else:
920 to_abs_parts = to_parts
921 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100922 cf = self._flavour.casefold_parts
923 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100924 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
Rotuna44832532020-05-25 21:42:28 +0200925 raise ValueError("{!r} is not in the subpath of {!r}"
926 " OR one path is relative and the other is absolute."
Antoine Pitrou31119e42013-11-22 17:38:12 +0100927 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100928 return self._from_parsed_parts('', root if n == 1 else '',
929 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100930
Hai Shi82642a02019-08-13 14:54:02 -0500931 def is_relative_to(self, *other):
932 """Return True if the path is relative to another path or False.
933 """
934 try:
935 self.relative_to(*other)
936 return True
937 except ValueError:
938 return False
939
Antoine Pitrou31119e42013-11-22 17:38:12 +0100940 @property
941 def parts(self):
942 """An object providing sequence-like access to the
943 components in the filesystem path."""
944 # We cache the tuple to avoid building a new one each time .parts
945 # is accessed. XXX is this necessary?
946 try:
947 return self._pparts
948 except AttributeError:
949 self._pparts = tuple(self._parts)
950 return self._pparts
951
952 def joinpath(self, *args):
953 """Combine this path with one or several arguments, and return a
954 new path representing either a subpath (if all arguments are relative
955 paths) or a totally different path (if one of the arguments is
956 anchored).
957 """
958 return self._make_child(args)
959
960 def __truediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400961 try:
962 return self._make_child((key,))
963 except TypeError:
964 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100965
966 def __rtruediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400967 try:
968 return self._from_parts([key] + self._parts)
969 except TypeError:
970 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100971
972 @property
973 def parent(self):
974 """The logical parent of the path."""
975 drv = self._drv
976 root = self._root
977 parts = self._parts
978 if len(parts) == 1 and (drv or root):
979 return self
980 return self._from_parsed_parts(drv, root, parts[:-1])
981
982 @property
983 def parents(self):
984 """A sequence of this path's logical parents."""
985 return _PathParents(self)
986
987 def is_absolute(self):
988 """True if the path is absolute (has both a root and, if applicable,
989 a drive)."""
990 if not self._root:
991 return False
992 return not self._flavour.has_drv or bool(self._drv)
993
994 def is_reserved(self):
995 """Return True if the path contains one of the special names reserved
996 by the system, if any."""
997 return self._flavour.is_reserved(self._parts)
998
999 def match(self, path_pattern):
1000 """
1001 Return True if this path matches the given pattern.
1002 """
1003 cf = self._flavour.casefold
1004 path_pattern = cf(path_pattern)
1005 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
1006 if not pat_parts:
1007 raise ValueError("empty pattern")
1008 if drv and drv != cf(self._drv):
1009 return False
1010 if root and root != cf(self._root):
1011 return False
1012 parts = self._cparts
1013 if drv or root:
1014 if len(pat_parts) != len(parts):
1015 return False
1016 pat_parts = pat_parts[1:]
1017 elif len(pat_parts) > len(parts):
1018 return False
1019 for part, pat in zip(reversed(parts), reversed(pat_parts)):
1020 if not fnmatch.fnmatchcase(part, pat):
1021 return False
1022 return True
1023
Brett Cannon568be632016-06-10 12:20:49 -07001024# Can't subclass os.PathLike from PurePath and keep the constructor
1025# optimizations in PurePath._parse_args().
1026os.PathLike.register(PurePath)
1027
Antoine Pitrou31119e42013-11-22 17:38:12 +01001028
1029class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001030 """PurePath subclass for non-Windows systems.
1031
1032 On a POSIX system, instantiating a PurePath should return this object.
1033 However, you can also instantiate it directly on any system.
1034 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001035 _flavour = _posix_flavour
1036 __slots__ = ()
1037
1038
1039class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001040 """PurePath subclass for Windows systems.
1041
1042 On a Windows system, instantiating a PurePath should return this object.
1043 However, you can also instantiate it directly on any system.
1044 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001045 _flavour = _windows_flavour
1046 __slots__ = ()
1047
1048
1049# Filesystem-accessing classes
1050
1051
1052class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001053 """PurePath subclass that can make system calls.
1054
1055 Path represents a filesystem path but unlike PurePath, also offers
1056 methods to do system calls on path objects. Depending on your system,
1057 instantiating a Path will return either a PosixPath or a WindowsPath
1058 object. You can also instantiate a PosixPath or WindowsPath directly,
1059 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
1060 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001061 __slots__ = (
1062 '_accessor',
Antoine Pitrou31119e42013-11-22 17:38:12 +01001063 )
1064
1065 def __new__(cls, *args, **kwargs):
1066 if cls is Path:
1067 cls = WindowsPath if os.name == 'nt' else PosixPath
1068 self = cls._from_parts(args, init=False)
1069 if not self._flavour.is_supported:
1070 raise NotImplementedError("cannot instantiate %r on your system"
1071 % (cls.__name__,))
1072 self._init()
1073 return self
1074
1075 def _init(self,
1076 # Private non-constructor arguments
1077 template=None,
1078 ):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001079 if template is not None:
1080 self._accessor = template._accessor
1081 else:
1082 self._accessor = _normal_accessor
1083
1084 def _make_child_relpath(self, part):
1085 # This is an optimization used for dir walking. `part` must be
1086 # a single part relative to this path.
1087 parts = self._parts + [part]
1088 return self._from_parsed_parts(self._drv, self._root, parts)
1089
1090 def __enter__(self):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001091 return self
1092
1093 def __exit__(self, t, v, tb):
Barney Gale00002e62020-04-01 15:10:51 +01001094 # https://bugs.python.org/issue39682
1095 # In previous versions of pathlib, this method marked this path as
1096 # closed; subsequent attempts to perform I/O would raise an IOError.
1097 # This functionality was never documented, and had the effect of
1098 # making Path objects mutable, contrary to PEP 428. In Python 3.9 the
1099 # _closed attribute was removed, and this method made a no-op.
1100 # This method and __enter__()/__exit__() should be deprecated and
1101 # removed in the future.
1102 pass
Antoine Pitrou31119e42013-11-22 17:38:12 +01001103
1104 def _opener(self, name, flags, mode=0o666):
1105 # A stub for the opener argument to built-in open()
1106 return self._accessor.open(self, flags, mode)
1107
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001108 def _raw_open(self, flags, mode=0o777):
1109 """
1110 Open the file pointed by this path and return a file descriptor,
1111 as os.open() does.
1112 """
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001113 return self._accessor.open(self, flags, mode)
1114
Antoine Pitrou31119e42013-11-22 17:38:12 +01001115 # Public API
1116
1117 @classmethod
1118 def cwd(cls):
1119 """Return a new path pointing to the current working directory
1120 (as returned by os.getcwd()).
1121 """
1122 return cls(os.getcwd())
1123
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001124 @classmethod
1125 def home(cls):
1126 """Return a new path pointing to the user's home directory (as
1127 returned by os.path.expanduser('~')).
1128 """
1129 return cls(cls()._flavour.gethomedir(None))
1130
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001131 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001132 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001133 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001134 """
1135 st = self.stat()
1136 try:
1137 other_st = other_path.stat()
1138 except AttributeError:
Barney Gale5b1d9182020-04-17 18:47:27 +01001139 other_st = self._accessor.stat(other_path)
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001140 return os.path.samestat(st, other_st)
1141
Antoine Pitrou31119e42013-11-22 17:38:12 +01001142 def iterdir(self):
1143 """Iterate over the files in this directory. Does not yield any
1144 result for the special paths '.' and '..'.
1145 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001146 for name in self._accessor.listdir(self):
1147 if name in {'.', '..'}:
1148 # Yielding a path object for these makes little sense
1149 continue
1150 yield self._make_child_relpath(name)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001151
1152 def glob(self, pattern):
1153 """Iterate over this subtree and yield all existing files (of any
Eivind Teig537b6ca2019-02-11 11:47:09 +01001154 kind, including directories) matching the given relative pattern.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001155 """
Serhiy Storchakaf4f445b2020-02-12 12:11:34 +02001156 sys.audit("pathlib.Path.glob", self, pattern)
Berker Peksag4a208e42016-01-30 17:50:48 +02001157 if not pattern:
1158 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001159 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1160 if drv or root:
1161 raise NotImplementedError("Non-relative patterns are unsupported")
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +03001162 selector = _make_selector(tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001163 for p in selector.select_from(self):
1164 yield p
1165
1166 def rglob(self, pattern):
1167 """Recursively yield all existing files (of any kind, including
Eivind Teig537b6ca2019-02-11 11:47:09 +01001168 directories) matching the given relative pattern, anywhere in
1169 this subtree.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001170 """
Serhiy Storchakaf4f445b2020-02-12 12:11:34 +02001171 sys.audit("pathlib.Path.rglob", self, pattern)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001172 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1173 if drv or root:
1174 raise NotImplementedError("Non-relative patterns are unsupported")
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +03001175 selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001176 for p in selector.select_from(self):
1177 yield p
1178
1179 def absolute(self):
1180 """Return an absolute version of this path. This function works
1181 even if the path doesn't point to anything.
1182
1183 No normalization is done, i.e. all '.' and '..' will be kept along.
1184 Use resolve() to get the canonical path to a file.
1185 """
1186 # XXX untested yet!
Antoine Pitrou31119e42013-11-22 17:38:12 +01001187 if self.is_absolute():
1188 return self
1189 # FIXME this must defer to the specific flavour (and, under Windows,
1190 # use nt._getfullpathname())
1191 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1192 obj._init(template=self)
1193 return obj
1194
Steve Dower98eb3602016-11-09 12:58:17 -08001195 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001196 """
1197 Make the path absolute, resolving all symlinks on the way and also
1198 normalizing it (for example turning slashes into backslashes under
1199 Windows).
1200 """
Steve Dower98eb3602016-11-09 12:58:17 -08001201 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001202 if s is None:
1203 # No symlink resolution => for consistency, raise an error if
1204 # the path doesn't exist or is forbidden
1205 self.stat()
1206 s = str(self.absolute())
1207 # Now we have no symlinks in the path, it's safe to normalize it.
1208 normed = self._flavour.pathmod.normpath(s)
1209 obj = self._from_parts((normed,), init=False)
1210 obj._init(template=self)
1211 return obj
1212
1213 def stat(self):
1214 """
1215 Return the result of the stat() system call on this path, like
1216 os.stat() does.
1217 """
1218 return self._accessor.stat(self)
1219
1220 def owner(self):
1221 """
1222 Return the login name of the file owner.
1223 """
Barney Gale22386bb2020-04-17 17:41:07 +01001224 return self._accessor.owner(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001225
1226 def group(self):
1227 """
1228 Return the group name of the file gid.
1229 """
Barney Gale22386bb2020-04-17 17:41:07 +01001230 return self._accessor.group(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001231
Antoine Pitrou31119e42013-11-22 17:38:12 +01001232 def open(self, mode='r', buffering=-1, encoding=None,
1233 errors=None, newline=None):
1234 """
1235 Open the file pointed by this path and return a file object, as
1236 the built-in open() function does.
1237 """
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001238 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001239 opener=self._opener)
1240
Georg Brandlea683982014-10-01 19:12:33 +02001241 def read_bytes(self):
1242 """
1243 Open the file in bytes mode, read it, and close the file.
1244 """
1245 with self.open(mode='rb') as f:
1246 return f.read()
1247
1248 def read_text(self, encoding=None, errors=None):
1249 """
1250 Open the file in text mode, read it, and close the file.
1251 """
1252 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1253 return f.read()
1254
1255 def write_bytes(self, data):
1256 """
1257 Open the file in bytes mode, write to it, and close the file.
1258 """
1259 # type-check for the buffer interface before truncating the file
1260 view = memoryview(data)
1261 with self.open(mode='wb') as f:
1262 return f.write(view)
1263
1264 def write_text(self, data, encoding=None, errors=None):
1265 """
1266 Open the file in text mode, write to it, and close the file.
1267 """
1268 if not isinstance(data, str):
1269 raise TypeError('data must be str, not %s' %
1270 data.__class__.__name__)
1271 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1272 return f.write(data)
1273
Girtsa01ba332019-10-23 14:18:40 -07001274 def readlink(self):
1275 """
1276 Return the path to which the symbolic link points.
1277 """
1278 path = self._accessor.readlink(self)
1279 obj = self._from_parts((path,), init=False)
1280 obj._init(template=self)
1281 return obj
1282
Antoine Pitrou31119e42013-11-22 17:38:12 +01001283 def touch(self, mode=0o666, exist_ok=True):
1284 """
1285 Create this file with the given access mode, if it doesn't exist.
1286 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001287 if exist_ok:
1288 # First try to bump modification time
1289 # Implementation note: GNU touch uses the UTIME_NOW option of
1290 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001291 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001292 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001293 except OSError:
1294 # Avoid exception chaining
1295 pass
1296 else:
1297 return
1298 flags = os.O_CREAT | os.O_WRONLY
1299 if not exist_ok:
1300 flags |= os.O_EXCL
1301 fd = self._raw_open(flags, mode)
1302 os.close(fd)
1303
Barry Warsaw7c549c42014-08-05 11:28:12 -04001304 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001305 """
1306 Create a new directory at this given path.
1307 """
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001308 try:
1309 self._accessor.mkdir(self, mode)
1310 except FileNotFoundError:
1311 if not parents or self.parent == self:
1312 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001313 self.parent.mkdir(parents=True, exist_ok=True)
1314 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001315 except OSError:
1316 # Cannot rely on checking for EEXIST, since the operating system
1317 # could give priority to other errors like EACCES or EROFS
1318 if not exist_ok or not self.is_dir():
1319 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001320
1321 def chmod(self, mode):
1322 """
1323 Change the permissions of the path, like os.chmod().
1324 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001325 self._accessor.chmod(self, mode)
1326
1327 def lchmod(self, mode):
1328 """
1329 Like chmod(), except if the path points to a symlink, the symlink's
1330 permissions are changed, rather than its target's.
1331 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001332 self._accessor.lchmod(self, mode)
1333
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001334 def unlink(self, missing_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001335 """
1336 Remove this file or link.
1337 If the path is a directory, use rmdir() instead.
1338 """
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001339 try:
1340 self._accessor.unlink(self)
1341 except FileNotFoundError:
1342 if not missing_ok:
1343 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001344
1345 def rmdir(self):
1346 """
1347 Remove this directory. The directory must be empty.
1348 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001349 self._accessor.rmdir(self)
1350
1351 def lstat(self):
1352 """
1353 Like stat(), except if the path points to a symlink, the symlink's
1354 status information is returned, rather than its target's.
1355 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001356 return self._accessor.lstat(self)
1357
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -04001358 def link_to(self, target):
1359 """
1360 Create a hard link pointing to a path named target.
1361 """
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -04001362 self._accessor.link_to(self, target)
1363
Antoine Pitrou31119e42013-11-22 17:38:12 +01001364 def rename(self, target):
1365 """
hui shang088a09a2019-09-11 21:26:49 +08001366 Rename this path to the given path,
1367 and return a new Path instance pointing to the given path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001368 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001369 self._accessor.rename(self, target)
hui shang088a09a2019-09-11 21:26:49 +08001370 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001371
1372 def replace(self, target):
1373 """
1374 Rename this path to the given path, clobbering the existing
hui shang088a09a2019-09-11 21:26:49 +08001375 destination if it exists, and return a new Path instance
1376 pointing to the given path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001377 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001378 self._accessor.replace(self, target)
hui shang088a09a2019-09-11 21:26:49 +08001379 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001380
1381 def symlink_to(self, target, target_is_directory=False):
1382 """
1383 Make this path a symlink pointing to the given path.
1384 Note the order of arguments (self, target) is the reverse of os.symlink's.
1385 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001386 self._accessor.symlink(target, self, target_is_directory)
1387
1388 # Convenience functions for querying the stat results
1389
1390 def exists(self):
1391 """
1392 Whether this path exists.
1393 """
1394 try:
1395 self.stat()
1396 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001397 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001398 raise
1399 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001400 except ValueError:
1401 # Non-encodable path
1402 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001403 return True
1404
1405 def is_dir(self):
1406 """
1407 Whether this path is a directory.
1408 """
1409 try:
1410 return S_ISDIR(self.stat().st_mode)
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 # Path doesn't exist or is a broken symlink
1415 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1416 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001417 except ValueError:
1418 # Non-encodable path
1419 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001420
1421 def is_file(self):
1422 """
1423 Whether this path is a regular file (also True for symlinks pointing
1424 to regular files).
1425 """
1426 try:
1427 return S_ISREG(self.stat().st_mode)
1428 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001429 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001430 raise
1431 # Path doesn't exist or is a broken symlink
1432 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1433 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001434 except ValueError:
1435 # Non-encodable path
1436 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001437
Cooper Lees173ff4a2017-08-01 15:35:45 -07001438 def is_mount(self):
1439 """
1440 Check if this path is a POSIX mount point
1441 """
1442 # Need to exist and be a dir
1443 if not self.exists() or not self.is_dir():
1444 return False
1445
Cooper Lees173ff4a2017-08-01 15:35:45 -07001446 try:
Barney Galec746c4f2020-04-17 18:42:06 +01001447 parent_dev = self.parent.stat().st_dev
Cooper Lees173ff4a2017-08-01 15:35:45 -07001448 except OSError:
1449 return False
1450
1451 dev = self.stat().st_dev
1452 if dev != parent_dev:
1453 return True
1454 ino = self.stat().st_ino
Barney Galec746c4f2020-04-17 18:42:06 +01001455 parent_ino = self.parent.stat().st_ino
Cooper Lees173ff4a2017-08-01 15:35:45 -07001456 return ino == parent_ino
1457
Antoine Pitrou31119e42013-11-22 17:38:12 +01001458 def is_symlink(self):
1459 """
1460 Whether this path is a symbolic link.
1461 """
1462 try:
1463 return S_ISLNK(self.lstat().st_mode)
1464 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001465 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001466 raise
1467 # Path doesn't exist
1468 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001469 except ValueError:
1470 # Non-encodable path
1471 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001472
1473 def is_block_device(self):
1474 """
1475 Whether this path is a block device.
1476 """
1477 try:
1478 return S_ISBLK(self.stat().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 or is a broken symlink
1483 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1484 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001485 except ValueError:
1486 # Non-encodable path
1487 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001488
1489 def is_char_device(self):
1490 """
1491 Whether this path is a character device.
1492 """
1493 try:
1494 return S_ISCHR(self.stat().st_mode)
1495 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001496 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001497 raise
1498 # Path doesn't exist or is a broken symlink
1499 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1500 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001501 except ValueError:
1502 # Non-encodable path
1503 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001504
1505 def is_fifo(self):
1506 """
1507 Whether this path is a FIFO.
1508 """
1509 try:
1510 return S_ISFIFO(self.stat().st_mode)
1511 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001512 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001513 raise
1514 # Path doesn't exist or is a broken symlink
1515 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1516 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001517 except ValueError:
1518 # Non-encodable path
1519 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001520
1521 def is_socket(self):
1522 """
1523 Whether this path is a socket.
1524 """
1525 try:
1526 return S_ISSOCK(self.stat().st_mode)
1527 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001528 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001529 raise
1530 # Path doesn't exist or is a broken symlink
1531 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1532 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001533 except ValueError:
1534 # Non-encodable path
1535 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001536
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001537 def expanduser(self):
1538 """ Return a new path with expanded ~ and ~user constructs
1539 (as returned by os.path.expanduser)
1540 """
1541 if (not (self._drv or self._root) and
1542 self._parts and self._parts[0][:1] == '~'):
1543 homedir = self._flavour.gethomedir(self._parts[0][1:])
1544 return self._from_parts([homedir] + self._parts[1:])
1545
1546 return self
1547
Antoine Pitrou31119e42013-11-22 17:38:12 +01001548
1549class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001550 """Path subclass for non-Windows systems.
1551
1552 On a POSIX system, instantiating a Path should return this object.
1553 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001554 __slots__ = ()
1555
1556class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001557 """Path subclass for Windows systems.
1558
1559 On a Windows system, instantiating a Path should return this object.
1560 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001561 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001562
Cooper Lees173ff4a2017-08-01 15:35:45 -07001563 def is_mount(self):
1564 raise NotImplementedError("Path.is_mount() is unsupported on this system")