blob: 1ba98b19e833444bdd806b5eb97de608a5e50ef3 [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
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -0400414 link_to = os.link
415
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200416 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100417
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200418 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100419
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200420 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100421
422 if nt:
423 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200424 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100425 else:
426 def symlink(a, b, target_is_directory):
427 raise NotImplementedError("symlink() not available on this system")
428 else:
429 # Under POSIX, os.symlink() takes two args
430 @staticmethod
431 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200432 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100433
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200434 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100435
436 # Helper for resolve()
437 def readlink(self, path):
438 return os.readlink(path)
439
440
441_normal_accessor = _NormalAccessor()
442
443
444#
445# Globbing helpers
446#
447
Antoine Pitrou31119e42013-11-22 17:38:12 +0100448def _make_selector(pattern_parts):
449 pat = pattern_parts[0]
450 child_parts = pattern_parts[1:]
451 if pat == '**':
452 cls = _RecursiveWildcardSelector
453 elif '**' in pat:
454 raise ValueError("Invalid pattern: '**' can only be an entire path component")
455 elif _is_wildcard_pattern(pat):
456 cls = _WildcardSelector
457 else:
458 cls = _PreciseSelector
459 return cls(pat, child_parts)
460
461if hasattr(functools, "lru_cache"):
462 _make_selector = functools.lru_cache()(_make_selector)
463
464
465class _Selector:
466 """A selector matches a specific glob pattern part against the children
467 of a given path."""
468
469 def __init__(self, child_parts):
470 self.child_parts = child_parts
471 if child_parts:
472 self.successor = _make_selector(child_parts)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300473 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100474 else:
475 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300476 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100477
478 def select_from(self, parent_path):
479 """Iterate over all child paths of `parent_path` matched by this
480 selector. This can contain parent_path itself."""
481 path_cls = type(parent_path)
482 is_dir = path_cls.is_dir
483 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300484 scandir = parent_path._accessor.scandir
485 if not is_dir(parent_path):
486 return iter([])
487 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100488
489
490class _TerminatingSelector:
491
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300492 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100493 yield parent_path
494
495
496class _PreciseSelector(_Selector):
497
498 def __init__(self, name, child_parts):
499 self.name = name
500 _Selector.__init__(self, child_parts)
501
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300502 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800503 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800504 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300505 if (is_dir if self.dironly else exists)(path):
506 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800507 yield p
508 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100509 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100510
511
512class _WildcardSelector(_Selector):
513
514 def __init__(self, pat, child_parts):
515 self.pat = re.compile(fnmatch.translate(pat))
516 _Selector.__init__(self, child_parts)
517
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300518 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800519 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800520 cf = parent_path._flavour.casefold
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300521 entries = list(scandir(parent_path))
522 for entry in entries:
523 if not self.dironly or entry.is_dir():
524 name = entry.name
525 casefolded = cf(name)
526 if self.pat.match(casefolded):
527 path = parent_path._make_child_relpath(name)
528 for p in self.successor._select_from(path, is_dir, exists, scandir):
529 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800530 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100531 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800532
Antoine Pitrou31119e42013-11-22 17:38:12 +0100533
534
535class _RecursiveWildcardSelector(_Selector):
536
537 def __init__(self, pat, child_parts):
538 _Selector.__init__(self, child_parts)
539
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300540 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100541 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800542 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300543 entries = list(scandir(parent_path))
544 for entry in entries:
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200545 entry_is_dir = False
546 try:
547 entry_is_dir = entry.is_dir()
548 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -0800549 if not _ignore_error(e):
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200550 raise
551 if entry_is_dir and not entry.is_symlink():
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300552 path = parent_path._make_child_relpath(entry.name)
553 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800554 yield p
555 except PermissionError:
556 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100557
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300558 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800559 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300560 yielded = set()
561 try:
562 successor_select = self.successor._select_from
563 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
564 for p in successor_select(starting_point, is_dir, exists, scandir):
565 if p not in yielded:
566 yield p
567 yielded.add(p)
568 finally:
569 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800570 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100571 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100572
573
574#
575# Public API
576#
577
578class _PathParents(Sequence):
579 """This object provides sequence-like access to the logical ancestors
580 of a path. Don't try to construct it yourself."""
581 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
582
583 def __init__(self, path):
584 # We don't store the instance to avoid reference cycles
585 self._pathcls = type(path)
586 self._drv = path._drv
587 self._root = path._root
588 self._parts = path._parts
589
590 def __len__(self):
591 if self._drv or self._root:
592 return len(self._parts) - 1
593 else:
594 return len(self._parts)
595
596 def __getitem__(self, idx):
597 if idx < 0 or idx >= len(self):
598 raise IndexError(idx)
599 return self._pathcls._from_parsed_parts(self._drv, self._root,
600 self._parts[:-idx - 1])
601
602 def __repr__(self):
603 return "<{}.parents>".format(self._pathcls.__name__)
604
605
606class PurePath(object):
chasondfa015c2018-02-19 08:36:32 +0900607 """Base class for manipulating paths without I/O.
608
609 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100610 don't imply any actual filesystem I/O. Depending on your system,
611 instantiating a PurePath will return either a PurePosixPath or a
612 PureWindowsPath object. You can also instantiate either of these classes
613 directly, regardless of your system.
614 """
615 __slots__ = (
616 '_drv', '_root', '_parts',
617 '_str', '_hash', '_pparts', '_cached_cparts',
618 )
619
620 def __new__(cls, *args):
621 """Construct a PurePath from one or several strings and or existing
622 PurePath objects. The strings and path objects are combined so as
623 to yield a canonicalized path, which is incorporated into the
624 new PurePath object.
625 """
626 if cls is PurePath:
627 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
628 return cls._from_parts(args)
629
630 def __reduce__(self):
631 # Using the parts tuple helps share interned path parts
632 # when pickling related paths.
633 return (self.__class__, tuple(self._parts))
634
635 @classmethod
636 def _parse_args(cls, args):
637 # This is useful when you don't want to create an instance, just
638 # canonicalize some constructor arguments.
639 parts = []
640 for a in args:
641 if isinstance(a, PurePath):
642 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100643 else:
Brett Cannon568be632016-06-10 12:20:49 -0700644 a = os.fspath(a)
645 if isinstance(a, str):
646 # Force-cast str subclasses to str (issue #21127)
647 parts.append(str(a))
648 else:
649 raise TypeError(
650 "argument should be a str object or an os.PathLike "
651 "object returning str, not %r"
652 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100653 return cls._flavour.parse_parts(parts)
654
655 @classmethod
656 def _from_parts(cls, args, init=True):
657 # We need to call _parse_args on the instance, so as to get the
658 # right flavour.
659 self = object.__new__(cls)
660 drv, root, parts = self._parse_args(args)
661 self._drv = drv
662 self._root = root
663 self._parts = parts
664 if init:
665 self._init()
666 return self
667
668 @classmethod
669 def _from_parsed_parts(cls, drv, root, parts, init=True):
670 self = object.__new__(cls)
671 self._drv = drv
672 self._root = root
673 self._parts = parts
674 if init:
675 self._init()
676 return self
677
678 @classmethod
679 def _format_parsed_parts(cls, drv, root, parts):
680 if drv or root:
681 return drv + root + cls._flavour.join(parts[1:])
682 else:
683 return cls._flavour.join(parts)
684
685 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000686 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100687 pass
688
689 def _make_child(self, args):
690 drv, root, parts = self._parse_args(args)
691 drv, root, parts = self._flavour.join_parsed_parts(
692 self._drv, self._root, self._parts, drv, root, parts)
693 return self._from_parsed_parts(drv, root, parts)
694
695 def __str__(self):
696 """Return the string representation of the path, suitable for
697 passing to system calls."""
698 try:
699 return self._str
700 except AttributeError:
701 self._str = self._format_parsed_parts(self._drv, self._root,
702 self._parts) or '.'
703 return self._str
704
Brett Cannon568be632016-06-10 12:20:49 -0700705 def __fspath__(self):
706 return str(self)
707
Antoine Pitrou31119e42013-11-22 17:38:12 +0100708 def as_posix(self):
709 """Return the string representation of the path with forward (/)
710 slashes."""
711 f = self._flavour
712 return str(self).replace(f.sep, '/')
713
714 def __bytes__(self):
715 """Return the bytes representation of the path. This is only
716 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200717 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100718
719 def __repr__(self):
720 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
721
722 def as_uri(self):
723 """Return the path as a 'file' URI."""
724 if not self.is_absolute():
725 raise ValueError("relative path can't be expressed as a file URI")
726 return self._flavour.make_uri(self)
727
728 @property
729 def _cparts(self):
730 # Cached casefolded parts, for hashing and comparison
731 try:
732 return self._cached_cparts
733 except AttributeError:
734 self._cached_cparts = self._flavour.casefold_parts(self._parts)
735 return self._cached_cparts
736
737 def __eq__(self, other):
738 if not isinstance(other, PurePath):
739 return NotImplemented
740 return self._cparts == other._cparts and self._flavour is other._flavour
741
Antoine Pitrou31119e42013-11-22 17:38:12 +0100742 def __hash__(self):
743 try:
744 return self._hash
745 except AttributeError:
746 self._hash = hash(tuple(self._cparts))
747 return self._hash
748
749 def __lt__(self, other):
750 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
751 return NotImplemented
752 return self._cparts < other._cparts
753
754 def __le__(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 __gt__(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 __ge__(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 drive = property(attrgetter('_drv'),
770 doc="""The drive prefix (letter or UNC path), if any.""")
771
772 root = property(attrgetter('_root'),
773 doc="""The root of the path, if any.""")
774
775 @property
776 def anchor(self):
777 """The concatenation of the drive and root, or ''."""
778 anchor = self._drv + self._root
779 return anchor
780
781 @property
782 def name(self):
783 """The final path component, if any."""
784 parts = self._parts
785 if len(parts) == (1 if (self._drv or self._root) else 0):
786 return ''
787 return parts[-1]
788
789 @property
790 def suffix(self):
791 """The final component's last suffix, if any."""
792 name = self.name
793 i = name.rfind('.')
794 if 0 < i < len(name) - 1:
795 return name[i:]
796 else:
797 return ''
798
799 @property
800 def suffixes(self):
801 """A list of the final component's suffixes, if any."""
802 name = self.name
803 if name.endswith('.'):
804 return []
805 name = name.lstrip('.')
806 return ['.' + suffix for suffix in name.split('.')[1:]]
807
808 @property
809 def stem(self):
810 """The final path component, minus its last suffix."""
811 name = self.name
812 i = name.rfind('.')
813 if 0 < i < len(name) - 1:
814 return name[:i]
815 else:
816 return name
817
818 def with_name(self, name):
819 """Return a new path with the file name changed."""
820 if not self.name:
821 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400822 drv, root, parts = self._flavour.parse_parts((name,))
823 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
824 or drv or root or len(parts) != 1):
825 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100826 return self._from_parsed_parts(self._drv, self._root,
827 self._parts[:-1] + [name])
828
829 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200830 """Return a new path with the file suffix changed. If the path
831 has no suffix, add given suffix. If the given suffix is an empty
832 string, remove the suffix from the path.
833 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400834 f = self._flavour
835 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300836 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400837 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100838 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100839 name = self.name
840 if not name:
841 raise ValueError("%r has an empty name" % (self,))
842 old_suffix = self.suffix
843 if not old_suffix:
844 name = name + suffix
845 else:
846 name = name[:-len(old_suffix)] + suffix
847 return self._from_parsed_parts(self._drv, self._root,
848 self._parts[:-1] + [name])
849
850 def relative_to(self, *other):
851 """Return the relative path to another path identified by the passed
852 arguments. If the operation is not possible (because this is not
853 a subpath of the other path), raise ValueError.
854 """
855 # For the purpose of this method, drive and root are considered
856 # separate parts, i.e.:
857 # Path('c:/').relative_to('c:') gives Path('/')
858 # Path('c:/').relative_to('/') raise ValueError
859 if not other:
860 raise TypeError("need at least one argument")
861 parts = self._parts
862 drv = self._drv
863 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100864 if root:
865 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100866 else:
867 abs_parts = parts
868 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100869 if to_root:
870 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100871 else:
872 to_abs_parts = to_parts
873 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100874 cf = self._flavour.casefold_parts
875 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100876 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
877 raise ValueError("{!r} does not start with {!r}"
878 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100879 return self._from_parsed_parts('', root if n == 1 else '',
880 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100881
882 @property
883 def parts(self):
884 """An object providing sequence-like access to the
885 components in the filesystem path."""
886 # We cache the tuple to avoid building a new one each time .parts
887 # is accessed. XXX is this necessary?
888 try:
889 return self._pparts
890 except AttributeError:
891 self._pparts = tuple(self._parts)
892 return self._pparts
893
894 def joinpath(self, *args):
895 """Combine this path with one or several arguments, and return a
896 new path representing either a subpath (if all arguments are relative
897 paths) or a totally different path (if one of the arguments is
898 anchored).
899 """
900 return self._make_child(args)
901
902 def __truediv__(self, key):
903 return self._make_child((key,))
904
905 def __rtruediv__(self, key):
906 return self._from_parts([key] + self._parts)
907
908 @property
909 def parent(self):
910 """The logical parent of the path."""
911 drv = self._drv
912 root = self._root
913 parts = self._parts
914 if len(parts) == 1 and (drv or root):
915 return self
916 return self._from_parsed_parts(drv, root, parts[:-1])
917
918 @property
919 def parents(self):
920 """A sequence of this path's logical parents."""
921 return _PathParents(self)
922
923 def is_absolute(self):
924 """True if the path is absolute (has both a root and, if applicable,
925 a drive)."""
926 if not self._root:
927 return False
928 return not self._flavour.has_drv or bool(self._drv)
929
930 def is_reserved(self):
931 """Return True if the path contains one of the special names reserved
932 by the system, if any."""
933 return self._flavour.is_reserved(self._parts)
934
935 def match(self, path_pattern):
936 """
937 Return True if this path matches the given pattern.
938 """
939 cf = self._flavour.casefold
940 path_pattern = cf(path_pattern)
941 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
942 if not pat_parts:
943 raise ValueError("empty pattern")
944 if drv and drv != cf(self._drv):
945 return False
946 if root and root != cf(self._root):
947 return False
948 parts = self._cparts
949 if drv or root:
950 if len(pat_parts) != len(parts):
951 return False
952 pat_parts = pat_parts[1:]
953 elif len(pat_parts) > len(parts):
954 return False
955 for part, pat in zip(reversed(parts), reversed(pat_parts)):
956 if not fnmatch.fnmatchcase(part, pat):
957 return False
958 return True
959
Brett Cannon568be632016-06-10 12:20:49 -0700960# Can't subclass os.PathLike from PurePath and keep the constructor
961# optimizations in PurePath._parse_args().
962os.PathLike.register(PurePath)
963
Antoine Pitrou31119e42013-11-22 17:38:12 +0100964
965class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900966 """PurePath subclass for non-Windows systems.
967
968 On a POSIX system, instantiating a PurePath should return this object.
969 However, you can also instantiate it directly on any system.
970 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100971 _flavour = _posix_flavour
972 __slots__ = ()
973
974
975class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900976 """PurePath subclass for Windows systems.
977
978 On a Windows system, instantiating a PurePath should return this object.
979 However, you can also instantiate it directly on any system.
980 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100981 _flavour = _windows_flavour
982 __slots__ = ()
983
984
985# Filesystem-accessing classes
986
987
988class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900989 """PurePath subclass that can make system calls.
990
991 Path represents a filesystem path but unlike PurePath, also offers
992 methods to do system calls on path objects. Depending on your system,
993 instantiating a Path will return either a PosixPath or a WindowsPath
994 object. You can also instantiate a PosixPath or WindowsPath directly,
995 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
996 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100997 __slots__ = (
998 '_accessor',
999 '_closed',
1000 )
1001
1002 def __new__(cls, *args, **kwargs):
1003 if cls is Path:
1004 cls = WindowsPath if os.name == 'nt' else PosixPath
1005 self = cls._from_parts(args, init=False)
1006 if not self._flavour.is_supported:
1007 raise NotImplementedError("cannot instantiate %r on your system"
1008 % (cls.__name__,))
1009 self._init()
1010 return self
1011
1012 def _init(self,
1013 # Private non-constructor arguments
1014 template=None,
1015 ):
1016 self._closed = False
1017 if template is not None:
1018 self._accessor = template._accessor
1019 else:
1020 self._accessor = _normal_accessor
1021
1022 def _make_child_relpath(self, part):
1023 # This is an optimization used for dir walking. `part` must be
1024 # a single part relative to this path.
1025 parts = self._parts + [part]
1026 return self._from_parsed_parts(self._drv, self._root, parts)
1027
1028 def __enter__(self):
1029 if self._closed:
1030 self._raise_closed()
1031 return self
1032
1033 def __exit__(self, t, v, tb):
1034 self._closed = True
1035
1036 def _raise_closed(self):
1037 raise ValueError("I/O operation on closed path")
1038
1039 def _opener(self, name, flags, mode=0o666):
1040 # A stub for the opener argument to built-in open()
1041 return self._accessor.open(self, flags, mode)
1042
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001043 def _raw_open(self, flags, mode=0o777):
1044 """
1045 Open the file pointed by this path and return a file descriptor,
1046 as os.open() does.
1047 """
1048 if self._closed:
1049 self._raise_closed()
1050 return self._accessor.open(self, flags, mode)
1051
Antoine Pitrou31119e42013-11-22 17:38:12 +01001052 # Public API
1053
1054 @classmethod
1055 def cwd(cls):
1056 """Return a new path pointing to the current working directory
1057 (as returned by os.getcwd()).
1058 """
1059 return cls(os.getcwd())
1060
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001061 @classmethod
1062 def home(cls):
1063 """Return a new path pointing to the user's home directory (as
1064 returned by os.path.expanduser('~')).
1065 """
1066 return cls(cls()._flavour.gethomedir(None))
1067
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001068 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001069 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001070 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001071 """
1072 st = self.stat()
1073 try:
1074 other_st = other_path.stat()
1075 except AttributeError:
1076 other_st = os.stat(other_path)
1077 return os.path.samestat(st, other_st)
1078
Antoine Pitrou31119e42013-11-22 17:38:12 +01001079 def iterdir(self):
1080 """Iterate over the files in this directory. Does not yield any
1081 result for the special paths '.' and '..'.
1082 """
1083 if self._closed:
1084 self._raise_closed()
1085 for name in self._accessor.listdir(self):
1086 if name in {'.', '..'}:
1087 # Yielding a path object for these makes little sense
1088 continue
1089 yield self._make_child_relpath(name)
1090 if self._closed:
1091 self._raise_closed()
1092
1093 def glob(self, pattern):
1094 """Iterate over this subtree and yield all existing files (of any
Eivind Teig537b6ca2019-02-11 11:47:09 +01001095 kind, including directories) matching the given relative pattern.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001096 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001097 if not pattern:
1098 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001099 pattern = self._flavour.casefold(pattern)
1100 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1101 if drv or root:
1102 raise NotImplementedError("Non-relative patterns are unsupported")
1103 selector = _make_selector(tuple(pattern_parts))
1104 for p in selector.select_from(self):
1105 yield p
1106
1107 def rglob(self, pattern):
1108 """Recursively yield all existing files (of any kind, including
Eivind Teig537b6ca2019-02-11 11:47:09 +01001109 directories) matching the given relative pattern, anywhere in
1110 this subtree.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001111 """
1112 pattern = self._flavour.casefold(pattern)
1113 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1114 if drv or root:
1115 raise NotImplementedError("Non-relative patterns are unsupported")
1116 selector = _make_selector(("**",) + tuple(pattern_parts))
1117 for p in selector.select_from(self):
1118 yield p
1119
1120 def absolute(self):
1121 """Return an absolute version of this path. This function works
1122 even if the path doesn't point to anything.
1123
1124 No normalization is done, i.e. all '.' and '..' will be kept along.
1125 Use resolve() to get the canonical path to a file.
1126 """
1127 # XXX untested yet!
1128 if self._closed:
1129 self._raise_closed()
1130 if self.is_absolute():
1131 return self
1132 # FIXME this must defer to the specific flavour (and, under Windows,
1133 # use nt._getfullpathname())
1134 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1135 obj._init(template=self)
1136 return obj
1137
Steve Dower98eb3602016-11-09 12:58:17 -08001138 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001139 """
1140 Make the path absolute, resolving all symlinks on the way and also
1141 normalizing it (for example turning slashes into backslashes under
1142 Windows).
1143 """
1144 if self._closed:
1145 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001146 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001147 if s is None:
1148 # No symlink resolution => for consistency, raise an error if
1149 # the path doesn't exist or is forbidden
1150 self.stat()
1151 s = str(self.absolute())
1152 # Now we have no symlinks in the path, it's safe to normalize it.
1153 normed = self._flavour.pathmod.normpath(s)
1154 obj = self._from_parts((normed,), init=False)
1155 obj._init(template=self)
1156 return obj
1157
1158 def stat(self):
1159 """
1160 Return the result of the stat() system call on this path, like
1161 os.stat() does.
1162 """
1163 return self._accessor.stat(self)
1164
1165 def owner(self):
1166 """
1167 Return the login name of the file owner.
1168 """
1169 import pwd
1170 return pwd.getpwuid(self.stat().st_uid).pw_name
1171
1172 def group(self):
1173 """
1174 Return the group name of the file gid.
1175 """
1176 import grp
1177 return grp.getgrgid(self.stat().st_gid).gr_name
1178
Antoine Pitrou31119e42013-11-22 17:38:12 +01001179 def open(self, mode='r', buffering=-1, encoding=None,
1180 errors=None, newline=None):
1181 """
1182 Open the file pointed by this path and return a file object, as
1183 the built-in open() function does.
1184 """
1185 if self._closed:
1186 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001187 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001188 opener=self._opener)
1189
Georg Brandlea683982014-10-01 19:12:33 +02001190 def read_bytes(self):
1191 """
1192 Open the file in bytes mode, read it, and close the file.
1193 """
1194 with self.open(mode='rb') as f:
1195 return f.read()
1196
1197 def read_text(self, encoding=None, errors=None):
1198 """
1199 Open the file in text mode, read it, and close the file.
1200 """
1201 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1202 return f.read()
1203
1204 def write_bytes(self, data):
1205 """
1206 Open the file in bytes mode, write to it, and close the file.
1207 """
1208 # type-check for the buffer interface before truncating the file
1209 view = memoryview(data)
1210 with self.open(mode='wb') as f:
1211 return f.write(view)
1212
1213 def write_text(self, data, encoding=None, errors=None):
1214 """
1215 Open the file in text mode, write to it, and close the file.
1216 """
1217 if not isinstance(data, str):
1218 raise TypeError('data must be str, not %s' %
1219 data.__class__.__name__)
1220 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1221 return f.write(data)
1222
Antoine Pitrou31119e42013-11-22 17:38:12 +01001223 def touch(self, mode=0o666, exist_ok=True):
1224 """
1225 Create this file with the given access mode, if it doesn't exist.
1226 """
1227 if self._closed:
1228 self._raise_closed()
1229 if exist_ok:
1230 # First try to bump modification time
1231 # Implementation note: GNU touch uses the UTIME_NOW option of
1232 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001233 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001234 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001235 except OSError:
1236 # Avoid exception chaining
1237 pass
1238 else:
1239 return
1240 flags = os.O_CREAT | os.O_WRONLY
1241 if not exist_ok:
1242 flags |= os.O_EXCL
1243 fd = self._raw_open(flags, mode)
1244 os.close(fd)
1245
Barry Warsaw7c549c42014-08-05 11:28:12 -04001246 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001247 """
1248 Create a new directory at this given path.
1249 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001250 if self._closed:
1251 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001252 try:
1253 self._accessor.mkdir(self, mode)
1254 except FileNotFoundError:
1255 if not parents or self.parent == self:
1256 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001257 self.parent.mkdir(parents=True, exist_ok=True)
1258 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001259 except OSError:
1260 # Cannot rely on checking for EEXIST, since the operating system
1261 # could give priority to other errors like EACCES or EROFS
1262 if not exist_ok or not self.is_dir():
1263 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001264
1265 def chmod(self, mode):
1266 """
1267 Change the permissions of the path, like os.chmod().
1268 """
1269 if self._closed:
1270 self._raise_closed()
1271 self._accessor.chmod(self, mode)
1272
1273 def lchmod(self, mode):
1274 """
1275 Like chmod(), except if the path points to a symlink, the symlink's
1276 permissions are changed, rather than its target's.
1277 """
1278 if self._closed:
1279 self._raise_closed()
1280 self._accessor.lchmod(self, mode)
1281
1282 def unlink(self):
1283 """
1284 Remove this file or link.
1285 If the path is a directory, use rmdir() instead.
1286 """
1287 if self._closed:
1288 self._raise_closed()
1289 self._accessor.unlink(self)
1290
1291 def rmdir(self):
1292 """
1293 Remove this directory. The directory must be empty.
1294 """
1295 if self._closed:
1296 self._raise_closed()
1297 self._accessor.rmdir(self)
1298
1299 def lstat(self):
1300 """
1301 Like stat(), except if the path points to a symlink, the symlink's
1302 status information is returned, rather than its target's.
1303 """
1304 if self._closed:
1305 self._raise_closed()
1306 return self._accessor.lstat(self)
1307
Joannah Nanjekye6b5b0132019-05-04 11:27:10 -04001308 def link_to(self, target):
1309 """
1310 Create a hard link pointing to a path named target.
1311 """
1312 if self._closed:
1313 self._raise_closed()
1314 self._accessor.link_to(self, target)
1315
Antoine Pitrou31119e42013-11-22 17:38:12 +01001316 def rename(self, target):
1317 """
1318 Rename this path to the given path.
1319 """
1320 if self._closed:
1321 self._raise_closed()
1322 self._accessor.rename(self, target)
1323
1324 def replace(self, target):
1325 """
1326 Rename this path to the given path, clobbering the existing
1327 destination if it exists.
1328 """
1329 if self._closed:
1330 self._raise_closed()
1331 self._accessor.replace(self, target)
1332
1333 def symlink_to(self, target, target_is_directory=False):
1334 """
1335 Make this path a symlink pointing to the given path.
1336 Note the order of arguments (self, target) is the reverse of os.symlink's.
1337 """
1338 if self._closed:
1339 self._raise_closed()
1340 self._accessor.symlink(target, self, target_is_directory)
1341
1342 # Convenience functions for querying the stat results
1343
1344 def exists(self):
1345 """
1346 Whether this path exists.
1347 """
1348 try:
1349 self.stat()
1350 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001351 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001352 raise
1353 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001354 except ValueError:
1355 # Non-encodable path
1356 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001357 return True
1358
1359 def is_dir(self):
1360 """
1361 Whether this path is a directory.
1362 """
1363 try:
1364 return S_ISDIR(self.stat().st_mode)
1365 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001366 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001367 raise
1368 # Path doesn't exist or is a broken symlink
1369 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1370 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001371 except ValueError:
1372 # Non-encodable path
1373 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001374
1375 def is_file(self):
1376 """
1377 Whether this path is a regular file (also True for symlinks pointing
1378 to regular files).
1379 """
1380 try:
1381 return S_ISREG(self.stat().st_mode)
1382 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001383 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001384 raise
1385 # Path doesn't exist or is a broken symlink
1386 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1387 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001388 except ValueError:
1389 # Non-encodable path
1390 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001391
Cooper Lees173ff4a2017-08-01 15:35:45 -07001392 def is_mount(self):
1393 """
1394 Check if this path is a POSIX mount point
1395 """
1396 # Need to exist and be a dir
1397 if not self.exists() or not self.is_dir():
1398 return False
1399
1400 parent = Path(self.parent)
1401 try:
1402 parent_dev = parent.stat().st_dev
1403 except OSError:
1404 return False
1405
1406 dev = self.stat().st_dev
1407 if dev != parent_dev:
1408 return True
1409 ino = self.stat().st_ino
1410 parent_ino = parent.stat().st_ino
1411 return ino == parent_ino
1412
Antoine Pitrou31119e42013-11-22 17:38:12 +01001413 def is_symlink(self):
1414 """
1415 Whether this path is a symbolic link.
1416 """
1417 try:
1418 return S_ISLNK(self.lstat().st_mode)
1419 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001420 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001421 raise
1422 # Path doesn't exist
1423 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001424 except ValueError:
1425 # Non-encodable path
1426 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001427
1428 def is_block_device(self):
1429 """
1430 Whether this path is a block device.
1431 """
1432 try:
1433 return S_ISBLK(self.stat().st_mode)
1434 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001435 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001436 raise
1437 # Path doesn't exist or is a broken symlink
1438 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1439 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001440 except ValueError:
1441 # Non-encodable path
1442 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001443
1444 def is_char_device(self):
1445 """
1446 Whether this path is a character device.
1447 """
1448 try:
1449 return S_ISCHR(self.stat().st_mode)
1450 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001451 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001452 raise
1453 # Path doesn't exist or is a broken symlink
1454 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1455 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001456 except ValueError:
1457 # Non-encodable path
1458 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001459
1460 def is_fifo(self):
1461 """
1462 Whether this path is a FIFO.
1463 """
1464 try:
1465 return S_ISFIFO(self.stat().st_mode)
1466 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001467 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001468 raise
1469 # Path doesn't exist or is a broken symlink
1470 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1471 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001472 except ValueError:
1473 # Non-encodable path
1474 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001475
1476 def is_socket(self):
1477 """
1478 Whether this path is a socket.
1479 """
1480 try:
1481 return S_ISSOCK(self.stat().st_mode)
1482 except OSError as e:
Steve Dower2f6fae62019-02-03 23:08:18 -08001483 if not _ignore_error(e):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001484 raise
1485 # Path doesn't exist or is a broken symlink
1486 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1487 return False
Serhiy Storchaka0185f342018-09-18 11:28:51 +03001488 except ValueError:
1489 # Non-encodable path
1490 return False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001491
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001492 def expanduser(self):
1493 """ Return a new path with expanded ~ and ~user constructs
1494 (as returned by os.path.expanduser)
1495 """
1496 if (not (self._drv or self._root) and
1497 self._parts and self._parts[0][:1] == '~'):
1498 homedir = self._flavour.gethomedir(self._parts[0][1:])
1499 return self._from_parts([homedir] + self._parts[1:])
1500
1501 return self
1502
Antoine Pitrou31119e42013-11-22 17:38:12 +01001503
1504class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001505 """Path subclass for non-Windows systems.
1506
1507 On a POSIX system, instantiating a Path should return this object.
1508 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001509 __slots__ = ()
1510
1511class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001512 """Path subclass for Windows systems.
1513
1514 On a Windows system, instantiating a Path should return this object.
1515 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001516 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001517
1518 def owner(self):
1519 raise NotImplementedError("Path.owner() is unsupported on this system")
1520
1521 def group(self):
1522 raise NotImplementedError("Path.group() is unsupported on this system")
Cooper Lees173ff4a2017-08-01 15:35:45 -07001523
1524 def is_mount(self):
1525 raise NotImplementedError("Path.is_mount() is unsupported on this system")