blob: 80923c768268fcd7b1faae0c1b75a28e7e537186 [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
Steve Dower98eb3602016-11-09 12:58:17 -0800190 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100191 s = str(path)
192 if not s:
193 return os.getcwd()
Steve Dower98eb3602016-11-09 12:58:17 -0800194 previous_s = None
Antoine Pitrou31119e42013-11-22 17:38:12 +0100195 if _getfinalpathname is not None:
Steve Dower98eb3602016-11-09 12:58:17 -0800196 if strict:
197 return self._ext_to_normal(_getfinalpathname(s))
198 else:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200199 tail_parts = [] # End of the path after the first one not found
Steve Dower98eb3602016-11-09 12:58:17 -0800200 while True:
201 try:
202 s = self._ext_to_normal(_getfinalpathname(s))
203 except FileNotFoundError:
204 previous_s = s
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200205 s, tail = os.path.split(s)
206 tail_parts.append(tail)
Steve Dower4b1e98b2016-12-28 16:02:59 -0800207 if previous_s == s:
208 return path
Steve Dower98eb3602016-11-09 12:58:17 -0800209 else:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200210 return os.path.join(s, *reversed(tail_parts))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100211 # Means fallback on absolute
212 return None
213
214 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
215 prefix = ''
216 if s.startswith(ext_prefix):
217 prefix = s[:4]
218 s = s[4:]
219 if s.startswith('UNC\\'):
220 prefix += s[:3]
221 s = '\\' + s[3:]
222 return prefix, s
223
224 def _ext_to_normal(self, s):
225 # Turn back an extended path into a normal DOS-like path
226 return self._split_extended_path(s)[1]
227
228 def is_reserved(self, parts):
229 # NOTE: the rules for reserved names seem somewhat complicated
230 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
231 # We err on the side of caution and return True for paths which are
232 # not considered reserved by Windows.
233 if not parts:
234 return False
235 if parts[0].startswith('\\\\'):
236 # UNC paths are never reserved
237 return False
238 return parts[-1].partition('.')[0].upper() in self.reserved_names
239
240 def make_uri(self, path):
241 # Under Windows, file URIs use the UTF-8 encoding.
242 drive = path.drive
243 if len(drive) == 2 and drive[1] == ':':
244 # It's a path on a local drive => 'file:///c:/a/b'
245 rest = path.as_posix()[2:].lstrip('/')
246 return 'file:///%s/%s' % (
247 drive, urlquote_from_bytes(rest.encode('utf-8')))
248 else:
249 # It's a path on a network drive => 'file://host/share/a/b'
250 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
251
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100252 def gethomedir(self, username):
253 if 'HOME' in os.environ:
254 userhome = os.environ['HOME']
255 elif 'USERPROFILE' in os.environ:
256 userhome = os.environ['USERPROFILE']
257 elif 'HOMEPATH' in os.environ:
Antoine Pitrou5d4e27e2014-12-30 22:09:42 +0100258 try:
259 drv = os.environ['HOMEDRIVE']
260 except KeyError:
261 drv = ''
262 userhome = drv + os.environ['HOMEPATH']
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100263 else:
264 raise RuntimeError("Can't determine home directory")
265
266 if username:
267 # Try to guess user home directory. By default all users
268 # directories are located in the same place and are named by
269 # corresponding usernames. If current user home directory points
270 # to nonstandard place, this guess is likely wrong.
271 if os.environ['USERNAME'] != username:
272 drv, root, parts = self.parse_parts((userhome,))
273 if parts[-1] != os.environ['USERNAME']:
274 raise RuntimeError("Can't determine home directory "
275 "for %r" % username)
276 parts[-1] = username
277 if drv or root:
278 userhome = drv + root + self.join(parts[1:])
279 else:
280 userhome = self.join(parts)
281 return userhome
Antoine Pitrou31119e42013-11-22 17:38:12 +0100282
283class _PosixFlavour(_Flavour):
284 sep = '/'
285 altsep = ''
286 has_drv = False
287 pathmod = posixpath
288
289 is_supported = (os.name != 'nt')
290
291 def splitroot(self, part, sep=sep):
292 if part and part[0] == sep:
293 stripped_part = part.lstrip(sep)
294 # According to POSIX path resolution:
295 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
296 # "A pathname that begins with two successive slashes may be
297 # interpreted in an implementation-defined manner, although more
298 # than two leading slashes shall be treated as a single slash".
299 if len(part) - len(stripped_part) == 2:
300 return '', sep * 2, stripped_part
301 else:
302 return '', sep, stripped_part
303 else:
304 return '', '', part
305
306 def casefold(self, s):
307 return s
308
309 def casefold_parts(self, parts):
310 return parts
311
Steve Dower98eb3602016-11-09 12:58:17 -0800312 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100313 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100314 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100315 seen = {}
316 def _resolve(path, rest):
317 if rest.startswith(sep):
318 path = ''
319
320 for name in rest.split(sep):
321 if not name or name == '.':
322 # current dir
323 continue
324 if name == '..':
325 # parent dir
326 path, _, _ = path.rpartition(sep)
327 continue
328 newpath = path + sep + name
329 if newpath in seen:
330 # Already seen this path
331 path = seen[newpath]
332 if path is not None:
333 # use cached value
334 continue
335 # The symlink is not resolved, so we must have a symlink loop.
336 raise RuntimeError("Symlink loop from %r" % newpath)
337 # Resolve the symbolic link
338 try:
339 target = accessor.readlink(newpath)
340 except OSError as e:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200341 if e.errno != EINVAL and strict:
342 raise
343 # Not a symlink, or non-strict mode. We just leave the path
344 # untouched.
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100345 path = newpath
346 else:
347 seen[newpath] = None # not resolved symlink
348 path = _resolve(path, target)
349 seen[newpath] = path # resolved symlink
350
351 return path
352 # NOTE: according to POSIX, getcwd() cannot contain path components
353 # which are symlinks.
354 base = '' if path.is_absolute() else os.getcwd()
355 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100356
357 def is_reserved(self, parts):
358 return False
359
360 def make_uri(self, path):
361 # We represent the path using the local filesystem encoding,
362 # for portability to other applications.
363 bpath = bytes(path)
364 return 'file://' + urlquote_from_bytes(bpath)
365
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100366 def gethomedir(self, username):
367 if not username:
368 try:
369 return os.environ['HOME']
370 except KeyError:
371 import pwd
372 return pwd.getpwuid(os.getuid()).pw_dir
373 else:
374 import pwd
375 try:
376 return pwd.getpwnam(username).pw_dir
377 except KeyError:
378 raise RuntimeError("Can't determine home directory "
379 "for %r" % username)
380
Antoine Pitrou31119e42013-11-22 17:38:12 +0100381
382_windows_flavour = _WindowsFlavour()
383_posix_flavour = _PosixFlavour()
384
385
386class _Accessor:
387 """An accessor implements a particular (system-specific or not) way of
388 accessing paths on the filesystem."""
389
390
391class _NormalAccessor(_Accessor):
392
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200393 stat = os.stat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100394
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200395 lstat = os.lstat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100396
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200397 open = os.open
Antoine Pitrou31119e42013-11-22 17:38:12 +0100398
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200399 listdir = os.listdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100400
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200401 scandir = os.scandir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100402
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200403 chmod = os.chmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100404
405 if hasattr(os, "lchmod"):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200406 lchmod = os.lchmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100407 else:
408 def lchmod(self, pathobj, mode):
409 raise NotImplementedError("lchmod() not available on this system")
410
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200411 mkdir = os.mkdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100412
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200413 unlink = os.unlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100414
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -0400415 link_to = os.link
416
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200417 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100418
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200419 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100420
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200421 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100422
423 if nt:
424 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200425 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100426 else:
427 def symlink(a, b, target_is_directory):
428 raise NotImplementedError("symlink() not available on this system")
429 else:
430 # Under POSIX, os.symlink() takes two args
431 @staticmethod
432 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200433 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100434
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200435 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100436
437 # Helper for resolve()
438 def readlink(self, path):
439 return os.readlink(path)
440
441
442_normal_accessor = _NormalAccessor()
443
444
445#
446# Globbing helpers
447#
448
Antoine Pitrou31119e42013-11-22 17:38:12 +0100449def _make_selector(pattern_parts):
450 pat = pattern_parts[0]
451 child_parts = pattern_parts[1:]
452 if pat == '**':
453 cls = _RecursiveWildcardSelector
454 elif '**' in pat:
455 raise ValueError("Invalid pattern: '**' can only be an entire path component")
456 elif _is_wildcard_pattern(pat):
457 cls = _WildcardSelector
458 else:
459 cls = _PreciseSelector
460 return cls(pat, child_parts)
461
462if hasattr(functools, "lru_cache"):
463 _make_selector = functools.lru_cache()(_make_selector)
464
465
466class _Selector:
467 """A selector matches a specific glob pattern part against the children
468 of a given path."""
469
470 def __init__(self, child_parts):
471 self.child_parts = child_parts
472 if child_parts:
473 self.successor = _make_selector(child_parts)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300474 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100475 else:
476 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300477 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100478
479 def select_from(self, parent_path):
480 """Iterate over all child paths of `parent_path` matched by this
481 selector. This can contain parent_path itself."""
482 path_cls = type(parent_path)
483 is_dir = path_cls.is_dir
484 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300485 scandir = parent_path._accessor.scandir
486 if not is_dir(parent_path):
487 return iter([])
488 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100489
490
491class _TerminatingSelector:
492
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300493 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100494 yield parent_path
495
496
497class _PreciseSelector(_Selector):
498
499 def __init__(self, name, child_parts):
500 self.name = name
501 _Selector.__init__(self, child_parts)
502
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300503 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800504 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800505 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300506 if (is_dir if self.dironly else exists)(path):
507 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800508 yield p
509 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100510 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100511
512
513class _WildcardSelector(_Selector):
514
515 def __init__(self, pat, child_parts):
516 self.pat = re.compile(fnmatch.translate(pat))
517 _Selector.__init__(self, child_parts)
518
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300519 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800520 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800521 cf = parent_path._flavour.casefold
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300522 entries = list(scandir(parent_path))
523 for entry in entries:
Jörg Stucked5c120f2019-05-21 19:44:40 +0200524 entry_is_dir = False
525 try:
526 entry_is_dir = entry.is_dir()
527 except OSError as e:
528 if not _ignore_error(e):
529 raise
530 if not self.dironly or entry_is_dir:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300531 name = entry.name
532 casefolded = cf(name)
533 if self.pat.match(casefolded):
534 path = parent_path._make_child_relpath(name)
535 for p in self.successor._select_from(path, is_dir, exists, scandir):
536 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800537 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100538 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800539
Antoine Pitrou31119e42013-11-22 17:38:12 +0100540
541
542class _RecursiveWildcardSelector(_Selector):
543
544 def __init__(self, pat, child_parts):
545 _Selector.__init__(self, child_parts)
546
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300547 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100548 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800549 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300550 entries = list(scandir(parent_path))
551 for entry in entries:
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200552 entry_is_dir = False
553 try:
554 entry_is_dir = entry.is_dir()
555 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -0800556 if not _ignore_error(e):
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200557 raise
558 if entry_is_dir and not entry.is_symlink():
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300559 path = parent_path._make_child_relpath(entry.name)
560 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800561 yield p
562 except PermissionError:
563 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100564
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300565 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800566 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300567 yielded = set()
568 try:
569 successor_select = self.successor._select_from
570 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
571 for p in successor_select(starting_point, is_dir, exists, scandir):
572 if p not in yielded:
573 yield p
574 yielded.add(p)
575 finally:
576 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800577 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100578 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100579
580
581#
582# Public API
583#
584
585class _PathParents(Sequence):
586 """This object provides sequence-like access to the logical ancestors
587 of a path. Don't try to construct it yourself."""
588 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
589
590 def __init__(self, path):
591 # We don't store the instance to avoid reference cycles
592 self._pathcls = type(path)
593 self._drv = path._drv
594 self._root = path._root
595 self._parts = path._parts
596
597 def __len__(self):
598 if self._drv or self._root:
599 return len(self._parts) - 1
600 else:
601 return len(self._parts)
602
603 def __getitem__(self, idx):
604 if idx < 0 or idx >= len(self):
605 raise IndexError(idx)
606 return self._pathcls._from_parsed_parts(self._drv, self._root,
607 self._parts[:-idx - 1])
608
609 def __repr__(self):
610 return "<{}.parents>".format(self._pathcls.__name__)
611
612
613class PurePath(object):
chasondfa015c2018-02-19 08:36:32 +0900614 """Base class for manipulating paths without I/O.
615
616 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100617 don't imply any actual filesystem I/O. Depending on your system,
618 instantiating a PurePath will return either a PurePosixPath or a
619 PureWindowsPath object. You can also instantiate either of these classes
620 directly, regardless of your system.
621 """
622 __slots__ = (
623 '_drv', '_root', '_parts',
624 '_str', '_hash', '_pparts', '_cached_cparts',
625 )
626
627 def __new__(cls, *args):
628 """Construct a PurePath from one or several strings and or existing
629 PurePath objects. The strings and path objects are combined so as
630 to yield a canonicalized path, which is incorporated into the
631 new PurePath object.
632 """
633 if cls is PurePath:
634 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
635 return cls._from_parts(args)
636
637 def __reduce__(self):
638 # Using the parts tuple helps share interned path parts
639 # when pickling related paths.
640 return (self.__class__, tuple(self._parts))
641
642 @classmethod
643 def _parse_args(cls, args):
644 # This is useful when you don't want to create an instance, just
645 # canonicalize some constructor arguments.
646 parts = []
647 for a in args:
648 if isinstance(a, PurePath):
649 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100650 else:
Brett Cannon568be632016-06-10 12:20:49 -0700651 a = os.fspath(a)
652 if isinstance(a, str):
653 # Force-cast str subclasses to str (issue #21127)
654 parts.append(str(a))
655 else:
656 raise TypeError(
657 "argument should be a str object or an os.PathLike "
658 "object returning str, not %r"
659 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100660 return cls._flavour.parse_parts(parts)
661
662 @classmethod
663 def _from_parts(cls, args, init=True):
664 # We need to call _parse_args on the instance, so as to get the
665 # right flavour.
666 self = object.__new__(cls)
667 drv, root, parts = self._parse_args(args)
668 self._drv = drv
669 self._root = root
670 self._parts = parts
671 if init:
672 self._init()
673 return self
674
675 @classmethod
676 def _from_parsed_parts(cls, drv, root, parts, init=True):
677 self = object.__new__(cls)
678 self._drv = drv
679 self._root = root
680 self._parts = parts
681 if init:
682 self._init()
683 return self
684
685 @classmethod
686 def _format_parsed_parts(cls, drv, root, parts):
687 if drv or root:
688 return drv + root + cls._flavour.join(parts[1:])
689 else:
690 return cls._flavour.join(parts)
691
692 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000693 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100694 pass
695
696 def _make_child(self, args):
697 drv, root, parts = self._parse_args(args)
698 drv, root, parts = self._flavour.join_parsed_parts(
699 self._drv, self._root, self._parts, drv, root, parts)
700 return self._from_parsed_parts(drv, root, parts)
701
702 def __str__(self):
703 """Return the string representation of the path, suitable for
704 passing to system calls."""
705 try:
706 return self._str
707 except AttributeError:
708 self._str = self._format_parsed_parts(self._drv, self._root,
709 self._parts) or '.'
710 return self._str
711
Brett Cannon568be632016-06-10 12:20:49 -0700712 def __fspath__(self):
713 return str(self)
714
Antoine Pitrou31119e42013-11-22 17:38:12 +0100715 def as_posix(self):
716 """Return the string representation of the path with forward (/)
717 slashes."""
718 f = self._flavour
719 return str(self).replace(f.sep, '/')
720
721 def __bytes__(self):
722 """Return the bytes representation of the path. This is only
723 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200724 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100725
726 def __repr__(self):
727 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
728
729 def as_uri(self):
730 """Return the path as a 'file' URI."""
731 if not self.is_absolute():
732 raise ValueError("relative path can't be expressed as a file URI")
733 return self._flavour.make_uri(self)
734
735 @property
736 def _cparts(self):
737 # Cached casefolded parts, for hashing and comparison
738 try:
739 return self._cached_cparts
740 except AttributeError:
741 self._cached_cparts = self._flavour.casefold_parts(self._parts)
742 return self._cached_cparts
743
744 def __eq__(self, other):
745 if not isinstance(other, PurePath):
746 return NotImplemented
747 return self._cparts == other._cparts and self._flavour is other._flavour
748
Antoine Pitrou31119e42013-11-22 17:38:12 +0100749 def __hash__(self):
750 try:
751 return self._hash
752 except AttributeError:
753 self._hash = hash(tuple(self._cparts))
754 return self._hash
755
756 def __lt__(self, other):
757 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
758 return NotImplemented
759 return self._cparts < other._cparts
760
761 def __le__(self, other):
762 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
763 return NotImplemented
764 return self._cparts <= other._cparts
765
766 def __gt__(self, other):
767 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
768 return NotImplemented
769 return self._cparts > other._cparts
770
771 def __ge__(self, other):
772 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
773 return NotImplemented
774 return self._cparts >= other._cparts
775
776 drive = property(attrgetter('_drv'),
777 doc="""The drive prefix (letter or UNC path), if any.""")
778
779 root = property(attrgetter('_root'),
780 doc="""The root of the path, if any.""")
781
782 @property
783 def anchor(self):
784 """The concatenation of the drive and root, or ''."""
785 anchor = self._drv + self._root
786 return anchor
787
788 @property
789 def name(self):
790 """The final path component, if any."""
791 parts = self._parts
792 if len(parts) == (1 if (self._drv or self._root) else 0):
793 return ''
794 return parts[-1]
795
796 @property
797 def suffix(self):
798 """The final component's last suffix, if any."""
799 name = self.name
800 i = name.rfind('.')
801 if 0 < i < len(name) - 1:
802 return name[i:]
803 else:
804 return ''
805
806 @property
807 def suffixes(self):
808 """A list of the final component's suffixes, if any."""
809 name = self.name
810 if name.endswith('.'):
811 return []
812 name = name.lstrip('.')
813 return ['.' + suffix for suffix in name.split('.')[1:]]
814
815 @property
816 def stem(self):
817 """The final path component, minus its last suffix."""
818 name = self.name
819 i = name.rfind('.')
820 if 0 < i < len(name) - 1:
821 return name[:i]
822 else:
823 return name
824
825 def with_name(self, name):
826 """Return a new path with the file name changed."""
827 if not self.name:
828 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400829 drv, root, parts = self._flavour.parse_parts((name,))
830 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
831 or drv or root or len(parts) != 1):
832 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100833 return self._from_parsed_parts(self._drv, self._root,
834 self._parts[:-1] + [name])
835
836 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200837 """Return a new path with the file suffix changed. If the path
838 has no suffix, add given suffix. If the given suffix is an empty
839 string, remove the suffix from the path.
840 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400841 f = self._flavour
842 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300843 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400844 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100845 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100846 name = self.name
847 if not name:
848 raise ValueError("%r has an empty name" % (self,))
849 old_suffix = self.suffix
850 if not old_suffix:
851 name = name + suffix
852 else:
853 name = name[:-len(old_suffix)] + suffix
854 return self._from_parsed_parts(self._drv, self._root,
855 self._parts[:-1] + [name])
856
857 def relative_to(self, *other):
858 """Return the relative path to another path identified by the passed
859 arguments. If the operation is not possible (because this is not
860 a subpath of the other path), raise ValueError.
861 """
862 # For the purpose of this method, drive and root are considered
863 # separate parts, i.e.:
864 # Path('c:/').relative_to('c:') gives Path('/')
865 # Path('c:/').relative_to('/') raise ValueError
866 if not other:
867 raise TypeError("need at least one argument")
868 parts = self._parts
869 drv = self._drv
870 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100871 if root:
872 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100873 else:
874 abs_parts = parts
875 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100876 if to_root:
877 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100878 else:
879 to_abs_parts = to_parts
880 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100881 cf = self._flavour.casefold_parts
882 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100883 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
884 raise ValueError("{!r} does not start with {!r}"
885 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100886 return self._from_parsed_parts('', root if n == 1 else '',
887 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100888
Hai Shi82642a02019-08-13 14:54:02 -0500889 def is_relative_to(self, *other):
890 """Return True if the path is relative to another path or False.
891 """
892 try:
893 self.relative_to(*other)
894 return True
895 except ValueError:
896 return False
897
Antoine Pitrou31119e42013-11-22 17:38:12 +0100898 @property
899 def parts(self):
900 """An object providing sequence-like access to the
901 components in the filesystem path."""
902 # We cache the tuple to avoid building a new one each time .parts
903 # is accessed. XXX is this necessary?
904 try:
905 return self._pparts
906 except AttributeError:
907 self._pparts = tuple(self._parts)
908 return self._pparts
909
910 def joinpath(self, *args):
911 """Combine this path with one or several arguments, and return a
912 new path representing either a subpath (if all arguments are relative
913 paths) or a totally different path (if one of the arguments is
914 anchored).
915 """
916 return self._make_child(args)
917
918 def __truediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400919 try:
920 return self._make_child((key,))
921 except TypeError:
922 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100923
924 def __rtruediv__(self, key):
aiudirog4c69be22019-08-08 01:41:10 -0400925 try:
926 return self._from_parts([key] + self._parts)
927 except TypeError:
928 return NotImplemented
Antoine Pitrou31119e42013-11-22 17:38:12 +0100929
930 @property
931 def parent(self):
932 """The logical parent of the path."""
933 drv = self._drv
934 root = self._root
935 parts = self._parts
936 if len(parts) == 1 and (drv or root):
937 return self
938 return self._from_parsed_parts(drv, root, parts[:-1])
939
940 @property
941 def parents(self):
942 """A sequence of this path's logical parents."""
943 return _PathParents(self)
944
945 def is_absolute(self):
946 """True if the path is absolute (has both a root and, if applicable,
947 a drive)."""
948 if not self._root:
949 return False
950 return not self._flavour.has_drv or bool(self._drv)
951
952 def is_reserved(self):
953 """Return True if the path contains one of the special names reserved
954 by the system, if any."""
955 return self._flavour.is_reserved(self._parts)
956
957 def match(self, path_pattern):
958 """
959 Return True if this path matches the given pattern.
960 """
961 cf = self._flavour.casefold
962 path_pattern = cf(path_pattern)
963 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
964 if not pat_parts:
965 raise ValueError("empty pattern")
966 if drv and drv != cf(self._drv):
967 return False
968 if root and root != cf(self._root):
969 return False
970 parts = self._cparts
971 if drv or root:
972 if len(pat_parts) != len(parts):
973 return False
974 pat_parts = pat_parts[1:]
975 elif len(pat_parts) > len(parts):
976 return False
977 for part, pat in zip(reversed(parts), reversed(pat_parts)):
978 if not fnmatch.fnmatchcase(part, pat):
979 return False
980 return True
981
Brett Cannon568be632016-06-10 12:20:49 -0700982# Can't subclass os.PathLike from PurePath and keep the constructor
983# optimizations in PurePath._parse_args().
984os.PathLike.register(PurePath)
985
Antoine Pitrou31119e42013-11-22 17:38:12 +0100986
987class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900988 """PurePath subclass for non-Windows systems.
989
990 On a POSIX system, instantiating a PurePath should return this object.
991 However, you can also instantiate it directly on any system.
992 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100993 _flavour = _posix_flavour
994 __slots__ = ()
995
996
997class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900998 """PurePath subclass for Windows systems.
999
1000 On a Windows system, instantiating a PurePath should return this object.
1001 However, you can also instantiate it directly on any system.
1002 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001003 _flavour = _windows_flavour
1004 __slots__ = ()
1005
1006
1007# Filesystem-accessing classes
1008
1009
1010class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +09001011 """PurePath subclass that can make system calls.
1012
1013 Path represents a filesystem path but unlike PurePath, also offers
1014 methods to do system calls on path objects. Depending on your system,
1015 instantiating a Path will return either a PosixPath or a WindowsPath
1016 object. You can also instantiate a PosixPath or WindowsPath directly,
1017 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
1018 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001019 __slots__ = (
1020 '_accessor',
1021 '_closed',
1022 )
1023
1024 def __new__(cls, *args, **kwargs):
1025 if cls is Path:
1026 cls = WindowsPath if os.name == 'nt' else PosixPath
1027 self = cls._from_parts(args, init=False)
1028 if not self._flavour.is_supported:
1029 raise NotImplementedError("cannot instantiate %r on your system"
1030 % (cls.__name__,))
1031 self._init()
1032 return self
1033
1034 def _init(self,
1035 # Private non-constructor arguments
1036 template=None,
1037 ):
1038 self._closed = False
1039 if template is not None:
1040 self._accessor = template._accessor
1041 else:
1042 self._accessor = _normal_accessor
1043
1044 def _make_child_relpath(self, part):
1045 # This is an optimization used for dir walking. `part` must be
1046 # a single part relative to this path.
1047 parts = self._parts + [part]
1048 return self._from_parsed_parts(self._drv, self._root, parts)
1049
1050 def __enter__(self):
1051 if self._closed:
1052 self._raise_closed()
1053 return self
1054
1055 def __exit__(self, t, v, tb):
1056 self._closed = True
1057
1058 def _raise_closed(self):
1059 raise ValueError("I/O operation on closed path")
1060
1061 def _opener(self, name, flags, mode=0o666):
1062 # A stub for the opener argument to built-in open()
1063 return self._accessor.open(self, flags, mode)
1064
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001065 def _raw_open(self, flags, mode=0o777):
1066 """
1067 Open the file pointed by this path and return a file descriptor,
1068 as os.open() does.
1069 """
1070 if self._closed:
1071 self._raise_closed()
1072 return self._accessor.open(self, flags, mode)
1073
Antoine Pitrou31119e42013-11-22 17:38:12 +01001074 # Public API
1075
1076 @classmethod
1077 def cwd(cls):
1078 """Return a new path pointing to the current working directory
1079 (as returned by os.getcwd()).
1080 """
1081 return cls(os.getcwd())
1082
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001083 @classmethod
1084 def home(cls):
1085 """Return a new path pointing to the user's home directory (as
1086 returned by os.path.expanduser('~')).
1087 """
1088 return cls(cls()._flavour.gethomedir(None))
1089
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001090 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001091 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001092 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001093 """
1094 st = self.stat()
1095 try:
1096 other_st = other_path.stat()
1097 except AttributeError:
1098 other_st = os.stat(other_path)
1099 return os.path.samestat(st, other_st)
1100
Antoine Pitrou31119e42013-11-22 17:38:12 +01001101 def iterdir(self):
1102 """Iterate over the files in this directory. Does not yield any
1103 result for the special paths '.' and '..'.
1104 """
1105 if self._closed:
1106 self._raise_closed()
1107 for name in self._accessor.listdir(self):
1108 if name in {'.', '..'}:
1109 # Yielding a path object for these makes little sense
1110 continue
1111 yield self._make_child_relpath(name)
1112 if self._closed:
1113 self._raise_closed()
1114
1115 def glob(self, pattern):
1116 """Iterate over this subtree and yield all existing files (of any
Eivind Teig537b6ca2019-02-11 11:47:09 +01001117 kind, including directories) matching the given relative pattern.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001118 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001119 if not pattern:
1120 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001121 pattern = self._flavour.casefold(pattern)
1122 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1123 if drv or root:
1124 raise NotImplementedError("Non-relative patterns are unsupported")
1125 selector = _make_selector(tuple(pattern_parts))
1126 for p in selector.select_from(self):
1127 yield p
1128
1129 def rglob(self, pattern):
1130 """Recursively yield all existing files (of any kind, including
Eivind Teig537b6ca2019-02-11 11:47:09 +01001131 directories) matching the given relative pattern, anywhere in
1132 this subtree.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001133 """
1134 pattern = self._flavour.casefold(pattern)
1135 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1136 if drv or root:
1137 raise NotImplementedError("Non-relative patterns are unsupported")
1138 selector = _make_selector(("**",) + tuple(pattern_parts))
1139 for p in selector.select_from(self):
1140 yield p
1141
1142 def absolute(self):
1143 """Return an absolute version of this path. This function works
1144 even if the path doesn't point to anything.
1145
1146 No normalization is done, i.e. all '.' and '..' will be kept along.
1147 Use resolve() to get the canonical path to a file.
1148 """
1149 # XXX untested yet!
1150 if self._closed:
1151 self._raise_closed()
1152 if self.is_absolute():
1153 return self
1154 # FIXME this must defer to the specific flavour (and, under Windows,
1155 # use nt._getfullpathname())
1156 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1157 obj._init(template=self)
1158 return obj
1159
Steve Dower98eb3602016-11-09 12:58:17 -08001160 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001161 """
1162 Make the path absolute, resolving all symlinks on the way and also
1163 normalizing it (for example turning slashes into backslashes under
1164 Windows).
1165 """
1166 if self._closed:
1167 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001168 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001169 if s is None:
1170 # No symlink resolution => for consistency, raise an error if
1171 # the path doesn't exist or is forbidden
1172 self.stat()
1173 s = str(self.absolute())
1174 # Now we have no symlinks in the path, it's safe to normalize it.
1175 normed = self._flavour.pathmod.normpath(s)
1176 obj = self._from_parts((normed,), init=False)
1177 obj._init(template=self)
1178 return obj
1179
1180 def stat(self):
1181 """
1182 Return the result of the stat() system call on this path, like
1183 os.stat() does.
1184 """
1185 return self._accessor.stat(self)
1186
1187 def owner(self):
1188 """
1189 Return the login name of the file owner.
1190 """
1191 import pwd
1192 return pwd.getpwuid(self.stat().st_uid).pw_name
1193
1194 def group(self):
1195 """
1196 Return the group name of the file gid.
1197 """
1198 import grp
1199 return grp.getgrgid(self.stat().st_gid).gr_name
1200
Antoine Pitrou31119e42013-11-22 17:38:12 +01001201 def open(self, mode='r', buffering=-1, encoding=None,
1202 errors=None, newline=None):
1203 """
1204 Open the file pointed by this path and return a file object, as
1205 the built-in open() function does.
1206 """
1207 if self._closed:
1208 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001209 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001210 opener=self._opener)
1211
Georg Brandlea683982014-10-01 19:12:33 +02001212 def read_bytes(self):
1213 """
1214 Open the file in bytes mode, read it, and close the file.
1215 """
1216 with self.open(mode='rb') as f:
1217 return f.read()
1218
1219 def read_text(self, encoding=None, errors=None):
1220 """
1221 Open the file in text mode, read it, and close the file.
1222 """
1223 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1224 return f.read()
1225
1226 def write_bytes(self, data):
1227 """
1228 Open the file in bytes mode, write to it, and close the file.
1229 """
1230 # type-check for the buffer interface before truncating the file
1231 view = memoryview(data)
1232 with self.open(mode='wb') as f:
1233 return f.write(view)
1234
1235 def write_text(self, data, encoding=None, errors=None):
1236 """
1237 Open the file in text mode, write to it, and close the file.
1238 """
1239 if not isinstance(data, str):
1240 raise TypeError('data must be str, not %s' %
1241 data.__class__.__name__)
1242 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1243 return f.write(data)
1244
Antoine Pitrou31119e42013-11-22 17:38:12 +01001245 def touch(self, mode=0o666, exist_ok=True):
1246 """
1247 Create this file with the given access mode, if it doesn't exist.
1248 """
1249 if self._closed:
1250 self._raise_closed()
1251 if exist_ok:
1252 # First try to bump modification time
1253 # Implementation note: GNU touch uses the UTIME_NOW option of
1254 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001255 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001256 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001257 except OSError:
1258 # Avoid exception chaining
1259 pass
1260 else:
1261 return
1262 flags = os.O_CREAT | os.O_WRONLY
1263 if not exist_ok:
1264 flags |= os.O_EXCL
1265 fd = self._raw_open(flags, mode)
1266 os.close(fd)
1267
Barry Warsaw7c549c42014-08-05 11:28:12 -04001268 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001269 """
1270 Create a new directory at this given path.
1271 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001272 if self._closed:
1273 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001274 try:
1275 self._accessor.mkdir(self, mode)
1276 except FileNotFoundError:
1277 if not parents or self.parent == self:
1278 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001279 self.parent.mkdir(parents=True, exist_ok=True)
1280 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001281 except OSError:
1282 # Cannot rely on checking for EEXIST, since the operating system
1283 # could give priority to other errors like EACCES or EROFS
1284 if not exist_ok or not self.is_dir():
1285 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001286
1287 def chmod(self, mode):
1288 """
1289 Change the permissions of the path, like os.chmod().
1290 """
1291 if self._closed:
1292 self._raise_closed()
1293 self._accessor.chmod(self, mode)
1294
1295 def lchmod(self, mode):
1296 """
1297 Like chmod(), except if the path points to a symlink, the symlink's
1298 permissions are changed, rather than its target's.
1299 """
1300 if self._closed:
1301 self._raise_closed()
1302 self._accessor.lchmod(self, mode)
1303
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001304 def unlink(self, missing_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001305 """
1306 Remove this file or link.
1307 If the path is a directory, use rmdir() instead.
1308 """
1309 if self._closed:
1310 self._raise_closed()
‮zlohhcuB treboRd9e006b2019-05-16 00:02:11 +02001311 try:
1312 self._accessor.unlink(self)
1313 except FileNotFoundError:
1314 if not missing_ok:
1315 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001316
1317 def rmdir(self):
1318 """
1319 Remove this directory. The directory must be empty.
1320 """
1321 if self._closed:
1322 self._raise_closed()
1323 self._accessor.rmdir(self)
1324
1325 def lstat(self):
1326 """
1327 Like stat(), except if the path points to a symlink, the symlink's
1328 status information is returned, rather than its target's.
1329 """
1330 if self._closed:
1331 self._raise_closed()
1332 return self._accessor.lstat(self)
1333
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -04001334 def link_to(self, target):
1335 """
1336 Create a hard link pointing to a path named target.
1337 """
1338 if self._closed:
1339 self._raise_closed()
1340 self._accessor.link_to(self, target)
1341
Antoine Pitrou31119e42013-11-22 17:38:12 +01001342 def rename(self, target):
1343 """
1344 Rename this path to the given path.
1345 """
1346 if self._closed:
1347 self._raise_closed()
1348 self._accessor.rename(self, target)
1349
1350 def replace(self, target):
1351 """
1352 Rename this path to the given path, clobbering the existing
1353 destination if it exists.
1354 """
1355 if self._closed:
1356 self._raise_closed()
1357 self._accessor.replace(self, target)
1358
1359 def symlink_to(self, target, target_is_directory=False):
1360 """
1361 Make this path a symlink pointing to the given path.
1362 Note the order of arguments (self, target) is the reverse of os.symlink's.
1363 """
1364 if self._closed:
1365 self._raise_closed()
1366 self._accessor.symlink(target, self, target_is_directory)
1367
1368 # Convenience functions for querying the stat results
1369
1370 def exists(self):
1371 """
1372 Whether this path exists.
1373 """
1374 try:
1375 self.stat()
1376 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001377 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001378 raise
1379 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001380 except ValueError:
1381 # Non-encodable path
1382 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001383 return True
1384
1385 def is_dir(self):
1386 """
1387 Whether this path is a directory.
1388 """
1389 try:
1390 return S_ISDIR(self.stat().st_mode)
1391 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001392 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001393 raise
1394 # Path doesn't exist or is a broken symlink
1395 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1396 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001397 except ValueError:
1398 # Non-encodable path
1399 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001400
1401 def is_file(self):
1402 """
1403 Whether this path is a regular file (also True for symlinks pointing
1404 to regular files).
1405 """
1406 try:
1407 return S_ISREG(self.stat().st_mode)
1408 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001409 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001410 raise
1411 # Path doesn't exist or is a broken symlink
1412 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1413 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001414 except ValueError:
1415 # Non-encodable path
1416 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001417
Cooper Lees173ff4a2017-08-01 15:35:45 -07001418 def is_mount(self):
1419 """
1420 Check if this path is a POSIX mount point
1421 """
1422 # Need to exist and be a dir
1423 if not self.exists() or not self.is_dir():
1424 return False
1425
1426 parent = Path(self.parent)
1427 try:
1428 parent_dev = parent.stat().st_dev
1429 except OSError:
1430 return False
1431
1432 dev = self.stat().st_dev
1433 if dev != parent_dev:
1434 return True
1435 ino = self.stat().st_ino
1436 parent_ino = parent.stat().st_ino
1437 return ino == parent_ino
1438
Antoine Pitrou31119e42013-11-22 17:38:12 +01001439 def is_symlink(self):
1440 """
1441 Whether this path is a symbolic link.
1442 """
1443 try:
1444 return S_ISLNK(self.lstat().st_mode)
1445 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001446 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001447 raise
1448 # Path doesn't exist
1449 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001450 except ValueError:
1451 # Non-encodable path
1452 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001453
1454 def is_block_device(self):
1455 """
1456 Whether this path is a block device.
1457 """
1458 try:
1459 return S_ISBLK(self.stat().st_mode)
1460 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001461 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001462 raise
1463 # Path doesn't exist or is a broken symlink
1464 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1465 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001466 except ValueError:
1467 # Non-encodable path
1468 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001469
1470 def is_char_device(self):
1471 """
1472 Whether this path is a character device.
1473 """
1474 try:
1475 return S_ISCHR(self.stat().st_mode)
1476 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001477 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001478 raise
1479 # Path doesn't exist or is a broken symlink
1480 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1481 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001482 except ValueError:
1483 # Non-encodable path
1484 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001485
1486 def is_fifo(self):
1487 """
1488 Whether this path is a FIFO.
1489 """
1490 try:
1491 return S_ISFIFO(self.stat().st_mode)
1492 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001493 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001494 raise
1495 # Path doesn't exist or is a broken symlink
1496 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1497 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001498 except ValueError:
1499 # Non-encodable path
1500 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001501
1502 def is_socket(self):
1503 """
1504 Whether this path is a socket.
1505 """
1506 try:
1507 return S_ISSOCK(self.stat().st_mode)
1508 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001509 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001510 raise
1511 # Path doesn't exist or is a broken symlink
1512 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1513 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001514 except ValueError:
1515 # Non-encodable path
1516 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001517
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001518 def expanduser(self):
1519 """ Return a new path with expanded ~ and ~user constructs
1520 (as returned by os.path.expanduser)
1521 """
1522 if (not (self._drv or self._root) and
1523 self._parts and self._parts[0][:1] == '~'):
1524 homedir = self._flavour.gethomedir(self._parts[0][1:])
1525 return self._from_parts([homedir] + self._parts[1:])
1526
1527 return self
1528
Antoine Pitrou31119e42013-11-22 17:38:12 +01001529
1530class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001531 """Path subclass for non-Windows systems.
1532
1533 On a POSIX system, instantiating a Path should return this object.
1534 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001535 __slots__ = ()
1536
1537class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001538 """Path subclass for Windows systems.
1539
1540 On a Windows system, instantiating a Path should return this object.
1541 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001542 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001543
1544 def owner(self):
1545 raise NotImplementedError("Path.owner() is unsupported on this system")
1546
1547 def group(self):
1548 raise NotImplementedError("Path.group() is unsupported on this system")
Cooper Lees173ff4a2017-08-01 15:35:45 -07001549
1550 def is_mount(self):
1551 raise NotImplementedError("Path.is_mount() is unsupported on this system")