blob: c2986bd022d09457fdd3ef5e9badcc6376890f62 [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
Antoine Pitrou31119e42013-11-22 17:38:12 +010040def _is_wildcard_pattern(pat):
41 # Whether this pattern needs actual matching using fnmatch, or can
42 # be looked up directly as a file.
43 return "*" in pat or "?" in pat or "[" in pat
44
45
46class _Flavour(object):
47 """A flavour implements a particular (platform-specific) set of path
48 semantics."""
49
50 def __init__(self):
51 self.join = self.sep.join
52
53 def parse_parts(self, parts):
54 parsed = []
55 sep = self.sep
56 altsep = self.altsep
57 drv = root = ''
58 it = reversed(parts)
59 for part in it:
60 if not part:
61 continue
62 if altsep:
63 part = part.replace(altsep, sep)
64 drv, root, rel = self.splitroot(part)
65 if sep in rel:
66 for x in reversed(rel.split(sep)):
67 if x and x != '.':
68 parsed.append(sys.intern(x))
69 else:
70 if rel and rel != '.':
71 parsed.append(sys.intern(rel))
72 if drv or root:
73 if not drv:
74 # If no drive is present, try to find one in the previous
75 # parts. This makes the result of parsing e.g.
76 # ("C:", "/", "a") reasonably intuitive.
77 for part in it:
Antoine Pitrou57fffd62015-02-15 18:03:59 +010078 if not part:
79 continue
80 if altsep:
81 part = part.replace(altsep, sep)
Antoine Pitrou31119e42013-11-22 17:38:12 +010082 drv = self.splitroot(part)[0]
83 if drv:
84 break
85 break
86 if drv or root:
87 parsed.append(drv + root)
88 parsed.reverse()
89 return drv, root, parsed
90
91 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
92 """
93 Join the two paths represented by the respective
94 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple.
95 """
96 if root2:
Serhiy Storchakaa9939022013-12-06 17:14:12 +020097 if not drv2 and drv:
98 return drv, root2, [drv + root2] + parts2[1:]
99 elif drv2:
100 if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
101 # Same drive => second path is relative to the first
102 return drv, root, parts + parts2[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100103 else:
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200104 # Second path is non-anchored (common case)
105 return drv, root, parts + parts2
106 return drv2, root2, parts2
Antoine Pitrou31119e42013-11-22 17:38:12 +0100107
108
109class _WindowsFlavour(_Flavour):
110 # Reference for Windows paths can be found at
111 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
112
113 sep = '\\'
114 altsep = '/'
115 has_drv = True
116 pathmod = ntpath
117
Antoine Pitroudb118f52014-11-19 00:32:08 +0100118 is_supported = (os.name == 'nt')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100119
Jon Dufresne39726282017-05-18 07:35:54 -0700120 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100121 ext_namespace_prefix = '\\\\?\\'
122
123 reserved_names = (
124 {'CON', 'PRN', 'AUX', 'NUL'} |
125 {'COM%d' % i for i in range(1, 10)} |
126 {'LPT%d' % i for i in range(1, 10)}
127 )
128
129 # Interesting findings about extended paths:
130 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
131 # but '\\?\c:/a' is not
132 # - extended paths are always absolute; "relative" extended paths will
133 # fail.
134
135 def splitroot(self, part, sep=sep):
136 first = part[0:1]
137 second = part[1:2]
138 if (second == sep and first == sep):
139 # XXX extended paths should also disable the collapsing of "."
140 # components (according to MSDN docs).
141 prefix, part = self._split_extended_path(part)
142 first = part[0:1]
143 second = part[1:2]
144 else:
145 prefix = ''
146 third = part[2:3]
147 if (second == sep and first == sep and third != sep):
148 # is a UNC path:
149 # vvvvvvvvvvvvvvvvvvvvv root
150 # \\machine\mountpoint\directory\etc\...
151 # directory ^^^^^^^^^^^^^^
152 index = part.find(sep, 2)
153 if index != -1:
154 index2 = part.find(sep, index + 1)
155 # a UNC path can't have two slashes in a row
156 # (after the initial two)
157 if index2 != index + 1:
158 if index2 == -1:
159 index2 = len(part)
160 if prefix:
161 return prefix + part[1:index2], sep, part[index2+1:]
162 else:
163 return part[:index2], sep, part[index2+1:]
164 drv = root = ''
165 if second == ':' and first in self.drive_letters:
166 drv = part[:2]
167 part = part[2:]
168 first = third
169 if first == sep:
170 root = first
171 part = part.lstrip(sep)
172 return prefix + drv, root, part
173
174 def casefold(self, s):
175 return s.lower()
176
177 def casefold_parts(self, parts):
178 return [p.lower() for p in parts]
179
Steve Dower98eb3602016-11-09 12:58:17 -0800180 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100181 s = str(path)
182 if not s:
183 return os.getcwd()
Steve Dower98eb3602016-11-09 12:58:17 -0800184 previous_s = None
Antoine Pitrou31119e42013-11-22 17:38:12 +0100185 if _getfinalpathname is not None:
Steve Dower98eb3602016-11-09 12:58:17 -0800186 if strict:
187 return self._ext_to_normal(_getfinalpathname(s))
188 else:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200189 tail_parts = [] # End of the path after the first one not found
Steve Dower98eb3602016-11-09 12:58:17 -0800190 while True:
191 try:
192 s = self._ext_to_normal(_getfinalpathname(s))
193 except FileNotFoundError:
194 previous_s = s
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200195 s, tail = os.path.split(s)
196 tail_parts.append(tail)
Steve Dower4b1e98b2016-12-28 16:02:59 -0800197 if previous_s == s:
198 return path
Steve Dower98eb3602016-11-09 12:58:17 -0800199 else:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200200 return os.path.join(s, *reversed(tail_parts))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100201 # Means fallback on absolute
202 return None
203
204 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
205 prefix = ''
206 if s.startswith(ext_prefix):
207 prefix = s[:4]
208 s = s[4:]
209 if s.startswith('UNC\\'):
210 prefix += s[:3]
211 s = '\\' + s[3:]
212 return prefix, s
213
214 def _ext_to_normal(self, s):
215 # Turn back an extended path into a normal DOS-like path
216 return self._split_extended_path(s)[1]
217
218 def is_reserved(self, parts):
219 # NOTE: the rules for reserved names seem somewhat complicated
220 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
221 # We err on the side of caution and return True for paths which are
222 # not considered reserved by Windows.
223 if not parts:
224 return False
225 if parts[0].startswith('\\\\'):
226 # UNC paths are never reserved
227 return False
228 return parts[-1].partition('.')[0].upper() in self.reserved_names
229
230 def make_uri(self, path):
231 # Under Windows, file URIs use the UTF-8 encoding.
232 drive = path.drive
233 if len(drive) == 2 and drive[1] == ':':
234 # It's a path on a local drive => 'file:///c:/a/b'
235 rest = path.as_posix()[2:].lstrip('/')
236 return 'file:///%s/%s' % (
237 drive, urlquote_from_bytes(rest.encode('utf-8')))
238 else:
239 # It's a path on a network drive => 'file://host/share/a/b'
240 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
241
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100242 def gethomedir(self, username):
243 if 'HOME' in os.environ:
244 userhome = os.environ['HOME']
245 elif 'USERPROFILE' in os.environ:
246 userhome = os.environ['USERPROFILE']
247 elif 'HOMEPATH' in os.environ:
Antoine Pitrou5d4e27e2014-12-30 22:09:42 +0100248 try:
249 drv = os.environ['HOMEDRIVE']
250 except KeyError:
251 drv = ''
252 userhome = drv + os.environ['HOMEPATH']
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100253 else:
254 raise RuntimeError("Can't determine home directory")
255
256 if username:
257 # Try to guess user home directory. By default all users
258 # directories are located in the same place and are named by
259 # corresponding usernames. If current user home directory points
260 # to nonstandard place, this guess is likely wrong.
261 if os.environ['USERNAME'] != username:
262 drv, root, parts = self.parse_parts((userhome,))
263 if parts[-1] != os.environ['USERNAME']:
264 raise RuntimeError("Can't determine home directory "
265 "for %r" % username)
266 parts[-1] = username
267 if drv or root:
268 userhome = drv + root + self.join(parts[1:])
269 else:
270 userhome = self.join(parts)
271 return userhome
Antoine Pitrou31119e42013-11-22 17:38:12 +0100272
273class _PosixFlavour(_Flavour):
274 sep = '/'
275 altsep = ''
276 has_drv = False
277 pathmod = posixpath
278
279 is_supported = (os.name != 'nt')
280
281 def splitroot(self, part, sep=sep):
282 if part and part[0] == sep:
283 stripped_part = part.lstrip(sep)
284 # According to POSIX path resolution:
285 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
286 # "A pathname that begins with two successive slashes may be
287 # interpreted in an implementation-defined manner, although more
288 # than two leading slashes shall be treated as a single slash".
289 if len(part) - len(stripped_part) == 2:
290 return '', sep * 2, stripped_part
291 else:
292 return '', sep, stripped_part
293 else:
294 return '', '', part
295
296 def casefold(self, s):
297 return s
298
299 def casefold_parts(self, parts):
300 return parts
301
Steve Dower98eb3602016-11-09 12:58:17 -0800302 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100303 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100304 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100305 seen = {}
306 def _resolve(path, rest):
307 if rest.startswith(sep):
308 path = ''
309
310 for name in rest.split(sep):
311 if not name or name == '.':
312 # current dir
313 continue
314 if name == '..':
315 # parent dir
316 path, _, _ = path.rpartition(sep)
317 continue
318 newpath = path + sep + name
319 if newpath in seen:
320 # Already seen this path
321 path = seen[newpath]
322 if path is not None:
323 # use cached value
324 continue
325 # The symlink is not resolved, so we must have a symlink loop.
326 raise RuntimeError("Symlink loop from %r" % newpath)
327 # Resolve the symbolic link
328 try:
329 target = accessor.readlink(newpath)
330 except OSError as e:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200331 if e.errno != EINVAL and strict:
332 raise
333 # Not a symlink, or non-strict mode. We just leave the path
334 # untouched.
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100335 path = newpath
336 else:
337 seen[newpath] = None # not resolved symlink
338 path = _resolve(path, target)
339 seen[newpath] = path # resolved symlink
340
341 return path
342 # NOTE: according to POSIX, getcwd() cannot contain path components
343 # which are symlinks.
344 base = '' if path.is_absolute() else os.getcwd()
345 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100346
347 def is_reserved(self, parts):
348 return False
349
350 def make_uri(self, path):
351 # We represent the path using the local filesystem encoding,
352 # for portability to other applications.
353 bpath = bytes(path)
354 return 'file://' + urlquote_from_bytes(bpath)
355
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100356 def gethomedir(self, username):
357 if not username:
358 try:
359 return os.environ['HOME']
360 except KeyError:
361 import pwd
362 return pwd.getpwuid(os.getuid()).pw_dir
363 else:
364 import pwd
365 try:
366 return pwd.getpwnam(username).pw_dir
367 except KeyError:
368 raise RuntimeError("Can't determine home directory "
369 "for %r" % username)
370
Antoine Pitrou31119e42013-11-22 17:38:12 +0100371
372_windows_flavour = _WindowsFlavour()
373_posix_flavour = _PosixFlavour()
374
375
376class _Accessor:
377 """An accessor implements a particular (system-specific or not) way of
378 accessing paths on the filesystem."""
379
380
381class _NormalAccessor(_Accessor):
382
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200383 stat = os.stat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100384
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200385 lstat = os.lstat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100386
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200387 open = os.open
Antoine Pitrou31119e42013-11-22 17:38:12 +0100388
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200389 listdir = os.listdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100390
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200391 scandir = os.scandir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100392
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200393 chmod = os.chmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100394
395 if hasattr(os, "lchmod"):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200396 lchmod = os.lchmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100397 else:
398 def lchmod(self, pathobj, mode):
399 raise NotImplementedError("lchmod() not available on this system")
400
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200401 mkdir = os.mkdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100402
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200403 unlink = os.unlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100404
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200405 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100406
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200407 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100408
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200409 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100410
411 if nt:
412 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200413 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100414 else:
415 def symlink(a, b, target_is_directory):
416 raise NotImplementedError("symlink() not available on this system")
417 else:
418 # Under POSIX, os.symlink() takes two args
419 @staticmethod
420 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200421 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100422
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200423 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100424
425 # Helper for resolve()
426 def readlink(self, path):
427 return os.readlink(path)
428
429
430_normal_accessor = _NormalAccessor()
431
432
433#
434# Globbing helpers
435#
436
Antoine Pitrou31119e42013-11-22 17:38:12 +0100437def _make_selector(pattern_parts):
438 pat = pattern_parts[0]
439 child_parts = pattern_parts[1:]
440 if pat == '**':
441 cls = _RecursiveWildcardSelector
442 elif '**' in pat:
443 raise ValueError("Invalid pattern: '**' can only be an entire path component")
444 elif _is_wildcard_pattern(pat):
445 cls = _WildcardSelector
446 else:
447 cls = _PreciseSelector
448 return cls(pat, child_parts)
449
450if hasattr(functools, "lru_cache"):
451 _make_selector = functools.lru_cache()(_make_selector)
452
453
454class _Selector:
455 """A selector matches a specific glob pattern part against the children
456 of a given path."""
457
458 def __init__(self, child_parts):
459 self.child_parts = child_parts
460 if child_parts:
461 self.successor = _make_selector(child_parts)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300462 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100463 else:
464 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300465 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100466
467 def select_from(self, parent_path):
468 """Iterate over all child paths of `parent_path` matched by this
469 selector. This can contain parent_path itself."""
470 path_cls = type(parent_path)
471 is_dir = path_cls.is_dir
472 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300473 scandir = parent_path._accessor.scandir
474 if not is_dir(parent_path):
475 return iter([])
476 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100477
478
479class _TerminatingSelector:
480
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300481 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100482 yield parent_path
483
484
485class _PreciseSelector(_Selector):
486
487 def __init__(self, name, child_parts):
488 self.name = name
489 _Selector.__init__(self, child_parts)
490
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300491 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800492 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800493 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300494 if (is_dir if self.dironly else exists)(path):
495 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800496 yield p
497 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100498 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100499
500
501class _WildcardSelector(_Selector):
502
503 def __init__(self, pat, child_parts):
504 self.pat = re.compile(fnmatch.translate(pat))
505 _Selector.__init__(self, child_parts)
506
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300507 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800508 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800509 cf = parent_path._flavour.casefold
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300510 entries = list(scandir(parent_path))
511 for entry in entries:
512 if not self.dironly or entry.is_dir():
513 name = entry.name
514 casefolded = cf(name)
515 if self.pat.match(casefolded):
516 path = parent_path._make_child_relpath(name)
517 for p in self.successor._select_from(path, is_dir, exists, scandir):
518 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800519 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100520 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800521
Antoine Pitrou31119e42013-11-22 17:38:12 +0100522
523
524class _RecursiveWildcardSelector(_Selector):
525
526 def __init__(self, pat, child_parts):
527 _Selector.__init__(self, child_parts)
528
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300529 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100530 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800531 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300532 entries = list(scandir(parent_path))
533 for entry in entries:
Przemysław Spodymek216b7452018-08-27 23:33:45 +0200534 entry_is_dir = False
535 try:
536 entry_is_dir = entry.is_dir()
537 except OSError as e:
538 if e.errno not in _IGNORED_ERROS:
539 raise
540 if entry_is_dir and not entry.is_symlink():
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300541 path = parent_path._make_child_relpath(entry.name)
542 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800543 yield p
544 except PermissionError:
545 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100546
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300547 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800548 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300549 yielded = set()
550 try:
551 successor_select = self.successor._select_from
552 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
553 for p in successor_select(starting_point, is_dir, exists, scandir):
554 if p not in yielded:
555 yield p
556 yielded.add(p)
557 finally:
558 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800559 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100560 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100561
562
563#
564# Public API
565#
566
567class _PathParents(Sequence):
568 """This object provides sequence-like access to the logical ancestors
569 of a path. Don't try to construct it yourself."""
570 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
571
572 def __init__(self, path):
573 # We don't store the instance to avoid reference cycles
574 self._pathcls = type(path)
575 self._drv = path._drv
576 self._root = path._root
577 self._parts = path._parts
578
579 def __len__(self):
580 if self._drv or self._root:
581 return len(self._parts) - 1
582 else:
583 return len(self._parts)
584
585 def __getitem__(self, idx):
586 if idx < 0 or idx >= len(self):
587 raise IndexError(idx)
588 return self._pathcls._from_parsed_parts(self._drv, self._root,
589 self._parts[:-idx - 1])
590
591 def __repr__(self):
592 return "<{}.parents>".format(self._pathcls.__name__)
593
594
595class PurePath(object):
chasondfa015c2018-02-19 08:36:32 +0900596 """Base class for manipulating paths without I/O.
597
598 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100599 don't imply any actual filesystem I/O. Depending on your system,
600 instantiating a PurePath will return either a PurePosixPath or a
601 PureWindowsPath object. You can also instantiate either of these classes
602 directly, regardless of your system.
603 """
604 __slots__ = (
605 '_drv', '_root', '_parts',
606 '_str', '_hash', '_pparts', '_cached_cparts',
607 )
608
609 def __new__(cls, *args):
610 """Construct a PurePath from one or several strings and or existing
611 PurePath objects. The strings and path objects are combined so as
612 to yield a canonicalized path, which is incorporated into the
613 new PurePath object.
614 """
615 if cls is PurePath:
616 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
617 return cls._from_parts(args)
618
619 def __reduce__(self):
620 # Using the parts tuple helps share interned path parts
621 # when pickling related paths.
622 return (self.__class__, tuple(self._parts))
623
624 @classmethod
625 def _parse_args(cls, args):
626 # This is useful when you don't want to create an instance, just
627 # canonicalize some constructor arguments.
628 parts = []
629 for a in args:
630 if isinstance(a, PurePath):
631 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100632 else:
Brett Cannon568be632016-06-10 12:20:49 -0700633 a = os.fspath(a)
634 if isinstance(a, str):
635 # Force-cast str subclasses to str (issue #21127)
636 parts.append(str(a))
637 else:
638 raise TypeError(
639 "argument should be a str object or an os.PathLike "
640 "object returning str, not %r"
641 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100642 return cls._flavour.parse_parts(parts)
643
644 @classmethod
645 def _from_parts(cls, args, init=True):
646 # We need to call _parse_args on the instance, so as to get the
647 # right flavour.
648 self = object.__new__(cls)
649 drv, root, parts = self._parse_args(args)
650 self._drv = drv
651 self._root = root
652 self._parts = parts
653 if init:
654 self._init()
655 return self
656
657 @classmethod
658 def _from_parsed_parts(cls, drv, root, parts, init=True):
659 self = object.__new__(cls)
660 self._drv = drv
661 self._root = root
662 self._parts = parts
663 if init:
664 self._init()
665 return self
666
667 @classmethod
668 def _format_parsed_parts(cls, drv, root, parts):
669 if drv or root:
670 return drv + root + cls._flavour.join(parts[1:])
671 else:
672 return cls._flavour.join(parts)
673
674 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000675 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100676 pass
677
678 def _make_child(self, args):
679 drv, root, parts = self._parse_args(args)
680 drv, root, parts = self._flavour.join_parsed_parts(
681 self._drv, self._root, self._parts, drv, root, parts)
682 return self._from_parsed_parts(drv, root, parts)
683
684 def __str__(self):
685 """Return the string representation of the path, suitable for
686 passing to system calls."""
687 try:
688 return self._str
689 except AttributeError:
690 self._str = self._format_parsed_parts(self._drv, self._root,
691 self._parts) or '.'
692 return self._str
693
Brett Cannon568be632016-06-10 12:20:49 -0700694 def __fspath__(self):
695 return str(self)
696
Antoine Pitrou31119e42013-11-22 17:38:12 +0100697 def as_posix(self):
698 """Return the string representation of the path with forward (/)
699 slashes."""
700 f = self._flavour
701 return str(self).replace(f.sep, '/')
702
703 def __bytes__(self):
704 """Return the bytes representation of the path. This is only
705 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200706 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100707
708 def __repr__(self):
709 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
710
711 def as_uri(self):
712 """Return the path as a 'file' URI."""
713 if not self.is_absolute():
714 raise ValueError("relative path can't be expressed as a file URI")
715 return self._flavour.make_uri(self)
716
717 @property
718 def _cparts(self):
719 # Cached casefolded parts, for hashing and comparison
720 try:
721 return self._cached_cparts
722 except AttributeError:
723 self._cached_cparts = self._flavour.casefold_parts(self._parts)
724 return self._cached_cparts
725
726 def __eq__(self, other):
727 if not isinstance(other, PurePath):
728 return NotImplemented
729 return self._cparts == other._cparts and self._flavour is other._flavour
730
Antoine Pitrou31119e42013-11-22 17:38:12 +0100731 def __hash__(self):
732 try:
733 return self._hash
734 except AttributeError:
735 self._hash = hash(tuple(self._cparts))
736 return self._hash
737
738 def __lt__(self, other):
739 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
740 return NotImplemented
741 return self._cparts < other._cparts
742
743 def __le__(self, other):
744 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
745 return NotImplemented
746 return self._cparts <= other._cparts
747
748 def __gt__(self, other):
749 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
750 return NotImplemented
751 return self._cparts > other._cparts
752
753 def __ge__(self, other):
754 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
755 return NotImplemented
756 return self._cparts >= other._cparts
757
758 drive = property(attrgetter('_drv'),
759 doc="""The drive prefix (letter or UNC path), if any.""")
760
761 root = property(attrgetter('_root'),
762 doc="""The root of the path, if any.""")
763
764 @property
765 def anchor(self):
766 """The concatenation of the drive and root, or ''."""
767 anchor = self._drv + self._root
768 return anchor
769
770 @property
771 def name(self):
772 """The final path component, if any."""
773 parts = self._parts
774 if len(parts) == (1 if (self._drv or self._root) else 0):
775 return ''
776 return parts[-1]
777
778 @property
779 def suffix(self):
780 """The final component's last suffix, if any."""
781 name = self.name
782 i = name.rfind('.')
783 if 0 < i < len(name) - 1:
784 return name[i:]
785 else:
786 return ''
787
788 @property
789 def suffixes(self):
790 """A list of the final component's suffixes, if any."""
791 name = self.name
792 if name.endswith('.'):
793 return []
794 name = name.lstrip('.')
795 return ['.' + suffix for suffix in name.split('.')[1:]]
796
797 @property
798 def stem(self):
799 """The final path component, minus its last suffix."""
800 name = self.name
801 i = name.rfind('.')
802 if 0 < i < len(name) - 1:
803 return name[:i]
804 else:
805 return name
806
807 def with_name(self, name):
808 """Return a new path with the file name changed."""
809 if not self.name:
810 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400811 drv, root, parts = self._flavour.parse_parts((name,))
812 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
813 or drv or root or len(parts) != 1):
814 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100815 return self._from_parsed_parts(self._drv, self._root,
816 self._parts[:-1] + [name])
817
818 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200819 """Return a new path with the file suffix changed. If the path
820 has no suffix, add given suffix. If the given suffix is an empty
821 string, remove the suffix from the path.
822 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400823 f = self._flavour
824 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300825 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400826 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100827 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100828 name = self.name
829 if not name:
830 raise ValueError("%r has an empty name" % (self,))
831 old_suffix = self.suffix
832 if not old_suffix:
833 name = name + suffix
834 else:
835 name = name[:-len(old_suffix)] + suffix
836 return self._from_parsed_parts(self._drv, self._root,
837 self._parts[:-1] + [name])
838
839 def relative_to(self, *other):
840 """Return the relative path to another path identified by the passed
841 arguments. If the operation is not possible (because this is not
842 a subpath of the other path), raise ValueError.
843 """
844 # For the purpose of this method, drive and root are considered
845 # separate parts, i.e.:
846 # Path('c:/').relative_to('c:') gives Path('/')
847 # Path('c:/').relative_to('/') raise ValueError
848 if not other:
849 raise TypeError("need at least one argument")
850 parts = self._parts
851 drv = self._drv
852 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100853 if root:
854 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100855 else:
856 abs_parts = parts
857 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100858 if to_root:
859 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100860 else:
861 to_abs_parts = to_parts
862 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100863 cf = self._flavour.casefold_parts
864 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100865 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
866 raise ValueError("{!r} does not start with {!r}"
867 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100868 return self._from_parsed_parts('', root if n == 1 else '',
869 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100870
871 @property
872 def parts(self):
873 """An object providing sequence-like access to the
874 components in the filesystem path."""
875 # We cache the tuple to avoid building a new one each time .parts
876 # is accessed. XXX is this necessary?
877 try:
878 return self._pparts
879 except AttributeError:
880 self._pparts = tuple(self._parts)
881 return self._pparts
882
883 def joinpath(self, *args):
884 """Combine this path with one or several arguments, and return a
885 new path representing either a subpath (if all arguments are relative
886 paths) or a totally different path (if one of the arguments is
887 anchored).
888 """
889 return self._make_child(args)
890
891 def __truediv__(self, key):
892 return self._make_child((key,))
893
894 def __rtruediv__(self, key):
895 return self._from_parts([key] + self._parts)
896
897 @property
898 def parent(self):
899 """The logical parent of the path."""
900 drv = self._drv
901 root = self._root
902 parts = self._parts
903 if len(parts) == 1 and (drv or root):
904 return self
905 return self._from_parsed_parts(drv, root, parts[:-1])
906
907 @property
908 def parents(self):
909 """A sequence of this path's logical parents."""
910 return _PathParents(self)
911
912 def is_absolute(self):
913 """True if the path is absolute (has both a root and, if applicable,
914 a drive)."""
915 if not self._root:
916 return False
917 return not self._flavour.has_drv or bool(self._drv)
918
919 def is_reserved(self):
920 """Return True if the path contains one of the special names reserved
921 by the system, if any."""
922 return self._flavour.is_reserved(self._parts)
923
924 def match(self, path_pattern):
925 """
926 Return True if this path matches the given pattern.
927 """
928 cf = self._flavour.casefold
929 path_pattern = cf(path_pattern)
930 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
931 if not pat_parts:
932 raise ValueError("empty pattern")
933 if drv and drv != cf(self._drv):
934 return False
935 if root and root != cf(self._root):
936 return False
937 parts = self._cparts
938 if drv or root:
939 if len(pat_parts) != len(parts):
940 return False
941 pat_parts = pat_parts[1:]
942 elif len(pat_parts) > len(parts):
943 return False
944 for part, pat in zip(reversed(parts), reversed(pat_parts)):
945 if not fnmatch.fnmatchcase(part, pat):
946 return False
947 return True
948
Brett Cannon568be632016-06-10 12:20:49 -0700949# Can't subclass os.PathLike from PurePath and keep the constructor
950# optimizations in PurePath._parse_args().
951os.PathLike.register(PurePath)
952
Antoine Pitrou31119e42013-11-22 17:38:12 +0100953
954class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900955 """PurePath subclass for non-Windows systems.
956
957 On a POSIX system, instantiating a PurePath should return this object.
958 However, you can also instantiate it directly on any system.
959 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100960 _flavour = _posix_flavour
961 __slots__ = ()
962
963
964class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900965 """PurePath subclass for Windows systems.
966
967 On a Windows system, instantiating a PurePath should return this object.
968 However, you can also instantiate it directly on any system.
969 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100970 _flavour = _windows_flavour
971 __slots__ = ()
972
973
974# Filesystem-accessing classes
975
976
977class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900978 """PurePath subclass that can make system calls.
979
980 Path represents a filesystem path but unlike PurePath, also offers
981 methods to do system calls on path objects. Depending on your system,
982 instantiating a Path will return either a PosixPath or a WindowsPath
983 object. You can also instantiate a PosixPath or WindowsPath directly,
984 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
985 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100986 __slots__ = (
987 '_accessor',
988 '_closed',
989 )
990
991 def __new__(cls, *args, **kwargs):
992 if cls is Path:
993 cls = WindowsPath if os.name == 'nt' else PosixPath
994 self = cls._from_parts(args, init=False)
995 if not self._flavour.is_supported:
996 raise NotImplementedError("cannot instantiate %r on your system"
997 % (cls.__name__,))
998 self._init()
999 return self
1000
1001 def _init(self,
1002 # Private non-constructor arguments
1003 template=None,
1004 ):
1005 self._closed = False
1006 if template is not None:
1007 self._accessor = template._accessor
1008 else:
1009 self._accessor = _normal_accessor
1010
1011 def _make_child_relpath(self, part):
1012 # This is an optimization used for dir walking. `part` must be
1013 # a single part relative to this path.
1014 parts = self._parts + [part]
1015 return self._from_parsed_parts(self._drv, self._root, parts)
1016
1017 def __enter__(self):
1018 if self._closed:
1019 self._raise_closed()
1020 return self
1021
1022 def __exit__(self, t, v, tb):
1023 self._closed = True
1024
1025 def _raise_closed(self):
1026 raise ValueError("I/O operation on closed path")
1027
1028 def _opener(self, name, flags, mode=0o666):
1029 # A stub for the opener argument to built-in open()
1030 return self._accessor.open(self, flags, mode)
1031
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001032 def _raw_open(self, flags, mode=0o777):
1033 """
1034 Open the file pointed by this path and return a file descriptor,
1035 as os.open() does.
1036 """
1037 if self._closed:
1038 self._raise_closed()
1039 return self._accessor.open(self, flags, mode)
1040
Antoine Pitrou31119e42013-11-22 17:38:12 +01001041 # Public API
1042
1043 @classmethod
1044 def cwd(cls):
1045 """Return a new path pointing to the current working directory
1046 (as returned by os.getcwd()).
1047 """
1048 return cls(os.getcwd())
1049
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001050 @classmethod
1051 def home(cls):
1052 """Return a new path pointing to the user's home directory (as
1053 returned by os.path.expanduser('~')).
1054 """
1055 return cls(cls()._flavour.gethomedir(None))
1056
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001057 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001058 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001059 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001060 """
1061 st = self.stat()
1062 try:
1063 other_st = other_path.stat()
1064 except AttributeError:
1065 other_st = os.stat(other_path)
1066 return os.path.samestat(st, other_st)
1067
Antoine Pitrou31119e42013-11-22 17:38:12 +01001068 def iterdir(self):
1069 """Iterate over the files in this directory. Does not yield any
1070 result for the special paths '.' and '..'.
1071 """
1072 if self._closed:
1073 self._raise_closed()
1074 for name in self._accessor.listdir(self):
1075 if name in {'.', '..'}:
1076 # Yielding a path object for these makes little sense
1077 continue
1078 yield self._make_child_relpath(name)
1079 if self._closed:
1080 self._raise_closed()
1081
1082 def glob(self, pattern):
1083 """Iterate over this subtree and yield all existing files (of any
1084 kind, including directories) matching the given pattern.
1085 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001086 if not pattern:
1087 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001088 pattern = self._flavour.casefold(pattern)
1089 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1090 if drv or root:
1091 raise NotImplementedError("Non-relative patterns are unsupported")
1092 selector = _make_selector(tuple(pattern_parts))
1093 for p in selector.select_from(self):
1094 yield p
1095
1096 def rglob(self, pattern):
1097 """Recursively yield all existing files (of any kind, including
1098 directories) matching the given pattern, anywhere in this subtree.
1099 """
1100 pattern = self._flavour.casefold(pattern)
1101 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1102 if drv or root:
1103 raise NotImplementedError("Non-relative patterns are unsupported")
1104 selector = _make_selector(("**",) + tuple(pattern_parts))
1105 for p in selector.select_from(self):
1106 yield p
1107
1108 def absolute(self):
1109 """Return an absolute version of this path. This function works
1110 even if the path doesn't point to anything.
1111
1112 No normalization is done, i.e. all '.' and '..' will be kept along.
1113 Use resolve() to get the canonical path to a file.
1114 """
1115 # XXX untested yet!
1116 if self._closed:
1117 self._raise_closed()
1118 if self.is_absolute():
1119 return self
1120 # FIXME this must defer to the specific flavour (and, under Windows,
1121 # use nt._getfullpathname())
1122 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1123 obj._init(template=self)
1124 return obj
1125
Steve Dower98eb3602016-11-09 12:58:17 -08001126 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001127 """
1128 Make the path absolute, resolving all symlinks on the way and also
1129 normalizing it (for example turning slashes into backslashes under
1130 Windows).
1131 """
1132 if self._closed:
1133 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001134 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001135 if s is None:
1136 # No symlink resolution => for consistency, raise an error if
1137 # the path doesn't exist or is forbidden
1138 self.stat()
1139 s = str(self.absolute())
1140 # Now we have no symlinks in the path, it's safe to normalize it.
1141 normed = self._flavour.pathmod.normpath(s)
1142 obj = self._from_parts((normed,), init=False)
1143 obj._init(template=self)
1144 return obj
1145
1146 def stat(self):
1147 """
1148 Return the result of the stat() system call on this path, like
1149 os.stat() does.
1150 """
1151 return self._accessor.stat(self)
1152
1153 def owner(self):
1154 """
1155 Return the login name of the file owner.
1156 """
1157 import pwd
1158 return pwd.getpwuid(self.stat().st_uid).pw_name
1159
1160 def group(self):
1161 """
1162 Return the group name of the file gid.
1163 """
1164 import grp
1165 return grp.getgrgid(self.stat().st_gid).gr_name
1166
Antoine Pitrou31119e42013-11-22 17:38:12 +01001167 def open(self, mode='r', buffering=-1, encoding=None,
1168 errors=None, newline=None):
1169 """
1170 Open the file pointed by this path and return a file object, as
1171 the built-in open() function does.
1172 """
1173 if self._closed:
1174 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001175 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001176 opener=self._opener)
1177
Georg Brandlea683982014-10-01 19:12:33 +02001178 def read_bytes(self):
1179 """
1180 Open the file in bytes mode, read it, and close the file.
1181 """
1182 with self.open(mode='rb') as f:
1183 return f.read()
1184
1185 def read_text(self, encoding=None, errors=None):
1186 """
1187 Open the file in text mode, read it, and close the file.
1188 """
1189 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1190 return f.read()
1191
1192 def write_bytes(self, data):
1193 """
1194 Open the file in bytes mode, write to it, and close the file.
1195 """
1196 # type-check for the buffer interface before truncating the file
1197 view = memoryview(data)
1198 with self.open(mode='wb') as f:
1199 return f.write(view)
1200
1201 def write_text(self, data, encoding=None, errors=None):
1202 """
1203 Open the file in text mode, write to it, and close the file.
1204 """
1205 if not isinstance(data, str):
1206 raise TypeError('data must be str, not %s' %
1207 data.__class__.__name__)
1208 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1209 return f.write(data)
1210
Antoine Pitrou31119e42013-11-22 17:38:12 +01001211 def touch(self, mode=0o666, exist_ok=True):
1212 """
1213 Create this file with the given access mode, if it doesn't exist.
1214 """
1215 if self._closed:
1216 self._raise_closed()
1217 if exist_ok:
1218 # First try to bump modification time
1219 # Implementation note: GNU touch uses the UTIME_NOW option of
1220 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001221 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001222 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001223 except OSError:
1224 # Avoid exception chaining
1225 pass
1226 else:
1227 return
1228 flags = os.O_CREAT | os.O_WRONLY
1229 if not exist_ok:
1230 flags |= os.O_EXCL
1231 fd = self._raw_open(flags, mode)
1232 os.close(fd)
1233
Barry Warsaw7c549c42014-08-05 11:28:12 -04001234 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001235 """
1236 Create a new directory at this given path.
1237 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001238 if self._closed:
1239 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001240 try:
1241 self._accessor.mkdir(self, mode)
1242 except FileNotFoundError:
1243 if not parents or self.parent == self:
1244 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001245 self.parent.mkdir(parents=True, exist_ok=True)
1246 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001247 except OSError:
1248 # Cannot rely on checking for EEXIST, since the operating system
1249 # could give priority to other errors like EACCES or EROFS
1250 if not exist_ok or not self.is_dir():
1251 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001252
1253 def chmod(self, mode):
1254 """
1255 Change the permissions of the path, like os.chmod().
1256 """
1257 if self._closed:
1258 self._raise_closed()
1259 self._accessor.chmod(self, mode)
1260
1261 def lchmod(self, mode):
1262 """
1263 Like chmod(), except if the path points to a symlink, the symlink's
1264 permissions are changed, rather than its target's.
1265 """
1266 if self._closed:
1267 self._raise_closed()
1268 self._accessor.lchmod(self, mode)
1269
1270 def unlink(self):
1271 """
1272 Remove this file or link.
1273 If the path is a directory, use rmdir() instead.
1274 """
1275 if self._closed:
1276 self._raise_closed()
1277 self._accessor.unlink(self)
1278
1279 def rmdir(self):
1280 """
1281 Remove this directory. The directory must be empty.
1282 """
1283 if self._closed:
1284 self._raise_closed()
1285 self._accessor.rmdir(self)
1286
1287 def lstat(self):
1288 """
1289 Like stat(), except if the path points to a symlink, the symlink's
1290 status information is returned, rather than its target's.
1291 """
1292 if self._closed:
1293 self._raise_closed()
1294 return self._accessor.lstat(self)
1295
1296 def rename(self, target):
1297 """
1298 Rename this path to the given path.
1299 """
1300 if self._closed:
1301 self._raise_closed()
1302 self._accessor.rename(self, target)
1303
1304 def replace(self, target):
1305 """
1306 Rename this path to the given path, clobbering the existing
1307 destination if it exists.
1308 """
1309 if self._closed:
1310 self._raise_closed()
1311 self._accessor.replace(self, target)
1312
1313 def symlink_to(self, target, target_is_directory=False):
1314 """
1315 Make this path a symlink pointing to the given path.
1316 Note the order of arguments (self, target) is the reverse of os.symlink's.
1317 """
1318 if self._closed:
1319 self._raise_closed()
1320 self._accessor.symlink(target, self, target_is_directory)
1321
1322 # Convenience functions for querying the stat results
1323
1324 def exists(self):
1325 """
1326 Whether this path exists.
1327 """
1328 try:
1329 self.stat()
1330 except OSError as e:
Przemysław Spodymek216b7452018-08-27 23:33:45 +02001331 if e.errno not in _IGNORED_ERROS:
Antoine Pitrou31119e42013-11-22 17:38:12 +01001332 raise
1333 return False
1334 return True
1335
1336 def is_dir(self):
1337 """
1338 Whether this path is a directory.
1339 """
1340 try:
1341 return S_ISDIR(self.stat().st_mode)
1342 except OSError as e:
Przemysław Spodymek216b7452018-08-27 23:33:45 +02001343 if e.errno not in _IGNORED_ERROS:
Antoine Pitrou31119e42013-11-22 17:38:12 +01001344 raise
1345 # Path doesn't exist or is a broken symlink
1346 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1347 return False
1348
1349 def is_file(self):
1350 """
1351 Whether this path is a regular file (also True for symlinks pointing
1352 to regular files).
1353 """
1354 try:
1355 return S_ISREG(self.stat().st_mode)
1356 except OSError as e:
Przemysław Spodymek216b7452018-08-27 23:33:45 +02001357 if e.errno not in _IGNORED_ERROS:
Antoine Pitrou31119e42013-11-22 17:38:12 +01001358 raise
1359 # Path doesn't exist or is a broken symlink
1360 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1361 return False
1362
Cooper Lees173ff4a2017-08-01 15:35:45 -07001363 def is_mount(self):
1364 """
1365 Check if this path is a POSIX mount point
1366 """
1367 # Need to exist and be a dir
1368 if not self.exists() or not self.is_dir():
1369 return False
1370
1371 parent = Path(self.parent)
1372 try:
1373 parent_dev = parent.stat().st_dev
1374 except OSError:
1375 return False
1376
1377 dev = self.stat().st_dev
1378 if dev != parent_dev:
1379 return True
1380 ino = self.stat().st_ino
1381 parent_ino = parent.stat().st_ino
1382 return ino == parent_ino
1383
Antoine Pitrou31119e42013-11-22 17:38:12 +01001384 def is_symlink(self):
1385 """
1386 Whether this path is a symbolic link.
1387 """
1388 try:
1389 return S_ISLNK(self.lstat().st_mode)
1390 except OSError as e:
Przemysław Spodymek216b7452018-08-27 23:33:45 +02001391 if e.errno not in _IGNORED_ERROS:
Antoine Pitrou31119e42013-11-22 17:38:12 +01001392 raise
1393 # Path doesn't exist
1394 return False
1395
1396 def is_block_device(self):
1397 """
1398 Whether this path is a block device.
1399 """
1400 try:
1401 return S_ISBLK(self.stat().st_mode)
1402 except OSError as e:
Przemysław Spodymek216b7452018-08-27 23:33:45 +02001403 if e.errno not in _IGNORED_ERROS:
Antoine Pitrou31119e42013-11-22 17:38:12 +01001404 raise
1405 # Path doesn't exist or is a broken symlink
1406 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1407 return False
1408
1409 def is_char_device(self):
1410 """
1411 Whether this path is a character device.
1412 """
1413 try:
1414 return S_ISCHR(self.stat().st_mode)
1415 except OSError as e:
Przemysław Spodymek216b7452018-08-27 23:33:45 +02001416 if e.errno not in _IGNORED_ERROS:
Antoine Pitrou31119e42013-11-22 17:38:12 +01001417 raise
1418 # Path doesn't exist or is a broken symlink
1419 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1420 return False
1421
1422 def is_fifo(self):
1423 """
1424 Whether this path is a FIFO.
1425 """
1426 try:
1427 return S_ISFIFO(self.stat().st_mode)
1428 except OSError as e:
Przemysław Spodymek216b7452018-08-27 23:33:45 +02001429 if e.errno not in _IGNORED_ERROS:
Antoine Pitrou31119e42013-11-22 17:38:12 +01001430 raise
1431 # Path doesn't exist or is a broken symlink
1432 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1433 return False
1434
1435 def is_socket(self):
1436 """
1437 Whether this path is a socket.
1438 """
1439 try:
1440 return S_ISSOCK(self.stat().st_mode)
1441 except OSError as e:
Przemysław Spodymek216b7452018-08-27 23:33:45 +02001442 if e.errno not in _IGNORED_ERROS:
Antoine Pitrou31119e42013-11-22 17:38:12 +01001443 raise
1444 # Path doesn't exist or is a broken symlink
1445 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1446 return False
1447
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001448 def expanduser(self):
1449 """ Return a new path with expanded ~ and ~user constructs
1450 (as returned by os.path.expanduser)
1451 """
1452 if (not (self._drv or self._root) and
1453 self._parts and self._parts[0][:1] == '~'):
1454 homedir = self._flavour.gethomedir(self._parts[0][1:])
1455 return self._from_parts([homedir] + self._parts[1:])
1456
1457 return self
1458
Antoine Pitrou31119e42013-11-22 17:38:12 +01001459
1460class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001461 """Path subclass for non-Windows systems.
1462
1463 On a POSIX system, instantiating a Path should return this object.
1464 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001465 __slots__ = ()
1466
1467class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001468 """Path subclass for Windows systems.
1469
1470 On a Windows system, instantiating a Path should return this object.
1471 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001472 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001473
1474 def owner(self):
1475 raise NotImplementedError("Path.owner() is unsupported on this system")
1476
1477 def group(self):
1478 raise NotImplementedError("Path.group() is unsupported on this system")
Cooper Lees173ff4a2017-08-01 15:35:45 -07001479
1480 def is_mount(self):
1481 raise NotImplementedError("Path.is_mount() is unsupported on this system")