blob: 911b774b5649846c27c2995ce4a55a0727d5c0c9 [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
Przemysław Spodymek216b7452018-08-27 23:33:45 +020010from errno import EINVAL, ENOENT, ENOTDIR, EBADF
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
Przemysław Spodymek216b7452018-08-27 23:33:45 +020037# EBADF - guard agains macOS `stat` throwing EBADF
38_IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF)
39
Steve Dower2f6fae62019-02-03 23:08:18 -080040_IGNORED_WINERRORS = (
41 21, # ERROR_NOT_READY - drive exists but is not accessible
42)
43
44def _ignore_error(exception):
45 return (getattr(exception, 'errno', None) in _IGNORED_ERROS or
46 getattr(exception, 'winerror', None) in _IGNORED_WINERRORS)
47
48
Antoine Pitrou31119e42013-11-22 17:38:12 +010049def _is_wildcard_pattern(pat):
50 # Whether this pattern needs actual matching using fnmatch, or can
51 # be looked up directly as a file.
52 return "*" in pat or "?" in pat or "[" in pat
53
54
55class _Flavour(object):
56 """A flavour implements a particular (platform-specific) set of path
57 semantics."""
58
59 def __init__(self):
60 self.join = self.sep.join
61
62 def parse_parts(self, parts):
63 parsed = []
64 sep = self.sep
65 altsep = self.altsep
66 drv = root = ''
67 it = reversed(parts)
68 for part in it:
69 if not part:
70 continue
71 if altsep:
72 part = part.replace(altsep, sep)
73 drv, root, rel = self.splitroot(part)
74 if sep in rel:
75 for x in reversed(rel.split(sep)):
76 if x and x != '.':
77 parsed.append(sys.intern(x))
78 else:
79 if rel and rel != '.':
80 parsed.append(sys.intern(rel))
81 if drv or root:
82 if not drv:
83 # If no drive is present, try to find one in the previous
84 # parts. This makes the result of parsing e.g.
85 # ("C:", "/", "a") reasonably intuitive.
86 for part in it:
Antoine Pitrou57fffd62015-02-15 18:03:59 +010087 if not part:
88 continue
89 if altsep:
90 part = part.replace(altsep, sep)
Antoine Pitrou31119e42013-11-22 17:38:12 +010091 drv = self.splitroot(part)[0]
92 if drv:
93 break
94 break
95 if drv or root:
96 parsed.append(drv + root)
97 parsed.reverse()
98 return drv, root, parsed
99
100 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
101 """
102 Join the two paths represented by the respective
103 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple.
104 """
105 if root2:
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200106 if not drv2 and drv:
107 return drv, root2, [drv + root2] + parts2[1:]
108 elif drv2:
109 if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
110 # Same drive => second path is relative to the first
111 return drv, root, parts + parts2[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100112 else:
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200113 # Second path is non-anchored (common case)
114 return drv, root, parts + parts2
115 return drv2, root2, parts2
Antoine Pitrou31119e42013-11-22 17:38:12 +0100116
117
118class _WindowsFlavour(_Flavour):
119 # Reference for Windows paths can be found at
120 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
121
122 sep = '\\'
123 altsep = '/'
124 has_drv = True
125 pathmod = ntpath
126
Antoine Pitroudb118f52014-11-19 00:32:08 +0100127 is_supported = (os.name == 'nt')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100128
Jon Dufresne39726282017-05-18 07:35:54 -0700129 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100130 ext_namespace_prefix = '\\\\?\\'
131
132 reserved_names = (
133 {'CON', 'PRN', 'AUX', 'NUL'} |
134 {'COM%d' % i for i in range(1, 10)} |
135 {'LPT%d' % i for i in range(1, 10)}
136 )
137
138 # Interesting findings about extended paths:
139 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
140 # but '\\?\c:/a' is not
141 # - extended paths are always absolute; "relative" extended paths will
142 # fail.
143
144 def splitroot(self, part, sep=sep):
145 first = part[0:1]
146 second = part[1:2]
147 if (second == sep and first == sep):
148 # XXX extended paths should also disable the collapsing of "."
149 # components (according to MSDN docs).
150 prefix, part = self._split_extended_path(part)
151 first = part[0:1]
152 second = part[1:2]
153 else:
154 prefix = ''
155 third = part[2:3]
156 if (second == sep and first == sep and third != sep):
157 # is a UNC path:
158 # vvvvvvvvvvvvvvvvvvvvv root
159 # \\machine\mountpoint\directory\etc\...
160 # directory ^^^^^^^^^^^^^^
161 index = part.find(sep, 2)
162 if index != -1:
163 index2 = part.find(sep, index + 1)
164 # a UNC path can't have two slashes in a row
165 # (after the initial two)
166 if index2 != index + 1:
167 if index2 == -1:
168 index2 = len(part)
169 if prefix:
170 return prefix + part[1:index2], sep, part[index2+1:]
171 else:
172 return part[:index2], sep, part[index2+1:]
173 drv = root = ''
174 if second == ':' and first in self.drive_letters:
175 drv = part[:2]
176 part = part[2:]
177 first = third
178 if first == sep:
179 root = first
180 part = part.lstrip(sep)
181 return prefix + drv, root, part
182
183 def casefold(self, s):
184 return s.lower()
185
186 def casefold_parts(self, parts):
187 return [p.lower() for p in parts]
188
Steve Dower98eb3602016-11-09 12:58:17 -0800189 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100190 s = str(path)
191 if not s:
192 return os.getcwd()
Steve Dower98eb3602016-11-09 12:58:17 -0800193 previous_s = None
Antoine Pitrou31119e42013-11-22 17:38:12 +0100194 if _getfinalpathname is not None:
Steve Dower98eb3602016-11-09 12:58:17 -0800195 if strict:
196 return self._ext_to_normal(_getfinalpathname(s))
197 else:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200198 tail_parts = [] # End of the path after the first one not found
Steve Dower98eb3602016-11-09 12:58:17 -0800199 while True:
200 try:
201 s = self._ext_to_normal(_getfinalpathname(s))
202 except FileNotFoundError:
203 previous_s = s
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200204 s, tail = os.path.split(s)
205 tail_parts.append(tail)
Steve Dower4b1e98b2016-12-28 16:02:59 -0800206 if previous_s == s:
207 return path
Steve Dower98eb3602016-11-09 12:58:17 -0800208 else:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200209 return os.path.join(s, *reversed(tail_parts))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100210 # Means fallback on absolute
211 return None
212
213 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
214 prefix = ''
215 if s.startswith(ext_prefix):
216 prefix = s[:4]
217 s = s[4:]
218 if s.startswith('UNC\\'):
219 prefix += s[:3]
220 s = '\\' + s[3:]
221 return prefix, s
222
223 def _ext_to_normal(self, s):
224 # Turn back an extended path into a normal DOS-like path
225 return self._split_extended_path(s)[1]
226
227 def is_reserved(self, parts):
228 # NOTE: the rules for reserved names seem somewhat complicated
229 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
230 # We err on the side of caution and return True for paths which are
231 # not considered reserved by Windows.
232 if not parts:
233 return False
234 if parts[0].startswith('\\\\'):
235 # UNC paths are never reserved
236 return False
237 return parts[-1].partition('.')[0].upper() in self.reserved_names
238
239 def make_uri(self, path):
240 # Under Windows, file URIs use the UTF-8 encoding.
241 drive = path.drive
242 if len(drive) == 2 and drive[1] == ':':
243 # It's a path on a local drive => 'file:///c:/a/b'
244 rest = path.as_posix()[2:].lstrip('/')
245 return 'file:///%s/%s' % (
246 drive, urlquote_from_bytes(rest.encode('utf-8')))
247 else:
248 # It's a path on a network drive => 'file://host/share/a/b'
249 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
250
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100251 def gethomedir(self, username):
252 if 'HOME' in os.environ:
253 userhome = os.environ['HOME']
254 elif 'USERPROFILE' in os.environ:
255 userhome = os.environ['USERPROFILE']
256 elif 'HOMEPATH' in os.environ:
Antoine Pitrou5d4e27e2014-12-30 22:09:42 +0100257 try:
258 drv = os.environ['HOMEDRIVE']
259 except KeyError:
260 drv = ''
261 userhome = drv + os.environ['HOMEPATH']
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100262 else:
263 raise RuntimeError("Can't determine home directory")
264
265 if username:
266 # Try to guess user home directory. By default all users
267 # directories are located in the same place and are named by
268 # corresponding usernames. If current user home directory points
269 # to nonstandard place, this guess is likely wrong.
270 if os.environ['USERNAME'] != username:
271 drv, root, parts = self.parse_parts((userhome,))
272 if parts[-1] != os.environ['USERNAME']:
273 raise RuntimeError("Can't determine home directory "
274 "for %r" % username)
275 parts[-1] = username
276 if drv or root:
277 userhome = drv + root + self.join(parts[1:])
278 else:
279 userhome = self.join(parts)
280 return userhome
Antoine Pitrou31119e42013-11-22 17:38:12 +0100281
282class _PosixFlavour(_Flavour):
283 sep = '/'
284 altsep = ''
285 has_drv = False
286 pathmod = posixpath
287
288 is_supported = (os.name != 'nt')
289
290 def splitroot(self, part, sep=sep):
291 if part and part[0] == sep:
292 stripped_part = part.lstrip(sep)
293 # According to POSIX path resolution:
294 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
295 # "A pathname that begins with two successive slashes may be
296 # interpreted in an implementation-defined manner, although more
297 # than two leading slashes shall be treated as a single slash".
298 if len(part) - len(stripped_part) == 2:
299 return '', sep * 2, stripped_part
300 else:
301 return '', sep, stripped_part
302 else:
303 return '', '', part
304
305 def casefold(self, s):
306 return s
307
308 def casefold_parts(self, parts):
309 return parts
310
Steve Dower98eb3602016-11-09 12:58:17 -0800311 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100312 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100313 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100314 seen = {}
315 def _resolve(path, rest):
316 if rest.startswith(sep):
317 path = ''
318
319 for name in rest.split(sep):
320 if not name or name == '.':
321 # current dir
322 continue
323 if name == '..':
324 # parent dir
325 path, _, _ = path.rpartition(sep)
326 continue
327 newpath = path + sep + name
328 if newpath in seen:
329 # Already seen this path
330 path = seen[newpath]
331 if path is not None:
332 # use cached value
333 continue
334 # The symlink is not resolved, so we must have a symlink loop.
335 raise RuntimeError("Symlink loop from %r" % newpath)
336 # Resolve the symbolic link
337 try:
338 target = accessor.readlink(newpath)
339 except OSError as e:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200340 if e.errno != EINVAL and strict:
341 raise
342 # Not a symlink, or non-strict mode. We just leave the path
343 # untouched.
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100344 path = newpath
345 else:
346 seen[newpath] = None # not resolved symlink
347 path = _resolve(path, target)
348 seen[newpath] = path # resolved symlink
349
350 return path
351 # NOTE: according to POSIX, getcwd() cannot contain path components
352 # which are symlinks.
353 base = '' if path.is_absolute() else os.getcwd()
354 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100355
356 def is_reserved(self, parts):
357 return False
358
359 def make_uri(self, path):
360 # We represent the path using the local filesystem encoding,
361 # for portability to other applications.
362 bpath = bytes(path)
363 return 'file://' + urlquote_from_bytes(bpath)
364
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100365 def gethomedir(self, username):
366 if not username:
367 try:
368 return os.environ['HOME']
369 except KeyError:
370 import pwd
371 return pwd.getpwuid(os.getuid()).pw_dir
372 else:
373 import pwd
374 try:
375 return pwd.getpwnam(username).pw_dir
376 except KeyError:
377 raise RuntimeError("Can't determine home directory "
378 "for %r" % username)
379
Antoine Pitrou31119e42013-11-22 17:38:12 +0100380
381_windows_flavour = _WindowsFlavour()
382_posix_flavour = _PosixFlavour()
383
384
385class _Accessor:
386 """An accessor implements a particular (system-specific or not) way of
387 accessing paths on the filesystem."""
388
389
390class _NormalAccessor(_Accessor):
391
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200392 stat = os.stat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100393
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200394 lstat = os.lstat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100395
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200396 open = os.open
Antoine Pitrou31119e42013-11-22 17:38:12 +0100397
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200398 listdir = os.listdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100399
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200400 scandir = os.scandir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100401
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200402 chmod = os.chmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100403
404 if hasattr(os, "lchmod"):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200405 lchmod = os.lchmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100406 else:
407 def lchmod(self, pathobj, mode):
408 raise NotImplementedError("lchmod() not available on this system")
409
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200410 mkdir = os.mkdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100411
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200412 unlink = os.unlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100413
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200414 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100415
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200416 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100417
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200418 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100419
420 if nt:
421 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200422 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100423 else:
424 def symlink(a, b, target_is_directory):
425 raise NotImplementedError("symlink() not available on this system")
426 else:
427 # Under POSIX, os.symlink() takes two args
428 @staticmethod
429 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200430 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100431
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200432 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100433
434 # Helper for resolve()
435 def readlink(self, path):
436 return os.readlink(path)
437
438
439_normal_accessor = _NormalAccessor()
440
441
442#
443# Globbing helpers
444#
445
Antoine Pitrou31119e42013-11-22 17:38:12 +0100446def _make_selector(pattern_parts):
447 pat = pattern_parts[0]
448 child_parts = pattern_parts[1:]
449 if pat == '**':
450 cls = _RecursiveWildcardSelector
451 elif '**' in pat:
452 raise ValueError("Invalid pattern: '**' can only be an entire path component")
453 elif _is_wildcard_pattern(pat):
454 cls = _WildcardSelector
455 else:
456 cls = _PreciseSelector
457 return cls(pat, child_parts)
458
459if hasattr(functools, "lru_cache"):
460 _make_selector = functools.lru_cache()(_make_selector)
461
462
463class _Selector:
464 """A selector matches a specific glob pattern part against the children
465 of a given path."""
466
467 def __init__(self, child_parts):
468 self.child_parts = child_parts
469 if child_parts:
470 self.successor = _make_selector(child_parts)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300471 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100472 else:
473 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300474 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100475
476 def select_from(self, parent_path):
477 """Iterate over all child paths of `parent_path` matched by this
478 selector. This can contain parent_path itself."""
479 path_cls = type(parent_path)
480 is_dir = path_cls.is_dir
481 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300482 scandir = parent_path._accessor.scandir
483 if not is_dir(parent_path):
484 return iter([])
485 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100486
487
488class _TerminatingSelector:
489
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300490 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100491 yield parent_path
492
493
494class _PreciseSelector(_Selector):
495
496 def __init__(self, name, child_parts):
497 self.name = name
498 _Selector.__init__(self, child_parts)
499
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300500 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800501 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800502 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300503 if (is_dir if self.dironly else exists)(path):
504 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800505 yield p
506 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100507 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100508
509
510class _WildcardSelector(_Selector):
511
512 def __init__(self, pat, child_parts):
513 self.pat = re.compile(fnmatch.translate(pat))
514 _Selector.__init__(self, child_parts)
515
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300516 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800517 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800518 cf = parent_path._flavour.casefold
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300519 entries = list(scandir(parent_path))
520 for entry in entries:
521 if not self.dironly or entry.is_dir():
522 name = entry.name
523 casefolded = cf(name)
524 if self.pat.match(casefolded):
525 path = parent_path._make_child_relpath(name)
526 for p in self.successor._select_from(path, is_dir, exists, scandir):
527 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800528 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100529 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800530
Antoine Pitrou31119e42013-11-22 17:38:12 +0100531
532
533class _RecursiveWildcardSelector(_Selector):
534
535 def __init__(self, pat, child_parts):
536 _Selector.__init__(self, child_parts)
537
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300538 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100539 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800540 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300541 entries = list(scandir(parent_path))
542 for entry in entries:
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200543 entry_is_dir = False
544 try:
545 entry_is_dir = entry.is_dir()
546 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -0800547 if not _ignore_error(e):
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200548 raise
549 if entry_is_dir and not entry.is_symlink():
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300550 path = parent_path._make_child_relpath(entry.name)
551 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800552 yield p
553 except PermissionError:
554 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100555
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300556 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800557 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300558 yielded = set()
559 try:
560 successor_select = self.successor._select_from
561 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
562 for p in successor_select(starting_point, is_dir, exists, scandir):
563 if p not in yielded:
564 yield p
565 yielded.add(p)
566 finally:
567 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800568 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100569 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100570
571
572#
573# Public API
574#
575
576class _PathParents(Sequence):
577 """This object provides sequence-like access to the logical ancestors
578 of a path. Don't try to construct it yourself."""
579 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
580
581 def __init__(self, path):
582 # We don't store the instance to avoid reference cycles
583 self._pathcls = type(path)
584 self._drv = path._drv
585 self._root = path._root
586 self._parts = path._parts
587
588 def __len__(self):
589 if self._drv or self._root:
590 return len(self._parts) - 1
591 else:
592 return len(self._parts)
593
594 def __getitem__(self, idx):
595 if idx < 0 or idx >= len(self):
596 raise IndexError(idx)
597 return self._pathcls._from_parsed_parts(self._drv, self._root,
598 self._parts[:-idx - 1])
599
600 def __repr__(self):
601 return "<{}.parents>".format(self._pathcls.__name__)
602
603
604class PurePath(object):
chasondfa015c2018-02-19 08:36:32 +0900605 """Base class for manipulating paths without I/O.
606
607 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100608 don't imply any actual filesystem I/O. Depending on your system,
609 instantiating a PurePath will return either a PurePosixPath or a
610 PureWindowsPath object. You can also instantiate either of these classes
611 directly, regardless of your system.
612 """
613 __slots__ = (
614 '_drv', '_root', '_parts',
615 '_str', '_hash', '_pparts', '_cached_cparts',
616 )
617
618 def __new__(cls, *args):
619 """Construct a PurePath from one or several strings and or existing
620 PurePath objects. The strings and path objects are combined so as
621 to yield a canonicalized path, which is incorporated into the
622 new PurePath object.
623 """
624 if cls is PurePath:
625 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
626 return cls._from_parts(args)
627
628 def __reduce__(self):
629 # Using the parts tuple helps share interned path parts
630 # when pickling related paths.
631 return (self.__class__, tuple(self._parts))
632
633 @classmethod
634 def _parse_args(cls, args):
635 # This is useful when you don't want to create an instance, just
636 # canonicalize some constructor arguments.
637 parts = []
638 for a in args:
639 if isinstance(a, PurePath):
640 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100641 else:
Brett Cannon568be632016-06-10 12:20:49 -0700642 a = os.fspath(a)
643 if isinstance(a, str):
644 # Force-cast str subclasses to str (issue #21127)
645 parts.append(str(a))
646 else:
647 raise TypeError(
648 "argument should be a str object or an os.PathLike "
649 "object returning str, not %r"
650 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100651 return cls._flavour.parse_parts(parts)
652
653 @classmethod
654 def _from_parts(cls, args, init=True):
655 # We need to call _parse_args on the instance, so as to get the
656 # right flavour.
657 self = object.__new__(cls)
658 drv, root, parts = self._parse_args(args)
659 self._drv = drv
660 self._root = root
661 self._parts = parts
662 if init:
663 self._init()
664 return self
665
666 @classmethod
667 def _from_parsed_parts(cls, drv, root, parts, init=True):
668 self = object.__new__(cls)
669 self._drv = drv
670 self._root = root
671 self._parts = parts
672 if init:
673 self._init()
674 return self
675
676 @classmethod
677 def _format_parsed_parts(cls, drv, root, parts):
678 if drv or root:
679 return drv + root + cls._flavour.join(parts[1:])
680 else:
681 return cls._flavour.join(parts)
682
683 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000684 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100685 pass
686
687 def _make_child(self, args):
688 drv, root, parts = self._parse_args(args)
689 drv, root, parts = self._flavour.join_parsed_parts(
690 self._drv, self._root, self._parts, drv, root, parts)
691 return self._from_parsed_parts(drv, root, parts)
692
693 def __str__(self):
694 """Return the string representation of the path, suitable for
695 passing to system calls."""
696 try:
697 return self._str
698 except AttributeError:
699 self._str = self._format_parsed_parts(self._drv, self._root,
700 self._parts) or '.'
701 return self._str
702
Brett Cannon568be632016-06-10 12:20:49 -0700703 def __fspath__(self):
704 return str(self)
705
Antoine Pitrou31119e42013-11-22 17:38:12 +0100706 def as_posix(self):
707 """Return the string representation of the path with forward (/)
708 slashes."""
709 f = self._flavour
710 return str(self).replace(f.sep, '/')
711
712 def __bytes__(self):
713 """Return the bytes representation of the path. This is only
714 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200715 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100716
717 def __repr__(self):
718 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
719
720 def as_uri(self):
721 """Return the path as a 'file' URI."""
722 if not self.is_absolute():
723 raise ValueError("relative path can't be expressed as a file URI")
724 return self._flavour.make_uri(self)
725
726 @property
727 def _cparts(self):
728 # Cached casefolded parts, for hashing and comparison
729 try:
730 return self._cached_cparts
731 except AttributeError:
732 self._cached_cparts = self._flavour.casefold_parts(self._parts)
733 return self._cached_cparts
734
735 def __eq__(self, other):
736 if not isinstance(other, PurePath):
737 return NotImplemented
738 return self._cparts == other._cparts and self._flavour is other._flavour
739
Antoine Pitrou31119e42013-11-22 17:38:12 +0100740 def __hash__(self):
741 try:
742 return self._hash
743 except AttributeError:
744 self._hash = hash(tuple(self._cparts))
745 return self._hash
746
747 def __lt__(self, other):
748 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
749 return NotImplemented
750 return self._cparts < other._cparts
751
752 def __le__(self, other):
753 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
754 return NotImplemented
755 return self._cparts <= other._cparts
756
757 def __gt__(self, other):
758 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
759 return NotImplemented
760 return self._cparts > other._cparts
761
762 def __ge__(self, other):
763 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
764 return NotImplemented
765 return self._cparts >= other._cparts
766
767 drive = property(attrgetter('_drv'),
768 doc="""The drive prefix (letter or UNC path), if any.""")
769
770 root = property(attrgetter('_root'),
771 doc="""The root of the path, if any.""")
772
773 @property
774 def anchor(self):
775 """The concatenation of the drive and root, or ''."""
776 anchor = self._drv + self._root
777 return anchor
778
779 @property
780 def name(self):
781 """The final path component, if any."""
782 parts = self._parts
783 if len(parts) == (1 if (self._drv or self._root) else 0):
784 return ''
785 return parts[-1]
786
787 @property
788 def suffix(self):
789 """The final component's last suffix, if any."""
790 name = self.name
791 i = name.rfind('.')
792 if 0 < i < len(name) - 1:
793 return name[i:]
794 else:
795 return ''
796
797 @property
798 def suffixes(self):
799 """A list of the final component's suffixes, if any."""
800 name = self.name
801 if name.endswith('.'):
802 return []
803 name = name.lstrip('.')
804 return ['.' + suffix for suffix in name.split('.')[1:]]
805
806 @property
807 def stem(self):
808 """The final path component, minus its last suffix."""
809 name = self.name
810 i = name.rfind('.')
811 if 0 < i < len(name) - 1:
812 return name[:i]
813 else:
814 return name
815
816 def with_name(self, name):
817 """Return a new path with the file name changed."""
818 if not self.name:
819 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400820 drv, root, parts = self._flavour.parse_parts((name,))
821 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
822 or drv or root or len(parts) != 1):
823 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100824 return self._from_parsed_parts(self._drv, self._root,
825 self._parts[:-1] + [name])
826
827 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200828 """Return a new path with the file suffix changed. If the path
829 has no suffix, add given suffix. If the given suffix is an empty
830 string, remove the suffix from the path.
831 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400832 f = self._flavour
833 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300834 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400835 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100836 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100837 name = self.name
838 if not name:
839 raise ValueError("%r has an empty name" % (self,))
840 old_suffix = self.suffix
841 if not old_suffix:
842 name = name + suffix
843 else:
844 name = name[:-len(old_suffix)] + suffix
845 return self._from_parsed_parts(self._drv, self._root,
846 self._parts[:-1] + [name])
847
848 def relative_to(self, *other):
849 """Return the relative path to another path identified by the passed
850 arguments. If the operation is not possible (because this is not
851 a subpath of the other path), raise ValueError.
852 """
853 # For the purpose of this method, drive and root are considered
854 # separate parts, i.e.:
855 # Path('c:/').relative_to('c:') gives Path('/')
856 # Path('c:/').relative_to('/') raise ValueError
857 if not other:
858 raise TypeError("need at least one argument")
859 parts = self._parts
860 drv = self._drv
861 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100862 if root:
863 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100864 else:
865 abs_parts = parts
866 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100867 if to_root:
868 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100869 else:
870 to_abs_parts = to_parts
871 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100872 cf = self._flavour.casefold_parts
873 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100874 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
875 raise ValueError("{!r} does not start with {!r}"
876 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100877 return self._from_parsed_parts('', root if n == 1 else '',
878 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100879
880 @property
881 def parts(self):
882 """An object providing sequence-like access to the
883 components in the filesystem path."""
884 # We cache the tuple to avoid building a new one each time .parts
885 # is accessed. XXX is this necessary?
886 try:
887 return self._pparts
888 except AttributeError:
889 self._pparts = tuple(self._parts)
890 return self._pparts
891
892 def joinpath(self, *args):
893 """Combine this path with one or several arguments, and return a
894 new path representing either a subpath (if all arguments are relative
895 paths) or a totally different path (if one of the arguments is
896 anchored).
897 """
898 return self._make_child(args)
899
900 def __truediv__(self, key):
901 return self._make_child((key,))
902
903 def __rtruediv__(self, key):
904 return self._from_parts([key] + self._parts)
905
906 @property
907 def parent(self):
908 """The logical parent of the path."""
909 drv = self._drv
910 root = self._root
911 parts = self._parts
912 if len(parts) == 1 and (drv or root):
913 return self
914 return self._from_parsed_parts(drv, root, parts[:-1])
915
916 @property
917 def parents(self):
918 """A sequence of this path's logical parents."""
919 return _PathParents(self)
920
921 def is_absolute(self):
922 """True if the path is absolute (has both a root and, if applicable,
923 a drive)."""
924 if not self._root:
925 return False
926 return not self._flavour.has_drv or bool(self._drv)
927
928 def is_reserved(self):
929 """Return True if the path contains one of the special names reserved
930 by the system, if any."""
931 return self._flavour.is_reserved(self._parts)
932
933 def match(self, path_pattern):
934 """
935 Return True if this path matches the given pattern.
936 """
937 cf = self._flavour.casefold
938 path_pattern = cf(path_pattern)
939 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
940 if not pat_parts:
941 raise ValueError("empty pattern")
942 if drv and drv != cf(self._drv):
943 return False
944 if root and root != cf(self._root):
945 return False
946 parts = self._cparts
947 if drv or root:
948 if len(pat_parts) != len(parts):
949 return False
950 pat_parts = pat_parts[1:]
951 elif len(pat_parts) > len(parts):
952 return False
953 for part, pat in zip(reversed(parts), reversed(pat_parts)):
954 if not fnmatch.fnmatchcase(part, pat):
955 return False
956 return True
957
Brett Cannon568be632016-06-10 12:20:49 -0700958# Can't subclass os.PathLike from PurePath and keep the constructor
959# optimizations in PurePath._parse_args().
960os.PathLike.register(PurePath)
961
Antoine Pitrou31119e42013-11-22 17:38:12 +0100962
963class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900964 """PurePath subclass for non-Windows systems.
965
966 On a POSIX system, instantiating a PurePath should return this object.
967 However, you can also instantiate it directly on any system.
968 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100969 _flavour = _posix_flavour
970 __slots__ = ()
971
972
973class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900974 """PurePath subclass for Windows systems.
975
976 On a Windows system, instantiating a PurePath should return this object.
977 However, you can also instantiate it directly on any system.
978 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100979 _flavour = _windows_flavour
980 __slots__ = ()
981
982
983# Filesystem-accessing classes
984
985
986class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900987 """PurePath subclass that can make system calls.
988
989 Path represents a filesystem path but unlike PurePath, also offers
990 methods to do system calls on path objects. Depending on your system,
991 instantiating a Path will return either a PosixPath or a WindowsPath
992 object. You can also instantiate a PosixPath or WindowsPath directly,
993 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
994 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100995 __slots__ = (
996 '_accessor',
997 '_closed',
998 )
999
1000 def __new__(cls, *args, **kwargs):
1001 if cls is Path:
1002 cls = WindowsPath if os.name == 'nt' else PosixPath
1003 self = cls._from_parts(args, init=False)
1004 if not self._flavour.is_supported:
1005 raise NotImplementedError("cannot instantiate %r on your system"
1006 % (cls.__name__,))
1007 self._init()
1008 return self
1009
1010 def _init(self,
1011 # Private non-constructor arguments
1012 template=None,
1013 ):
1014 self._closed = False
1015 if template is not None:
1016 self._accessor = template._accessor
1017 else:
1018 self._accessor = _normal_accessor
1019
1020 def _make_child_relpath(self, part):
1021 # This is an optimization used for dir walking. `part` must be
1022 # a single part relative to this path.
1023 parts = self._parts + [part]
1024 return self._from_parsed_parts(self._drv, self._root, parts)
1025
1026 def __enter__(self):
1027 if self._closed:
1028 self._raise_closed()
1029 return self
1030
1031 def __exit__(self, t, v, tb):
1032 self._closed = True
1033
1034 def _raise_closed(self):
1035 raise ValueError("I/O operation on closed path")
1036
1037 def _opener(self, name, flags, mode=0o666):
1038 # A stub for the opener argument to built-in open()
1039 return self._accessor.open(self, flags, mode)
1040
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001041 def _raw_open(self, flags, mode=0o777):
1042 """
1043 Open the file pointed by this path and return a file descriptor,
1044 as os.open() does.
1045 """
1046 if self._closed:
1047 self._raise_closed()
1048 return self._accessor.open(self, flags, mode)
1049
Antoine Pitrou31119e42013-11-22 17:38:12 +01001050 # Public API
1051
1052 @classmethod
1053 def cwd(cls):
1054 """Return a new path pointing to the current working directory
1055 (as returned by os.getcwd()).
1056 """
1057 return cls(os.getcwd())
1058
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001059 @classmethod
1060 def home(cls):
1061 """Return a new path pointing to the user's home directory (as
1062 returned by os.path.expanduser('~')).
1063 """
1064 return cls(cls()._flavour.gethomedir(None))
1065
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001066 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001067 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001068 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001069 """
1070 st = self.stat()
1071 try:
1072 other_st = other_path.stat()
1073 except AttributeError:
1074 other_st = os.stat(other_path)
1075 return os.path.samestat(st, other_st)
1076
Antoine Pitrou31119e42013-11-22 17:38:12 +01001077 def iterdir(self):
1078 """Iterate over the files in this directory. Does not yield any
1079 result for the special paths '.' and '..'.
1080 """
1081 if self._closed:
1082 self._raise_closed()
1083 for name in self._accessor.listdir(self):
1084 if name in {'.', '..'}:
1085 # Yielding a path object for these makes little sense
1086 continue
1087 yield self._make_child_relpath(name)
1088 if self._closed:
1089 self._raise_closed()
1090
1091 def glob(self, pattern):
1092 """Iterate over this subtree and yield all existing files (of any
Eivind Teig537b6ca2019-02-11 11:47:09 +01001093 kind, including directories) matching the given relative pattern.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001094 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001095 if not pattern:
1096 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001097 pattern = self._flavour.casefold(pattern)
1098 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1099 if drv or root:
1100 raise NotImplementedError("Non-relative patterns are unsupported")
1101 selector = _make_selector(tuple(pattern_parts))
1102 for p in selector.select_from(self):
1103 yield p
1104
1105 def rglob(self, pattern):
1106 """Recursively yield all existing files (of any kind, including
Eivind Teig537b6ca2019-02-11 11:47:09 +01001107 directories) matching the given relative pattern, anywhere in
1108 this subtree.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001109 """
1110 pattern = self._flavour.casefold(pattern)
1111 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1112 if drv or root:
1113 raise NotImplementedError("Non-relative patterns are unsupported")
1114 selector = _make_selector(("**",) + tuple(pattern_parts))
1115 for p in selector.select_from(self):
1116 yield p
1117
1118 def absolute(self):
1119 """Return an absolute version of this path. This function works
1120 even if the path doesn't point to anything.
1121
1122 No normalization is done, i.e. all '.' and '..' will be kept along.
1123 Use resolve() to get the canonical path to a file.
1124 """
1125 # XXX untested yet!
1126 if self._closed:
1127 self._raise_closed()
1128 if self.is_absolute():
1129 return self
1130 # FIXME this must defer to the specific flavour (and, under Windows,
1131 # use nt._getfullpathname())
1132 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1133 obj._init(template=self)
1134 return obj
1135
Steve Dower98eb3602016-11-09 12:58:17 -08001136 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001137 """
1138 Make the path absolute, resolving all symlinks on the way and also
1139 normalizing it (for example turning slashes into backslashes under
1140 Windows).
1141 """
1142 if self._closed:
1143 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001144 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001145 if s is None:
1146 # No symlink resolution => for consistency, raise an error if
1147 # the path doesn't exist or is forbidden
1148 self.stat()
1149 s = str(self.absolute())
1150 # Now we have no symlinks in the path, it's safe to normalize it.
1151 normed = self._flavour.pathmod.normpath(s)
1152 obj = self._from_parts((normed,), init=False)
1153 obj._init(template=self)
1154 return obj
1155
1156 def stat(self):
1157 """
1158 Return the result of the stat() system call on this path, like
1159 os.stat() does.
1160 """
1161 return self._accessor.stat(self)
1162
1163 def owner(self):
1164 """
1165 Return the login name of the file owner.
1166 """
1167 import pwd
1168 return pwd.getpwuid(self.stat().st_uid).pw_name
1169
1170 def group(self):
1171 """
1172 Return the group name of the file gid.
1173 """
1174 import grp
1175 return grp.getgrgid(self.stat().st_gid).gr_name
1176
Antoine Pitrou31119e42013-11-22 17:38:12 +01001177 def open(self, mode='r', buffering=-1, encoding=None,
1178 errors=None, newline=None):
1179 """
1180 Open the file pointed by this path and return a file object, as
1181 the built-in open() function does.
1182 """
1183 if self._closed:
1184 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001185 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001186 opener=self._opener)
1187
Georg Brandlea683982014-10-01 19:12:33 +02001188 def read_bytes(self):
1189 """
1190 Open the file in bytes mode, read it, and close the file.
1191 """
1192 with self.open(mode='rb') as f:
1193 return f.read()
1194
1195 def read_text(self, encoding=None, errors=None):
1196 """
1197 Open the file in text mode, read it, and close the file.
1198 """
1199 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1200 return f.read()
1201
1202 def write_bytes(self, data):
1203 """
1204 Open the file in bytes mode, write to it, and close the file.
1205 """
1206 # type-check for the buffer interface before truncating the file
1207 view = memoryview(data)
1208 with self.open(mode='wb') as f:
1209 return f.write(view)
1210
1211 def write_text(self, data, encoding=None, errors=None):
1212 """
1213 Open the file in text mode, write to it, and close the file.
1214 """
1215 if not isinstance(data, str):
1216 raise TypeError('data must be str, not %s' %
1217 data.__class__.__name__)
1218 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1219 return f.write(data)
1220
Antoine Pitrou31119e42013-11-22 17:38:12 +01001221 def touch(self, mode=0o666, exist_ok=True):
1222 """
1223 Create this file with the given access mode, if it doesn't exist.
1224 """
1225 if self._closed:
1226 self._raise_closed()
1227 if exist_ok:
1228 # First try to bump modification time
1229 # Implementation note: GNU touch uses the UTIME_NOW option of
1230 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001231 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001232 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001233 except OSError:
1234 # Avoid exception chaining
1235 pass
1236 else:
1237 return
1238 flags = os.O_CREAT | os.O_WRONLY
1239 if not exist_ok:
1240 flags |= os.O_EXCL
1241 fd = self._raw_open(flags, mode)
1242 os.close(fd)
1243
Barry Warsaw7c549c42014-08-05 11:28:12 -04001244 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001245 """
1246 Create a new directory at this given path.
1247 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001248 if self._closed:
1249 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001250 try:
1251 self._accessor.mkdir(self, mode)
1252 except FileNotFoundError:
1253 if not parents or self.parent == self:
1254 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001255 self.parent.mkdir(parents=True, exist_ok=True)
1256 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001257 except OSError:
1258 # Cannot rely on checking for EEXIST, since the operating system
1259 # could give priority to other errors like EACCES or EROFS
1260 if not exist_ok or not self.is_dir():
1261 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001262
1263 def chmod(self, mode):
1264 """
1265 Change the permissions of the path, like os.chmod().
1266 """
1267 if self._closed:
1268 self._raise_closed()
1269 self._accessor.chmod(self, mode)
1270
1271 def lchmod(self, mode):
1272 """
1273 Like chmod(), except if the path points to a symlink, the symlink's
1274 permissions are changed, rather than its target's.
1275 """
1276 if self._closed:
1277 self._raise_closed()
1278 self._accessor.lchmod(self, mode)
1279
1280 def unlink(self):
1281 """
1282 Remove this file or link.
1283 If the path is a directory, use rmdir() instead.
1284 """
1285 if self._closed:
1286 self._raise_closed()
1287 self._accessor.unlink(self)
1288
1289 def rmdir(self):
1290 """
1291 Remove this directory. The directory must be empty.
1292 """
1293 if self._closed:
1294 self._raise_closed()
1295 self._accessor.rmdir(self)
1296
1297 def lstat(self):
1298 """
1299 Like stat(), except if the path points to a symlink, the symlink's
1300 status information is returned, rather than its target's.
1301 """
1302 if self._closed:
1303 self._raise_closed()
1304 return self._accessor.lstat(self)
1305
1306 def rename(self, target):
1307 """
1308 Rename this path to the given path.
1309 """
1310 if self._closed:
1311 self._raise_closed()
1312 self._accessor.rename(self, target)
1313
1314 def replace(self, target):
1315 """
1316 Rename this path to the given path, clobbering the existing
1317 destination if it exists.
1318 """
1319 if self._closed:
1320 self._raise_closed()
1321 self._accessor.replace(self, target)
1322
1323 def symlink_to(self, target, target_is_directory=False):
1324 """
1325 Make this path a symlink pointing to the given path.
1326 Note the order of arguments (self, target) is the reverse of os.symlink's.
1327 """
1328 if self._closed:
1329 self._raise_closed()
1330 self._accessor.symlink(target, self, target_is_directory)
1331
1332 # Convenience functions for querying the stat results
1333
1334 def exists(self):
1335 """
1336 Whether this path exists.
1337 """
1338 try:
1339 self.stat()
1340 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001341 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001342 raise
1343 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001344 except ValueError:
1345 # Non-encodable path
1346 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001347 return True
1348
1349 def is_dir(self):
1350 """
1351 Whether this path is a directory.
1352 """
1353 try:
1354 return S_ISDIR(self.stat().st_mode)
1355 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001356 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001357 raise
1358 # Path doesn't exist or is a broken symlink
1359 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1360 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001361 except ValueError:
1362 # Non-encodable path
1363 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001364
1365 def is_file(self):
1366 """
1367 Whether this path is a regular file (also True for symlinks pointing
1368 to regular files).
1369 """
1370 try:
1371 return S_ISREG(self.stat().st_mode)
1372 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001373 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001374 raise
1375 # Path doesn't exist or is a broken symlink
1376 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1377 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001378 except ValueError:
1379 # Non-encodable path
1380 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001381
Cooper Lees173ff4a2017-08-01 15:35:45 -07001382 def is_mount(self):
1383 """
1384 Check if this path is a POSIX mount point
1385 """
1386 # Need to exist and be a dir
1387 if not self.exists() or not self.is_dir():
1388 return False
1389
1390 parent = Path(self.parent)
1391 try:
1392 parent_dev = parent.stat().st_dev
1393 except OSError:
1394 return False
1395
1396 dev = self.stat().st_dev
1397 if dev != parent_dev:
1398 return True
1399 ino = self.stat().st_ino
1400 parent_ino = parent.stat().st_ino
1401 return ino == parent_ino
1402
Antoine Pitrou31119e42013-11-22 17:38:12 +01001403 def is_symlink(self):
1404 """
1405 Whether this path is a symbolic link.
1406 """
1407 try:
1408 return S_ISLNK(self.lstat().st_mode)
1409 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001410 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001411 raise
1412 # Path doesn't exist
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
1418 def is_block_device(self):
1419 """
1420 Whether this path is a block device.
1421 """
1422 try:
1423 return S_ISBLK(self.stat().st_mode)
1424 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001425 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001426 raise
1427 # Path doesn't exist or is a broken symlink
1428 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1429 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001430 except ValueError:
1431 # Non-encodable path
1432 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001433
1434 def is_char_device(self):
1435 """
1436 Whether this path is a character device.
1437 """
1438 try:
1439 return S_ISCHR(self.stat().st_mode)
1440 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001441 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001442 raise
1443 # Path doesn't exist or is a broken symlink
1444 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1445 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001446 except ValueError:
1447 # Non-encodable path
1448 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001449
1450 def is_fifo(self):
1451 """
1452 Whether this path is a FIFO.
1453 """
1454 try:
1455 return S_ISFIFO(self.stat().st_mode)
1456 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001457 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001458 raise
1459 # Path doesn't exist or is a broken symlink
1460 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1461 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001462 except ValueError:
1463 # Non-encodable path
1464 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001465
1466 def is_socket(self):
1467 """
1468 Whether this path is a socket.
1469 """
1470 try:
1471 return S_ISSOCK(self.stat().st_mode)
1472 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001473 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001474 raise
1475 # Path doesn't exist or is a broken symlink
1476 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1477 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001478 except ValueError:
1479 # Non-encodable path
1480 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001481
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001482 def expanduser(self):
1483 """ Return a new path with expanded ~ and ~user constructs
1484 (as returned by os.path.expanduser)
1485 """
1486 if (not (self._drv or self._root) and
1487 self._parts and self._parts[0][:1] == '~'):
1488 homedir = self._flavour.gethomedir(self._parts[0][1:])
1489 return self._from_parts([homedir] + self._parts[1:])
1490
1491 return self
1492
Antoine Pitrou31119e42013-11-22 17:38:12 +01001493
1494class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001495 """Path subclass for non-Windows systems.
1496
1497 On a POSIX system, instantiating a Path should return this object.
1498 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001499 __slots__ = ()
1500
1501class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001502 """Path subclass for Windows systems.
1503
1504 On a Windows system, instantiating a Path should return this object.
1505 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001506 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001507
1508 def owner(self):
1509 raise NotImplementedError("Path.owner() is unsupported on this system")
1510
1511 def group(self):
1512 raise NotImplementedError("Path.group() is unsupported on this system")
Cooper Lees173ff4a2017-08-01 15:35:45 -07001513
1514 def is_mount(self):
1515 raise NotImplementedError("Path.is_mount() is unsupported on this system")