blob: 6be372ed320a2c4e1c2d98875260822304ba2623 [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
Antoine Pitrou2b2852b2014-10-30 23:14:03 +010010from errno import EINVAL, ENOENT, ENOTDIR
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
37def _is_wildcard_pattern(pat):
38 # Whether this pattern needs actual matching using fnmatch, or can
39 # be looked up directly as a file.
40 return "*" in pat or "?" in pat or "[" in pat
41
42
43class _Flavour(object):
44 """A flavour implements a particular (platform-specific) set of path
45 semantics."""
46
47 def __init__(self):
48 self.join = self.sep.join
49
50 def parse_parts(self, parts):
51 parsed = []
52 sep = self.sep
53 altsep = self.altsep
54 drv = root = ''
55 it = reversed(parts)
56 for part in it:
57 if not part:
58 continue
59 if altsep:
60 part = part.replace(altsep, sep)
61 drv, root, rel = self.splitroot(part)
62 if sep in rel:
63 for x in reversed(rel.split(sep)):
64 if x and x != '.':
65 parsed.append(sys.intern(x))
66 else:
67 if rel and rel != '.':
68 parsed.append(sys.intern(rel))
69 if drv or root:
70 if not drv:
71 # If no drive is present, try to find one in the previous
72 # parts. This makes the result of parsing e.g.
73 # ("C:", "/", "a") reasonably intuitive.
74 for part in it:
Antoine Pitrou57fffd62015-02-15 18:03:59 +010075 if not part:
76 continue
77 if altsep:
78 part = part.replace(altsep, sep)
Antoine Pitrou31119e42013-11-22 17:38:12 +010079 drv = self.splitroot(part)[0]
80 if drv:
81 break
82 break
83 if drv or root:
84 parsed.append(drv + root)
85 parsed.reverse()
86 return drv, root, parsed
87
88 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
89 """
90 Join the two paths represented by the respective
91 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple.
92 """
93 if root2:
Serhiy Storchakaa9939022013-12-06 17:14:12 +020094 if not drv2 and drv:
95 return drv, root2, [drv + root2] + parts2[1:]
96 elif drv2:
97 if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
98 # Same drive => second path is relative to the first
99 return drv, root, parts + parts2[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100100 else:
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200101 # Second path is non-anchored (common case)
102 return drv, root, parts + parts2
103 return drv2, root2, parts2
Antoine Pitrou31119e42013-11-22 17:38:12 +0100104
105
106class _WindowsFlavour(_Flavour):
107 # Reference for Windows paths can be found at
108 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
109
110 sep = '\\'
111 altsep = '/'
112 has_drv = True
113 pathmod = ntpath
114
Antoine Pitroudb118f52014-11-19 00:32:08 +0100115 is_supported = (os.name == 'nt')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100116
Jon Dufresne39726282017-05-18 07:35:54 -0700117 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100118 ext_namespace_prefix = '\\\\?\\'
119
120 reserved_names = (
121 {'CON', 'PRN', 'AUX', 'NUL'} |
122 {'COM%d' % i for i in range(1, 10)} |
123 {'LPT%d' % i for i in range(1, 10)}
124 )
125
126 # Interesting findings about extended paths:
127 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
128 # but '\\?\c:/a' is not
129 # - extended paths are always absolute; "relative" extended paths will
130 # fail.
131
132 def splitroot(self, part, sep=sep):
133 first = part[0:1]
134 second = part[1:2]
135 if (second == sep and first == sep):
136 # XXX extended paths should also disable the collapsing of "."
137 # components (according to MSDN docs).
138 prefix, part = self._split_extended_path(part)
139 first = part[0:1]
140 second = part[1:2]
141 else:
142 prefix = ''
143 third = part[2:3]
144 if (second == sep and first == sep and third != sep):
145 # is a UNC path:
146 # vvvvvvvvvvvvvvvvvvvvv root
147 # \\machine\mountpoint\directory\etc\...
148 # directory ^^^^^^^^^^^^^^
149 index = part.find(sep, 2)
150 if index != -1:
151 index2 = part.find(sep, index + 1)
152 # a UNC path can't have two slashes in a row
153 # (after the initial two)
154 if index2 != index + 1:
155 if index2 == -1:
156 index2 = len(part)
157 if prefix:
158 return prefix + part[1:index2], sep, part[index2+1:]
159 else:
160 return part[:index2], sep, part[index2+1:]
161 drv = root = ''
162 if second == ':' and first in self.drive_letters:
163 drv = part[:2]
164 part = part[2:]
165 first = third
166 if first == sep:
167 root = first
168 part = part.lstrip(sep)
169 return prefix + drv, root, part
170
171 def casefold(self, s):
172 return s.lower()
173
174 def casefold_parts(self, parts):
175 return [p.lower() for p in parts]
176
Steve Dower98eb3602016-11-09 12:58:17 -0800177 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100178 s = str(path)
179 if not s:
180 return os.getcwd()
Steve Dower98eb3602016-11-09 12:58:17 -0800181 previous_s = None
Antoine Pitrou31119e42013-11-22 17:38:12 +0100182 if _getfinalpathname is not None:
Steve Dower98eb3602016-11-09 12:58:17 -0800183 if strict:
184 return self._ext_to_normal(_getfinalpathname(s))
185 else:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200186 tail_parts = [] # End of the path after the first one not found
Steve Dower98eb3602016-11-09 12:58:17 -0800187 while True:
188 try:
189 s = self._ext_to_normal(_getfinalpathname(s))
190 except FileNotFoundError:
191 previous_s = s
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200192 s, tail = os.path.split(s)
193 tail_parts.append(tail)
Steve Dower4b1e98b2016-12-28 16:02:59 -0800194 if previous_s == s:
195 return path
Steve Dower98eb3602016-11-09 12:58:17 -0800196 else:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200197 return os.path.join(s, *reversed(tail_parts))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100198 # Means fallback on absolute
199 return None
200
201 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
202 prefix = ''
203 if s.startswith(ext_prefix):
204 prefix = s[:4]
205 s = s[4:]
206 if s.startswith('UNC\\'):
207 prefix += s[:3]
208 s = '\\' + s[3:]
209 return prefix, s
210
211 def _ext_to_normal(self, s):
212 # Turn back an extended path into a normal DOS-like path
213 return self._split_extended_path(s)[1]
214
215 def is_reserved(self, parts):
216 # NOTE: the rules for reserved names seem somewhat complicated
217 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
218 # We err on the side of caution and return True for paths which are
219 # not considered reserved by Windows.
220 if not parts:
221 return False
222 if parts[0].startswith('\\\\'):
223 # UNC paths are never reserved
224 return False
225 return parts[-1].partition('.')[0].upper() in self.reserved_names
226
227 def make_uri(self, path):
228 # Under Windows, file URIs use the UTF-8 encoding.
229 drive = path.drive
230 if len(drive) == 2 and drive[1] == ':':
231 # It's a path on a local drive => 'file:///c:/a/b'
232 rest = path.as_posix()[2:].lstrip('/')
233 return 'file:///%s/%s' % (
234 drive, urlquote_from_bytes(rest.encode('utf-8')))
235 else:
236 # It's a path on a network drive => 'file://host/share/a/b'
237 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
238
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100239 def gethomedir(self, username):
240 if 'HOME' in os.environ:
241 userhome = os.environ['HOME']
242 elif 'USERPROFILE' in os.environ:
243 userhome = os.environ['USERPROFILE']
244 elif 'HOMEPATH' in os.environ:
Antoine Pitrou5d4e27e2014-12-30 22:09:42 +0100245 try:
246 drv = os.environ['HOMEDRIVE']
247 except KeyError:
248 drv = ''
249 userhome = drv + os.environ['HOMEPATH']
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100250 else:
251 raise RuntimeError("Can't determine home directory")
252
253 if username:
254 # Try to guess user home directory. By default all users
255 # directories are located in the same place and are named by
256 # corresponding usernames. If current user home directory points
257 # to nonstandard place, this guess is likely wrong.
258 if os.environ['USERNAME'] != username:
259 drv, root, parts = self.parse_parts((userhome,))
260 if parts[-1] != os.environ['USERNAME']:
261 raise RuntimeError("Can't determine home directory "
262 "for %r" % username)
263 parts[-1] = username
264 if drv or root:
265 userhome = drv + root + self.join(parts[1:])
266 else:
267 userhome = self.join(parts)
268 return userhome
Antoine Pitrou31119e42013-11-22 17:38:12 +0100269
270class _PosixFlavour(_Flavour):
271 sep = '/'
272 altsep = ''
273 has_drv = False
274 pathmod = posixpath
275
276 is_supported = (os.name != 'nt')
277
278 def splitroot(self, part, sep=sep):
279 if part and part[0] == sep:
280 stripped_part = part.lstrip(sep)
281 # According to POSIX path resolution:
282 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
283 # "A pathname that begins with two successive slashes may be
284 # interpreted in an implementation-defined manner, although more
285 # than two leading slashes shall be treated as a single slash".
286 if len(part) - len(stripped_part) == 2:
287 return '', sep * 2, stripped_part
288 else:
289 return '', sep, stripped_part
290 else:
291 return '', '', part
292
293 def casefold(self, s):
294 return s
295
296 def casefold_parts(self, parts):
297 return parts
298
Steve Dower98eb3602016-11-09 12:58:17 -0800299 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100300 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100301 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100302 seen = {}
303 def _resolve(path, rest):
304 if rest.startswith(sep):
305 path = ''
306
307 for name in rest.split(sep):
308 if not name or name == '.':
309 # current dir
310 continue
311 if name == '..':
312 # parent dir
313 path, _, _ = path.rpartition(sep)
314 continue
315 newpath = path + sep + name
316 if newpath in seen:
317 # Already seen this path
318 path = seen[newpath]
319 if path is not None:
320 # use cached value
321 continue
322 # The symlink is not resolved, so we must have a symlink loop.
323 raise RuntimeError("Symlink loop from %r" % newpath)
324 # Resolve the symbolic link
325 try:
326 target = accessor.readlink(newpath)
327 except OSError as e:
Antoine Pietriadd98eb2017-06-07 17:29:17 +0200328 if e.errno != EINVAL and strict:
329 raise
330 # Not a symlink, or non-strict mode. We just leave the path
331 # untouched.
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100332 path = newpath
333 else:
334 seen[newpath] = None # not resolved symlink
335 path = _resolve(path, target)
336 seen[newpath] = path # resolved symlink
337
338 return path
339 # NOTE: according to POSIX, getcwd() cannot contain path components
340 # which are symlinks.
341 base = '' if path.is_absolute() else os.getcwd()
342 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100343
344 def is_reserved(self, parts):
345 return False
346
347 def make_uri(self, path):
348 # We represent the path using the local filesystem encoding,
349 # for portability to other applications.
350 bpath = bytes(path)
351 return 'file://' + urlquote_from_bytes(bpath)
352
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100353 def gethomedir(self, username):
354 if not username:
355 try:
356 return os.environ['HOME']
357 except KeyError:
358 import pwd
359 return pwd.getpwuid(os.getuid()).pw_dir
360 else:
361 import pwd
362 try:
363 return pwd.getpwnam(username).pw_dir
364 except KeyError:
365 raise RuntimeError("Can't determine home directory "
366 "for %r" % username)
367
Antoine Pitrou31119e42013-11-22 17:38:12 +0100368
369_windows_flavour = _WindowsFlavour()
370_posix_flavour = _PosixFlavour()
371
372
373class _Accessor:
374 """An accessor implements a particular (system-specific or not) way of
375 accessing paths on the filesystem."""
376
377
378class _NormalAccessor(_Accessor):
379
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200380 stat = os.stat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100381
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200382 lstat = os.lstat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100383
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200384 open = os.open
Antoine Pitrou31119e42013-11-22 17:38:12 +0100385
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200386 listdir = os.listdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100387
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200388 scandir = os.scandir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100389
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200390 chmod = os.chmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100391
392 if hasattr(os, "lchmod"):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200393 lchmod = os.lchmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100394 else:
395 def lchmod(self, pathobj, mode):
396 raise NotImplementedError("lchmod() not available on this system")
397
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200398 mkdir = os.mkdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100399
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200400 unlink = os.unlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100401
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200402 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100403
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200404 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100405
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200406 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100407
408 if nt:
409 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200410 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100411 else:
412 def symlink(a, b, target_is_directory):
413 raise NotImplementedError("symlink() not available on this system")
414 else:
415 # Under POSIX, os.symlink() takes two args
416 @staticmethod
417 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200418 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100419
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200420 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100421
422 # Helper for resolve()
423 def readlink(self, path):
424 return os.readlink(path)
425
426
427_normal_accessor = _NormalAccessor()
428
429
430#
431# Globbing helpers
432#
433
Antoine Pitrou31119e42013-11-22 17:38:12 +0100434def _make_selector(pattern_parts):
435 pat = pattern_parts[0]
436 child_parts = pattern_parts[1:]
437 if pat == '**':
438 cls = _RecursiveWildcardSelector
439 elif '**' in pat:
440 raise ValueError("Invalid pattern: '**' can only be an entire path component")
441 elif _is_wildcard_pattern(pat):
442 cls = _WildcardSelector
443 else:
444 cls = _PreciseSelector
445 return cls(pat, child_parts)
446
447if hasattr(functools, "lru_cache"):
448 _make_selector = functools.lru_cache()(_make_selector)
449
450
451class _Selector:
452 """A selector matches a specific glob pattern part against the children
453 of a given path."""
454
455 def __init__(self, child_parts):
456 self.child_parts = child_parts
457 if child_parts:
458 self.successor = _make_selector(child_parts)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300459 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100460 else:
461 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300462 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100463
464 def select_from(self, parent_path):
465 """Iterate over all child paths of `parent_path` matched by this
466 selector. This can contain parent_path itself."""
467 path_cls = type(parent_path)
468 is_dir = path_cls.is_dir
469 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300470 scandir = parent_path._accessor.scandir
471 if not is_dir(parent_path):
472 return iter([])
473 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100474
475
476class _TerminatingSelector:
477
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300478 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100479 yield parent_path
480
481
482class _PreciseSelector(_Selector):
483
484 def __init__(self, name, child_parts):
485 self.name = name
486 _Selector.__init__(self, child_parts)
487
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300488 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800489 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800490 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300491 if (is_dir if self.dironly else exists)(path):
492 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800493 yield p
494 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100495 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100496
497
498class _WildcardSelector(_Selector):
499
500 def __init__(self, pat, child_parts):
501 self.pat = re.compile(fnmatch.translate(pat))
502 _Selector.__init__(self, child_parts)
503
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300504 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800505 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800506 cf = parent_path._flavour.casefold
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300507 entries = list(scandir(parent_path))
508 for entry in entries:
509 if not self.dironly or entry.is_dir():
510 name = entry.name
511 casefolded = cf(name)
512 if self.pat.match(casefolded):
513 path = parent_path._make_child_relpath(name)
514 for p in self.successor._select_from(path, is_dir, exists, scandir):
515 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800516 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100517 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800518
Antoine Pitrou31119e42013-11-22 17:38:12 +0100519
520
521class _RecursiveWildcardSelector(_Selector):
522
523 def __init__(self, pat, child_parts):
524 _Selector.__init__(self, child_parts)
525
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300526 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100527 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800528 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300529 entries = list(scandir(parent_path))
530 for entry in entries:
531 if entry.is_dir() and not entry.is_symlink():
532 path = parent_path._make_child_relpath(entry.name)
533 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800534 yield p
535 except PermissionError:
536 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100537
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300538 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800539 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300540 yielded = set()
541 try:
542 successor_select = self.successor._select_from
543 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
544 for p in successor_select(starting_point, is_dir, exists, scandir):
545 if p not in yielded:
546 yield p
547 yielded.add(p)
548 finally:
549 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800550 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100551 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100552
553
554#
555# Public API
556#
557
558class _PathParents(Sequence):
559 """This object provides sequence-like access to the logical ancestors
560 of a path. Don't try to construct it yourself."""
561 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
562
563 def __init__(self, path):
564 # We don't store the instance to avoid reference cycles
565 self._pathcls = type(path)
566 self._drv = path._drv
567 self._root = path._root
568 self._parts = path._parts
569
570 def __len__(self):
571 if self._drv or self._root:
572 return len(self._parts) - 1
573 else:
574 return len(self._parts)
575
576 def __getitem__(self, idx):
577 if idx < 0 or idx >= len(self):
578 raise IndexError(idx)
579 return self._pathcls._from_parsed_parts(self._drv, self._root,
580 self._parts[:-idx - 1])
581
582 def __repr__(self):
583 return "<{}.parents>".format(self._pathcls.__name__)
584
585
586class PurePath(object):
chasondfa015c2018-02-19 08:36:32 +0900587 """Base class for manipulating paths without I/O.
588
589 PurePath represents a filesystem path and offers operations which
Antoine Pitrou31119e42013-11-22 17:38:12 +0100590 don't imply any actual filesystem I/O. Depending on your system,
591 instantiating a PurePath will return either a PurePosixPath or a
592 PureWindowsPath object. You can also instantiate either of these classes
593 directly, regardless of your system.
594 """
595 __slots__ = (
596 '_drv', '_root', '_parts',
597 '_str', '_hash', '_pparts', '_cached_cparts',
598 )
599
600 def __new__(cls, *args):
601 """Construct a PurePath from one or several strings and or existing
602 PurePath objects. The strings and path objects are combined so as
603 to yield a canonicalized path, which is incorporated into the
604 new PurePath object.
605 """
606 if cls is PurePath:
607 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
608 return cls._from_parts(args)
609
610 def __reduce__(self):
611 # Using the parts tuple helps share interned path parts
612 # when pickling related paths.
613 return (self.__class__, tuple(self._parts))
614
615 @classmethod
616 def _parse_args(cls, args):
617 # This is useful when you don't want to create an instance, just
618 # canonicalize some constructor arguments.
619 parts = []
620 for a in args:
621 if isinstance(a, PurePath):
622 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100623 else:
Brett Cannon568be632016-06-10 12:20:49 -0700624 a = os.fspath(a)
625 if isinstance(a, str):
626 # Force-cast str subclasses to str (issue #21127)
627 parts.append(str(a))
628 else:
629 raise TypeError(
630 "argument should be a str object or an os.PathLike "
631 "object returning str, not %r"
632 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100633 return cls._flavour.parse_parts(parts)
634
635 @classmethod
636 def _from_parts(cls, args, init=True):
637 # We need to call _parse_args on the instance, so as to get the
638 # right flavour.
639 self = object.__new__(cls)
640 drv, root, parts = self._parse_args(args)
641 self._drv = drv
642 self._root = root
643 self._parts = parts
644 if init:
645 self._init()
646 return self
647
648 @classmethod
649 def _from_parsed_parts(cls, drv, root, parts, init=True):
650 self = object.__new__(cls)
651 self._drv = drv
652 self._root = root
653 self._parts = parts
654 if init:
655 self._init()
656 return self
657
658 @classmethod
659 def _format_parsed_parts(cls, drv, root, parts):
660 if drv or root:
661 return drv + root + cls._flavour.join(parts[1:])
662 else:
663 return cls._flavour.join(parts)
664
665 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000666 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100667 pass
668
669 def _make_child(self, args):
670 drv, root, parts = self._parse_args(args)
671 drv, root, parts = self._flavour.join_parsed_parts(
672 self._drv, self._root, self._parts, drv, root, parts)
673 return self._from_parsed_parts(drv, root, parts)
674
675 def __str__(self):
676 """Return the string representation of the path, suitable for
677 passing to system calls."""
678 try:
679 return self._str
680 except AttributeError:
681 self._str = self._format_parsed_parts(self._drv, self._root,
682 self._parts) or '.'
683 return self._str
684
Brett Cannon568be632016-06-10 12:20:49 -0700685 def __fspath__(self):
686 return str(self)
687
Antoine Pitrou31119e42013-11-22 17:38:12 +0100688 def as_posix(self):
689 """Return the string representation of the path with forward (/)
690 slashes."""
691 f = self._flavour
692 return str(self).replace(f.sep, '/')
693
694 def __bytes__(self):
695 """Return the bytes representation of the path. This is only
696 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200697 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100698
699 def __repr__(self):
700 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
701
702 def as_uri(self):
703 """Return the path as a 'file' URI."""
704 if not self.is_absolute():
705 raise ValueError("relative path can't be expressed as a file URI")
706 return self._flavour.make_uri(self)
707
708 @property
709 def _cparts(self):
710 # Cached casefolded parts, for hashing and comparison
711 try:
712 return self._cached_cparts
713 except AttributeError:
714 self._cached_cparts = self._flavour.casefold_parts(self._parts)
715 return self._cached_cparts
716
717 def __eq__(self, other):
718 if not isinstance(other, PurePath):
719 return NotImplemented
720 return self._cparts == other._cparts and self._flavour is other._flavour
721
Antoine Pitrou31119e42013-11-22 17:38:12 +0100722 def __hash__(self):
723 try:
724 return self._hash
725 except AttributeError:
726 self._hash = hash(tuple(self._cparts))
727 return self._hash
728
729 def __lt__(self, other):
730 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
731 return NotImplemented
732 return self._cparts < other._cparts
733
734 def __le__(self, other):
735 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
736 return NotImplemented
737 return self._cparts <= other._cparts
738
739 def __gt__(self, other):
740 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
741 return NotImplemented
742 return self._cparts > other._cparts
743
744 def __ge__(self, other):
745 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
746 return NotImplemented
747 return self._cparts >= other._cparts
748
749 drive = property(attrgetter('_drv'),
750 doc="""The drive prefix (letter or UNC path), if any.""")
751
752 root = property(attrgetter('_root'),
753 doc="""The root of the path, if any.""")
754
755 @property
756 def anchor(self):
757 """The concatenation of the drive and root, or ''."""
758 anchor = self._drv + self._root
759 return anchor
760
761 @property
762 def name(self):
763 """The final path component, if any."""
764 parts = self._parts
765 if len(parts) == (1 if (self._drv or self._root) else 0):
766 return ''
767 return parts[-1]
768
769 @property
770 def suffix(self):
771 """The final component's last suffix, if any."""
772 name = self.name
773 i = name.rfind('.')
774 if 0 < i < len(name) - 1:
775 return name[i:]
776 else:
777 return ''
778
779 @property
780 def suffixes(self):
781 """A list of the final component's suffixes, if any."""
782 name = self.name
783 if name.endswith('.'):
784 return []
785 name = name.lstrip('.')
786 return ['.' + suffix for suffix in name.split('.')[1:]]
787
788 @property
789 def stem(self):
790 """The final path component, minus its last suffix."""
791 name = self.name
792 i = name.rfind('.')
793 if 0 < i < len(name) - 1:
794 return name[:i]
795 else:
796 return name
797
798 def with_name(self, name):
799 """Return a new path with the file name changed."""
800 if not self.name:
801 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400802 drv, root, parts = self._flavour.parse_parts((name,))
803 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
804 or drv or root or len(parts) != 1):
805 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100806 return self._from_parsed_parts(self._drv, self._root,
807 self._parts[:-1] + [name])
808
809 def with_suffix(self, suffix):
Stefan Otte46dc4e32018-08-03 22:49:42 +0200810 """Return a new path with the file suffix changed. If the path
811 has no suffix, add given suffix. If the given suffix is an empty
812 string, remove the suffix from the path.
813 """
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400814 f = self._flavour
815 if f.sep in suffix or f.altsep and f.altsep in suffix:
Berker Peksag423d05f2018-08-11 08:45:06 +0300816 raise ValueError("Invalid suffix %r" % (suffix,))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400817 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100818 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100819 name = self.name
820 if not name:
821 raise ValueError("%r has an empty name" % (self,))
822 old_suffix = self.suffix
823 if not old_suffix:
824 name = name + suffix
825 else:
826 name = name[:-len(old_suffix)] + suffix
827 return self._from_parsed_parts(self._drv, self._root,
828 self._parts[:-1] + [name])
829
830 def relative_to(self, *other):
831 """Return the relative path to another path identified by the passed
832 arguments. If the operation is not possible (because this is not
833 a subpath of the other path), raise ValueError.
834 """
835 # For the purpose of this method, drive and root are considered
836 # separate parts, i.e.:
837 # Path('c:/').relative_to('c:') gives Path('/')
838 # Path('c:/').relative_to('/') raise ValueError
839 if not other:
840 raise TypeError("need at least one argument")
841 parts = self._parts
842 drv = self._drv
843 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100844 if root:
845 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100846 else:
847 abs_parts = parts
848 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100849 if to_root:
850 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100851 else:
852 to_abs_parts = to_parts
853 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100854 cf = self._flavour.casefold_parts
855 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100856 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
857 raise ValueError("{!r} does not start with {!r}"
858 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100859 return self._from_parsed_parts('', root if n == 1 else '',
860 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100861
862 @property
863 def parts(self):
864 """An object providing sequence-like access to the
865 components in the filesystem path."""
866 # We cache the tuple to avoid building a new one each time .parts
867 # is accessed. XXX is this necessary?
868 try:
869 return self._pparts
870 except AttributeError:
871 self._pparts = tuple(self._parts)
872 return self._pparts
873
874 def joinpath(self, *args):
875 """Combine this path with one or several arguments, and return a
876 new path representing either a subpath (if all arguments are relative
877 paths) or a totally different path (if one of the arguments is
878 anchored).
879 """
880 return self._make_child(args)
881
882 def __truediv__(self, key):
883 return self._make_child((key,))
884
885 def __rtruediv__(self, key):
886 return self._from_parts([key] + self._parts)
887
888 @property
889 def parent(self):
890 """The logical parent of the path."""
891 drv = self._drv
892 root = self._root
893 parts = self._parts
894 if len(parts) == 1 and (drv or root):
895 return self
896 return self._from_parsed_parts(drv, root, parts[:-1])
897
898 @property
899 def parents(self):
900 """A sequence of this path's logical parents."""
901 return _PathParents(self)
902
903 def is_absolute(self):
904 """True if the path is absolute (has both a root and, if applicable,
905 a drive)."""
906 if not self._root:
907 return False
908 return not self._flavour.has_drv or bool(self._drv)
909
910 def is_reserved(self):
911 """Return True if the path contains one of the special names reserved
912 by the system, if any."""
913 return self._flavour.is_reserved(self._parts)
914
915 def match(self, path_pattern):
916 """
917 Return True if this path matches the given pattern.
918 """
919 cf = self._flavour.casefold
920 path_pattern = cf(path_pattern)
921 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
922 if not pat_parts:
923 raise ValueError("empty pattern")
924 if drv and drv != cf(self._drv):
925 return False
926 if root and root != cf(self._root):
927 return False
928 parts = self._cparts
929 if drv or root:
930 if len(pat_parts) != len(parts):
931 return False
932 pat_parts = pat_parts[1:]
933 elif len(pat_parts) > len(parts):
934 return False
935 for part, pat in zip(reversed(parts), reversed(pat_parts)):
936 if not fnmatch.fnmatchcase(part, pat):
937 return False
938 return True
939
Brett Cannon568be632016-06-10 12:20:49 -0700940# Can't subclass os.PathLike from PurePath and keep the constructor
941# optimizations in PurePath._parse_args().
942os.PathLike.register(PurePath)
943
Antoine Pitrou31119e42013-11-22 17:38:12 +0100944
945class PurePosixPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900946 """PurePath subclass for non-Windows systems.
947
948 On a POSIX system, instantiating a PurePath should return this object.
949 However, you can also instantiate it directly on any system.
950 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100951 _flavour = _posix_flavour
952 __slots__ = ()
953
954
955class PureWindowsPath(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900956 """PurePath subclass for Windows systems.
957
958 On a Windows system, instantiating a PurePath should return this object.
959 However, you can also instantiate it directly on any system.
960 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100961 _flavour = _windows_flavour
962 __slots__ = ()
963
964
965# Filesystem-accessing classes
966
967
968class Path(PurePath):
chasondfa015c2018-02-19 08:36:32 +0900969 """PurePath subclass that can make system calls.
970
971 Path represents a filesystem path but unlike PurePath, also offers
972 methods to do system calls on path objects. Depending on your system,
973 instantiating a Path will return either a PosixPath or a WindowsPath
974 object. You can also instantiate a PosixPath or WindowsPath directly,
975 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
976 """
Antoine Pitrou31119e42013-11-22 17:38:12 +0100977 __slots__ = (
978 '_accessor',
979 '_closed',
980 )
981
982 def __new__(cls, *args, **kwargs):
983 if cls is Path:
984 cls = WindowsPath if os.name == 'nt' else PosixPath
985 self = cls._from_parts(args, init=False)
986 if not self._flavour.is_supported:
987 raise NotImplementedError("cannot instantiate %r on your system"
988 % (cls.__name__,))
989 self._init()
990 return self
991
992 def _init(self,
993 # Private non-constructor arguments
994 template=None,
995 ):
996 self._closed = False
997 if template is not None:
998 self._accessor = template._accessor
999 else:
1000 self._accessor = _normal_accessor
1001
1002 def _make_child_relpath(self, part):
1003 # This is an optimization used for dir walking. `part` must be
1004 # a single part relative to this path.
1005 parts = self._parts + [part]
1006 return self._from_parsed_parts(self._drv, self._root, parts)
1007
1008 def __enter__(self):
1009 if self._closed:
1010 self._raise_closed()
1011 return self
1012
1013 def __exit__(self, t, v, tb):
1014 self._closed = True
1015
1016 def _raise_closed(self):
1017 raise ValueError("I/O operation on closed path")
1018
1019 def _opener(self, name, flags, mode=0o666):
1020 # A stub for the opener argument to built-in open()
1021 return self._accessor.open(self, flags, mode)
1022
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001023 def _raw_open(self, flags, mode=0o777):
1024 """
1025 Open the file pointed by this path and return a file descriptor,
1026 as os.open() does.
1027 """
1028 if self._closed:
1029 self._raise_closed()
1030 return self._accessor.open(self, flags, mode)
1031
Antoine Pitrou31119e42013-11-22 17:38:12 +01001032 # Public API
1033
1034 @classmethod
1035 def cwd(cls):
1036 """Return a new path pointing to the current working directory
1037 (as returned by os.getcwd()).
1038 """
1039 return cls(os.getcwd())
1040
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001041 @classmethod
1042 def home(cls):
1043 """Return a new path pointing to the user's home directory (as
1044 returned by os.path.expanduser('~')).
1045 """
1046 return cls(cls()._flavour.gethomedir(None))
1047
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001048 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001049 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001050 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001051 """
1052 st = self.stat()
1053 try:
1054 other_st = other_path.stat()
1055 except AttributeError:
1056 other_st = os.stat(other_path)
1057 return os.path.samestat(st, other_st)
1058
Antoine Pitrou31119e42013-11-22 17:38:12 +01001059 def iterdir(self):
1060 """Iterate over the files in this directory. Does not yield any
1061 result for the special paths '.' and '..'.
1062 """
1063 if self._closed:
1064 self._raise_closed()
1065 for name in self._accessor.listdir(self):
1066 if name in {'.', '..'}:
1067 # Yielding a path object for these makes little sense
1068 continue
1069 yield self._make_child_relpath(name)
1070 if self._closed:
1071 self._raise_closed()
1072
1073 def glob(self, pattern):
1074 """Iterate over this subtree and yield all existing files (of any
1075 kind, including directories) matching the given pattern.
1076 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001077 if not pattern:
1078 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001079 pattern = self._flavour.casefold(pattern)
1080 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1081 if drv or root:
1082 raise NotImplementedError("Non-relative patterns are unsupported")
1083 selector = _make_selector(tuple(pattern_parts))
1084 for p in selector.select_from(self):
1085 yield p
1086
1087 def rglob(self, pattern):
1088 """Recursively yield all existing files (of any kind, including
1089 directories) matching the given pattern, anywhere in this subtree.
1090 """
1091 pattern = self._flavour.casefold(pattern)
1092 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1093 if drv or root:
1094 raise NotImplementedError("Non-relative patterns are unsupported")
1095 selector = _make_selector(("**",) + tuple(pattern_parts))
1096 for p in selector.select_from(self):
1097 yield p
1098
1099 def absolute(self):
1100 """Return an absolute version of this path. This function works
1101 even if the path doesn't point to anything.
1102
1103 No normalization is done, i.e. all '.' and '..' will be kept along.
1104 Use resolve() to get the canonical path to a file.
1105 """
1106 # XXX untested yet!
1107 if self._closed:
1108 self._raise_closed()
1109 if self.is_absolute():
1110 return self
1111 # FIXME this must defer to the specific flavour (and, under Windows,
1112 # use nt._getfullpathname())
1113 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1114 obj._init(template=self)
1115 return obj
1116
Steve Dower98eb3602016-11-09 12:58:17 -08001117 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001118 """
1119 Make the path absolute, resolving all symlinks on the way and also
1120 normalizing it (for example turning slashes into backslashes under
1121 Windows).
1122 """
1123 if self._closed:
1124 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001125 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001126 if s is None:
1127 # No symlink resolution => for consistency, raise an error if
1128 # the path doesn't exist or is forbidden
1129 self.stat()
1130 s = str(self.absolute())
1131 # Now we have no symlinks in the path, it's safe to normalize it.
1132 normed = self._flavour.pathmod.normpath(s)
1133 obj = self._from_parts((normed,), init=False)
1134 obj._init(template=self)
1135 return obj
1136
1137 def stat(self):
1138 """
1139 Return the result of the stat() system call on this path, like
1140 os.stat() does.
1141 """
1142 return self._accessor.stat(self)
1143
1144 def owner(self):
1145 """
1146 Return the login name of the file owner.
1147 """
1148 import pwd
1149 return pwd.getpwuid(self.stat().st_uid).pw_name
1150
1151 def group(self):
1152 """
1153 Return the group name of the file gid.
1154 """
1155 import grp
1156 return grp.getgrgid(self.stat().st_gid).gr_name
1157
Antoine Pitrou31119e42013-11-22 17:38:12 +01001158 def open(self, mode='r', buffering=-1, encoding=None,
1159 errors=None, newline=None):
1160 """
1161 Open the file pointed by this path and return a file object, as
1162 the built-in open() function does.
1163 """
1164 if self._closed:
1165 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001166 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001167 opener=self._opener)
1168
Georg Brandlea683982014-10-01 19:12:33 +02001169 def read_bytes(self):
1170 """
1171 Open the file in bytes mode, read it, and close the file.
1172 """
1173 with self.open(mode='rb') as f:
1174 return f.read()
1175
1176 def read_text(self, encoding=None, errors=None):
1177 """
1178 Open the file in text mode, read it, and close the file.
1179 """
1180 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1181 return f.read()
1182
1183 def write_bytes(self, data):
1184 """
1185 Open the file in bytes mode, write to it, and close the file.
1186 """
1187 # type-check for the buffer interface before truncating the file
1188 view = memoryview(data)
1189 with self.open(mode='wb') as f:
1190 return f.write(view)
1191
1192 def write_text(self, data, encoding=None, errors=None):
1193 """
1194 Open the file in text mode, write to it, and close the file.
1195 """
1196 if not isinstance(data, str):
1197 raise TypeError('data must be str, not %s' %
1198 data.__class__.__name__)
1199 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1200 return f.write(data)
1201
Antoine Pitrou31119e42013-11-22 17:38:12 +01001202 def touch(self, mode=0o666, exist_ok=True):
1203 """
1204 Create this file with the given access mode, if it doesn't exist.
1205 """
1206 if self._closed:
1207 self._raise_closed()
1208 if exist_ok:
1209 # First try to bump modification time
1210 # Implementation note: GNU touch uses the UTIME_NOW option of
1211 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001212 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001213 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001214 except OSError:
1215 # Avoid exception chaining
1216 pass
1217 else:
1218 return
1219 flags = os.O_CREAT | os.O_WRONLY
1220 if not exist_ok:
1221 flags |= os.O_EXCL
1222 fd = self._raw_open(flags, mode)
1223 os.close(fd)
1224
Barry Warsaw7c549c42014-08-05 11:28:12 -04001225 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001226 """
1227 Create a new directory at this given path.
1228 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001229 if self._closed:
1230 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001231 try:
1232 self._accessor.mkdir(self, mode)
1233 except FileNotFoundError:
1234 if not parents or self.parent == self:
1235 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001236 self.parent.mkdir(parents=True, exist_ok=True)
1237 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001238 except OSError:
1239 # Cannot rely on checking for EEXIST, since the operating system
1240 # could give priority to other errors like EACCES or EROFS
1241 if not exist_ok or not self.is_dir():
1242 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001243
1244 def chmod(self, mode):
1245 """
1246 Change the permissions of the path, like os.chmod().
1247 """
1248 if self._closed:
1249 self._raise_closed()
1250 self._accessor.chmod(self, mode)
1251
1252 def lchmod(self, mode):
1253 """
1254 Like chmod(), except if the path points to a symlink, the symlink's
1255 permissions are changed, rather than its target's.
1256 """
1257 if self._closed:
1258 self._raise_closed()
1259 self._accessor.lchmod(self, mode)
1260
1261 def unlink(self):
1262 """
1263 Remove this file or link.
1264 If the path is a directory, use rmdir() instead.
1265 """
1266 if self._closed:
1267 self._raise_closed()
1268 self._accessor.unlink(self)
1269
1270 def rmdir(self):
1271 """
1272 Remove this directory. The directory must be empty.
1273 """
1274 if self._closed:
1275 self._raise_closed()
1276 self._accessor.rmdir(self)
1277
1278 def lstat(self):
1279 """
1280 Like stat(), except if the path points to a symlink, the symlink's
1281 status information is returned, rather than its target's.
1282 """
1283 if self._closed:
1284 self._raise_closed()
1285 return self._accessor.lstat(self)
1286
1287 def rename(self, target):
1288 """
1289 Rename this path to the given path.
1290 """
1291 if self._closed:
1292 self._raise_closed()
1293 self._accessor.rename(self, target)
1294
1295 def replace(self, target):
1296 """
1297 Rename this path to the given path, clobbering the existing
1298 destination if it exists.
1299 """
1300 if self._closed:
1301 self._raise_closed()
1302 self._accessor.replace(self, target)
1303
1304 def symlink_to(self, target, target_is_directory=False):
1305 """
1306 Make this path a symlink pointing to the given path.
1307 Note the order of arguments (self, target) is the reverse of os.symlink's.
1308 """
1309 if self._closed:
1310 self._raise_closed()
1311 self._accessor.symlink(target, self, target_is_directory)
1312
1313 # Convenience functions for querying the stat results
1314
1315 def exists(self):
1316 """
1317 Whether this path exists.
1318 """
1319 try:
1320 self.stat()
1321 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001322 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001323 raise
1324 return False
1325 return True
1326
1327 def is_dir(self):
1328 """
1329 Whether this path is a directory.
1330 """
1331 try:
1332 return S_ISDIR(self.stat().st_mode)
1333 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001334 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001335 raise
1336 # Path doesn't exist or is a broken symlink
1337 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1338 return False
1339
1340 def is_file(self):
1341 """
1342 Whether this path is a regular file (also True for symlinks pointing
1343 to regular files).
1344 """
1345 try:
1346 return S_ISREG(self.stat().st_mode)
1347 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001348 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001349 raise
1350 # Path doesn't exist or is a broken symlink
1351 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1352 return False
1353
Cooper Lees173ff4a2017-08-01 15:35:45 -07001354 def is_mount(self):
1355 """
1356 Check if this path is a POSIX mount point
1357 """
1358 # Need to exist and be a dir
1359 if not self.exists() or not self.is_dir():
1360 return False
1361
1362 parent = Path(self.parent)
1363 try:
1364 parent_dev = parent.stat().st_dev
1365 except OSError:
1366 return False
1367
1368 dev = self.stat().st_dev
1369 if dev != parent_dev:
1370 return True
1371 ino = self.stat().st_ino
1372 parent_ino = parent.stat().st_ino
1373 return ino == parent_ino
1374
Antoine Pitrou31119e42013-11-22 17:38:12 +01001375 def is_symlink(self):
1376 """
1377 Whether this path is a symbolic link.
1378 """
1379 try:
1380 return S_ISLNK(self.lstat().st_mode)
1381 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001382 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001383 raise
1384 # Path doesn't exist
1385 return False
1386
1387 def is_block_device(self):
1388 """
1389 Whether this path is a block device.
1390 """
1391 try:
1392 return S_ISBLK(self.stat().st_mode)
1393 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001394 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001395 raise
1396 # Path doesn't exist or is a broken symlink
1397 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1398 return False
1399
1400 def is_char_device(self):
1401 """
1402 Whether this path is a character device.
1403 """
1404 try:
1405 return S_ISCHR(self.stat().st_mode)
1406 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001407 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001408 raise
1409 # Path doesn't exist or is a broken symlink
1410 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1411 return False
1412
1413 def is_fifo(self):
1414 """
1415 Whether this path is a FIFO.
1416 """
1417 try:
1418 return S_ISFIFO(self.stat().st_mode)
1419 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001420 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001421 raise
1422 # Path doesn't exist or is a broken symlink
1423 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1424 return False
1425
1426 def is_socket(self):
1427 """
1428 Whether this path is a socket.
1429 """
1430 try:
1431 return S_ISSOCK(self.stat().st_mode)
1432 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001433 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001434 raise
1435 # Path doesn't exist or is a broken symlink
1436 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1437 return False
1438
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001439 def expanduser(self):
1440 """ Return a new path with expanded ~ and ~user constructs
1441 (as returned by os.path.expanduser)
1442 """
1443 if (not (self._drv or self._root) and
1444 self._parts and self._parts[0][:1] == '~'):
1445 homedir = self._flavour.gethomedir(self._parts[0][1:])
1446 return self._from_parts([homedir] + self._parts[1:])
1447
1448 return self
1449
Antoine Pitrou31119e42013-11-22 17:38:12 +01001450
1451class PosixPath(Path, PurePosixPath):
chasondfa015c2018-02-19 08:36:32 +09001452 """Path subclass for non-Windows systems.
1453
1454 On a POSIX system, instantiating a Path should return this object.
1455 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001456 __slots__ = ()
1457
1458class WindowsPath(Path, PureWindowsPath):
chasondfa015c2018-02-19 08:36:32 +09001459 """Path subclass for Windows systems.
1460
1461 On a Windows system, instantiating a Path should return this object.
1462 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001463 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001464
1465 def owner(self):
1466 raise NotImplementedError("Path.owner() is unsupported on this system")
1467
1468 def group(self):
1469 raise NotImplementedError("Path.group() is unsupported on this system")
Cooper Lees173ff4a2017-08-01 15:35:45 -07001470
1471 def is_mount(self):
1472 raise NotImplementedError("Path.is_mount() is unsupported on this system")