blob: 015370a860e656f6bec1fe7c576cb81a10be0e04 [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):
Miss Islington (bot)595b5162020-01-28 01:59:43 -0800256 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
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700313 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
Miss Islington (bot)8d0f3692019-12-16 04:42:20 -0800419 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
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700458def _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
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700469 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
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700479 def __init__(self, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100480 self.child_parts = child_parts
481 if child_parts:
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700482 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
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700508 def __init__(self, name, child_parts, flavour):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100509 self.name = name
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700510 _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
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700524 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
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700540 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
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -0700551 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
783 drive = property(attrgetter('_drv'),
784 doc="""The drive prefix (letter or UNC path), if any.""")
785
786 root = property(attrgetter('_root'),
787 doc="""The root of the path, if any.""")
788
789 @property
790 def anchor(self):
791 """The concatenation of the drive and root, or ''."""
792 anchor = self._drv + self._root
793 return anchor
794
795 @property
796 def name(self):
797 """The final path component, if any."""
798 parts = self._parts
799 if len(parts) == (1 if (self._drv or self._root) else 0):
800 return ''
801 return parts[-1]
802
803 @property
804 def suffix(self):
Miss Skeleton (bot)aa1fee82019-11-02 10:04:10 -0700805 """
806 The final component's last suffix, if any.
807
808 This includes the leading period. For example: '.txt'
809 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100810 name = self.name
811 i = name.rfind('.')
812 if 0 < i < len(name) - 1:
813 return name[i:]
814 else:
815 return ''
816
817 @property
818 def suffixes(self):
Miss Skeleton (bot)aa1fee82019-11-02 10:04:10 -0700819 """
820 A list of the final component's suffixes, if any.
821
822 These include the leading periods. For example: ['.tar', '.gz']
823 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100824 name = self.name
825 if name.endswith('.'):
826 return []
827 name = name.lstrip('.')
828 return ['.' + suffix for suffix in name.split('.')[1:]]
829
830 @property
831 def stem(self):
832 """The final path component, minus its last suffix."""
833 name = self.name
834 i = name.rfind('.')
835 if 0 < i < len(name) - 1:
836 return name[:i]
837 else:
838 return name
839
840 def with_name(self, name):
841 """Return a new path with the file name changed."""
842 if not self.name:
843 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400844 drv, root, parts = self._flavour.parse_parts((name,))
845 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
846 or drv or root or len(parts) != 1):
847 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100848 return self._from_parsed_parts(self._drv, self._root,
849 self._parts[:-1] + [name])
850
851 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200852 """Return a new path with the file suffix changed. If the path
853 has no suffix, add given suffix. If the given suffix is an empty
854 string, remove the suffix from the path.
855 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400856 f = self._flavour
857 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300858 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400859 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100860 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100861 name = self.name
862 if not name:
863 raise ValueError("%r has an empty name" % (self,))
864 old_suffix = self.suffix
865 if not old_suffix:
866 name = name + suffix
867 else:
868 name = name[:-len(old_suffix)] + suffix
869 return self._from_parsed_parts(self._drv, self._root,
870 self._parts[:-1] + [name])
871
872 def relative_to(self, *other):
873 """Return the relative path to another path identified by the passed
874 arguments. If the operation is not possible (because this is not
875 a subpath of the other path), raise ValueError.
876 """
877 # For the purpose of this method, drive and root are considered
878 # separate parts, i.e.:
879 # Path('c:/').relative_to('c:') gives Path('/')
880 # Path('c:/').relative_to('/') raise ValueError
881 if not other:
882 raise TypeError("need at least one argument")
883 parts = self._parts
884 drv = self._drv
885 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100886 if root:
887 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100888 else:
889 abs_parts = parts
890 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100891 if to_root:
892 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100893 else:
894 to_abs_parts = to_parts
895 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100896 cf = self._flavour.casefold_parts
897 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100898 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
899 raise ValueError("{!r} does not start with {!r}"
900 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100901 return self._from_parsed_parts('', root if n == 1 else '',
902 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100903
904 @property
905 def parts(self):
906 """An object providing sequence-like access to the
907 components in the filesystem path."""
908 # We cache the tuple to avoid building a new one each time .parts
909 # is accessed. XXX is this necessary?
910 try:
911 return self._pparts
912 except AttributeError:
913 self._pparts = tuple(self._parts)
914 return self._pparts
915
916 def joinpath(self, *args):
917 """Combine this path with one or several arguments, and return a
918 new path representing either a subpath (if all arguments are relative
919 paths) or a totally different path (if one of the arguments is
920 anchored).
921 """
922 return self._make_child(args)
923
924 def __truediv__(self, key):
Miss Islington (bot)4adcaf82019-08-28 22:05:59 -0700925 try:
926 return self._make_child((key,))
927 except TypeError:
928 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100929
930 def __rtruediv__(self, key):
Miss Islington (bot)4adcaf82019-08-28 22:05:59 -0700931 try:
932 return self._from_parts([key] + self._parts)
933 except TypeError:
934 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100935
936 @property
937 def parent(self):
938 """The logical parent of the path."""
939 drv = self._drv
940 root = self._root
941 parts = self._parts
942 if len(parts) == 1 and (drv or root):
943 return self
944 return self._from_parsed_parts(drv, root, parts[:-1])
945
946 @property
947 def parents(self):
948 """A sequence of this path's logical parents."""
949 return _PathParents(self)
950
951 def is_absolute(self):
952 """True if the path is absolute (has both a root and, if applicable,
953 a drive)."""
954 if not self._root:
955 return False
956 return not self._flavour.has_drv or bool(self._drv)
957
958 def is_reserved(self):
959 """Return True if the path contains one of the special names reserved
960 by the system, if any."""
961 return self._flavour.is_reserved(self._parts)
962
963 def match(self, path_pattern):
964 """
965 Return True if this path matches the given pattern.
966 """
967 cf = self._flavour.casefold
968 path_pattern = cf(path_pattern)
969 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
970 if not pat_parts:
971 raise ValueError("empty pattern")
972 if drv and drv != cf(self._drv):
973 return False
974 if root and root != cf(self._root):
975 return False
976 parts = self._cparts
977 if drv or root:
978 if len(pat_parts) != len(parts):
979 return False
980 pat_parts = pat_parts[1:]
981 elif len(pat_parts) > len(parts):
982 return False
983 for part, pat in zip(reversed(parts), reversed(pat_parts)):
984 if not fnmatch.fnmatchcase(part, pat):
985 return False
986 return True
987
Brett Cannon568be632016-06-10 12:20:49 -0700988# Can't subclass os.PathLike from PurePath and keep the constructor
989# optimizations in PurePath._parse_args().
990os.PathLike.register(PurePath)
991
Antoine Pitrou31119e42013-11-22 17:38:12 +0100992
993class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900994 """PurePath subclass for non-Windows systems.
995
996 On a POSIX system, instantiating a PurePath should return this object.
997 However, you can also instantiate it directly on any system.
998 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100999 _flavour = _posix_flavour
1000 __slots__ = ()
1001
1002
1003class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001004 """PurePath subclass for Windows systems.
1005
1006 On a Windows system, instantiating a PurePath should return this object.
1007 However, you can also instantiate it directly on any system.
1008 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001009 _flavour = _windows_flavour
1010 __slots__ = ()
1011
1012
1013# Filesystem-accessing classes
1014
1015
1016class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001017 """PurePath subclass that can make system calls.
1018
1019 Path represents a filesystem path but unlike PurePath, also offers
1020 methods to do system calls on path objects. Depending on your system,
1021 instantiating a Path will return either a PosixPath or a WindowsPath
1022 object. You can also instantiate a PosixPath or WindowsPath directly,
1023 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
1024 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001025 __slots__ = (
1026 '_accessor',
1027 '_closed',
1028 )
1029
1030 def __new__(cls, *args, **kwargs):
1031 if cls is Path:
1032 cls = WindowsPath if os.name == 'nt' else PosixPath
1033 self = cls._from_parts(args, init=False)
1034 if not self._flavour.is_supported:
1035 raise NotImplementedError("cannot instantiate %r on your system"
1036 % (cls.__name__,))
1037 self._init()
1038 return self
1039
1040 def _init(self,
1041 # Private non-constructor arguments
1042 template=None,
1043 ):
1044 self._closed = False
1045 if template is not None:
1046 self._accessor = template._accessor
1047 else:
1048 self._accessor = _normal_accessor
1049
1050 def _make_child_relpath(self, part):
1051 # This is an optimization used for dir walking. `part` must be
1052 # a single part relative to this path.
1053 parts = self._parts + [part]
1054 return self._from_parsed_parts(self._drv, self._root, parts)
1055
1056 def __enter__(self):
1057 if self._closed:
1058 self._raise_closed()
1059 return self
1060
1061 def __exit__(self, t, v, tb):
1062 self._closed = True
1063
1064 def _raise_closed(self):
1065 raise ValueError("I/O operation on closed path")
1066
1067 def _opener(self, name, flags, mode=0o666):
1068 # A stub for the opener argument to built-in open()
1069 return self._accessor.open(self, flags, mode)
1070
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001071 def _raw_open(self, flags, mode=0o777):
1072 """
1073 Open the file pointed by this path and return a file descriptor,
1074 as os.open() does.
1075 """
1076 if self._closed:
1077 self._raise_closed()
1078 return self._accessor.open(self, flags, mode)
1079
Antoine Pitrou31119e42013-11-22 17:38:12 +01001080 # Public API
1081
1082 @classmethod
1083 def cwd(cls):
1084 """Return a new path pointing to the current working directory
1085 (as returned by os.getcwd()).
1086 """
1087 return cls(os.getcwd())
1088
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001089 @classmethod
1090 def home(cls):
1091 """Return a new path pointing to the user's home directory (as
1092 returned by os.path.expanduser('~')).
1093 """
1094 return cls(cls()._flavour.gethomedir(None))
1095
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001096 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001097 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001098 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001099 """
1100 st = self.stat()
1101 try:
1102 other_st = other_path.stat()
1103 except AttributeError:
1104 other_st = os.stat(other_path)
1105 return os.path.samestat(st, other_st)
1106
Antoine Pitrou31119e42013-11-22 17:38:12 +01001107 def iterdir(self):
1108 """Iterate over the files in this directory. Does not yield any
1109 result for the special paths '.' and '..'.
1110 """
1111 if self._closed:
1112 self._raise_closed()
1113 for name in self._accessor.listdir(self):
1114 if name in {'.', '..'}:
1115 # Yielding a path object for these makes little sense
1116 continue
1117 yield self._make_child_relpath(name)
1118 if self._closed:
1119 self._raise_closed()
1120
1121 def glob(self, pattern):
1122 """Iterate over this subtree and yield all existing files (of any
Eivind Teig537b6ca2019-02-11 11:47:09 +01001123 kind, including directories) matching the given relative pattern.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001124 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001125 if not pattern:
1126 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001127 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1128 if drv or root:
1129 raise NotImplementedError("Non-relative patterns are unsupported")
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -07001130 selector = _make_selector(tuple(pattern_parts), self._flavour)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001131 for p in selector.select_from(self):
1132 yield p
1133
1134 def rglob(self, pattern):
1135 """Recursively yield all existing files (of any kind, including
Eivind Teig537b6ca2019-02-11 11:47:09 +01001136 directories) matching the given relative pattern, anywhere in
1137 this subtree.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001138 """
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")
Miss Skeleton (bot)2f8d4f02019-10-21 11:17:57 -07001142 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 absolute(self):
1147 """Return an absolute version of this path. This function works
1148 even if the path doesn't point to anything.
1149
1150 No normalization is done, i.e. all '.' and '..' will be kept along.
1151 Use resolve() to get the canonical path to a file.
1152 """
1153 # XXX untested yet!
1154 if self._closed:
1155 self._raise_closed()
1156 if self.is_absolute():
1157 return self
1158 # FIXME this must defer to the specific flavour (and, under Windows,
1159 # use nt._getfullpathname())
1160 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1161 obj._init(template=self)
1162 return obj
1163
Steve Dower98eb3602016-11-09 12:58:17 -08001164 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001165 """
1166 Make the path absolute, resolving all symlinks on the way and also
1167 normalizing it (for example turning slashes into backslashes under
1168 Windows).
1169 """
1170 if self._closed:
1171 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001172 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001173 if s is None:
1174 # No symlink resolution => for consistency, raise an error if
1175 # the path doesn't exist or is forbidden
1176 self.stat()
1177 s = str(self.absolute())
1178 # Now we have no symlinks in the path, it's safe to normalize it.
1179 normed = self._flavour.pathmod.normpath(s)
1180 obj = self._from_parts((normed,), init=False)
1181 obj._init(template=self)
1182 return obj
1183
1184 def stat(self):
1185 """
1186 Return the result of the stat() system call on this path, like
1187 os.stat() does.
1188 """
1189 return self._accessor.stat(self)
1190
1191 def owner(self):
1192 """
1193 Return the login name of the file owner.
1194 """
1195 import pwd
1196 return pwd.getpwuid(self.stat().st_uid).pw_name
1197
1198 def group(self):
1199 """
1200 Return the group name of the file gid.
1201 """
1202 import grp
1203 return grp.getgrgid(self.stat().st_gid).gr_name
1204
Antoine Pitrou31119e42013-11-22 17:38:12 +01001205 def open(self, mode='r', buffering=-1, encoding=None,
1206 errors=None, newline=None):
1207 """
1208 Open the file pointed by this path and return a file object, as
1209 the built-in open() function does.
1210 """
1211 if self._closed:
1212 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001213 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001214 opener=self._opener)
1215
Georg Brandlea683982014-10-01 19:12:33 +02001216 def read_bytes(self):
1217 """
1218 Open the file in bytes mode, read it, and close the file.
1219 """
1220 with self.open(mode='rb') as f:
1221 return f.read()
1222
1223 def read_text(self, encoding=None, errors=None):
1224 """
1225 Open the file in text mode, read it, and close the file.
1226 """
1227 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1228 return f.read()
1229
1230 def write_bytes(self, data):
1231 """
1232 Open the file in bytes mode, write to it, and close the file.
1233 """
1234 # type-check for the buffer interface before truncating the file
1235 view = memoryview(data)
1236 with self.open(mode='wb') as f:
1237 return f.write(view)
1238
1239 def write_text(self, data, encoding=None, errors=None):
1240 """
1241 Open the file in text mode, write to it, and close the file.
1242 """
1243 if not isinstance(data, str):
1244 raise TypeError('data must be str, not %s' %
1245 data.__class__.__name__)
1246 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1247 return f.write(data)
1248
Antoine Pitrou31119e42013-11-22 17:38:12 +01001249 def touch(self, mode=0o666, exist_ok=True):
1250 """
1251 Create this file with the given access mode, if it doesn't exist.
1252 """
1253 if self._closed:
1254 self._raise_closed()
1255 if exist_ok:
1256 # First try to bump modification time
1257 # Implementation note: GNU touch uses the UTIME_NOW option of
1258 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001259 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001260 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001261 except OSError:
1262 # Avoid exception chaining
1263 pass
1264 else:
1265 return
1266 flags = os.O_CREAT | os.O_WRONLY
1267 if not exist_ok:
1268 flags |= os.O_EXCL
1269 fd = self._raw_open(flags, mode)
1270 os.close(fd)
1271
Barry Warsaw7c549c42014-08-05 11:28:12 -04001272 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001273 """
1274 Create a new directory at this given path.
1275 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001276 if self._closed:
1277 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001278 try:
1279 self._accessor.mkdir(self, mode)
1280 except FileNotFoundError:
1281 if not parents or self.parent == self:
1282 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001283 self.parent.mkdir(parents=True, exist_ok=True)
1284 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001285 except OSError:
1286 # Cannot rely on checking for EEXIST, since the operating system
1287 # could give priority to other errors like EACCES or EROFS
1288 if not exist_ok or not self.is_dir():
1289 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001290
1291 def chmod(self, mode):
1292 """
1293 Change the permissions of the path, like os.chmod().
1294 """
1295 if self._closed:
1296 self._raise_closed()
1297 self._accessor.chmod(self, mode)
1298
1299 def lchmod(self, mode):
1300 """
1301 Like chmod(), except if the path points to a symlink, the symlink's
1302 permissions are changed, rather than its target's.
1303 """
1304 if self._closed:
1305 self._raise_closed()
1306 self._accessor.lchmod(self, mode)
1307
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001308 def unlink(self, missing_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001309 """
1310 Remove this file or link.
1311 If the path is a directory, use rmdir() instead.
1312 """
1313 if self._closed:
1314 self._raise_closed()
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001315 try:
1316 self._accessor.unlink(self)
1317 except FileNotFoundError:
1318 if not missing_ok:
1319 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001320
1321 def rmdir(self):
1322 """
1323 Remove this directory. The directory must be empty.
1324 """
1325 if self._closed:
1326 self._raise_closed()
1327 self._accessor.rmdir(self)
1328
1329 def lstat(self):
1330 """
1331 Like stat(), except if the path points to a symlink, the symlink's
1332 status information is returned, rather than its target's.
1333 """
1334 if self._closed:
1335 self._raise_closed()
1336 return self._accessor.lstat(self)
1337
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -04001338 def link_to(self, target):
1339 """
1340 Create a hard link pointing to a path named target.
1341 """
1342 if self._closed:
1343 self._raise_closed()
1344 self._accessor.link_to(self, target)
1345
Antoine Pitrou31119e42013-11-22 17:38:12 +01001346 def rename(self, target):
1347 """
Miss Islington (bot)cbd7b2a2019-09-11 07:12:54 -07001348 Rename this path to the given path,
1349 and return a new Path instance pointing to the given path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001350 """
1351 if self._closed:
1352 self._raise_closed()
1353 self._accessor.rename(self, target)
Miss Islington (bot)cbd7b2a2019-09-11 07:12:54 -07001354 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001355
1356 def replace(self, target):
1357 """
1358 Rename this path to the given path, clobbering the existing
Miss Islington (bot)cbd7b2a2019-09-11 07:12:54 -07001359 destination if it exists, and return a new Path instance
1360 pointing to the given path.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001361 """
1362 if self._closed:
1363 self._raise_closed()
1364 self._accessor.replace(self, target)
Miss Islington (bot)cbd7b2a2019-09-11 07:12:54 -07001365 return self.__class__(target)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001366
1367 def symlink_to(self, target, target_is_directory=False):
1368 """
1369 Make this path a symlink pointing to the given path.
1370 Note the order of arguments (self, target) is the reverse of os.symlink's.
1371 """
1372 if self._closed:
1373 self._raise_closed()
1374 self._accessor.symlink(target, self, target_is_directory)
1375
1376 # Convenience functions for querying the stat results
1377
1378 def exists(self):
1379 """
1380 Whether this path exists.
1381 """
1382 try:
1383 self.stat()
1384 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001385 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001386 raise
1387 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001388 except ValueError:
1389 # Non-encodable path
1390 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001391 return True
1392
1393 def is_dir(self):
1394 """
1395 Whether this path is a directory.
1396 """
1397 try:
1398 return S_ISDIR(self.stat().st_mode)
1399 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001400 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001401 raise
1402 # Path doesn't exist or is a broken symlink
1403 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1404 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001405 except ValueError:
1406 # Non-encodable path
1407 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001408
1409 def is_file(self):
1410 """
1411 Whether this path is a regular file (also True for symlinks pointing
1412 to regular files).
1413 """
1414 try:
1415 return S_ISREG(self.stat().st_mode)
1416 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001417 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001418 raise
1419 # Path doesn't exist or is a broken symlink
1420 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1421 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001422 except ValueError:
1423 # Non-encodable path
1424 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001425
Cooper Lees173ff4a2017-08-01 15:35:45 -07001426 def is_mount(self):
1427 """
1428 Check if this path is a POSIX mount point
1429 """
1430 # Need to exist and be a dir
1431 if not self.exists() or not self.is_dir():
1432 return False
1433
1434 parent = Path(self.parent)
1435 try:
1436 parent_dev = parent.stat().st_dev
1437 except OSError:
1438 return False
1439
1440 dev = self.stat().st_dev
1441 if dev != parent_dev:
1442 return True
1443 ino = self.stat().st_ino
1444 parent_ino = parent.stat().st_ino
1445 return ino == parent_ino
1446
Antoine Pitrou31119e42013-11-22 17:38:12 +01001447 def is_symlink(self):
1448 """
1449 Whether this path is a symbolic link.
1450 """
1451 try:
1452 return S_ISLNK(self.lstat().st_mode)
1453 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001454 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001455 raise
1456 # Path doesn't exist
1457 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001458 except ValueError:
1459 # Non-encodable path
1460 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001461
1462 def is_block_device(self):
1463 """
1464 Whether this path is a block device.
1465 """
1466 try:
1467 return S_ISBLK(self.stat().st_mode)
1468 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001469 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001470 raise
1471 # Path doesn't exist or is a broken symlink
1472 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1473 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001474 except ValueError:
1475 # Non-encodable path
1476 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001477
1478 def is_char_device(self):
1479 """
1480 Whether this path is a character device.
1481 """
1482 try:
1483 return S_ISCHR(self.stat().st_mode)
1484 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001485 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001486 raise
1487 # Path doesn't exist or is a broken symlink
1488 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1489 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001490 except ValueError:
1491 # Non-encodable path
1492 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001493
1494 def is_fifo(self):
1495 """
1496 Whether this path is a FIFO.
1497 """
1498 try:
1499 return S_ISFIFO(self.stat().st_mode)
1500 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001501 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001502 raise
1503 # Path doesn't exist or is a broken symlink
1504 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1505 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001506 except ValueError:
1507 # Non-encodable path
1508 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001509
1510 def is_socket(self):
1511 """
1512 Whether this path is a socket.
1513 """
1514 try:
1515 return S_ISSOCK(self.stat().st_mode)
1516 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001517 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001518 raise
1519 # Path doesn't exist or is a broken symlink
1520 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1521 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001522 except ValueError:
1523 # Non-encodable path
1524 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001525
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001526 def expanduser(self):
1527 """ Return a new path with expanded ~ and ~user constructs
1528 (as returned by os.path.expanduser)
1529 """
1530 if (not (self._drv or self._root) and
1531 self._parts and self._parts[0][:1] == '~'):
1532 homedir = self._flavour.gethomedir(self._parts[0][1:])
1533 return self._from_parts([homedir] + self._parts[1:])
1534
1535 return self
1536
Antoine Pitrou31119e42013-11-22 17:38:12 +01001537
1538class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001539 """Path subclass for non-Windows systems.
1540
1541 On a POSIX system, instantiating a Path should return this object.
1542 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001543 __slots__ = ()
1544
1545class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001546 """Path subclass for Windows systems.
1547
1548 On a Windows system, instantiating a Path should return this object.
1549 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001550 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001551
1552 def owner(self):
1553 raise NotImplementedError("Path.owner() is unsupported on this system")
1554
1555 def group(self):
1556 raise NotImplementedError("Path.group() is unsupported on this system")
Cooper Lees173ff4a2017-08-01 15:35:45 -07001557
1558 def is_mount(self):
1559 raise NotImplementedError("Path.is_mount() is unsupported on this system")