blob: 8ed3c883c60067dfe7af85c3edb9586009c153b0 [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
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700190 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
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700315 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
Miss Islington (bot)8d0f3692019-12-16 04:42:20 -0800421 if hasattr(os, "link"):
422 link_to = os.link
423 else:
424 @staticmethod
425 def link_to(self, target):
426 raise NotImplementedError("os.link() not available on this system")
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -0400427
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200428 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100429
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200430 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100431
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200432 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100433
434 if nt:
435 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200436 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100437 else:
438 def symlink(a, b, target_is_directory):
439 raise NotImplementedError("symlink() not available on this system")
440 else:
441 # Under POSIX, os.symlink() takes two args
442 @staticmethod
443 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200444 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100445
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200446 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100447
448 # Helper for resolve()
449 def readlink(self, path):
450 return os.readlink(path)
451
452
453_normal_accessor = _NormalAccessor()
454
455
456#
457# Globbing helpers
458#
459
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700460def _make_selector(pattern_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100461 pat = pattern_parts[0]
462 child_parts = pattern_parts[1:]
463 if pat == '**':
464 cls = _RecursiveWildcardSelector
465 elif '**' in pat:
466 raise ValueError("Invalid pattern: '**' can only be an entire path component")
467 elif _is_wildcard_pattern(pat):
468 cls = _WildcardSelector
469 else:
470 cls = _PreciseSelector
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700471 return cls(pat, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100472
473if hasattr(functools, "lru_cache"):
474 _make_selector = functools.lru_cache()(_make_selector)
475
476
477class _Selector:
478 """A selector matches a specific glob pattern part against the children
479 of a given path."""
480
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700481 def __init__(self, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100482 self.child_parts = child_parts
483 if child_parts:
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700484 self.successor = _make_selector(child_parts, flavour)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300485 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100486 else:
487 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300488 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100489
490 def select_from(self, parent_path):
491 """Iterate over all child paths of `parent_path` matched by this
492 selector. This can contain parent_path itself."""
493 path_cls = type(parent_path)
494 is_dir = path_cls.is_dir
495 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300496 scandir = parent_path._accessor.scandir
497 if not is_dir(parent_path):
498 return iter([])
499 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100500
501
502class _TerminatingSelector:
503
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300504 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100505 yield parent_path
506
507
508class _PreciseSelector(_Selector):
509
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700510 def __init__(self, name, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100511 self.name = name
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700512 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100513
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300514 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800515 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800516 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300517 if (is_dir if self.dironly else exists)(path):
518 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800519 yield p
520 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100521 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100522
523
524class _WildcardSelector(_Selector):
525
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700526 def __init__(self, pat, child_parts, flavour):
527 self.match = flavour.compile_pattern(pat)
528 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100529
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300530 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800531 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300532 entries = list(scandir(parent_path))
533 for entry in entries:
Jörg Stucked5c120f2019-05-21 19:44:40 +0200534 entry_is_dir = False
535 try:
536 entry_is_dir = entry.is_dir()
537 except OSError as e:
538 if not _ignore_error(e):
539 raise
540 if not self.dironly or entry_is_dir:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300541 name = entry.name
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700542 if self.match(name):
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300543 path = parent_path._make_child_relpath(name)
544 for p in self.successor._select_from(path, is_dir, exists, scandir):
545 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800546 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100547 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800548
Antoine Pitrou31119e42013-11-22 17:38:12 +0100549
550
551class _RecursiveWildcardSelector(_Selector):
552
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700553 def __init__(self, pat, child_parts, flavour):
554 _Selector.__init__(self, child_parts, flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100555
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300556 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100557 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800558 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300559 entries = list(scandir(parent_path))
560 for entry in entries:
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200561 entry_is_dir = False
562 try:
563 entry_is_dir = entry.is_dir()
564 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -0800565 if not _ignore_error(e):
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200566 raise
567 if entry_is_dir and not entry.is_symlink():
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300568 path = parent_path._make_child_relpath(entry.name)
569 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800570 yield p
571 except PermissionError:
572 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100573
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300574 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800575 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300576 yielded = set()
577 try:
578 successor_select = self.successor._select_from
579 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
580 for p in successor_select(starting_point, is_dir, exists, scandir):
581 if p not in yielded:
582 yield p
583 yielded.add(p)
584 finally:
585 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800586 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100587 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100588
589
590#
591# Public API
592#
593
594class _PathParents(Sequence):
595 """This object provides sequence-like access to the logical ancestors
596 of a path. Don't try to construct it yourself."""
597 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
598
599 def __init__(self, path):
600 # We don't store the instance to avoid reference cycles
601 self._pathcls = type(path)
602 self._drv = path._drv
603 self._root = path._root
604 self._parts = path._parts
605
606 def __len__(self):
607 if self._drv or self._root:
608 return len(self._parts) - 1
609 else:
610 return len(self._parts)
611
612 def __getitem__(self, idx):
613 if idx < 0 or idx >= len(self):
614 raise IndexError(idx)
615 return self._pathcls._from_parsed_parts(self._drv, self._root,
616 self._parts[:-idx - 1])
617
618 def __repr__(self):
619 return "<{}.parents>".format(self._pathcls.__name__)
620
621
622class PurePath(object):
chasondfa015c2018-02-19 08:36:32 +0900623 """Base class for manipulating paths without I/O.
624
625 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100626 don't imply any actual filesystem I/O. Depending on your system,
627 instantiating a PurePath will return either a PurePosixPath or a
628 PureWindowsPath object. You can also instantiate either of these classes
629 directly, regardless of your system.
630 """
631 __slots__ = (
632 '_drv', '_root', '_parts',
633 '_str', '_hash', '_pparts', '_cached_cparts',
634 )
635
636 def __new__(cls, *args):
637 """Construct a PurePath from one or several strings and or existing
638 PurePath objects. The strings and path objects are combined so as
639 to yield a canonicalized path, which is incorporated into the
640 new PurePath object.
641 """
642 if cls is PurePath:
643 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
644 return cls._from_parts(args)
645
646 def __reduce__(self):
647 # Using the parts tuple helps share interned path parts
648 # when pickling related paths.
649 return (self.__class__, tuple(self._parts))
650
651 @classmethod
652 def _parse_args(cls, args):
653 # This is useful when you don't want to create an instance, just
654 # canonicalize some constructor arguments.
655 parts = []
656 for a in args:
657 if isinstance(a, PurePath):
658 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100659 else:
Brett Cannon568be632016-06-10 12:20:49 -0700660 a = os.fspath(a)
661 if isinstance(a, str):
662 # Force-cast str subclasses to str (issue #21127)
663 parts.append(str(a))
664 else:
665 raise TypeError(
666 "argument should be a str object or an os.PathLike "
667 "object returning str, not %r"
668 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100669 return cls._flavour.parse_parts(parts)
670
671 @classmethod
672 def _from_parts(cls, args, init=True):
673 # We need to call _parse_args on the instance, so as to get the
674 # right flavour.
675 self = object.__new__(cls)
676 drv, root, parts = self._parse_args(args)
677 self._drv = drv
678 self._root = root
679 self._parts = parts
680 if init:
681 self._init()
682 return self
683
684 @classmethod
685 def _from_parsed_parts(cls, drv, root, parts, init=True):
686 self = object.__new__(cls)
687 self._drv = drv
688 self._root = root
689 self._parts = parts
690 if init:
691 self._init()
692 return self
693
694 @classmethod
695 def _format_parsed_parts(cls, drv, root, parts):
696 if drv or root:
697 return drv + root + cls._flavour.join(parts[1:])
698 else:
699 return cls._flavour.join(parts)
700
701 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000702 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100703 pass
704
705 def _make_child(self, args):
706 drv, root, parts = self._parse_args(args)
707 drv, root, parts = self._flavour.join_parsed_parts(
708 self._drv, self._root, self._parts, drv, root, parts)
709 return self._from_parsed_parts(drv, root, parts)
710
711 def __str__(self):
712 """Return the string representation of the path, suitable for
713 passing to system calls."""
714 try:
715 return self._str
716 except AttributeError:
717 self._str = self._format_parsed_parts(self._drv, self._root,
718 self._parts) or '.'
719 return self._str
720
Brett Cannon568be632016-06-10 12:20:49 -0700721 def __fspath__(self):
722 return str(self)
723
Antoine Pitrou31119e42013-11-22 17:38:12 +0100724 def as_posix(self):
725 """Return the string representation of the path with forward (/)
726 slashes."""
727 f = self._flavour
728 return str(self).replace(f.sep, '/')
729
730 def __bytes__(self):
731 """Return the bytes representation of the path. This is only
732 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200733 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100734
735 def __repr__(self):
736 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
737
738 def as_uri(self):
739 """Return the path as a 'file' URI."""
740 if not self.is_absolute():
741 raise ValueError("relative path can't be expressed as a file URI")
742 return self._flavour.make_uri(self)
743
744 @property
745 def _cparts(self):
746 # Cached casefolded parts, for hashing and comparison
747 try:
748 return self._cached_cparts
749 except AttributeError:
750 self._cached_cparts = self._flavour.casefold_parts(self._parts)
751 return self._cached_cparts
752
753 def __eq__(self, other):
754 if not isinstance(other, PurePath):
755 return NotImplemented
756 return self._cparts == other._cparts and self._flavour is other._flavour
757
Antoine Pitrou31119e42013-11-22 17:38:12 +0100758 def __hash__(self):
759 try:
760 return self._hash
761 except AttributeError:
762 self._hash = hash(tuple(self._cparts))
763 return self._hash
764
765 def __lt__(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 __le__(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 __gt__(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 def __ge__(self, other):
781 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
782 return NotImplemented
783 return self._cparts >= other._cparts
784
785 drive = property(attrgetter('_drv'),
786 doc="""The drive prefix (letter or UNC path), if any.""")
787
788 root = property(attrgetter('_root'),
789 doc="""The root of the path, if any.""")
790
791 @property
792 def anchor(self):
793 """The concatenation of the drive and root, or ''."""
794 anchor = self._drv + self._root
795 return anchor
796
797 @property
798 def name(self):
799 """The final path component, if any."""
800 parts = self._parts
801 if len(parts) == (1 if (self._drv or self._root) else 0):
802 return ''
803 return parts[-1]
804
805 @property
806 def suffix(self):
Miss Skeleton (bot)aa1fee82019-11-02 10:04:10 -0700807 """
808 The final component's last suffix, if any.
809
810 This includes the leading period. For example: '.txt'
811 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100812 name = self.name
813 i = name.rfind('.')
814 if 0 < i < len(name) - 1:
815 return name[i:]
816 else:
817 return ''
818
819 @property
820 def suffixes(self):
Miss Skeleton (bot)aa1fee82019-11-02 10:04:10 -0700821 """
822 A list of the final component's suffixes, if any.
823
824 These include the leading periods. For example: ['.tar', '.gz']
825 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100826 name = self.name
827 if name.endswith('.'):
828 return []
829 name = name.lstrip('.')
830 return ['.' + suffix for suffix in name.split('.')[1:]]
831
832 @property
833 def stem(self):
834 """The final path component, minus its last suffix."""
835 name = self.name
836 i = name.rfind('.')
837 if 0 < i < len(name) - 1:
838 return name[:i]
839 else:
840 return name
841
842 def with_name(self, name):
843 """Return a new path with the file name changed."""
844 if not self.name:
845 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400846 drv, root, parts = self._flavour.parse_parts((name,))
847 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
848 or drv or root or len(parts) != 1):
849 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100850 return self._from_parsed_parts(self._drv, self._root,
851 self._parts[:-1] + [name])
852
853 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200854 """Return a new path with the file suffix changed. If the path
855 has no suffix, add given suffix. If the given suffix is an empty
856 string, remove the suffix from the path.
857 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400858 f = self._flavour
859 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300860 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400861 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100862 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100863 name = self.name
864 if not name:
865 raise ValueError("%r has an empty name" % (self,))
866 old_suffix = self.suffix
867 if not old_suffix:
868 name = name + suffix
869 else:
870 name = name[:-len(old_suffix)] + suffix
871 return self._from_parsed_parts(self._drv, self._root,
872 self._parts[:-1] + [name])
873
874 def relative_to(self, *other):
875 """Return the relative path to another path identified by the passed
876 arguments. If the operation is not possible (because this is not
877 a subpath of the other path), raise ValueError.
878 """
879 # For the purpose of this method, drive and root are considered
880 # separate parts, i.e.:
881 # Path('c:/').relative_to('c:') gives Path('/')
882 # Path('c:/').relative_to('/') raise ValueError
883 if not other:
884 raise TypeError("need at least one argument")
885 parts = self._parts
886 drv = self._drv
887 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100888 if root:
889 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100890 else:
891 abs_parts = parts
892 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100893 if to_root:
894 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100895 else:
896 to_abs_parts = to_parts
897 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100898 cf = self._flavour.casefold_parts
899 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100900 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
901 raise ValueError("{!r} does not start with {!r}"
902 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100903 return self._from_parsed_parts('', root if n == 1 else '',
904 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100905
906 @property
907 def parts(self):
908 """An object providing sequence-like access to the
909 components in the filesystem path."""
910 # We cache the tuple to avoid building a new one each time .parts
911 # is accessed. XXX is this necessary?
912 try:
913 return self._pparts
914 except AttributeError:
915 self._pparts = tuple(self._parts)
916 return self._pparts
917
918 def joinpath(self, *args):
919 """Combine this path with one or several arguments, and return a
920 new path representing either a subpath (if all arguments are relative
921 paths) or a totally different path (if one of the arguments is
922 anchored).
923 """
924 return self._make_child(args)
925
926 def __truediv__(self, key):
Miss Islington (bot)4adcaf82019-08-28 22:05:59 -0700927 try:
928 return self._make_child((key,))
929 except TypeError:
930 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100931
932 def __rtruediv__(self, key):
Miss Islington (bot)4adcaf82019-08-28 22:05:59 -0700933 try:
934 return self._from_parts([key] + self._parts)
935 except TypeError:
936 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100937
938 @property
939 def parent(self):
940 """The logical parent of the path."""
941 drv = self._drv
942 root = self._root
943 parts = self._parts
944 if len(parts) == 1 and (drv or root):
945 return self
946 return self._from_parsed_parts(drv, root, parts[:-1])
947
948 @property
949 def parents(self):
950 """A sequence of this path's logical parents."""
951 return _PathParents(self)
952
953 def is_absolute(self):
954 """True if the path is absolute (has both a root and, if applicable,
955 a drive)."""
956 if not self._root:
957 return False
958 return not self._flavour.has_drv or bool(self._drv)
959
960 def is_reserved(self):
961 """Return True if the path contains one of the special names reserved
962 by the system, if any."""
963 return self._flavour.is_reserved(self._parts)
964
965 def match(self, path_pattern):
966 """
967 Return True if this path matches the given pattern.
968 """
969 cf = self._flavour.casefold
970 path_pattern = cf(path_pattern)
971 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
972 if not pat_parts:
973 raise ValueError("empty pattern")
974 if drv and drv != cf(self._drv):
975 return False
976 if root and root != cf(self._root):
977 return False
978 parts = self._cparts
979 if drv or root:
980 if len(pat_parts) != len(parts):
981 return False
982 pat_parts = pat_parts[1:]
983 elif len(pat_parts) > len(parts):
984 return False
985 for part, pat in zip(reversed(parts), reversed(pat_parts)):
986 if not fnmatch.fnmatchcase(part, pat):
987 return False
988 return True
989
Brett Cannon568be632016-06-10 12:20:49 -0700990# Can't subclass os.PathLike from PurePath and keep the constructor
991# optimizations in PurePath._parse_args().
992os.PathLike.register(PurePath)
993
Antoine Pitrou31119e42013-11-22 17:38:12 +0100994
995class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900996 """PurePath subclass for non-Windows systems.
997
998 On a POSIX system, instantiating a PurePath should return this object.
999 However, you can also instantiate it directly on any system.
1000 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001001 _flavour = _posix_flavour
1002 __slots__ = ()
1003
1004
1005class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001006 """PurePath subclass for Windows systems.
1007
1008 On a Windows 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 = _windows_flavour
1012 __slots__ = ()
1013
1014
1015# Filesystem-accessing classes
1016
1017
1018class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001019 """PurePath subclass that can make system calls.
1020
1021 Path represents a filesystem path but unlike PurePath, also offers
1022 methods to do system calls on path objects. Depending on your system,
1023 instantiating a Path will return either a PosixPath or a WindowsPath
1024 object. You can also instantiate a PosixPath or WindowsPath directly,
1025 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
1026 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001027 __slots__ = (
1028 '_accessor',
1029 '_closed',
1030 )
1031
1032 def __new__(cls, *args, **kwargs):
1033 if cls is Path:
1034 cls = WindowsPath if os.name == 'nt' else PosixPath
1035 self = cls._from_parts(args, init=False)
1036 if not self._flavour.is_supported:
1037 raise NotImplementedError("cannot instantiate %r on your system"
1038 % (cls.__name__,))
1039 self._init()
1040 return self
1041
1042 def _init(self,
1043 # Private non-constructor arguments
1044 template=None,
1045 ):
1046 self._closed = False
1047 if template is not None:
1048 self._accessor = template._accessor
1049 else:
1050 self._accessor = _normal_accessor
1051
1052 def _make_child_relpath(self, part):
1053 # This is an optimization used for dir walking. `part` must be
1054 # a single part relative to this path.
1055 parts = self._parts + [part]
1056 return self._from_parsed_parts(self._drv, self._root, parts)
1057
1058 def __enter__(self):
1059 if self._closed:
1060 self._raise_closed()
1061 return self
1062
1063 def __exit__(self, t, v, tb):
1064 self._closed = True
1065
1066 def _raise_closed(self):
1067 raise ValueError("I/O operation on closed path")
1068
1069 def _opener(self, name, flags, mode=0o666):
1070 # A stub for the opener argument to built-in open()
1071 return self._accessor.open(self, flags, mode)
1072
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001073 def _raw_open(self, flags, mode=0o777):
1074 """
1075 Open the file pointed by this path and return a file descriptor,
1076 as os.open() does.
1077 """
1078 if self._closed:
1079 self._raise_closed()
1080 return self._accessor.open(self, flags, mode)
1081
Antoine Pitrou31119e42013-11-22 17:38:12 +01001082 # Public API
1083
1084 @classmethod
1085 def cwd(cls):
1086 """Return a new path pointing to the current working directory
1087 (as returned by os.getcwd()).
1088 """
1089 return cls(os.getcwd())
1090
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001091 @classmethod
1092 def home(cls):
1093 """Return a new path pointing to the user's home directory (as
1094 returned by os.path.expanduser('~')).
1095 """
1096 return cls(cls()._flavour.gethomedir(None))
1097
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001098 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001099 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001100 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001101 """
1102 st = self.stat()
1103 try:
1104 other_st = other_path.stat()
1105 except AttributeError:
1106 other_st = os.stat(other_path)
1107 return os.path.samestat(st, other_st)
1108
Antoine Pitrou31119e42013-11-22 17:38:12 +01001109 def iterdir(self):
1110 """Iterate over the files in this directory. Does not yield any
1111 result for the special paths '.' and '..'.
1112 """
1113 if self._closed:
1114 self._raise_closed()
1115 for name in self._accessor.listdir(self):
1116 if name in {'.', '..'}:
1117 # Yielding a path object for these makes little sense
1118 continue
1119 yield self._make_child_relpath(name)
1120 if self._closed:
1121 self._raise_closed()
1122
1123 def glob(self, pattern):
1124 """Iterate over this subtree and yield all existing files (of any
Eivind Teig537b6ca2019-02-11 11:47:09 +01001125 kind, including directories) matching the given relative pattern.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001126 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001127 if not pattern:
1128 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001129 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1130 if drv or root:
1131 raise NotImplementedError("Non-relative patterns are unsupported")
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -07001132 selector = _make_selector(tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001133 for p in selector.select_from(self):
1134 yield p
1135
1136 def rglob(self, pattern):
1137 """Recursively yield all existing files (of any kind, including
Eivind Teig537b6ca2019-02-11 11:47:09 +01001138 directories) matching the given relative pattern, anywhere in
1139 this subtree.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001140 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001141 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1142 if drv or root:
1143 raise NotImplementedError("Non-relative patterns are unsupported")
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -07001144 selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001145 for p in selector.select_from(self):
1146 yield p
1147
1148 def absolute(self):
1149 """Return an absolute version of this path. This function works
1150 even if the path doesn't point to anything.
1151
1152 No normalization is done, i.e. all '.' and '..' will be kept along.
1153 Use resolve() to get the canonical path to a file.
1154 """
1155 # XXX untested yet!
1156 if self._closed:
1157 self._raise_closed()
1158 if self.is_absolute():
1159 return self
1160 # FIXME this must defer to the specific flavour (and, under Windows,
1161 # use nt._getfullpathname())
1162 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1163 obj._init(template=self)
1164 return obj
1165
Steve Dower98eb3602016-11-09 12:58:17 -08001166 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001167 """
1168 Make the path absolute, resolving all symlinks on the way and also
1169 normalizing it (for example turning slashes into backslashes under
1170 Windows).
1171 """
1172 if self._closed:
1173 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001174 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001175 if s is None:
1176 # No symlink resolution => for consistency, raise an error if
1177 # the path doesn't exist or is forbidden
1178 self.stat()
1179 s = str(self.absolute())
1180 # Now we have no symlinks in the path, it's safe to normalize it.
1181 normed = self._flavour.pathmod.normpath(s)
1182 obj = self._from_parts((normed,), init=False)
1183 obj._init(template=self)
1184 return obj
1185
1186 def stat(self):
1187 """
1188 Return the result of the stat() system call on this path, like
1189 os.stat() does.
1190 """
1191 return self._accessor.stat(self)
1192
1193 def owner(self):
1194 """
1195 Return the login name of the file owner.
1196 """
1197 import pwd
1198 return pwd.getpwuid(self.stat().st_uid).pw_name
1199
1200 def group(self):
1201 """
1202 Return the group name of the file gid.
1203 """
1204 import grp
1205 return grp.getgrgid(self.stat().st_gid).gr_name
1206
Antoine Pitrou31119e42013-11-22 17:38:12 +01001207 def open(self, mode='r', buffering=-1, encoding=None,
1208 errors=None, newline=None):
1209 """
1210 Open the file pointed by this path and return a file object, as
1211 the built-in open() function does.
1212 """
1213 if self._closed:
1214 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001215 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001216 opener=self._opener)
1217
Georg Brandlea683982014-10-01 19:12:33 +02001218 def read_bytes(self):
1219 """
1220 Open the file in bytes mode, read it, and close the file.
1221 """
1222 with self.open(mode='rb') as f:
1223 return f.read()
1224
1225 def read_text(self, encoding=None, errors=None):
1226 """
1227 Open the file in text mode, read it, and close the file.
1228 """
1229 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1230 return f.read()
1231
1232 def write_bytes(self, data):
1233 """
1234 Open the file in bytes mode, write to it, and close the file.
1235 """
1236 # type-check for the buffer interface before truncating the file
1237 view = memoryview(data)
1238 with self.open(mode='wb') as f:
1239 return f.write(view)
1240
1241 def write_text(self, data, encoding=None, errors=None):
1242 """
1243 Open the file in text mode, write to it, and close the file.
1244 """
1245 if not isinstance(data, str):
1246 raise TypeError('data must be str, not %s' %
1247 data.__class__.__name__)
1248 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1249 return f.write(data)
1250
Antoine Pitrou31119e42013-11-22 17:38:12 +01001251 def touch(self, mode=0o666, exist_ok=True):
1252 """
1253 Create this file with the given access mode, if it doesn't exist.
1254 """
1255 if self._closed:
1256 self._raise_closed()
1257 if exist_ok:
1258 # First try to bump modification time
1259 # Implementation note: GNU touch uses the UTIME_NOW option of
1260 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001261 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001262 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001263 except OSError:
1264 # Avoid exception chaining
1265 pass
1266 else:
1267 return
1268 flags = os.O_CREAT | os.O_WRONLY
1269 if not exist_ok:
1270 flags |= os.O_EXCL
1271 fd = self._raw_open(flags, mode)
1272 os.close(fd)
1273
Barry Warsaw7c549c42014-08-05 11:28:12 -04001274 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001275 """
1276 Create a new directory at this given path.
1277 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001278 if self._closed:
1279 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001280 try:
1281 self._accessor.mkdir(self, mode)
1282 except FileNotFoundError:
1283 if not parents or self.parent == self:
1284 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001285 self.parent.mkdir(parents=True, exist_ok=True)
1286 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001287 except OSError:
1288 # Cannot rely on checking for EEXIST, since the operating system
1289 # could give priority to other errors like EACCES or EROFS
1290 if not exist_ok or not self.is_dir():
1291 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001292
1293 def chmod(self, mode):
1294 """
1295 Change the permissions of the path, like os.chmod().
1296 """
1297 if self._closed:
1298 self._raise_closed()
1299 self._accessor.chmod(self, mode)
1300
1301 def lchmod(self, mode):
1302 """
1303 Like chmod(), except if the path points to a symlink, the symlink's
1304 permissions are changed, rather than its target's.
1305 """
1306 if self._closed:
1307 self._raise_closed()
1308 self._accessor.lchmod(self, mode)
1309
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001310 def unlink(self, missing_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001311 """
1312 Remove this file or link.
1313 If the path is a directory, use rmdir() instead.
1314 """
1315 if self._closed:
1316 self._raise_closed()
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001317 try:
1318 self._accessor.unlink(self)
1319 except FileNotFoundError:
1320 if not missing_ok:
1321 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001322
1323 def rmdir(self):
1324 """
1325 Remove this directory. The directory must be empty.
1326 """
1327 if self._closed:
1328 self._raise_closed()
1329 self._accessor.rmdir(self)
1330
1331 def lstat(self):
1332 """
1333 Like stat(), except if the path points to a symlink, the symlink's
1334 status information is returned, rather than its target's.
1335 """
1336 if self._closed:
1337 self._raise_closed()
1338 return self._accessor.lstat(self)
1339
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -04001340 def link_to(self, target):
1341 """
1342 Create a hard link pointing to a path named target.
1343 """
1344 if self._closed:
1345 self._raise_closed()
1346 self._accessor.link_to(self, target)
1347
Antoine Pitrou31119e42013-11-22 17:38:12 +01001348 def rename(self, target):
1349 """
Miss Islington (bot)cbd7b2a2019-09-11 07:12:54 -07001350 Rename this path to the given path,
1351 and return a new Path instance pointing to the given path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001352 """
1353 if self._closed:
1354 self._raise_closed()
1355 self._accessor.rename(self, target)
Miss Islington (bot)cbd7b2a2019-09-11 07:12:54 -07001356 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001357
1358 def replace(self, target):
1359 """
1360 Rename this path to the given path, clobbering the existing
Miss Islington (bot)cbd7b2a2019-09-11 07:12:54 -07001361 destination if it exists, and return a new Path instance
1362 pointing to the given path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001363 """
1364 if self._closed:
1365 self._raise_closed()
1366 self._accessor.replace(self, target)
Miss Islington (bot)cbd7b2a2019-09-11 07:12:54 -07001367 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001368
1369 def symlink_to(self, target, target_is_directory=False):
1370 """
1371 Make this path a symlink pointing to the given path.
1372 Note the order of arguments (self, target) is the reverse of os.symlink's.
1373 """
1374 if self._closed:
1375 self._raise_closed()
1376 self._accessor.symlink(target, self, target_is_directory)
1377
1378 # Convenience functions for querying the stat results
1379
1380 def exists(self):
1381 """
1382 Whether this path exists.
1383 """
1384 try:
1385 self.stat()
1386 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001387 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001388 raise
1389 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001390 except ValueError:
1391 # Non-encodable path
1392 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001393 return True
1394
1395 def is_dir(self):
1396 """
1397 Whether this path is a directory.
1398 """
1399 try:
1400 return S_ISDIR(self.stat().st_mode)
1401 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001402 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001403 raise
1404 # Path doesn't exist or is a broken symlink
1405 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1406 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001407 except ValueError:
1408 # Non-encodable path
1409 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001410
1411 def is_file(self):
1412 """
1413 Whether this path is a regular file (also True for symlinks pointing
1414 to regular files).
1415 """
1416 try:
1417 return S_ISREG(self.stat().st_mode)
1418 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001419 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001420 raise
1421 # Path doesn't exist or is a broken symlink
1422 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1423 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001424 except ValueError:
1425 # Non-encodable path
1426 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001427
Cooper Lees173ff4a2017-08-01 15:35:45 -07001428 def is_mount(self):
1429 """
1430 Check if this path is a POSIX mount point
1431 """
1432 # Need to exist and be a dir
1433 if not self.exists() or not self.is_dir():
1434 return False
1435
1436 parent = Path(self.parent)
1437 try:
1438 parent_dev = parent.stat().st_dev
1439 except OSError:
1440 return False
1441
1442 dev = self.stat().st_dev
1443 if dev != parent_dev:
1444 return True
1445 ino = self.stat().st_ino
1446 parent_ino = parent.stat().st_ino
1447 return ino == parent_ino
1448
Antoine Pitrou31119e42013-11-22 17:38:12 +01001449 def is_symlink(self):
1450 """
1451 Whether this path is a symbolic link.
1452 """
1453 try:
1454 return S_ISLNK(self.lstat().st_mode)
1455 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001456 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001457 raise
1458 # Path doesn't exist
1459 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001460 except ValueError:
1461 # Non-encodable path
1462 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001463
1464 def is_block_device(self):
1465 """
1466 Whether this path is a block device.
1467 """
1468 try:
1469 return S_ISBLK(self.stat().st_mode)
1470 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001471 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001472 raise
1473 # Path doesn't exist or is a broken symlink
1474 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1475 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001476 except ValueError:
1477 # Non-encodable path
1478 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001479
1480 def is_char_device(self):
1481 """
1482 Whether this path is a character device.
1483 """
1484 try:
1485 return S_ISCHR(self.stat().st_mode)
1486 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001487 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001488 raise
1489 # Path doesn't exist or is a broken symlink
1490 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1491 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001492 except ValueError:
1493 # Non-encodable path
1494 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001495
1496 def is_fifo(self):
1497 """
1498 Whether this path is a FIFO.
1499 """
1500 try:
1501 return S_ISFIFO(self.stat().st_mode)
1502 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001503 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001504 raise
1505 # Path doesn't exist or is a broken symlink
1506 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1507 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001508 except ValueError:
1509 # Non-encodable path
1510 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001511
1512 def is_socket(self):
1513 """
1514 Whether this path is a socket.
1515 """
1516 try:
1517 return S_ISSOCK(self.stat().st_mode)
1518 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001519 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001520 raise
1521 # Path doesn't exist or is a broken symlink
1522 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1523 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001524 except ValueError:
1525 # Non-encodable path
1526 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001527
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001528 def expanduser(self):
1529 """ Return a new path with expanded ~ and ~user constructs
1530 (as returned by os.path.expanduser)
1531 """
1532 if (not (self._drv or self._root) and
1533 self._parts and self._parts[0][:1] == '~'):
1534 homedir = self._flavour.gethomedir(self._parts[0][1:])
1535 return self._from_parts([homedir] + self._parts[1:])
1536
1537 return self
1538
Antoine Pitrou31119e42013-11-22 17:38:12 +01001539
1540class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001541 """Path subclass for non-Windows systems.
1542
1543 On a POSIX system, instantiating a Path should return this object.
1544 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001545 __slots__ = ()
1546
1547class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001548 """Path subclass for Windows systems.
1549
1550 On a Windows system, instantiating a Path should return this object.
1551 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001552 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001553
1554 def owner(self):
1555 raise NotImplementedError("Path.owner() is unsupported on this system")
1556
1557 def group(self):
1558 raise NotImplementedError("Path.group() is unsupported on this system")
Cooper Lees173ff4a2017-08-01 15:35:45 -07001559
1560 def is_mount(self):
1561 raise NotImplementedError("Path.is_mount() is unsupported on this system")