blob: a5f3313902e1b88529e14b2182d37db6f4e7ae57 [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
450
451_normal_accessor = _NormalAccessor()
452
453
454#
455# Globbing helpers
456#
457
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300458def _make_selector(pattern_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100459 pat = pattern_parts[0]
460 child_parts = pattern_parts[1:]
461 if pat == '**':
462 cls = _RecursiveWildcardSelector
463 elif '**' in pat:
464 raise ValueError("Invalid pattern: '**' can only be an entire path component")
465 elif _is_wildcard_pattern(pat):
466 cls = _WildcardSelector
467 else:
468 cls = _PreciseSelector
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300469 return cls(pat, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100470
471if hasattr(functools, "lru_cache"):
472 _make_selector = functools.lru_cache()(_make_selector)
473
474
475class _Selector:
476 """A selector matches a specific glob pattern part against the children
477 of a given path."""
478
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300479 def __init__(self, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100480 self.child_parts = child_parts
481 if child_parts:
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300482 self.successor = _make_selector(child_parts, flavour)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300483 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100484 else:
485 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300486 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100487
488 def select_from(self, parent_path):
489 """Iterate over all child paths of `parent_path` matched by this
490 selector. This can contain parent_path itself."""
491 path_cls = type(parent_path)
492 is_dir = path_cls.is_dir
493 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300494 scandir = parent_path._accessor.scandir
495 if not is_dir(parent_path):
496 return iter([])
497 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100498
499
500class _TerminatingSelector:
501
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300502 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100503 yield parent_path
504
505
506class _PreciseSelector(_Selector):
507
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300508 def __init__(self, name, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100509 self.name = name
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300510 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100511
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300512 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800513 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800514 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300515 if (is_dir if self.dironly else exists)(path):
516 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800517 yield p
518 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100519 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100520
521
522class _WildcardSelector(_Selector):
523
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300524 def __init__(self, pat, child_parts, flavour):
525 self.match = flavour.compile_pattern(pat)
526 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100527
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300528 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800529 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300530 entries = list(scandir(parent_path))
531 for entry in entries:
Jörg Stucked5c120f2019-05-21 19:44:40 +0200532 entry_is_dir = False
533 try:
534 entry_is_dir = entry.is_dir()
535 except OSError as e:
536 if not _ignore_error(e):
537 raise
538 if not self.dironly or entry_is_dir:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300539 name = entry.name
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300540 if self.match(name):
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300541 path = parent_path._make_child_relpath(name)
542 for p in self.successor._select_from(path, is_dir, exists, scandir):
543 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800544 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100545 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800546
Antoine Pitrou31119e42013-11-22 17:38:12 +0100547
548
549class _RecursiveWildcardSelector(_Selector):
550
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +0300551 def __init__(self, pat, child_parts, flavour):
552 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100553
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300554 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100555 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800556 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300557 entries = list(scandir(parent_path))
558 for entry in entries:
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200559 entry_is_dir = False
560 try:
561 entry_is_dir = entry.is_dir()
562 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -0800563 if not _ignore_error(e):
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200564 raise
565 if entry_is_dir and not entry.is_symlink():
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300566 path = parent_path._make_child_relpath(entry.name)
567 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800568 yield p
569 except PermissionError:
570 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100571
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300572 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800573 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300574 yielded = set()
575 try:
576 successor_select = self.successor._select_from
577 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
578 for p in successor_select(starting_point, is_dir, exists, scandir):
579 if p not in yielded:
580 yield p
581 yielded.add(p)
582 finally:
583 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800584 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100585 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100586
587
588#
589# Public API
590#
591
592class _PathParents(Sequence):
593 """This object provides sequence-like access to the logical ancestors
594 of a path. Don't try to construct it yourself."""
595 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
596
597 def __init__(self, path):
598 # We don't store the instance to avoid reference cycles
599 self._pathcls = type(path)
600 self._drv = path._drv
601 self._root = path._root
602 self._parts = path._parts
603
604 def __len__(self):
605 if self._drv or self._root:
606 return len(self._parts) - 1
607 else:
608 return len(self._parts)
609
610 def __getitem__(self, idx):
611 if idx < 0 or idx >= len(self):
612 raise IndexError(idx)
613 return self._pathcls._from_parsed_parts(self._drv, self._root,
614 self._parts[:-idx - 1])
615
616 def __repr__(self):
617 return "<{}.parents>".format(self._pathcls.__name__)
618
619
620class PurePath(object):
chasondfa015c2018-02-19 08:36:32 +0900621 """Base class for manipulating paths without I/O.
622
623 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100624 don't imply any actual filesystem I/O. Depending on your system,
625 instantiating a PurePath will return either a PurePosixPath or a
626 PureWindowsPath object. You can also instantiate either of these classes
627 directly, regardless of your system.
628 """
629 __slots__ = (
630 '_drv', '_root', '_parts',
631 '_str', '_hash', '_pparts', '_cached_cparts',
632 )
633
634 def __new__(cls, *args):
635 """Construct a PurePath from one or several strings and or existing
636 PurePath objects. The strings and path objects are combined so as
637 to yield a canonicalized path, which is incorporated into the
638 new PurePath object.
639 """
640 if cls is PurePath:
641 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
642 return cls._from_parts(args)
643
644 def __reduce__(self):
645 # Using the parts tuple helps share interned path parts
646 # when pickling related paths.
647 return (self.__class__, tuple(self._parts))
648
649 @classmethod
650 def _parse_args(cls, args):
651 # This is useful when you don't want to create an instance, just
652 # canonicalize some constructor arguments.
653 parts = []
654 for a in args:
655 if isinstance(a, PurePath):
656 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100657 else:
Brett Cannon568be632016-06-10 12:20:49 -0700658 a = os.fspath(a)
659 if isinstance(a, str):
660 # Force-cast str subclasses to str (issue #21127)
661 parts.append(str(a))
662 else:
663 raise TypeError(
664 "argument should be a str object or an os.PathLike "
665 "object returning str, not %r"
666 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100667 return cls._flavour.parse_parts(parts)
668
669 @classmethod
670 def _from_parts(cls, args, init=True):
671 # We need to call _parse_args on the instance, so as to get the
672 # right flavour.
673 self = object.__new__(cls)
674 drv, root, parts = self._parse_args(args)
675 self._drv = drv
676 self._root = root
677 self._parts = parts
678 if init:
679 self._init()
680 return self
681
682 @classmethod
683 def _from_parsed_parts(cls, drv, root, parts, init=True):
684 self = object.__new__(cls)
685 self._drv = drv
686 self._root = root
687 self._parts = parts
688 if init:
689 self._init()
690 return self
691
692 @classmethod
693 def _format_parsed_parts(cls, drv, root, parts):
694 if drv or root:
695 return drv + root + cls._flavour.join(parts[1:])
696 else:
697 return cls._flavour.join(parts)
698
699 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000700 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100701 pass
702
703 def _make_child(self, args):
704 drv, root, parts = self._parse_args(args)
705 drv, root, parts = self._flavour.join_parsed_parts(
706 self._drv, self._root, self._parts, drv, root, parts)
707 return self._from_parsed_parts(drv, root, parts)
708
709 def __str__(self):
710 """Return the string representation of the path, suitable for
711 passing to system calls."""
712 try:
713 return self._str
714 except AttributeError:
715 self._str = self._format_parsed_parts(self._drv, self._root,
716 self._parts) or '.'
717 return self._str
718
Brett Cannon568be632016-06-10 12:20:49 -0700719 def __fspath__(self):
720 return str(self)
721
Antoine Pitrou31119e42013-11-22 17:38:12 +0100722 def as_posix(self):
723 """Return the string representation of the path with forward (/)
724 slashes."""
725 f = self._flavour
726 return str(self).replace(f.sep, '/')
727
728 def __bytes__(self):
729 """Return the bytes representation of the path. This is only
730 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200731 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100732
733 def __repr__(self):
734 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
735
736 def as_uri(self):
737 """Return the path as a 'file' URI."""
738 if not self.is_absolute():
739 raise ValueError("relative path can't be expressed as a file URI")
740 return self._flavour.make_uri(self)
741
742 @property
743 def _cparts(self):
744 # Cached casefolded parts, for hashing and comparison
745 try:
746 return self._cached_cparts
747 except AttributeError:
748 self._cached_cparts = self._flavour.casefold_parts(self._parts)
749 return self._cached_cparts
750
751 def __eq__(self, other):
752 if not isinstance(other, PurePath):
753 return NotImplemented
754 return self._cparts == other._cparts and self._flavour is other._flavour
755
Antoine Pitrou31119e42013-11-22 17:38:12 +0100756 def __hash__(self):
757 try:
758 return self._hash
759 except AttributeError:
760 self._hash = hash(tuple(self._cparts))
761 return self._hash
762
763 def __lt__(self, other):
764 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
765 return NotImplemented
766 return self._cparts < other._cparts
767
768 def __le__(self, other):
769 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
770 return NotImplemented
771 return self._cparts <= other._cparts
772
773 def __gt__(self, other):
774 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
775 return NotImplemented
776 return self._cparts > other._cparts
777
778 def __ge__(self, other):
779 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
780 return NotImplemented
781 return self._cparts >= other._cparts
782
Batuhan Taşkaya526606b2019-12-08 23:31:15 +0300783 def __class_getitem__(cls, type):
784 return cls
785
Antoine Pitrou31119e42013-11-22 17:38:12 +0100786 drive = property(attrgetter('_drv'),
787 doc="""The drive prefix (letter or UNC path), if any.""")
788
789 root = property(attrgetter('_root'),
790 doc="""The root of the path, if any.""")
791
792 @property
793 def anchor(self):
794 """The concatenation of the drive and root, or ''."""
795 anchor = self._drv + self._root
796 return anchor
797
798 @property
799 def name(self):
800 """The final path component, if any."""
801 parts = self._parts
802 if len(parts) == (1 if (self._drv or self._root) else 0):
803 return ''
804 return parts[-1]
805
806 @property
807 def suffix(self):
Ram Rachum8d4fef42019-11-02 18:46:24 +0200808 """
809 The final component's last suffix, if any.
810
811 This includes the leading period. For example: '.txt'
812 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100813 name = self.name
814 i = name.rfind('.')
815 if 0 < i < len(name) - 1:
816 return name[i:]
817 else:
818 return ''
819
820 @property
821 def suffixes(self):
Ram Rachum8d4fef42019-11-02 18:46:24 +0200822 """
823 A list of the final component's suffixes, if any.
824
825 These include the leading periods. For example: ['.tar', '.gz']
826 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100827 name = self.name
828 if name.endswith('.'):
829 return []
830 name = name.lstrip('.')
831 return ['.' + suffix for suffix in name.split('.')[1:]]
832
833 @property
834 def stem(self):
835 """The final path component, minus its last suffix."""
836 name = self.name
837 i = name.rfind('.')
838 if 0 < i < len(name) - 1:
839 return name[:i]
840 else:
841 return name
842
843 def with_name(self, name):
844 """Return a new path with the file name changed."""
845 if not self.name:
846 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400847 drv, root, parts = self._flavour.parse_parts((name,))
848 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
849 or drv or root or len(parts) != 1):
850 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100851 return self._from_parsed_parts(self._drv, self._root,
852 self._parts[:-1] + [name])
853
854 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200855 """Return a new path with the file suffix changed. If the path
856 has no suffix, add given suffix. If the given suffix is an empty
857 string, remove the suffix from the path.
858 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400859 f = self._flavour
860 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300861 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400862 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100863 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100864 name = self.name
865 if not name:
866 raise ValueError("%r has an empty name" % (self,))
867 old_suffix = self.suffix
868 if not old_suffix:
869 name = name + suffix
870 else:
871 name = name[:-len(old_suffix)] + suffix
872 return self._from_parsed_parts(self._drv, self._root,
873 self._parts[:-1] + [name])
874
875 def relative_to(self, *other):
876 """Return the relative path to another path identified by the passed
877 arguments. If the operation is not possible (because this is not
878 a subpath of the other path), raise ValueError.
879 """
880 # For the purpose of this method, drive and root are considered
881 # separate parts, i.e.:
882 # Path('c:/').relative_to('c:') gives Path('/')
883 # Path('c:/').relative_to('/') raise ValueError
884 if not other:
885 raise TypeError("need at least one argument")
886 parts = self._parts
887 drv = self._drv
888 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100889 if root:
890 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100891 else:
892 abs_parts = parts
893 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100894 if to_root:
895 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100896 else:
897 to_abs_parts = to_parts
898 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100899 cf = self._flavour.casefold_parts
900 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100901 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
902 raise ValueError("{!r} does not start with {!r}"
903 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100904 return self._from_parsed_parts('', root if n == 1 else '',
905 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100906
Hai Shi82642a02019-08-13 14:54:02 -0500907 def is_relative_to(self, *other):
908 """Return True if the path is relative to another path or False.
909 """
910 try:
911 self.relative_to(*other)
912 return True
913 except ValueError:
914 return False
915
Antoine Pitrou31119e42013-11-22 17:38:12 +0100916 @property
917 def parts(self):
918 """An object providing sequence-like access to the
919 components in the filesystem path."""
920 # We cache the tuple to avoid building a new one each time .parts
921 # is accessed. XXX is this necessary?
922 try:
923 return self._pparts
924 except AttributeError:
925 self._pparts = tuple(self._parts)
926 return self._pparts
927
928 def joinpath(self, *args):
929 """Combine this path with one or several arguments, and return a
930 new path representing either a subpath (if all arguments are relative
931 paths) or a totally different path (if one of the arguments is
932 anchored).
933 """
934 return self._make_child(args)
935
936 def __truediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400937 try:
938 return self._make_child((key,))
939 except TypeError:
940 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100941
942 def __rtruediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400943 try:
944 return self._from_parts([key] + self._parts)
945 except TypeError:
946 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100947
948 @property
949 def parent(self):
950 """The logical parent of the path."""
951 drv = self._drv
952 root = self._root
953 parts = self._parts
954 if len(parts) == 1 and (drv or root):
955 return self
956 return self._from_parsed_parts(drv, root, parts[:-1])
957
958 @property
959 def parents(self):
960 """A sequence of this path's logical parents."""
961 return _PathParents(self)
962
963 def is_absolute(self):
964 """True if the path is absolute (has both a root and, if applicable,
965 a drive)."""
966 if not self._root:
967 return False
968 return not self._flavour.has_drv or bool(self._drv)
969
970 def is_reserved(self):
971 """Return True if the path contains one of the special names reserved
972 by the system, if any."""
973 return self._flavour.is_reserved(self._parts)
974
975 def match(self, path_pattern):
976 """
977 Return True if this path matches the given pattern.
978 """
979 cf = self._flavour.casefold
980 path_pattern = cf(path_pattern)
981 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
982 if not pat_parts:
983 raise ValueError("empty pattern")
984 if drv and drv != cf(self._drv):
985 return False
986 if root and root != cf(self._root):
987 return False
988 parts = self._cparts
989 if drv or root:
990 if len(pat_parts) != len(parts):
991 return False
992 pat_parts = pat_parts[1:]
993 elif len(pat_parts) > len(parts):
994 return False
995 for part, pat in zip(reversed(parts), reversed(pat_parts)):
996 if not fnmatch.fnmatchcase(part, pat):
997 return False
998 return True
999
Brett Cannon568be632016-06-10 12:20:49 -07001000# Can't subclass os.PathLike from PurePath and keep the constructor
1001# optimizations in PurePath._parse_args().
1002os.PathLike.register(PurePath)
1003
Antoine Pitrou31119e42013-11-22 17:38:12 +01001004
1005class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001006 """PurePath subclass for non-Windows systems.
1007
1008 On a POSIX system, instantiating a PurePath should return this object.
1009 However, you can also instantiate it directly on any system.
1010 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001011 _flavour = _posix_flavour
1012 __slots__ = ()
1013
1014
1015class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001016 """PurePath subclass for Windows systems.
1017
1018 On a Windows system, instantiating a PurePath should return this object.
1019 However, you can also instantiate it directly on any system.
1020 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001021 _flavour = _windows_flavour
1022 __slots__ = ()
1023
1024
1025# Filesystem-accessing classes
1026
1027
1028class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001029 """PurePath subclass that can make system calls.
1030
1031 Path represents a filesystem path but unlike PurePath, also offers
1032 methods to do system calls on path objects. Depending on your system,
1033 instantiating a Path will return either a PosixPath or a WindowsPath
1034 object. You can also instantiate a PosixPath or WindowsPath directly,
1035 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
1036 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001037 __slots__ = (
1038 '_accessor',
1039 '_closed',
1040 )
1041
1042 def __new__(cls, *args, **kwargs):
1043 if cls is Path:
1044 cls = WindowsPath if os.name == 'nt' else PosixPath
1045 self = cls._from_parts(args, init=False)
1046 if not self._flavour.is_supported:
1047 raise NotImplementedError("cannot instantiate %r on your system"
1048 % (cls.__name__,))
1049 self._init()
1050 return self
1051
1052 def _init(self,
1053 # Private non-constructor arguments
1054 template=None,
1055 ):
1056 self._closed = False
1057 if template is not None:
1058 self._accessor = template._accessor
1059 else:
1060 self._accessor = _normal_accessor
1061
1062 def _make_child_relpath(self, part):
1063 # This is an optimization used for dir walking. `part` must be
1064 # a single part relative to this path.
1065 parts = self._parts + [part]
1066 return self._from_parsed_parts(self._drv, self._root, parts)
1067
1068 def __enter__(self):
1069 if self._closed:
1070 self._raise_closed()
1071 return self
1072
1073 def __exit__(self, t, v, tb):
1074 self._closed = True
1075
1076 def _raise_closed(self):
1077 raise ValueError("I/O operation on closed path")
1078
1079 def _opener(self, name, flags, mode=0o666):
1080 # A stub for the opener argument to built-in open()
1081 return self._accessor.open(self, flags, mode)
1082
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001083 def _raw_open(self, flags, mode=0o777):
1084 """
1085 Open the file pointed by this path and return a file descriptor,
1086 as os.open() does.
1087 """
1088 if self._closed:
1089 self._raise_closed()
1090 return self._accessor.open(self, flags, mode)
1091
Antoine Pitrou31119e42013-11-22 17:38:12 +01001092 # Public API
1093
1094 @classmethod
1095 def cwd(cls):
1096 """Return a new path pointing to the current working directory
1097 (as returned by os.getcwd()).
1098 """
1099 return cls(os.getcwd())
1100
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001101 @classmethod
1102 def home(cls):
1103 """Return a new path pointing to the user's home directory (as
1104 returned by os.path.expanduser('~')).
1105 """
1106 return cls(cls()._flavour.gethomedir(None))
1107
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001108 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001109 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001110 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001111 """
1112 st = self.stat()
1113 try:
1114 other_st = other_path.stat()
1115 except AttributeError:
1116 other_st = os.stat(other_path)
1117 return os.path.samestat(st, other_st)
1118
Antoine Pitrou31119e42013-11-22 17:38:12 +01001119 def iterdir(self):
1120 """Iterate over the files in this directory. Does not yield any
1121 result for the special paths '.' and '..'.
1122 """
1123 if self._closed:
1124 self._raise_closed()
1125 for name in self._accessor.listdir(self):
1126 if name in {'.', '..'}:
1127 # Yielding a path object for these makes little sense
1128 continue
1129 yield self._make_child_relpath(name)
1130 if self._closed:
1131 self._raise_closed()
1132
1133 def glob(self, pattern):
1134 """Iterate over this subtree and yield all existing files (of any
Eivind Teig537b6ca2019-02-11 11:47:09 +01001135 kind, including directories) matching the given relative pattern.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001136 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001137 if not pattern:
1138 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001139 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1140 if drv or root:
1141 raise NotImplementedError("Non-relative patterns are unsupported")
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +03001142 selector = _make_selector(tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001143 for p in selector.select_from(self):
1144 yield p
1145
1146 def rglob(self, pattern):
1147 """Recursively yield all existing files (of any kind, including
Eivind Teig537b6ca2019-02-11 11:47:09 +01001148 directories) matching the given relative pattern, anywhere in
1149 this subtree.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001150 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001151 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1152 if drv or root:
1153 raise NotImplementedError("Non-relative patterns are unsupported")
Serhiy Storchaka10ecbad2019-10-21 20:37:15 +03001154 selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001155 for p in selector.select_from(self):
1156 yield p
1157
1158 def absolute(self):
1159 """Return an absolute version of this path. This function works
1160 even if the path doesn't point to anything.
1161
1162 No normalization is done, i.e. all '.' and '..' will be kept along.
1163 Use resolve() to get the canonical path to a file.
1164 """
1165 # XXX untested yet!
1166 if self._closed:
1167 self._raise_closed()
1168 if self.is_absolute():
1169 return self
1170 # FIXME this must defer to the specific flavour (and, under Windows,
1171 # use nt._getfullpathname())
1172 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1173 obj._init(template=self)
1174 return obj
1175
Steve Dower98eb3602016-11-09 12:58:17 -08001176 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001177 """
1178 Make the path absolute, resolving all symlinks on the way and also
1179 normalizing it (for example turning slashes into backslashes under
1180 Windows).
1181 """
1182 if self._closed:
1183 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001184 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001185 if s is None:
1186 # No symlink resolution => for consistency, raise an error if
1187 # the path doesn't exist or is forbidden
1188 self.stat()
1189 s = str(self.absolute())
1190 # Now we have no symlinks in the path, it's safe to normalize it.
1191 normed = self._flavour.pathmod.normpath(s)
1192 obj = self._from_parts((normed,), init=False)
1193 obj._init(template=self)
1194 return obj
1195
1196 def stat(self):
1197 """
1198 Return the result of the stat() system call on this path, like
1199 os.stat() does.
1200 """
1201 return self._accessor.stat(self)
1202
1203 def owner(self):
1204 """
1205 Return the login name of the file owner.
1206 """
1207 import pwd
1208 return pwd.getpwuid(self.stat().st_uid).pw_name
1209
1210 def group(self):
1211 """
1212 Return the group name of the file gid.
1213 """
1214 import grp
1215 return grp.getgrgid(self.stat().st_gid).gr_name
1216
Antoine Pitrou31119e42013-11-22 17:38:12 +01001217 def open(self, mode='r', buffering=-1, encoding=None,
1218 errors=None, newline=None):
1219 """
1220 Open the file pointed by this path and return a file object, as
1221 the built-in open() function does.
1222 """
1223 if self._closed:
1224 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001225 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001226 opener=self._opener)
1227
Georg Brandlea683982014-10-01 19:12:33 +02001228 def read_bytes(self):
1229 """
1230 Open the file in bytes mode, read it, and close the file.
1231 """
1232 with self.open(mode='rb') as f:
1233 return f.read()
1234
1235 def read_text(self, encoding=None, errors=None):
1236 """
1237 Open the file in text mode, read it, and close the file.
1238 """
1239 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1240 return f.read()
1241
1242 def write_bytes(self, data):
1243 """
1244 Open the file in bytes mode, write to it, and close the file.
1245 """
1246 # type-check for the buffer interface before truncating the file
1247 view = memoryview(data)
1248 with self.open(mode='wb') as f:
1249 return f.write(view)
1250
1251 def write_text(self, data, encoding=None, errors=None):
1252 """
1253 Open the file in text mode, write to it, and close the file.
1254 """
1255 if not isinstance(data, str):
1256 raise TypeError('data must be str, not %s' %
1257 data.__class__.__name__)
1258 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1259 return f.write(data)
1260
Girtsa01ba332019-10-23 14:18:40 -07001261 def readlink(self):
1262 """
1263 Return the path to which the symbolic link points.
1264 """
1265 path = self._accessor.readlink(self)
1266 obj = self._from_parts((path,), init=False)
1267 obj._init(template=self)
1268 return obj
1269
Antoine Pitrou31119e42013-11-22 17:38:12 +01001270 def touch(self, mode=0o666, exist_ok=True):
1271 """
1272 Create this file with the given access mode, if it doesn't exist.
1273 """
1274 if self._closed:
1275 self._raise_closed()
1276 if exist_ok:
1277 # First try to bump modification time
1278 # Implementation note: GNU touch uses the UTIME_NOW option of
1279 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001280 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001281 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001282 except OSError:
1283 # Avoid exception chaining
1284 pass
1285 else:
1286 return
1287 flags = os.O_CREAT | os.O_WRONLY
1288 if not exist_ok:
1289 flags |= os.O_EXCL
1290 fd = self._raw_open(flags, mode)
1291 os.close(fd)
1292
Barry Warsaw7c549c42014-08-05 11:28:12 -04001293 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001294 """
1295 Create a new directory at this given path.
1296 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001297 if self._closed:
1298 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001299 try:
1300 self._accessor.mkdir(self, mode)
1301 except FileNotFoundError:
1302 if not parents or self.parent == self:
1303 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001304 self.parent.mkdir(parents=True, exist_ok=True)
1305 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001306 except OSError:
1307 # Cannot rely on checking for EEXIST, since the operating system
1308 # could give priority to other errors like EACCES or EROFS
1309 if not exist_ok or not self.is_dir():
1310 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001311
1312 def chmod(self, mode):
1313 """
1314 Change the permissions of the path, like os.chmod().
1315 """
1316 if self._closed:
1317 self._raise_closed()
1318 self._accessor.chmod(self, mode)
1319
1320 def lchmod(self, mode):
1321 """
1322 Like chmod(), except if the path points to a symlink, the symlink's
1323 permissions are changed, rather than its target's.
1324 """
1325 if self._closed:
1326 self._raise_closed()
1327 self._accessor.lchmod(self, mode)
1328
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001329 def unlink(self, missing_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001330 """
1331 Remove this file or link.
1332 If the path is a directory, use rmdir() instead.
1333 """
1334 if self._closed:
1335 self._raise_closed()
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001336 try:
1337 self._accessor.unlink(self)
1338 except FileNotFoundError:
1339 if not missing_ok:
1340 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001341
1342 def rmdir(self):
1343 """
1344 Remove this directory. The directory must be empty.
1345 """
1346 if self._closed:
1347 self._raise_closed()
1348 self._accessor.rmdir(self)
1349
1350 def lstat(self):
1351 """
1352 Like stat(), except if the path points to a symlink, the symlink's
1353 status information is returned, rather than its target's.
1354 """
1355 if self._closed:
1356 self._raise_closed()
1357 return self._accessor.lstat(self)
1358
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -04001359 def link_to(self, target):
1360 """
1361 Create a hard link pointing to a path named target.
1362 """
1363 if self._closed:
1364 self._raise_closed()
1365 self._accessor.link_to(self, target)
1366
Antoine Pitrou31119e42013-11-22 17:38:12 +01001367 def rename(self, target):
1368 """
hui shang088a09a2019-09-11 21:26:49 +08001369 Rename this path to the given path,
1370 and return a new Path instance pointing to the given path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001371 """
1372 if self._closed:
1373 self._raise_closed()
1374 self._accessor.rename(self, target)
hui shang088a09a2019-09-11 21:26:49 +08001375 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001376
1377 def replace(self, target):
1378 """
1379 Rename this path to the given path, clobbering the existing
hui shang088a09a2019-09-11 21:26:49 +08001380 destination if it exists, and return a new Path instance
1381 pointing to the given path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001382 """
1383 if self._closed:
1384 self._raise_closed()
1385 self._accessor.replace(self, target)
hui shang088a09a2019-09-11 21:26:49 +08001386 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001387
1388 def symlink_to(self, target, target_is_directory=False):
1389 """
1390 Make this path a symlink pointing to the given path.
1391 Note the order of arguments (self, target) is the reverse of os.symlink's.
1392 """
1393 if self._closed:
1394 self._raise_closed()
1395 self._accessor.symlink(target, self, target_is_directory)
1396
1397 # Convenience functions for querying the stat results
1398
1399 def exists(self):
1400 """
1401 Whether this path exists.
1402 """
1403 try:
1404 self.stat()
1405 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001406 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001407 raise
1408 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001409 except ValueError:
1410 # Non-encodable path
1411 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001412 return True
1413
1414 def is_dir(self):
1415 """
1416 Whether this path is a directory.
1417 """
1418 try:
1419 return S_ISDIR(self.stat().st_mode)
1420 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001421 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001422 raise
1423 # Path doesn't exist or is a broken symlink
1424 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1425 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001426 except ValueError:
1427 # Non-encodable path
1428 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001429
1430 def is_file(self):
1431 """
1432 Whether this path is a regular file (also True for symlinks pointing
1433 to regular files).
1434 """
1435 try:
1436 return S_ISREG(self.stat().st_mode)
1437 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001438 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001439 raise
1440 # Path doesn't exist or is a broken symlink
1441 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1442 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001443 except ValueError:
1444 # Non-encodable path
1445 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001446
Cooper Lees173ff4a2017-08-01 15:35:45 -07001447 def is_mount(self):
1448 """
1449 Check if this path is a POSIX mount point
1450 """
1451 # Need to exist and be a dir
1452 if not self.exists() or not self.is_dir():
1453 return False
1454
1455 parent = Path(self.parent)
1456 try:
1457 parent_dev = parent.stat().st_dev
1458 except OSError:
1459 return False
1460
1461 dev = self.stat().st_dev
1462 if dev != parent_dev:
1463 return True
1464 ino = self.stat().st_ino
1465 parent_ino = parent.stat().st_ino
1466 return ino == parent_ino
1467
Antoine Pitrou31119e42013-11-22 17:38:12 +01001468 def is_symlink(self):
1469 """
1470 Whether this path is a symbolic link.
1471 """
1472 try:
1473 return S_ISLNK(self.lstat().st_mode)
1474 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001475 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001476 raise
1477 # Path doesn't exist
1478 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001479 except ValueError:
1480 # Non-encodable path
1481 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001482
1483 def is_block_device(self):
1484 """
1485 Whether this path is a block device.
1486 """
1487 try:
1488 return S_ISBLK(self.stat().st_mode)
1489 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001490 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001491 raise
1492 # Path doesn't exist or is a broken symlink
1493 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1494 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001495 except ValueError:
1496 # Non-encodable path
1497 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001498
1499 def is_char_device(self):
1500 """
1501 Whether this path is a character device.
1502 """
1503 try:
1504 return S_ISCHR(self.stat().st_mode)
1505 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001506 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001507 raise
1508 # Path doesn't exist or is a broken symlink
1509 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1510 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001511 except ValueError:
1512 # Non-encodable path
1513 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001514
1515 def is_fifo(self):
1516 """
1517 Whether this path is a FIFO.
1518 """
1519 try:
1520 return S_ISFIFO(self.stat().st_mode)
1521 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001522 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001523 raise
1524 # Path doesn't exist or is a broken symlink
1525 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1526 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001527 except ValueError:
1528 # Non-encodable path
1529 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001530
1531 def is_socket(self):
1532 """
1533 Whether this path is a socket.
1534 """
1535 try:
1536 return S_ISSOCK(self.stat().st_mode)
1537 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001538 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001539 raise
1540 # Path doesn't exist or is a broken symlink
1541 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1542 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001543 except ValueError:
1544 # Non-encodable path
1545 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001546
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001547 def expanduser(self):
1548 """ Return a new path with expanded ~ and ~user constructs
1549 (as returned by os.path.expanduser)
1550 """
1551 if (not (self._drv or self._root) and
1552 self._parts and self._parts[0][:1] == '~'):
1553 homedir = self._flavour.gethomedir(self._parts[0][1:])
1554 return self._from_parts([homedir] + self._parts[1:])
1555
1556 return self
1557
Antoine Pitrou31119e42013-11-22 17:38:12 +01001558
1559class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001560 """Path subclass for non-Windows systems.
1561
1562 On a POSIX system, instantiating a Path should return this object.
1563 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001564 __slots__ = ()
1565
1566class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001567 """Path subclass for Windows systems.
1568
1569 On a Windows system, instantiating a Path should return this object.
1570 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001571 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001572
1573 def owner(self):
1574 raise NotImplementedError("Path.owner() is unsupported on this system")
1575
1576 def group(self):
1577 raise NotImplementedError("Path.group() is unsupported on this system")
Cooper Lees173ff4a2017-08-01 15:35:45 -07001578
1579 def is_mount(self):
1580 raise NotImplementedError("Path.is_mount() is unsupported on this system")