blob: d70fde0ea3b452ce0aa8b41c3bfe8fbc95a32997 [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):
256 if 'HOME' in os.environ:
257 userhome = os.environ['HOME']
258 elif 'USERPROFILE' in os.environ:
259 userhome = os.environ['USERPROFILE']
260 elif 'HOMEPATH' in os.environ:
Antoine Pitrou5d4e27e2014-12-30 22:09:42 +0100261 try:
262 drv = os.environ['HOMEDRIVE']
263 except KeyError:
264 drv = ''
265 userhome = drv + os.environ['HOMEPATH']
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100266 else:
267 raise RuntimeError("Can't determine home directory")
268
269 if username:
270 # Try to guess user home directory. By default all users
271 # directories are located in the same place and are named by
272 # corresponding usernames. If current user home directory points
273 # to nonstandard place, this guess is likely wrong.
274 if os.environ['USERNAME'] != username:
275 drv, root, parts = self.parse_parts((userhome,))
276 if parts[-1] != os.environ['USERNAME']:
277 raise RuntimeError("Can't determine home directory "
278 "for %r" % username)
279 parts[-1] = username
280 if drv or root:
281 userhome = drv + root + self.join(parts[1:])
282 else:
283 userhome = self.join(parts)
284 return userhome
Antoine Pitrou31119e42013-11-22 17:38:12 +0100285
286class _PosixFlavour(_Flavour):
287 sep = '/'
288 altsep = ''
289 has_drv = False
290 pathmod = posixpath
291
292 is_supported = (os.name != 'nt')
293
294 def splitroot(self, part, sep=sep):
295 if part and part[0] == sep:
296 stripped_part = part.lstrip(sep)
297 # According to POSIX path resolution:
298 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
299 # "A pathname that begins with two successive slashes may be
300 # interpreted in an implementation-defined manner, although more
301 # than two leading slashes shall be treated as a single slash".
302 if len(part) - len(stripped_part) == 2:
303 return '', sep * 2, stripped_part
304 else:
305 return '', sep, stripped_part
306 else:
307 return '', '', part
308
309 def casefold(self, s):
310 return s
311
312 def casefold_parts(self, parts):
313 return parts
314
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300315 def compile_pattern(self, pattern):
316 return re.compile(fnmatch.translate(pattern)).fullmatch
317
Steve Dower98eb3602016-11-09 12:58:17 -0800318 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100319 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100320 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100321 seen = {}
322 def _resolve(path, rest):
323 if rest.startswith(sep):
324 path = ''
325
326 for name in rest.split(sep):
327 if not name or name == '.':
328 # current dir
329 continue
330 if name == '..':
331 # parent dir
332 path, _, _ = path.rpartition(sep)
333 continue
334 newpath = path + sep + name
335 if newpath in seen:
336 # Already seen this path
337 path = seen[newpath]
338 if path is not None:
339 # use cached value
340 continue
341 # The symlink is not resolved, so we must have a symlink loop.
342 raise RuntimeError("Symlink loop from %r" % newpath)
343 # Resolve the symbolic link
344 try:
345 target = accessor.readlink(newpath)
346 except OSError as e:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200347 if e.errno != EINVAL and strict:
348 raise
349 # Not a symlink, or non-strict mode. We just leave the path
350 # untouched.
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100351 path = newpath
352 else:
353 seen[newpath] = None # not resolved symlink
354 path = _resolve(path, target)
355 seen[newpath] = path # resolved symlink
356
357 return path
358 # NOTE: according to POSIX, getcwd() cannot contain path components
359 # which are symlinks.
360 base = '' if path.is_absolute() else os.getcwd()
361 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100362
363 def is_reserved(self, parts):
364 return False
365
366 def make_uri(self, path):
367 # We represent the path using the local filesystem encoding,
368 # for portability to other applications.
369 bpath = bytes(path)
370 return 'file://' + urlquote_from_bytes(bpath)
371
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100372 def gethomedir(self, username):
373 if not username:
374 try:
375 return os.environ['HOME']
376 except KeyError:
377 import pwd
378 return pwd.getpwuid(os.getuid()).pw_dir
379 else:
380 import pwd
381 try:
382 return pwd.getpwnam(username).pw_dir
383 except KeyError:
384 raise RuntimeError("Can't determine home directory "
385 "for %r" % username)
386
Antoine Pitrou31119e42013-11-22 17:38:12 +0100387
388_windows_flavour = _WindowsFlavour()
389_posix_flavour = _PosixFlavour()
390
391
392class _Accessor:
393 """An accessor implements a particular (system-specific or not) way of
394 accessing paths on the filesystem."""
395
396
397class _NormalAccessor(_Accessor):
398
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200399 stat = os.stat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100400
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200401 lstat = os.lstat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100402
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200403 open = os.open
Antoine Pitrou31119e42013-11-22 17:38:12 +0100404
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200405 listdir = os.listdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100406
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200407 scandir = os.scandir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100408
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200409 chmod = os.chmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100410
411 if hasattr(os, "lchmod"):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200412 lchmod = os.lchmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100413 else:
414 def lchmod(self, pathobj, mode):
415 raise NotImplementedError("lchmod() not available on this system")
416
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200417 mkdir = os.mkdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100418
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200419 unlink = os.unlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100420
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -0400421 link_to = os.link
422
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200423 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100424
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200425 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100426
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200427 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100428
429 if nt:
430 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200431 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100432 else:
433 def symlink(a, b, target_is_directory):
434 raise NotImplementedError("symlink() not available on this system")
435 else:
436 # Under POSIX, os.symlink() takes two args
437 @staticmethod
438 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200439 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100440
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200441 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100442
443 # Helper for resolve()
444 def readlink(self, path):
445 return os.readlink(path)
446
447
448_normal_accessor = _NormalAccessor()
449
450
451#
452# Globbing helpers
453#
454
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300455def _make_selector(pattern_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100456 pat = pattern_parts[0]
457 child_parts = pattern_parts[1:]
458 if pat == '**':
459 cls = _RecursiveWildcardSelector
460 elif '**' in pat:
461 raise ValueError("Invalid pattern: '**' can only be an entire path component")
462 elif _is_wildcard_pattern(pat):
463 cls = _WildcardSelector
464 else:
465 cls = _PreciseSelector
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300466 return cls(pat, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100467
468if hasattr(functools, "lru_cache"):
469 _make_selector = functools.lru_cache()(_make_selector)
470
471
472class _Selector:
473 """A selector matches a specific glob pattern part against the children
474 of a given path."""
475
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300476 def __init__(self, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100477 self.child_parts = child_parts
478 if child_parts:
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300479 self.successor = _make_selector(child_parts, flavour)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300480 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100481 else:
482 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300483 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100484
485 def select_from(self, parent_path):
486 """Iterate over all child paths of `parent_path` matched by this
487 selector. This can contain parent_path itself."""
488 path_cls = type(parent_path)
489 is_dir = path_cls.is_dir
490 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300491 scandir = parent_path._accessor.scandir
492 if not is_dir(parent_path):
493 return iter([])
494 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100495
496
497class _TerminatingSelector:
498
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300499 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100500 yield parent_path
501
502
503class _PreciseSelector(_Selector):
504
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300505 def __init__(self, name, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100506 self.name = name
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300507 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100508
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300509 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800510 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800511 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300512 if (is_dir if self.dironly else exists)(path):
513 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800514 yield p
515 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100516 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100517
518
519class _WildcardSelector(_Selector):
520
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300521 def __init__(self, pat, child_parts, flavour):
522 self.match = flavour.compile_pattern(pat)
523 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100524
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300525 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800526 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300527 entries = list(scandir(parent_path))
528 for entry in entries:
Jörg Stucked5c120f2019-05-21 19:44:40 +0200529 entry_is_dir = False
530 try:
531 entry_is_dir = entry.is_dir()
532 except OSError as e:
533 if not _ignore_error(e):
534 raise
535 if not self.dironly or entry_is_dir:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300536 name = entry.name
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300537 if self.match(name):
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300538 path = parent_path._make_child_relpath(name)
539 for p in self.successor._select_from(path, is_dir, exists, scandir):
540 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800541 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100542 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800543
Antoine Pitrou31119e42013-11-22 17:38:12 +0100544
545
546class _RecursiveWildcardSelector(_Selector):
547
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300548 def __init__(self, pat, child_parts, flavour):
549 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100550
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300551 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100552 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800553 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300554 entries = list(scandir(parent_path))
555 for entry in entries:
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200556 entry_is_dir = False
557 try:
558 entry_is_dir = entry.is_dir()
559 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -0800560 if not _ignore_error(e):
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200561 raise
562 if entry_is_dir and not entry.is_symlink():
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300563 path = parent_path._make_child_relpath(entry.name)
564 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800565 yield p
566 except PermissionError:
567 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100568
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300569 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800570 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300571 yielded = set()
572 try:
573 successor_select = self.successor._select_from
574 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
575 for p in successor_select(starting_point, is_dir, exists, scandir):
576 if p not in yielded:
577 yield p
578 yielded.add(p)
579 finally:
580 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800581 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100582 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100583
584
585#
586# Public API
587#
588
589class _PathParents(Sequence):
590 """This object provides sequence-like access to the logical ancestors
591 of a path. Don't try to construct it yourself."""
592 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
593
594 def __init__(self, path):
595 # We don't store the instance to avoid reference cycles
596 self._pathcls = type(path)
597 self._drv = path._drv
598 self._root = path._root
599 self._parts = path._parts
600
601 def __len__(self):
602 if self._drv or self._root:
603 return len(self._parts) - 1
604 else:
605 return len(self._parts)
606
607 def __getitem__(self, idx):
608 if idx < 0 or idx >= len(self):
609 raise IndexError(idx)
610 return self._pathcls._from_parsed_parts(self._drv, self._root,
611 self._parts[:-idx - 1])
612
613 def __repr__(self):
614 return "<{}.parents>".format(self._pathcls.__name__)
615
616
617class PurePath(object):
chasondfa015c2018-02-19 08:36:32 +0900618 """Base class for manipulating paths without I/O.
619
620 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100621 don't imply any actual filesystem I/O. Depending on your system,
622 instantiating a PurePath will return either a PurePosixPath or a
623 PureWindowsPath object. You can also instantiate either of these classes
624 directly, regardless of your system.
625 """
626 __slots__ = (
627 '_drv', '_root', '_parts',
628 '_str', '_hash', '_pparts', '_cached_cparts',
629 )
630
631 def __new__(cls, *args):
632 """Construct a PurePath from one or several strings and or existing
633 PurePath objects. The strings and path objects are combined so as
634 to yield a canonicalized path, which is incorporated into the
635 new PurePath object.
636 """
637 if cls is PurePath:
638 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
639 return cls._from_parts(args)
640
641 def __reduce__(self):
642 # Using the parts tuple helps share interned path parts
643 # when pickling related paths.
644 return (self.__class__, tuple(self._parts))
645
646 @classmethod
647 def _parse_args(cls, args):
648 # This is useful when you don't want to create an instance, just
649 # canonicalize some constructor arguments.
650 parts = []
651 for a in args:
652 if isinstance(a, PurePath):
653 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100654 else:
Brett Cannon568be632016-06-10 12:20:49 -0700655 a = os.fspath(a)
656 if isinstance(a, str):
657 # Force-cast str subclasses to str (issue #21127)
658 parts.append(str(a))
659 else:
660 raise TypeError(
661 "argument should be a str object or an os.PathLike "
662 "object returning str, not %r"
663 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100664 return cls._flavour.parse_parts(parts)
665
666 @classmethod
667 def _from_parts(cls, args, init=True):
668 # We need to call _parse_args on the instance, so as to get the
669 # right flavour.
670 self = object.__new__(cls)
671 drv, root, parts = self._parse_args(args)
672 self._drv = drv
673 self._root = root
674 self._parts = parts
675 if init:
676 self._init()
677 return self
678
679 @classmethod
680 def _from_parsed_parts(cls, drv, root, parts, init=True):
681 self = object.__new__(cls)
682 self._drv = drv
683 self._root = root
684 self._parts = parts
685 if init:
686 self._init()
687 return self
688
689 @classmethod
690 def _format_parsed_parts(cls, drv, root, parts):
691 if drv or root:
692 return drv + root + cls._flavour.join(parts[1:])
693 else:
694 return cls._flavour.join(parts)
695
696 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000697 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100698 pass
699
700 def _make_child(self, args):
701 drv, root, parts = self._parse_args(args)
702 drv, root, parts = self._flavour.join_parsed_parts(
703 self._drv, self._root, self._parts, drv, root, parts)
704 return self._from_parsed_parts(drv, root, parts)
705
706 def __str__(self):
707 """Return the string representation of the path, suitable for
708 passing to system calls."""
709 try:
710 return self._str
711 except AttributeError:
712 self._str = self._format_parsed_parts(self._drv, self._root,
713 self._parts) or '.'
714 return self._str
715
Brett Cannon568be632016-06-10 12:20:49 -0700716 def __fspath__(self):
717 return str(self)
718
Antoine Pitrou31119e42013-11-22 17:38:12 +0100719 def as_posix(self):
720 """Return the string representation of the path with forward (/)
721 slashes."""
722 f = self._flavour
723 return str(self).replace(f.sep, '/')
724
725 def __bytes__(self):
726 """Return the bytes representation of the path. This is only
727 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200728 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100729
730 def __repr__(self):
731 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
732
733 def as_uri(self):
734 """Return the path as a 'file' URI."""
735 if not self.is_absolute():
736 raise ValueError("relative path can't be expressed as a file URI")
737 return self._flavour.make_uri(self)
738
739 @property
740 def _cparts(self):
741 # Cached casefolded parts, for hashing and comparison
742 try:
743 return self._cached_cparts
744 except AttributeError:
745 self._cached_cparts = self._flavour.casefold_parts(self._parts)
746 return self._cached_cparts
747
748 def __eq__(self, other):
749 if not isinstance(other, PurePath):
750 return NotImplemented
751 return self._cparts == other._cparts and self._flavour is other._flavour
752
Antoine Pitrou31119e42013-11-22 17:38:12 +0100753 def __hash__(self):
754 try:
755 return self._hash
756 except AttributeError:
757 self._hash = hash(tuple(self._cparts))
758 return self._hash
759
760 def __lt__(self, other):
761 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
762 return NotImplemented
763 return self._cparts < other._cparts
764
765 def __le__(self, other):
766 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
767 return NotImplemented
768 return self._cparts <= other._cparts
769
770 def __gt__(self, other):
771 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
772 return NotImplemented
773 return self._cparts > other._cparts
774
775 def __ge__(self, other):
776 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
777 return NotImplemented
778 return self._cparts >= other._cparts
779
780 drive = property(attrgetter('_drv'),
781 doc="""The drive prefix (letter or UNC path), if any.""")
782
783 root = property(attrgetter('_root'),
784 doc="""The root of the path, if any.""")
785
786 @property
787 def anchor(self):
788 """The concatenation of the drive and root, or ''."""
789 anchor = self._drv + self._root
790 return anchor
791
792 @property
793 def name(self):
794 """The final path component, if any."""
795 parts = self._parts
796 if len(parts) == (1 if (self._drv or self._root) else 0):
797 return ''
798 return parts[-1]
799
800 @property
801 def suffix(self):
Ram Rachum8d4fef42019-11-02 18:46:24 +0200802 """
803 The final component's last suffix, if any.
804
805 This includes the leading period. For example: '.txt'
806 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100807 name = self.name
808 i = name.rfind('.')
809 if 0 < i < len(name) - 1:
810 return name[i:]
811 else:
812 return ''
813
814 @property
815 def suffixes(self):
Ram Rachum8d4fef42019-11-02 18:46:24 +0200816 """
817 A list of the final component's suffixes, if any.
818
819 These include the leading periods. For example: ['.tar', '.gz']
820 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100821 name = self.name
822 if name.endswith('.'):
823 return []
824 name = name.lstrip('.')
825 return ['.' + suffix for suffix in name.split('.')[1:]]
826
827 @property
828 def stem(self):
829 """The final path component, minus its last suffix."""
830 name = self.name
831 i = name.rfind('.')
832 if 0 < i < len(name) - 1:
833 return name[:i]
834 else:
835 return name
836
837 def with_name(self, name):
838 """Return a new path with the file name changed."""
839 if not self.name:
840 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400841 drv, root, parts = self._flavour.parse_parts((name,))
842 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
843 or drv or root or len(parts) != 1):
844 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100845 return self._from_parsed_parts(self._drv, self._root,
846 self._parts[:-1] + [name])
847
848 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200849 """Return a new path with the file suffix changed. If the path
850 has no suffix, add given suffix. If the given suffix is an empty
851 string, remove the suffix from the path.
852 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400853 f = self._flavour
854 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300855 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400856 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100857 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100858 name = self.name
859 if not name:
860 raise ValueError("%r has an empty name" % (self,))
861 old_suffix = self.suffix
862 if not old_suffix:
863 name = name + suffix
864 else:
865 name = name[:-len(old_suffix)] + suffix
866 return self._from_parsed_parts(self._drv, self._root,
867 self._parts[:-1] + [name])
868
869 def relative_to(self, *other):
870 """Return the relative path to another path identified by the passed
871 arguments. If the operation is not possible (because this is not
872 a subpath of the other path), raise ValueError.
873 """
874 # For the purpose of this method, drive and root are considered
875 # separate parts, i.e.:
876 # Path('c:/').relative_to('c:') gives Path('/')
877 # Path('c:/').relative_to('/') raise ValueError
878 if not other:
879 raise TypeError("need at least one argument")
880 parts = self._parts
881 drv = self._drv
882 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100883 if root:
884 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100885 else:
886 abs_parts = parts
887 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100888 if to_root:
889 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100890 else:
891 to_abs_parts = to_parts
892 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100893 cf = self._flavour.casefold_parts
894 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100895 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
896 raise ValueError("{!r} does not start with {!r}"
897 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100898 return self._from_parsed_parts('', root if n == 1 else '',
899 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100900
Hai Shi82642a02019-08-13 14:54:02 -0500901 def is_relative_to(self, *other):
902 """Return True if the path is relative to another path or False.
903 """
904 try:
905 self.relative_to(*other)
906 return True
907 except ValueError:
908 return False
909
Antoine Pitrou31119e42013-11-22 17:38:12 +0100910 @property
911 def parts(self):
912 """An object providing sequence-like access to the
913 components in the filesystem path."""
914 # We cache the tuple to avoid building a new one each time .parts
915 # is accessed. XXX is this necessary?
916 try:
917 return self._pparts
918 except AttributeError:
919 self._pparts = tuple(self._parts)
920 return self._pparts
921
922 def joinpath(self, *args):
923 """Combine this path with one or several arguments, and return a
924 new path representing either a subpath (if all arguments are relative
925 paths) or a totally different path (if one of the arguments is
926 anchored).
927 """
928 return self._make_child(args)
929
930 def __truediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400931 try:
932 return self._make_child((key,))
933 except TypeError:
934 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100935
936 def __rtruediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400937 try:
938 return self._from_parts([key] + self._parts)
939 except TypeError:
940 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100941
942 @property
943 def parent(self):
944 """The logical parent of the path."""
945 drv = self._drv
946 root = self._root
947 parts = self._parts
948 if len(parts) == 1 and (drv or root):
949 return self
950 return self._from_parsed_parts(drv, root, parts[:-1])
951
952 @property
953 def parents(self):
954 """A sequence of this path's logical parents."""
955 return _PathParents(self)
956
957 def is_absolute(self):
958 """True if the path is absolute (has both a root and, if applicable,
959 a drive)."""
960 if not self._root:
961 return False
962 return not self._flavour.has_drv or bool(self._drv)
963
964 def is_reserved(self):
965 """Return True if the path contains one of the special names reserved
966 by the system, if any."""
967 return self._flavour.is_reserved(self._parts)
968
969 def match(self, path_pattern):
970 """
971 Return True if this path matches the given pattern.
972 """
973 cf = self._flavour.casefold
974 path_pattern = cf(path_pattern)
975 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
976 if not pat_parts:
977 raise ValueError("empty pattern")
978 if drv and drv != cf(self._drv):
979 return False
980 if root and root != cf(self._root):
981 return False
982 parts = self._cparts
983 if drv or root:
984 if len(pat_parts) != len(parts):
985 return False
986 pat_parts = pat_parts[1:]
987 elif len(pat_parts) > len(parts):
988 return False
989 for part, pat in zip(reversed(parts), reversed(pat_parts)):
990 if not fnmatch.fnmatchcase(part, pat):
991 return False
992 return True
993
Brett Cannon568be632016-06-10 12:20:49 -0700994# Can't subclass os.PathLike from PurePath and keep the constructor
995# optimizations in PurePath._parse_args().
996os.PathLike.register(PurePath)
997
Antoine Pitrou31119e42013-11-22 17:38:12 +0100998
999class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001000 """PurePath subclass for non-Windows systems.
1001
1002 On a POSIX system, instantiating a PurePath should return this object.
1003 However, you can also instantiate it directly on any system.
1004 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001005 _flavour = _posix_flavour
1006 __slots__ = ()
1007
1008
1009class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001010 """PurePath subclass for Windows systems.
1011
1012 On a Windows system, instantiating a PurePath should return this object.
1013 However, you can also instantiate it directly on any system.
1014 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001015 _flavour = _windows_flavour
1016 __slots__ = ()
1017
1018
1019# Filesystem-accessing classes
1020
1021
1022class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001023 """PurePath subclass that can make system calls.
1024
1025 Path represents a filesystem path but unlike PurePath, also offers
1026 methods to do system calls on path objects. Depending on your system,
1027 instantiating a Path will return either a PosixPath or a WindowsPath
1028 object. You can also instantiate a PosixPath or WindowsPath directly,
1029 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
1030 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001031 __slots__ = (
1032 '_accessor',
1033 '_closed',
1034 )
1035
1036 def __new__(cls, *args, **kwargs):
1037 if cls is Path:
1038 cls = WindowsPath if os.name == 'nt' else PosixPath
1039 self = cls._from_parts(args, init=False)
1040 if not self._flavour.is_supported:
1041 raise NotImplementedError("cannot instantiate %r on your system"
1042 % (cls.__name__,))
1043 self._init()
1044 return self
1045
1046 def _init(self,
1047 # Private non-constructor arguments
1048 template=None,
1049 ):
1050 self._closed = False
1051 if template is not None:
1052 self._accessor = template._accessor
1053 else:
1054 self._accessor = _normal_accessor
1055
1056 def _make_child_relpath(self, part):
1057 # This is an optimization used for dir walking. `part` must be
1058 # a single part relative to this path.
1059 parts = self._parts + [part]
1060 return self._from_parsed_parts(self._drv, self._root, parts)
1061
1062 def __enter__(self):
1063 if self._closed:
1064 self._raise_closed()
1065 return self
1066
1067 def __exit__(self, t, v, tb):
1068 self._closed = True
1069
1070 def _raise_closed(self):
1071 raise ValueError("I/O operation on closed path")
1072
1073 def _opener(self, name, flags, mode=0o666):
1074 # A stub for the opener argument to built-in open()
1075 return self._accessor.open(self, flags, mode)
1076
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001077 def _raw_open(self, flags, mode=0o777):
1078 """
1079 Open the file pointed by this path and return a file descriptor,
1080 as os.open() does.
1081 """
1082 if self._closed:
1083 self._raise_closed()
1084 return self._accessor.open(self, flags, mode)
1085
Antoine Pitrou31119e42013-11-22 17:38:12 +01001086 # Public API
1087
1088 @classmethod
1089 def cwd(cls):
1090 """Return a new path pointing to the current working directory
1091 (as returned by os.getcwd()).
1092 """
1093 return cls(os.getcwd())
1094
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001095 @classmethod
1096 def home(cls):
1097 """Return a new path pointing to the user's home directory (as
1098 returned by os.path.expanduser('~')).
1099 """
1100 return cls(cls()._flavour.gethomedir(None))
1101
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001102 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001103 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001104 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001105 """
1106 st = self.stat()
1107 try:
1108 other_st = other_path.stat()
1109 except AttributeError:
1110 other_st = os.stat(other_path)
1111 return os.path.samestat(st, other_st)
1112
Antoine Pitrou31119e42013-11-22 17:38:12 +01001113 def iterdir(self):
1114 """Iterate over the files in this directory. Does not yield any
1115 result for the special paths '.' and '..'.
1116 """
1117 if self._closed:
1118 self._raise_closed()
1119 for name in self._accessor.listdir(self):
1120 if name in {'.', '..'}:
1121 # Yielding a path object for these makes little sense
1122 continue
1123 yield self._make_child_relpath(name)
1124 if self._closed:
1125 self._raise_closed()
1126
1127 def glob(self, pattern):
1128 """Iterate over this subtree and yield all existing files (of any
Eivind Teig537b6ca2019-02-11 11:47:09 +01001129 kind, including directories) matching the given relative pattern.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001130 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001131 if not pattern:
1132 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001133 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1134 if drv or root:
1135 raise NotImplementedError("Non-relative patterns are unsupported")
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +03001136 selector = _make_selector(tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001137 for p in selector.select_from(self):
1138 yield p
1139
1140 def rglob(self, pattern):
1141 """Recursively yield all existing files (of any kind, including
Eivind Teig537b6ca2019-02-11 11:47:09 +01001142 directories) matching the given relative pattern, anywhere in
1143 this subtree.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001144 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001145 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1146 if drv or root:
1147 raise NotImplementedError("Non-relative patterns are unsupported")
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +03001148 selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001149 for p in selector.select_from(self):
1150 yield p
1151
1152 def absolute(self):
1153 """Return an absolute version of this path. This function works
1154 even if the path doesn't point to anything.
1155
1156 No normalization is done, i.e. all '.' and '..' will be kept along.
1157 Use resolve() to get the canonical path to a file.
1158 """
1159 # XXX untested yet!
1160 if self._closed:
1161 self._raise_closed()
1162 if self.is_absolute():
1163 return self
1164 # FIXME this must defer to the specific flavour (and, under Windows,
1165 # use nt._getfullpathname())
1166 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1167 obj._init(template=self)
1168 return obj
1169
Steve Dower98eb3602016-11-09 12:58:17 -08001170 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001171 """
1172 Make the path absolute, resolving all symlinks on the way and also
1173 normalizing it (for example turning slashes into backslashes under
1174 Windows).
1175 """
1176 if self._closed:
1177 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001178 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001179 if s is None:
1180 # No symlink resolution => for consistency, raise an error if
1181 # the path doesn't exist or is forbidden
1182 self.stat()
1183 s = str(self.absolute())
1184 # Now we have no symlinks in the path, it's safe to normalize it.
1185 normed = self._flavour.pathmod.normpath(s)
1186 obj = self._from_parts((normed,), init=False)
1187 obj._init(template=self)
1188 return obj
1189
1190 def stat(self):
1191 """
1192 Return the result of the stat() system call on this path, like
1193 os.stat() does.
1194 """
1195 return self._accessor.stat(self)
1196
1197 def owner(self):
1198 """
1199 Return the login name of the file owner.
1200 """
1201 import pwd
1202 return pwd.getpwuid(self.stat().st_uid).pw_name
1203
1204 def group(self):
1205 """
1206 Return the group name of the file gid.
1207 """
1208 import grp
1209 return grp.getgrgid(self.stat().st_gid).gr_name
1210
Antoine Pitrou31119e42013-11-22 17:38:12 +01001211 def open(self, mode='r', buffering=-1, encoding=None,
1212 errors=None, newline=None):
1213 """
1214 Open the file pointed by this path and return a file object, as
1215 the built-in open() function does.
1216 """
1217 if self._closed:
1218 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001219 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001220 opener=self._opener)
1221
Georg Brandlea683982014-10-01 19:12:33 +02001222 def read_bytes(self):
1223 """
1224 Open the file in bytes mode, read it, and close the file.
1225 """
1226 with self.open(mode='rb') as f:
1227 return f.read()
1228
1229 def read_text(self, encoding=None, errors=None):
1230 """
1231 Open the file in text mode, read it, and close the file.
1232 """
1233 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1234 return f.read()
1235
1236 def write_bytes(self, data):
1237 """
1238 Open the file in bytes mode, write to it, and close the file.
1239 """
1240 # type-check for the buffer interface before truncating the file
1241 view = memoryview(data)
1242 with self.open(mode='wb') as f:
1243 return f.write(view)
1244
1245 def write_text(self, data, encoding=None, errors=None):
1246 """
1247 Open the file in text mode, write to it, and close the file.
1248 """
1249 if not isinstance(data, str):
1250 raise TypeError('data must be str, not %s' %
1251 data.__class__.__name__)
1252 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1253 return f.write(data)
1254
Girtsa01ba332019-10-23 14:18:40 -07001255 def readlink(self):
1256 """
1257 Return the path to which the symbolic link points.
1258 """
1259 path = self._accessor.readlink(self)
1260 obj = self._from_parts((path,), init=False)
1261 obj._init(template=self)
1262 return obj
1263
Antoine Pitrou31119e42013-11-22 17:38:12 +01001264 def touch(self, mode=0o666, exist_ok=True):
1265 """
1266 Create this file with the given access mode, if it doesn't exist.
1267 """
1268 if self._closed:
1269 self._raise_closed()
1270 if exist_ok:
1271 # First try to bump modification time
1272 # Implementation note: GNU touch uses the UTIME_NOW option of
1273 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001274 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001275 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001276 except OSError:
1277 # Avoid exception chaining
1278 pass
1279 else:
1280 return
1281 flags = os.O_CREAT | os.O_WRONLY
1282 if not exist_ok:
1283 flags |= os.O_EXCL
1284 fd = self._raw_open(flags, mode)
1285 os.close(fd)
1286
Barry Warsaw7c549c42014-08-05 11:28:12 -04001287 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001288 """
1289 Create a new directory at this given path.
1290 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001291 if self._closed:
1292 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001293 try:
1294 self._accessor.mkdir(self, mode)
1295 except FileNotFoundError:
1296 if not parents or self.parent == self:
1297 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001298 self.parent.mkdir(parents=True, exist_ok=True)
1299 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001300 except OSError:
1301 # Cannot rely on checking for EEXIST, since the operating system
1302 # could give priority to other errors like EACCES or EROFS
1303 if not exist_ok or not self.is_dir():
1304 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001305
1306 def chmod(self, mode):
1307 """
1308 Change the permissions of the path, like os.chmod().
1309 """
1310 if self._closed:
1311 self._raise_closed()
1312 self._accessor.chmod(self, mode)
1313
1314 def lchmod(self, mode):
1315 """
1316 Like chmod(), except if the path points to a symlink, the symlink's
1317 permissions are changed, rather than its target's.
1318 """
1319 if self._closed:
1320 self._raise_closed()
1321 self._accessor.lchmod(self, mode)
1322
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001323 def unlink(self, missing_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001324 """
1325 Remove this file or link.
1326 If the path is a directory, use rmdir() instead.
1327 """
1328 if self._closed:
1329 self._raise_closed()
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001330 try:
1331 self._accessor.unlink(self)
1332 except FileNotFoundError:
1333 if not missing_ok:
1334 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001335
1336 def rmdir(self):
1337 """
1338 Remove this directory. The directory must be empty.
1339 """
1340 if self._closed:
1341 self._raise_closed()
1342 self._accessor.rmdir(self)
1343
1344 def lstat(self):
1345 """
1346 Like stat(), except if the path points to a symlink, the symlink's
1347 status information is returned, rather than its target's.
1348 """
1349 if self._closed:
1350 self._raise_closed()
1351 return self._accessor.lstat(self)
1352
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -04001353 def link_to(self, target):
1354 """
1355 Create a hard link pointing to a path named target.
1356 """
1357 if self._closed:
1358 self._raise_closed()
1359 self._accessor.link_to(self, target)
1360
Antoine Pitrou31119e42013-11-22 17:38:12 +01001361 def rename(self, target):
1362 """
hui shang088a09a2019-09-11 21:26:49 +08001363 Rename this path to the given path,
1364 and return a new Path instance pointing to the given path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001365 """
1366 if self._closed:
1367 self._raise_closed()
1368 self._accessor.rename(self, target)
hui shang088a09a2019-09-11 21:26:49 +08001369 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001370
1371 def replace(self, target):
1372 """
1373 Rename this path to the given path, clobbering the existing
hui shang088a09a2019-09-11 21:26:49 +08001374 destination if it exists, and return a new Path instance
1375 pointing to the given path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001376 """
1377 if self._closed:
1378 self._raise_closed()
1379 self._accessor.replace(self, target)
hui shang088a09a2019-09-11 21:26:49 +08001380 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001381
1382 def symlink_to(self, target, target_is_directory=False):
1383 """
1384 Make this path a symlink pointing to the given path.
1385 Note the order of arguments (self, target) is the reverse of os.symlink's.
1386 """
1387 if self._closed:
1388 self._raise_closed()
1389 self._accessor.symlink(target, self, target_is_directory)
1390
1391 # Convenience functions for querying the stat results
1392
1393 def exists(self):
1394 """
1395 Whether this path exists.
1396 """
1397 try:
1398 self.stat()
1399 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001400 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001401 raise
1402 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001403 except ValueError:
1404 # Non-encodable path
1405 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001406 return True
1407
1408 def is_dir(self):
1409 """
1410 Whether this path is a directory.
1411 """
1412 try:
1413 return S_ISDIR(self.stat().st_mode)
1414 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001415 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001416 raise
1417 # Path doesn't exist or is a broken symlink
1418 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1419 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001420 except ValueError:
1421 # Non-encodable path
1422 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001423
1424 def is_file(self):
1425 """
1426 Whether this path is a regular file (also True for symlinks pointing
1427 to regular files).
1428 """
1429 try:
1430 return S_ISREG(self.stat().st_mode)
1431 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001432 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001433 raise
1434 # Path doesn't exist or is a broken symlink
1435 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1436 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001437 except ValueError:
1438 # Non-encodable path
1439 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001440
Cooper Lees173ff4a2017-08-01 15:35:45 -07001441 def is_mount(self):
1442 """
1443 Check if this path is a POSIX mount point
1444 """
1445 # Need to exist and be a dir
1446 if not self.exists() or not self.is_dir():
1447 return False
1448
1449 parent = Path(self.parent)
1450 try:
1451 parent_dev = parent.stat().st_dev
1452 except OSError:
1453 return False
1454
1455 dev = self.stat().st_dev
1456 if dev != parent_dev:
1457 return True
1458 ino = self.stat().st_ino
1459 parent_ino = parent.stat().st_ino
1460 return ino == parent_ino
1461
Antoine Pitrou31119e42013-11-22 17:38:12 +01001462 def is_symlink(self):
1463 """
1464 Whether this path is a symbolic link.
1465 """
1466 try:
1467 return S_ISLNK(self.lstat().st_mode)
1468 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001469 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001470 raise
1471 # Path doesn't exist
1472 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001473 except ValueError:
1474 # Non-encodable path
1475 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001476
1477 def is_block_device(self):
1478 """
1479 Whether this path is a block device.
1480 """
1481 try:
1482 return S_ISBLK(self.stat().st_mode)
1483 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001484 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001485 raise
1486 # Path doesn't exist or is a broken symlink
1487 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1488 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001489 except ValueError:
1490 # Non-encodable path
1491 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001492
1493 def is_char_device(self):
1494 """
1495 Whether this path is a character device.
1496 """
1497 try:
1498 return S_ISCHR(self.stat().st_mode)
1499 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001500 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001501 raise
1502 # Path doesn't exist or is a broken symlink
1503 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1504 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001505 except ValueError:
1506 # Non-encodable path
1507 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001508
1509 def is_fifo(self):
1510 """
1511 Whether this path is a FIFO.
1512 """
1513 try:
1514 return S_ISFIFO(self.stat().st_mode)
1515 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001516 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001517 raise
1518 # Path doesn't exist or is a broken symlink
1519 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1520 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001521 except ValueError:
1522 # Non-encodable path
1523 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001524
1525 def is_socket(self):
1526 """
1527 Whether this path is a socket.
1528 """
1529 try:
1530 return S_ISSOCK(self.stat().st_mode)
1531 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001532 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001533 raise
1534 # Path doesn't exist or is a broken symlink
1535 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1536 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001537 except ValueError:
1538 # Non-encodable path
1539 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001540
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001541 def expanduser(self):
1542 """ Return a new path with expanded ~ and ~user constructs
1543 (as returned by os.path.expanduser)
1544 """
1545 if (not (self._drv or self._root) and
1546 self._parts and self._parts[0][:1] == '~'):
1547 homedir = self._flavour.gethomedir(self._parts[0][1:])
1548 return self._from_parts([homedir] + self._parts[1:])
1549
1550 return self
1551
Antoine Pitrou31119e42013-11-22 17:38:12 +01001552
1553class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001554 """Path subclass for non-Windows systems.
1555
1556 On a POSIX system, instantiating a Path should return this object.
1557 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001558 __slots__ = ()
1559
1560class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001561 """Path subclass for Windows systems.
1562
1563 On a Windows system, instantiating a Path should return this object.
1564 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001565 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001566
1567 def owner(self):
1568 raise NotImplementedError("Path.owner() is unsupported on this system")
1569
1570 def group(self):
1571 raise NotImplementedError("Path.group() is unsupported on this system")
Cooper Lees173ff4a2017-08-01 15:35:45 -07001572
1573 def is_mount(self):
1574 raise NotImplementedError("Path.is_mount() is unsupported on this system")