blob: 24437f8f95d13bad831863f34b35876696d1a755 [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
Miss Islington (bot)aea49b12019-05-21 12:05:08 -070010from 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
penguindustinb2d29bf2019-05-06 16:55:19 -040037# EBADF - guard against macOS `stat` throwing EBADF
Miss Islington (bot)aea49b12019-05-21 12:05:08 -070038_IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF, ELOOP)
Miss Islington (bot)2cf33162018-08-27 18:37:18 -040039
Miss Islington (bot)69af4392019-02-03 23:27:37 -080040_IGNORED_WINERRORS = (
41 21, # ERROR_NOT_READY - drive exists but is not accessible
Miss Islington (bot)aea49b12019-05-21 12:05:08 -070042 1921, # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself
Miss Islington (bot)69af4392019-02-03 23:27:37 -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
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200415 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100416
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200417 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100418
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200419 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100420
421 if nt:
422 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200423 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100424 else:
425 def symlink(a, b, target_is_directory):
426 raise NotImplementedError("symlink() not available on this system")
427 else:
428 # Under POSIX, os.symlink() takes two args
429 @staticmethod
430 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200431 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100432
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200433 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100434
435 # Helper for resolve()
436 def readlink(self, path):
437 return os.readlink(path)
438
439
440_normal_accessor = _NormalAccessor()
441
442
443#
444# Globbing helpers
445#
446
Antoine Pitrou31119e42013-11-22 17:38:12 +0100447def _make_selector(pattern_parts):
448 pat = pattern_parts[0]
449 child_parts = pattern_parts[1:]
450 if pat == '**':
451 cls = _RecursiveWildcardSelector
452 elif '**' in pat:
453 raise ValueError("Invalid pattern: '**' can only be an entire path component")
454 elif _is_wildcard_pattern(pat):
455 cls = _WildcardSelector
456 else:
457 cls = _PreciseSelector
458 return cls(pat, child_parts)
459
460if hasattr(functools, "lru_cache"):
461 _make_selector = functools.lru_cache()(_make_selector)
462
463
464class _Selector:
465 """A selector matches a specific glob pattern part against the children
466 of a given path."""
467
468 def __init__(self, child_parts):
469 self.child_parts = child_parts
470 if child_parts:
471 self.successor = _make_selector(child_parts)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300472 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100473 else:
474 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300475 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100476
477 def select_from(self, parent_path):
478 """Iterate over all child paths of `parent_path` matched by this
479 selector. This can contain parent_path itself."""
480 path_cls = type(parent_path)
481 is_dir = path_cls.is_dir
482 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300483 scandir = parent_path._accessor.scandir
484 if not is_dir(parent_path):
485 return iter([])
486 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100487
488
489class _TerminatingSelector:
490
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300491 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100492 yield parent_path
493
494
495class _PreciseSelector(_Selector):
496
497 def __init__(self, name, child_parts):
498 self.name = name
499 _Selector.__init__(self, child_parts)
500
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300501 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800502 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800503 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300504 if (is_dir if self.dironly else exists)(path):
505 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800506 yield p
507 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100508 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100509
510
511class _WildcardSelector(_Selector):
512
513 def __init__(self, pat, child_parts):
514 self.pat = re.compile(fnmatch.translate(pat))
515 _Selector.__init__(self, child_parts)
516
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300517 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800518 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800519 cf = parent_path._flavour.casefold
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300520 entries = list(scandir(parent_path))
521 for entry in entries:
Miss Islington (bot)aea49b12019-05-21 12:05:08 -0700522 entry_is_dir = False
523 try:
524 entry_is_dir = entry.is_dir()
525 except OSError as e:
526 if not _ignore_error(e):
527 raise
528 if not self.dironly or entry_is_dir:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300529 name = entry.name
530 casefolded = cf(name)
531 if self.pat.match(casefolded):
532 path = parent_path._make_child_relpath(name)
533 for p in self.successor._select_from(path, is_dir, exists, scandir):
534 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800535 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100536 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800537
Antoine Pitrou31119e42013-11-22 17:38:12 +0100538
539
540class _RecursiveWildcardSelector(_Selector):
541
542 def __init__(self, pat, child_parts):
543 _Selector.__init__(self, child_parts)
544
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300545 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100546 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800547 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300548 entries = list(scandir(parent_path))
549 for entry in entries:
Miss Islington (bot)2cf33162018-08-27 18:37:18 -0400550 entry_is_dir = False
551 try:
552 entry_is_dir = entry.is_dir()
553 except OSError as e:
Miss Islington (bot)69af4392019-02-03 23:27:37 -0800554 if not _ignore_error(e):
Miss Islington (bot)2cf33162018-08-27 18:37:18 -0400555 raise
556 if entry_is_dir and not entry.is_symlink():
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300557 path = parent_path._make_child_relpath(entry.name)
558 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800559 yield p
560 except PermissionError:
561 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100562
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300563 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800564 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300565 yielded = set()
566 try:
567 successor_select = self.successor._select_from
568 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
569 for p in successor_select(starting_point, is_dir, exists, scandir):
570 if p not in yielded:
571 yield p
572 yielded.add(p)
573 finally:
574 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800575 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100576 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100577
578
579#
580# Public API
581#
582
583class _PathParents(Sequence):
584 """This object provides sequence-like access to the logical ancestors
585 of a path. Don't try to construct it yourself."""
586 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
587
588 def __init__(self, path):
589 # We don't store the instance to avoid reference cycles
590 self._pathcls = type(path)
591 self._drv = path._drv
592 self._root = path._root
593 self._parts = path._parts
594
595 def __len__(self):
596 if self._drv or self._root:
597 return len(self._parts) - 1
598 else:
599 return len(self._parts)
600
601 def __getitem__(self, idx):
602 if idx < 0 or idx >= len(self):
603 raise IndexError(idx)
604 return self._pathcls._from_parsed_parts(self._drv, self._root,
605 self._parts[:-idx - 1])
606
607 def __repr__(self):
608 return "<{}.parents>".format(self._pathcls.__name__)
609
610
611class PurePath(object):
Miss Islington (bot)19b7f662018-02-18 16:48:07 -0800612 """Base class for manipulating paths without I/O.
613
614 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100615 don't imply any actual filesystem I/O. Depending on your system,
616 instantiating a PurePath will return either a PurePosixPath or a
617 PureWindowsPath object. You can also instantiate either of these classes
618 directly, regardless of your system.
619 """
620 __slots__ = (
621 '_drv', '_root', '_parts',
622 '_str', '_hash', '_pparts', '_cached_cparts',
623 )
624
625 def __new__(cls, *args):
626 """Construct a PurePath from one or several strings and or existing
627 PurePath objects. The strings and path objects are combined so as
628 to yield a canonicalized path, which is incorporated into the
629 new PurePath object.
630 """
631 if cls is PurePath:
632 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
633 return cls._from_parts(args)
634
635 def __reduce__(self):
636 # Using the parts tuple helps share interned path parts
637 # when pickling related paths.
638 return (self.__class__, tuple(self._parts))
639
640 @classmethod
641 def _parse_args(cls, args):
642 # This is useful when you don't want to create an instance, just
643 # canonicalize some constructor arguments.
644 parts = []
645 for a in args:
646 if isinstance(a, PurePath):
647 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100648 else:
Brett Cannon568be632016-06-10 12:20:49 -0700649 a = os.fspath(a)
650 if isinstance(a, str):
651 # Force-cast str subclasses to str (issue #21127)
652 parts.append(str(a))
653 else:
654 raise TypeError(
655 "argument should be a str object or an os.PathLike "
656 "object returning str, not %r"
657 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100658 return cls._flavour.parse_parts(parts)
659
660 @classmethod
661 def _from_parts(cls, args, init=True):
662 # We need to call _parse_args on the instance, so as to get the
663 # right flavour.
664 self = object.__new__(cls)
665 drv, root, parts = self._parse_args(args)
666 self._drv = drv
667 self._root = root
668 self._parts = parts
669 if init:
670 self._init()
671 return self
672
673 @classmethod
674 def _from_parsed_parts(cls, drv, root, parts, init=True):
675 self = object.__new__(cls)
676 self._drv = drv
677 self._root = root
678 self._parts = parts
679 if init:
680 self._init()
681 return self
682
683 @classmethod
684 def _format_parsed_parts(cls, drv, root, parts):
685 if drv or root:
686 return drv + root + cls._flavour.join(parts[1:])
687 else:
688 return cls._flavour.join(parts)
689
690 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000691 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100692 pass
693
694 def _make_child(self, args):
695 drv, root, parts = self._parse_args(args)
696 drv, root, parts = self._flavour.join_parsed_parts(
697 self._drv, self._root, self._parts, drv, root, parts)
698 return self._from_parsed_parts(drv, root, parts)
699
700 def __str__(self):
701 """Return the string representation of the path, suitable for
702 passing to system calls."""
703 try:
704 return self._str
705 except AttributeError:
706 self._str = self._format_parsed_parts(self._drv, self._root,
707 self._parts) or '.'
708 return self._str
709
Brett Cannon568be632016-06-10 12:20:49 -0700710 def __fspath__(self):
711 return str(self)
712
Antoine Pitrou31119e42013-11-22 17:38:12 +0100713 def as_posix(self):
714 """Return the string representation of the path with forward (/)
715 slashes."""
716 f = self._flavour
717 return str(self).replace(f.sep, '/')
718
719 def __bytes__(self):
720 """Return the bytes representation of the path. This is only
721 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200722 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100723
724 def __repr__(self):
725 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
726
727 def as_uri(self):
728 """Return the path as a 'file' URI."""
729 if not self.is_absolute():
730 raise ValueError("relative path can't be expressed as a file URI")
731 return self._flavour.make_uri(self)
732
733 @property
734 def _cparts(self):
735 # Cached casefolded parts, for hashing and comparison
736 try:
737 return self._cached_cparts
738 except AttributeError:
739 self._cached_cparts = self._flavour.casefold_parts(self._parts)
740 return self._cached_cparts
741
742 def __eq__(self, other):
743 if not isinstance(other, PurePath):
744 return NotImplemented
745 return self._cparts == other._cparts and self._flavour is other._flavour
746
Antoine Pitrou31119e42013-11-22 17:38:12 +0100747 def __hash__(self):
748 try:
749 return self._hash
750 except AttributeError:
751 self._hash = hash(tuple(self._cparts))
752 return self._hash
753
754 def __lt__(self, other):
755 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
756 return NotImplemented
757 return self._cparts < other._cparts
758
759 def __le__(self, other):
760 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
761 return NotImplemented
762 return self._cparts <= other._cparts
763
764 def __gt__(self, other):
765 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
766 return NotImplemented
767 return self._cparts > other._cparts
768
769 def __ge__(self, other):
770 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
771 return NotImplemented
772 return self._cparts >= other._cparts
773
774 drive = property(attrgetter('_drv'),
775 doc="""The drive prefix (letter or UNC path), if any.""")
776
777 root = property(attrgetter('_root'),
778 doc="""The root of the path, if any.""")
779
780 @property
781 def anchor(self):
782 """The concatenation of the drive and root, or ''."""
783 anchor = self._drv + self._root
784 return anchor
785
786 @property
787 def name(self):
788 """The final path component, if any."""
789 parts = self._parts
790 if len(parts) == (1 if (self._drv or self._root) else 0):
791 return ''
792 return parts[-1]
793
794 @property
795 def suffix(self):
796 """The final component's last suffix, if any."""
797 name = self.name
798 i = name.rfind('.')
799 if 0 < i < len(name) - 1:
800 return name[i:]
801 else:
802 return ''
803
804 @property
805 def suffixes(self):
806 """A list of the final component's suffixes, if any."""
807 name = self.name
808 if name.endswith('.'):
809 return []
810 name = name.lstrip('.')
811 return ['.' + suffix for suffix in name.split('.')[1:]]
812
813 @property
814 def stem(self):
815 """The final path component, minus its last suffix."""
816 name = self.name
817 i = name.rfind('.')
818 if 0 < i < len(name) - 1:
819 return name[:i]
820 else:
821 return name
822
823 def with_name(self, name):
824 """Return a new path with the file name changed."""
825 if not self.name:
826 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400827 drv, root, parts = self._flavour.parse_parts((name,))
828 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
829 or drv or root or len(parts) != 1):
830 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100831 return self._from_parsed_parts(self._drv, self._root,
832 self._parts[:-1] + [name])
833
834 def with_suffix(self, suffix):
Miss Islington (bot)39fcd992018-08-03 14:45:46 -0700835 """Return a new path with the file suffix changed. If the path
836 has no suffix, add given suffix. If the given suffix is an empty
837 string, remove the suffix from the path.
838 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400839 f = self._flavour
840 if f.sep in suffix or f.altsep and f.altsep in suffix:
Miss Islington (bot)c6141212018-08-10 23:00:11 -0700841 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400842 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100843 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100844 name = self.name
845 if not name:
846 raise ValueError("%r has an empty name" % (self,))
847 old_suffix = self.suffix
848 if not old_suffix:
849 name = name + suffix
850 else:
851 name = name[:-len(old_suffix)] + suffix
852 return self._from_parsed_parts(self._drv, self._root,
853 self._parts[:-1] + [name])
854
855 def relative_to(self, *other):
856 """Return the relative path to another path identified by the passed
857 arguments. If the operation is not possible (because this is not
858 a subpath of the other path), raise ValueError.
859 """
860 # For the purpose of this method, drive and root are considered
861 # separate parts, i.e.:
862 # Path('c:/').relative_to('c:') gives Path('/')
863 # Path('c:/').relative_to('/') raise ValueError
864 if not other:
865 raise TypeError("need at least one argument")
866 parts = self._parts
867 drv = self._drv
868 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100869 if root:
870 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100871 else:
872 abs_parts = parts
873 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100874 if to_root:
875 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100876 else:
877 to_abs_parts = to_parts
878 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100879 cf = self._flavour.casefold_parts
880 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100881 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
882 raise ValueError("{!r} does not start with {!r}"
883 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100884 return self._from_parsed_parts('', root if n == 1 else '',
885 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100886
887 @property
888 def parts(self):
889 """An object providing sequence-like access to the
890 components in the filesystem path."""
891 # We cache the tuple to avoid building a new one each time .parts
892 # is accessed. XXX is this necessary?
893 try:
894 return self._pparts
895 except AttributeError:
896 self._pparts = tuple(self._parts)
897 return self._pparts
898
899 def joinpath(self, *args):
900 """Combine this path with one or several arguments, and return a
901 new path representing either a subpath (if all arguments are relative
902 paths) or a totally different path (if one of the arguments is
903 anchored).
904 """
905 return self._make_child(args)
906
907 def __truediv__(self, key):
908 return self._make_child((key,))
909
910 def __rtruediv__(self, key):
911 return self._from_parts([key] + self._parts)
912
913 @property
914 def parent(self):
915 """The logical parent of the path."""
916 drv = self._drv
917 root = self._root
918 parts = self._parts
919 if len(parts) == 1 and (drv or root):
920 return self
921 return self._from_parsed_parts(drv, root, parts[:-1])
922
923 @property
924 def parents(self):
925 """A sequence of this path's logical parents."""
926 return _PathParents(self)
927
928 def is_absolute(self):
929 """True if the path is absolute (has both a root and, if applicable,
930 a drive)."""
931 if not self._root:
932 return False
933 return not self._flavour.has_drv or bool(self._drv)
934
935 def is_reserved(self):
936 """Return True if the path contains one of the special names reserved
937 by the system, if any."""
938 return self._flavour.is_reserved(self._parts)
939
940 def match(self, path_pattern):
941 """
942 Return True if this path matches the given pattern.
943 """
944 cf = self._flavour.casefold
945 path_pattern = cf(path_pattern)
946 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
947 if not pat_parts:
948 raise ValueError("empty pattern")
949 if drv and drv != cf(self._drv):
950 return False
951 if root and root != cf(self._root):
952 return False
953 parts = self._cparts
954 if drv or root:
955 if len(pat_parts) != len(parts):
956 return False
957 pat_parts = pat_parts[1:]
958 elif len(pat_parts) > len(parts):
959 return False
960 for part, pat in zip(reversed(parts), reversed(pat_parts)):
961 if not fnmatch.fnmatchcase(part, pat):
962 return False
963 return True
964
Brett Cannon568be632016-06-10 12:20:49 -0700965# Can't subclass os.PathLike from PurePath and keep the constructor
966# optimizations in PurePath._parse_args().
967os.PathLike.register(PurePath)
968
Antoine Pitrou31119e42013-11-22 17:38:12 +0100969
970class PurePosixPath(PurePath):
Miss Islington (bot)19b7f662018-02-18 16:48:07 -0800971 """PurePath subclass for non-Windows systems.
972
973 On a POSIX system, instantiating a PurePath should return this object.
974 However, you can also instantiate it directly on any system.
975 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100976 _flavour = _posix_flavour
977 __slots__ = ()
978
979
980class PureWindowsPath(PurePath):
Miss Islington (bot)19b7f662018-02-18 16:48:07 -0800981 """PurePath subclass for Windows systems.
982
983 On a Windows system, instantiating a PurePath should return this object.
984 However, you can also instantiate it directly on any system.
985 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100986 _flavour = _windows_flavour
987 __slots__ = ()
988
989
990# Filesystem-accessing classes
991
992
993class Path(PurePath):
Miss Islington (bot)19b7f662018-02-18 16:48:07 -0800994 """PurePath subclass that can make system calls.
995
996 Path represents a filesystem path but unlike PurePath, also offers
997 methods to do system calls on path objects. Depending on your system,
998 instantiating a Path will return either a PosixPath or a WindowsPath
999 object. You can also instantiate a PosixPath or WindowsPath directly,
1000 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
1001 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001002 __slots__ = (
1003 '_accessor',
1004 '_closed',
1005 )
1006
1007 def __new__(cls, *args, **kwargs):
1008 if cls is Path:
1009 cls = WindowsPath if os.name == 'nt' else PosixPath
1010 self = cls._from_parts(args, init=False)
1011 if not self._flavour.is_supported:
1012 raise NotImplementedError("cannot instantiate %r on your system"
1013 % (cls.__name__,))
1014 self._init()
1015 return self
1016
1017 def _init(self,
1018 # Private non-constructor arguments
1019 template=None,
1020 ):
1021 self._closed = False
1022 if template is not None:
1023 self._accessor = template._accessor
1024 else:
1025 self._accessor = _normal_accessor
1026
1027 def _make_child_relpath(self, part):
1028 # This is an optimization used for dir walking. `part` must be
1029 # a single part relative to this path.
1030 parts = self._parts + [part]
1031 return self._from_parsed_parts(self._drv, self._root, parts)
1032
1033 def __enter__(self):
1034 if self._closed:
1035 self._raise_closed()
1036 return self
1037
1038 def __exit__(self, t, v, tb):
1039 self._closed = True
1040
1041 def _raise_closed(self):
1042 raise ValueError("I/O operation on closed path")
1043
1044 def _opener(self, name, flags, mode=0o666):
1045 # A stub for the opener argument to built-in open()
1046 return self._accessor.open(self, flags, mode)
1047
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001048 def _raw_open(self, flags, mode=0o777):
1049 """
1050 Open the file pointed by this path and return a file descriptor,
1051 as os.open() does.
1052 """
1053 if self._closed:
1054 self._raise_closed()
1055 return self._accessor.open(self, flags, mode)
1056
Antoine Pitrou31119e42013-11-22 17:38:12 +01001057 # Public API
1058
1059 @classmethod
1060 def cwd(cls):
1061 """Return a new path pointing to the current working directory
1062 (as returned by os.getcwd()).
1063 """
1064 return cls(os.getcwd())
1065
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001066 @classmethod
1067 def home(cls):
1068 """Return a new path pointing to the user's home directory (as
1069 returned by os.path.expanduser('~')).
1070 """
1071 return cls(cls()._flavour.gethomedir(None))
1072
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001073 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001074 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001075 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001076 """
1077 st = self.stat()
1078 try:
1079 other_st = other_path.stat()
1080 except AttributeError:
1081 other_st = os.stat(other_path)
1082 return os.path.samestat(st, other_st)
1083
Antoine Pitrou31119e42013-11-22 17:38:12 +01001084 def iterdir(self):
1085 """Iterate over the files in this directory. Does not yield any
1086 result for the special paths '.' and '..'.
1087 """
1088 if self._closed:
1089 self._raise_closed()
1090 for name in self._accessor.listdir(self):
1091 if name in {'.', '..'}:
1092 # Yielding a path object for these makes little sense
1093 continue
1094 yield self._make_child_relpath(name)
1095 if self._closed:
1096 self._raise_closed()
1097
1098 def glob(self, pattern):
1099 """Iterate over this subtree and yield all existing files (of any
Miss Islington (bot)2259b5a2019-02-11 04:02:57 -08001100 kind, including directories) matching the given relative pattern.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001101 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001102 if not pattern:
1103 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001104 pattern = self._flavour.casefold(pattern)
1105 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1106 if drv or root:
1107 raise NotImplementedError("Non-relative patterns are unsupported")
1108 selector = _make_selector(tuple(pattern_parts))
1109 for p in selector.select_from(self):
1110 yield p
1111
1112 def rglob(self, pattern):
1113 """Recursively yield all existing files (of any kind, including
Miss Islington (bot)2259b5a2019-02-11 04:02:57 -08001114 directories) matching the given relative pattern, anywhere in
1115 this subtree.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001116 """
1117 pattern = self._flavour.casefold(pattern)
1118 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1119 if drv or root:
1120 raise NotImplementedError("Non-relative patterns are unsupported")
1121 selector = _make_selector(("**",) + tuple(pattern_parts))
1122 for p in selector.select_from(self):
1123 yield p
1124
1125 def absolute(self):
1126 """Return an absolute version of this path. This function works
1127 even if the path doesn't point to anything.
1128
1129 No normalization is done, i.e. all '.' and '..' will be kept along.
1130 Use resolve() to get the canonical path to a file.
1131 """
1132 # XXX untested yet!
1133 if self._closed:
1134 self._raise_closed()
1135 if self.is_absolute():
1136 return self
1137 # FIXME this must defer to the specific flavour (and, under Windows,
1138 # use nt._getfullpathname())
1139 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1140 obj._init(template=self)
1141 return obj
1142
Steve Dower98eb3602016-11-09 12:58:17 -08001143 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001144 """
1145 Make the path absolute, resolving all symlinks on the way and also
1146 normalizing it (for example turning slashes into backslashes under
1147 Windows).
1148 """
1149 if self._closed:
1150 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001151 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001152 if s is None:
1153 # No symlink resolution => for consistency, raise an error if
1154 # the path doesn't exist or is forbidden
1155 self.stat()
1156 s = str(self.absolute())
1157 # Now we have no symlinks in the path, it's safe to normalize it.
1158 normed = self._flavour.pathmod.normpath(s)
1159 obj = self._from_parts((normed,), init=False)
1160 obj._init(template=self)
1161 return obj
1162
1163 def stat(self):
1164 """
1165 Return the result of the stat() system call on this path, like
1166 os.stat() does.
1167 """
1168 return self._accessor.stat(self)
1169
1170 def owner(self):
1171 """
1172 Return the login name of the file owner.
1173 """
1174 import pwd
1175 return pwd.getpwuid(self.stat().st_uid).pw_name
1176
1177 def group(self):
1178 """
1179 Return the group name of the file gid.
1180 """
1181 import grp
1182 return grp.getgrgid(self.stat().st_gid).gr_name
1183
Antoine Pitrou31119e42013-11-22 17:38:12 +01001184 def open(self, mode='r', buffering=-1, encoding=None,
1185 errors=None, newline=None):
1186 """
1187 Open the file pointed by this path and return a file object, as
1188 the built-in open() function does.
1189 """
1190 if self._closed:
1191 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001192 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001193 opener=self._opener)
1194
Georg Brandlea683982014-10-01 19:12:33 +02001195 def read_bytes(self):
1196 """
1197 Open the file in bytes mode, read it, and close the file.
1198 """
1199 with self.open(mode='rb') as f:
1200 return f.read()
1201
1202 def read_text(self, encoding=None, errors=None):
1203 """
1204 Open the file in text mode, read it, and close the file.
1205 """
1206 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1207 return f.read()
1208
1209 def write_bytes(self, data):
1210 """
1211 Open the file in bytes mode, write to it, and close the file.
1212 """
1213 # type-check for the buffer interface before truncating the file
1214 view = memoryview(data)
1215 with self.open(mode='wb') as f:
1216 return f.write(view)
1217
1218 def write_text(self, data, encoding=None, errors=None):
1219 """
1220 Open the file in text mode, write to it, and close the file.
1221 """
1222 if not isinstance(data, str):
1223 raise TypeError('data must be str, not %s' %
1224 data.__class__.__name__)
1225 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1226 return f.write(data)
1227
Antoine Pitrou31119e42013-11-22 17:38:12 +01001228 def touch(self, mode=0o666, exist_ok=True):
1229 """
1230 Create this file with the given access mode, if it doesn't exist.
1231 """
1232 if self._closed:
1233 self._raise_closed()
1234 if exist_ok:
1235 # First try to bump modification time
1236 # Implementation note: GNU touch uses the UTIME_NOW option of
1237 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001238 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001239 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001240 except OSError:
1241 # Avoid exception chaining
1242 pass
1243 else:
1244 return
1245 flags = os.O_CREAT | os.O_WRONLY
1246 if not exist_ok:
1247 flags |= os.O_EXCL
1248 fd = self._raw_open(flags, mode)
1249 os.close(fd)
1250
Barry Warsaw7c549c42014-08-05 11:28:12 -04001251 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001252 """
1253 Create a new directory at this given path.
1254 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001255 if self._closed:
1256 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001257 try:
1258 self._accessor.mkdir(self, mode)
1259 except FileNotFoundError:
1260 if not parents or self.parent == self:
1261 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001262 self.parent.mkdir(parents=True, exist_ok=True)
1263 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001264 except OSError:
1265 # Cannot rely on checking for EEXIST, since the operating system
1266 # could give priority to other errors like EACCES or EROFS
1267 if not exist_ok or not self.is_dir():
1268 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001269
1270 def chmod(self, mode):
1271 """
1272 Change the permissions of the path, like os.chmod().
1273 """
1274 if self._closed:
1275 self._raise_closed()
1276 self._accessor.chmod(self, mode)
1277
1278 def lchmod(self, mode):
1279 """
1280 Like chmod(), except if the path points to a symlink, the symlink's
1281 permissions are changed, rather than its target's.
1282 """
1283 if self._closed:
1284 self._raise_closed()
1285 self._accessor.lchmod(self, mode)
1286
1287 def unlink(self):
1288 """
1289 Remove this file or link.
1290 If the path is a directory, use rmdir() instead.
1291 """
1292 if self._closed:
1293 self._raise_closed()
1294 self._accessor.unlink(self)
1295
1296 def rmdir(self):
1297 """
1298 Remove this directory. The directory must be empty.
1299 """
1300 if self._closed:
1301 self._raise_closed()
1302 self._accessor.rmdir(self)
1303
1304 def lstat(self):
1305 """
1306 Like stat(), except if the path points to a symlink, the symlink's
1307 status information is returned, rather than its target's.
1308 """
1309 if self._closed:
1310 self._raise_closed()
1311 return self._accessor.lstat(self)
1312
1313 def rename(self, target):
1314 """
1315 Rename this path to the given path.
1316 """
1317 if self._closed:
1318 self._raise_closed()
1319 self._accessor.rename(self, target)
1320
1321 def replace(self, target):
1322 """
1323 Rename this path to the given path, clobbering the existing
1324 destination if it exists.
1325 """
1326 if self._closed:
1327 self._raise_closed()
1328 self._accessor.replace(self, target)
1329
1330 def symlink_to(self, target, target_is_directory=False):
1331 """
1332 Make this path a symlink pointing to the given path.
1333 Note the order of arguments (self, target) is the reverse of os.symlink's.
1334 """
1335 if self._closed:
1336 self._raise_closed()
1337 self._accessor.symlink(target, self, target_is_directory)
1338
1339 # Convenience functions for querying the stat results
1340
1341 def exists(self):
1342 """
1343 Whether this path exists.
1344 """
1345 try:
1346 self.stat()
1347 except OSError as e:
Miss Islington (bot)69af4392019-02-03 23:27:37 -08001348 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001349 raise
1350 return False
1351 return True
1352
1353 def is_dir(self):
1354 """
1355 Whether this path is a directory.
1356 """
1357 try:
1358 return S_ISDIR(self.stat().st_mode)
1359 except OSError as e:
Miss Islington (bot)69af4392019-02-03 23:27:37 -08001360 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001361 raise
1362 # Path doesn't exist or is a broken symlink
1363 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1364 return False
1365
1366 def is_file(self):
1367 """
1368 Whether this path is a regular file (also True for symlinks pointing
1369 to regular files).
1370 """
1371 try:
1372 return S_ISREG(self.stat().st_mode)
1373 except OSError as e:
Miss Islington (bot)69af4392019-02-03 23:27:37 -08001374 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001375 raise
1376 # Path doesn't exist or is a broken symlink
1377 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1378 return False
1379
Cooper Lees173ff4a2017-08-01 15:35:45 -07001380 def is_mount(self):
1381 """
1382 Check if this path is a POSIX mount point
1383 """
1384 # Need to exist and be a dir
1385 if not self.exists() or not self.is_dir():
1386 return False
1387
1388 parent = Path(self.parent)
1389 try:
1390 parent_dev = parent.stat().st_dev
1391 except OSError:
1392 return False
1393
1394 dev = self.stat().st_dev
1395 if dev != parent_dev:
1396 return True
1397 ino = self.stat().st_ino
1398 parent_ino = parent.stat().st_ino
1399 return ino == parent_ino
1400
Antoine Pitrou31119e42013-11-22 17:38:12 +01001401 def is_symlink(self):
1402 """
1403 Whether this path is a symbolic link.
1404 """
1405 try:
1406 return S_ISLNK(self.lstat().st_mode)
1407 except OSError as e:
Miss Islington (bot)69af4392019-02-03 23:27:37 -08001408 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001409 raise
1410 # Path doesn't exist
1411 return False
1412
1413 def is_block_device(self):
1414 """
1415 Whether this path is a block device.
1416 """
1417 try:
1418 return S_ISBLK(self.stat().st_mode)
1419 except OSError as e:
Miss Islington (bot)69af4392019-02-03 23:27:37 -08001420 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001421 raise
1422 # Path doesn't exist or is a broken symlink
1423 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1424 return False
1425
1426 def is_char_device(self):
1427 """
1428 Whether this path is a character device.
1429 """
1430 try:
1431 return S_ISCHR(self.stat().st_mode)
1432 except OSError as e:
Miss Islington (bot)69af4392019-02-03 23:27:37 -08001433 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001434 raise
1435 # Path doesn't exist or is a broken symlink
1436 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1437 return False
1438
1439 def is_fifo(self):
1440 """
1441 Whether this path is a FIFO.
1442 """
1443 try:
1444 return S_ISFIFO(self.stat().st_mode)
1445 except OSError as e:
Miss Islington (bot)69af4392019-02-03 23:27:37 -08001446 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001447 raise
1448 # Path doesn't exist or is a broken symlink
1449 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1450 return False
1451
1452 def is_socket(self):
1453 """
1454 Whether this path is a socket.
1455 """
1456 try:
1457 return S_ISSOCK(self.stat().st_mode)
1458 except OSError as e:
Miss Islington (bot)69af4392019-02-03 23:27:37 -08001459 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001460 raise
1461 # Path doesn't exist or is a broken symlink
1462 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1463 return False
1464
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001465 def expanduser(self):
1466 """ Return a new path with expanded ~ and ~user constructs
1467 (as returned by os.path.expanduser)
1468 """
1469 if (not (self._drv or self._root) and
1470 self._parts and self._parts[0][:1] == '~'):
1471 homedir = self._flavour.gethomedir(self._parts[0][1:])
1472 return self._from_parts([homedir] + self._parts[1:])
1473
1474 return self
1475
Antoine Pitrou31119e42013-11-22 17:38:12 +01001476
1477class PosixPath(Path, PurePosixPath):
Miss Islington (bot)19b7f662018-02-18 16:48:07 -08001478 """Path subclass for non-Windows systems.
1479
1480 On a POSIX system, instantiating a Path should return this object.
1481 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001482 __slots__ = ()
1483
1484class WindowsPath(Path, PureWindowsPath):
Miss Islington (bot)19b7f662018-02-18 16:48:07 -08001485 """Path subclass for Windows systems.
1486
1487 On a Windows system, instantiating a Path should return this object.
1488 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001489 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001490
1491 def owner(self):
1492 raise NotImplementedError("Path.owner() is unsupported on this system")
1493
1494 def group(self):
1495 raise NotImplementedError("Path.group() is unsupported on this system")
Cooper Lees173ff4a2017-08-01 15:35:45 -07001496
1497 def is_mount(self):
1498 raise NotImplementedError("Path.is_mount() is unsupported on this system")