blob: 4a1e9bb1068b375d46eb6ff80ae7867fef0dd19f [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
Antoine Pitrou069a5e12013-12-03 09:41:35 +01009from collections import Sequence
Antoine Pitrou31119e42013-11-22 17:38:12 +010010from contextlib import contextmanager
Antoine Pitrou2b2852b2014-10-30 23:14:03 +010011from errno import EINVAL, ENOENT, ENOTDIR
Antoine Pitrou31119e42013-11-22 17:38:12 +010012from operator import attrgetter
13from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
Antoine Pitrou069a5e12013-12-03 09:41:35 +010014from urllib.parse import quote_from_bytes as urlquote_from_bytes
Antoine Pitrou31119e42013-11-22 17:38:12 +010015
16
17supports_symlinks = True
Antoine Pitroudb118f52014-11-19 00:32:08 +010018if os.name == 'nt':
Antoine Pitrou31119e42013-11-22 17:38:12 +010019 import nt
Antoine Pitrou31119e42013-11-22 17:38:12 +010020 if sys.getwindowsversion()[:2] >= (6, 0):
21 from nt import _getfinalpathname
22 else:
23 supports_symlinks = False
24 _getfinalpathname = None
Antoine Pitroudb118f52014-11-19 00:32:08 +010025else:
26 nt = None
Antoine Pitrou31119e42013-11-22 17:38:12 +010027
28
29__all__ = [
30 "PurePath", "PurePosixPath", "PureWindowsPath",
31 "Path", "PosixPath", "WindowsPath",
32 ]
33
34#
35# Internals
36#
37
38def _is_wildcard_pattern(pat):
39 # Whether this pattern needs actual matching using fnmatch, or can
40 # be looked up directly as a file.
41 return "*" in pat or "?" in pat or "[" in pat
42
43
44class _Flavour(object):
45 """A flavour implements a particular (platform-specific) set of path
46 semantics."""
47
48 def __init__(self):
49 self.join = self.sep.join
50
51 def parse_parts(self, parts):
52 parsed = []
53 sep = self.sep
54 altsep = self.altsep
55 drv = root = ''
56 it = reversed(parts)
57 for part in it:
58 if not part:
59 continue
60 if altsep:
61 part = part.replace(altsep, sep)
62 drv, root, rel = self.splitroot(part)
63 if sep in rel:
64 for x in reversed(rel.split(sep)):
65 if x and x != '.':
66 parsed.append(sys.intern(x))
67 else:
68 if rel and rel != '.':
69 parsed.append(sys.intern(rel))
70 if drv or root:
71 if not drv:
72 # If no drive is present, try to find one in the previous
73 # parts. This makes the result of parsing e.g.
74 # ("C:", "/", "a") reasonably intuitive.
75 for part in it:
Antoine Pitrou57fffd62015-02-15 18:03:59 +010076 if not part:
77 continue
78 if altsep:
79 part = part.replace(altsep, sep)
Antoine Pitrou31119e42013-11-22 17:38:12 +010080 drv = self.splitroot(part)[0]
81 if drv:
82 break
83 break
84 if drv or root:
85 parsed.append(drv + root)
86 parsed.reverse()
87 return drv, root, parsed
88
89 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
90 """
91 Join the two paths represented by the respective
92 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple.
93 """
94 if root2:
Serhiy Storchakaa9939022013-12-06 17:14:12 +020095 if not drv2 and drv:
96 return drv, root2, [drv + root2] + parts2[1:]
97 elif drv2:
98 if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
99 # Same drive => second path is relative to the first
100 return drv, root, parts + parts2[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100101 else:
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200102 # Second path is non-anchored (common case)
103 return drv, root, parts + parts2
104 return drv2, root2, parts2
Antoine Pitrou31119e42013-11-22 17:38:12 +0100105
106
107class _WindowsFlavour(_Flavour):
108 # Reference for Windows paths can be found at
109 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
110
111 sep = '\\'
112 altsep = '/'
113 has_drv = True
114 pathmod = ntpath
115
Antoine Pitroudb118f52014-11-19 00:32:08 +0100116 is_supported = (os.name == 'nt')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100117
118 drive_letters = (
119 set(chr(x) for x in range(ord('a'), ord('z') + 1)) |
120 set(chr(x) for x in range(ord('A'), ord('Z') + 1))
121 )
122 ext_namespace_prefix = '\\\\?\\'
123
124 reserved_names = (
125 {'CON', 'PRN', 'AUX', 'NUL'} |
126 {'COM%d' % i for i in range(1, 10)} |
127 {'LPT%d' % i for i in range(1, 10)}
128 )
129
130 # Interesting findings about extended paths:
131 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
132 # but '\\?\c:/a' is not
133 # - extended paths are always absolute; "relative" extended paths will
134 # fail.
135
136 def splitroot(self, part, sep=sep):
137 first = part[0:1]
138 second = part[1:2]
139 if (second == sep and first == sep):
140 # XXX extended paths should also disable the collapsing of "."
141 # components (according to MSDN docs).
142 prefix, part = self._split_extended_path(part)
143 first = part[0:1]
144 second = part[1:2]
145 else:
146 prefix = ''
147 third = part[2:3]
148 if (second == sep and first == sep and third != sep):
149 # is a UNC path:
150 # vvvvvvvvvvvvvvvvvvvvv root
151 # \\machine\mountpoint\directory\etc\...
152 # directory ^^^^^^^^^^^^^^
153 index = part.find(sep, 2)
154 if index != -1:
155 index2 = part.find(sep, index + 1)
156 # a UNC path can't have two slashes in a row
157 # (after the initial two)
158 if index2 != index + 1:
159 if index2 == -1:
160 index2 = len(part)
161 if prefix:
162 return prefix + part[1:index2], sep, part[index2+1:]
163 else:
164 return part[:index2], sep, part[index2+1:]
165 drv = root = ''
166 if second == ':' and first in self.drive_letters:
167 drv = part[:2]
168 part = part[2:]
169 first = third
170 if first == sep:
171 root = first
172 part = part.lstrip(sep)
173 return prefix + drv, root, part
174
175 def casefold(self, s):
176 return s.lower()
177
178 def casefold_parts(self, parts):
179 return [p.lower() for p in parts]
180
Steve Dower98eb3602016-11-09 12:58:17 -0800181 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100182 s = str(path)
183 if not s:
184 return os.getcwd()
Steve Dower98eb3602016-11-09 12:58:17 -0800185 previous_s = None
Antoine Pitrou31119e42013-11-22 17:38:12 +0100186 if _getfinalpathname is not None:
Steve Dower98eb3602016-11-09 12:58:17 -0800187 if strict:
188 return self._ext_to_normal(_getfinalpathname(s))
189 else:
190 while True:
191 try:
192 s = self._ext_to_normal(_getfinalpathname(s))
193 except FileNotFoundError:
194 previous_s = s
Steve Dower4b1e98b2016-12-28 16:02:59 -0800195 s = os.path.dirname(s)
196 if previous_s == s:
197 return path
Steve Dower98eb3602016-11-09 12:58:17 -0800198 else:
199 if previous_s is None:
200 return s
201 else:
202 return s + os.path.sep + os.path.basename(previous_s)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100203 # Means fallback on absolute
204 return None
205
206 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
207 prefix = ''
208 if s.startswith(ext_prefix):
209 prefix = s[:4]
210 s = s[4:]
211 if s.startswith('UNC\\'):
212 prefix += s[:3]
213 s = '\\' + s[3:]
214 return prefix, s
215
216 def _ext_to_normal(self, s):
217 # Turn back an extended path into a normal DOS-like path
218 return self._split_extended_path(s)[1]
219
220 def is_reserved(self, parts):
221 # NOTE: the rules for reserved names seem somewhat complicated
222 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
223 # We err on the side of caution and return True for paths which are
224 # not considered reserved by Windows.
225 if not parts:
226 return False
227 if parts[0].startswith('\\\\'):
228 # UNC paths are never reserved
229 return False
230 return parts[-1].partition('.')[0].upper() in self.reserved_names
231
232 def make_uri(self, path):
233 # Under Windows, file URIs use the UTF-8 encoding.
234 drive = path.drive
235 if len(drive) == 2 and drive[1] == ':':
236 # It's a path on a local drive => 'file:///c:/a/b'
237 rest = path.as_posix()[2:].lstrip('/')
238 return 'file:///%s/%s' % (
239 drive, urlquote_from_bytes(rest.encode('utf-8')))
240 else:
241 # It's a path on a network drive => 'file://host/share/a/b'
242 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
243
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100244 def gethomedir(self, username):
245 if 'HOME' in os.environ:
246 userhome = os.environ['HOME']
247 elif 'USERPROFILE' in os.environ:
248 userhome = os.environ['USERPROFILE']
249 elif 'HOMEPATH' in os.environ:
Antoine Pitrou5d4e27e2014-12-30 22:09:42 +0100250 try:
251 drv = os.environ['HOMEDRIVE']
252 except KeyError:
253 drv = ''
254 userhome = drv + os.environ['HOMEPATH']
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100255 else:
256 raise RuntimeError("Can't determine home directory")
257
258 if username:
259 # Try to guess user home directory. By default all users
260 # directories are located in the same place and are named by
261 # corresponding usernames. If current user home directory points
262 # to nonstandard place, this guess is likely wrong.
263 if os.environ['USERNAME'] != username:
264 drv, root, parts = self.parse_parts((userhome,))
265 if parts[-1] != os.environ['USERNAME']:
266 raise RuntimeError("Can't determine home directory "
267 "for %r" % username)
268 parts[-1] = username
269 if drv or root:
270 userhome = drv + root + self.join(parts[1:])
271 else:
272 userhome = self.join(parts)
273 return userhome
Antoine Pitrou31119e42013-11-22 17:38:12 +0100274
275class _PosixFlavour(_Flavour):
276 sep = '/'
277 altsep = ''
278 has_drv = False
279 pathmod = posixpath
280
281 is_supported = (os.name != 'nt')
282
283 def splitroot(self, part, sep=sep):
284 if part and part[0] == sep:
285 stripped_part = part.lstrip(sep)
286 # According to POSIX path resolution:
287 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
288 # "A pathname that begins with two successive slashes may be
289 # interpreted in an implementation-defined manner, although more
290 # than two leading slashes shall be treated as a single slash".
291 if len(part) - len(stripped_part) == 2:
292 return '', sep * 2, stripped_part
293 else:
294 return '', sep, stripped_part
295 else:
296 return '', '', part
297
298 def casefold(self, s):
299 return s
300
301 def casefold_parts(self, parts):
302 return parts
303
Steve Dower98eb3602016-11-09 12:58:17 -0800304 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100305 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100306 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100307 seen = {}
308 def _resolve(path, rest):
309 if rest.startswith(sep):
310 path = ''
311
312 for name in rest.split(sep):
313 if not name or name == '.':
314 # current dir
315 continue
316 if name == '..':
317 # parent dir
318 path, _, _ = path.rpartition(sep)
319 continue
320 newpath = path + sep + name
321 if newpath in seen:
322 # Already seen this path
323 path = seen[newpath]
324 if path is not None:
325 # use cached value
326 continue
327 # The symlink is not resolved, so we must have a symlink loop.
328 raise RuntimeError("Symlink loop from %r" % newpath)
329 # Resolve the symbolic link
330 try:
331 target = accessor.readlink(newpath)
332 except OSError as e:
333 if e.errno != EINVAL:
Steve Dower98eb3602016-11-09 12:58:17 -0800334 if strict:
335 raise
336 else:
337 return newpath
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100338 # Not a symlink
339 path = newpath
340 else:
341 seen[newpath] = None # not resolved symlink
342 path = _resolve(path, target)
343 seen[newpath] = path # resolved symlink
344
345 return path
346 # NOTE: according to POSIX, getcwd() cannot contain path components
347 # which are symlinks.
348 base = '' if path.is_absolute() else os.getcwd()
349 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100350
351 def is_reserved(self, parts):
352 return False
353
354 def make_uri(self, path):
355 # We represent the path using the local filesystem encoding,
356 # for portability to other applications.
357 bpath = bytes(path)
358 return 'file://' + urlquote_from_bytes(bpath)
359
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100360 def gethomedir(self, username):
361 if not username:
362 try:
363 return os.environ['HOME']
364 except KeyError:
365 import pwd
366 return pwd.getpwuid(os.getuid()).pw_dir
367 else:
368 import pwd
369 try:
370 return pwd.getpwnam(username).pw_dir
371 except KeyError:
372 raise RuntimeError("Can't determine home directory "
373 "for %r" % username)
374
Antoine Pitrou31119e42013-11-22 17:38:12 +0100375
376_windows_flavour = _WindowsFlavour()
377_posix_flavour = _PosixFlavour()
378
379
380class _Accessor:
381 """An accessor implements a particular (system-specific or not) way of
382 accessing paths on the filesystem."""
383
384
385class _NormalAccessor(_Accessor):
386
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200387 stat = os.stat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100388
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200389 lstat = os.lstat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100390
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200391 open = os.open
Antoine Pitrou31119e42013-11-22 17:38:12 +0100392
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200393 listdir = os.listdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100394
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200395 scandir = os.scandir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100396
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200397 chmod = os.chmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100398
399 if hasattr(os, "lchmod"):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200400 lchmod = os.lchmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100401 else:
402 def lchmod(self, pathobj, mode):
403 raise NotImplementedError("lchmod() not available on this system")
404
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200405 mkdir = os.mkdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100406
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200407 unlink = os.unlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100408
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200409 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100410
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200411 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100412
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200413 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100414
415 if nt:
416 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200417 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100418 else:
419 def symlink(a, b, target_is_directory):
420 raise NotImplementedError("symlink() not available on this system")
421 else:
422 # Under POSIX, os.symlink() takes two args
423 @staticmethod
424 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200425 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100426
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200427 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100428
429 # Helper for resolve()
430 def readlink(self, path):
431 return os.readlink(path)
432
433
434_normal_accessor = _NormalAccessor()
435
436
437#
438# Globbing helpers
439#
440
Antoine Pitrou31119e42013-11-22 17:38:12 +0100441def _make_selector(pattern_parts):
442 pat = pattern_parts[0]
443 child_parts = pattern_parts[1:]
444 if pat == '**':
445 cls = _RecursiveWildcardSelector
446 elif '**' in pat:
447 raise ValueError("Invalid pattern: '**' can only be an entire path component")
448 elif _is_wildcard_pattern(pat):
449 cls = _WildcardSelector
450 else:
451 cls = _PreciseSelector
452 return cls(pat, child_parts)
453
454if hasattr(functools, "lru_cache"):
455 _make_selector = functools.lru_cache()(_make_selector)
456
457
458class _Selector:
459 """A selector matches a specific glob pattern part against the children
460 of a given path."""
461
462 def __init__(self, child_parts):
463 self.child_parts = child_parts
464 if child_parts:
465 self.successor = _make_selector(child_parts)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300466 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100467 else:
468 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300469 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100470
471 def select_from(self, parent_path):
472 """Iterate over all child paths of `parent_path` matched by this
473 selector. This can contain parent_path itself."""
474 path_cls = type(parent_path)
475 is_dir = path_cls.is_dir
476 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300477 scandir = parent_path._accessor.scandir
478 if not is_dir(parent_path):
479 return iter([])
480 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100481
482
483class _TerminatingSelector:
484
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300485 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100486 yield parent_path
487
488
489class _PreciseSelector(_Selector):
490
491 def __init__(self, name, child_parts):
492 self.name = name
493 _Selector.__init__(self, child_parts)
494
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300495 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800496 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800497 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300498 if (is_dir if self.dironly else exists)(path):
499 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800500 yield p
501 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100502 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100503
504
505class _WildcardSelector(_Selector):
506
507 def __init__(self, pat, child_parts):
508 self.pat = re.compile(fnmatch.translate(pat))
509 _Selector.__init__(self, child_parts)
510
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300511 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800512 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800513 cf = parent_path._flavour.casefold
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300514 entries = list(scandir(parent_path))
515 for entry in entries:
516 if not self.dironly or entry.is_dir():
517 name = entry.name
518 casefolded = cf(name)
519 if self.pat.match(casefolded):
520 path = parent_path._make_child_relpath(name)
521 for p in self.successor._select_from(path, is_dir, exists, scandir):
522 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800523 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100524 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800525
Antoine Pitrou31119e42013-11-22 17:38:12 +0100526
527
528class _RecursiveWildcardSelector(_Selector):
529
530 def __init__(self, pat, child_parts):
531 _Selector.__init__(self, child_parts)
532
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300533 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100534 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800535 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300536 entries = list(scandir(parent_path))
537 for entry in entries:
538 if entry.is_dir() and not entry.is_symlink():
539 path = parent_path._make_child_relpath(entry.name)
540 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800541 yield p
542 except PermissionError:
543 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100544
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300545 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800546 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300547 yielded = set()
548 try:
549 successor_select = self.successor._select_from
550 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
551 for p in successor_select(starting_point, is_dir, exists, scandir):
552 if p not in yielded:
553 yield p
554 yielded.add(p)
555 finally:
556 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800557 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100558 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100559
560
561#
562# Public API
563#
564
565class _PathParents(Sequence):
566 """This object provides sequence-like access to the logical ancestors
567 of a path. Don't try to construct it yourself."""
568 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
569
570 def __init__(self, path):
571 # We don't store the instance to avoid reference cycles
572 self._pathcls = type(path)
573 self._drv = path._drv
574 self._root = path._root
575 self._parts = path._parts
576
577 def __len__(self):
578 if self._drv or self._root:
579 return len(self._parts) - 1
580 else:
581 return len(self._parts)
582
583 def __getitem__(self, idx):
584 if idx < 0 or idx >= len(self):
585 raise IndexError(idx)
586 return self._pathcls._from_parsed_parts(self._drv, self._root,
587 self._parts[:-idx - 1])
588
589 def __repr__(self):
590 return "<{}.parents>".format(self._pathcls.__name__)
591
592
593class PurePath(object):
594 """PurePath represents a filesystem path and offers operations which
595 don't imply any actual filesystem I/O. Depending on your system,
596 instantiating a PurePath will return either a PurePosixPath or a
597 PureWindowsPath object. You can also instantiate either of these classes
598 directly, regardless of your system.
599 """
600 __slots__ = (
601 '_drv', '_root', '_parts',
602 '_str', '_hash', '_pparts', '_cached_cparts',
603 )
604
605 def __new__(cls, *args):
606 """Construct a PurePath from one or several strings and or existing
607 PurePath objects. The strings and path objects are combined so as
608 to yield a canonicalized path, which is incorporated into the
609 new PurePath object.
610 """
611 if cls is PurePath:
612 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
613 return cls._from_parts(args)
614
615 def __reduce__(self):
616 # Using the parts tuple helps share interned path parts
617 # when pickling related paths.
618 return (self.__class__, tuple(self._parts))
619
620 @classmethod
621 def _parse_args(cls, args):
622 # This is useful when you don't want to create an instance, just
623 # canonicalize some constructor arguments.
624 parts = []
625 for a in args:
626 if isinstance(a, PurePath):
627 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100628 else:
Brett Cannon568be632016-06-10 12:20:49 -0700629 a = os.fspath(a)
630 if isinstance(a, str):
631 # Force-cast str subclasses to str (issue #21127)
632 parts.append(str(a))
633 else:
634 raise TypeError(
635 "argument should be a str object or an os.PathLike "
636 "object returning str, not %r"
637 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100638 return cls._flavour.parse_parts(parts)
639
640 @classmethod
641 def _from_parts(cls, args, init=True):
642 # We need to call _parse_args on the instance, so as to get the
643 # right flavour.
644 self = object.__new__(cls)
645 drv, root, parts = self._parse_args(args)
646 self._drv = drv
647 self._root = root
648 self._parts = parts
649 if init:
650 self._init()
651 return self
652
653 @classmethod
654 def _from_parsed_parts(cls, drv, root, parts, init=True):
655 self = object.__new__(cls)
656 self._drv = drv
657 self._root = root
658 self._parts = parts
659 if init:
660 self._init()
661 return self
662
663 @classmethod
664 def _format_parsed_parts(cls, drv, root, parts):
665 if drv or root:
666 return drv + root + cls._flavour.join(parts[1:])
667 else:
668 return cls._flavour.join(parts)
669
670 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000671 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100672 pass
673
674 def _make_child(self, args):
675 drv, root, parts = self._parse_args(args)
676 drv, root, parts = self._flavour.join_parsed_parts(
677 self._drv, self._root, self._parts, drv, root, parts)
678 return self._from_parsed_parts(drv, root, parts)
679
680 def __str__(self):
681 """Return the string representation of the path, suitable for
682 passing to system calls."""
683 try:
684 return self._str
685 except AttributeError:
686 self._str = self._format_parsed_parts(self._drv, self._root,
687 self._parts) or '.'
688 return self._str
689
Brett Cannon568be632016-06-10 12:20:49 -0700690 def __fspath__(self):
691 return str(self)
692
Antoine Pitrou31119e42013-11-22 17:38:12 +0100693 def as_posix(self):
694 """Return the string representation of the path with forward (/)
695 slashes."""
696 f = self._flavour
697 return str(self).replace(f.sep, '/')
698
699 def __bytes__(self):
700 """Return the bytes representation of the path. This is only
701 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200702 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100703
704 def __repr__(self):
705 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
706
707 def as_uri(self):
708 """Return the path as a 'file' URI."""
709 if not self.is_absolute():
710 raise ValueError("relative path can't be expressed as a file URI")
711 return self._flavour.make_uri(self)
712
713 @property
714 def _cparts(self):
715 # Cached casefolded parts, for hashing and comparison
716 try:
717 return self._cached_cparts
718 except AttributeError:
719 self._cached_cparts = self._flavour.casefold_parts(self._parts)
720 return self._cached_cparts
721
722 def __eq__(self, other):
723 if not isinstance(other, PurePath):
724 return NotImplemented
725 return self._cparts == other._cparts and self._flavour is other._flavour
726
Antoine Pitrou31119e42013-11-22 17:38:12 +0100727 def __hash__(self):
728 try:
729 return self._hash
730 except AttributeError:
731 self._hash = hash(tuple(self._cparts))
732 return self._hash
733
734 def __lt__(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 __le__(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 __gt__(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 def __ge__(self, other):
750 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
751 return NotImplemented
752 return self._cparts >= other._cparts
753
754 drive = property(attrgetter('_drv'),
755 doc="""The drive prefix (letter or UNC path), if any.""")
756
757 root = property(attrgetter('_root'),
758 doc="""The root of the path, if any.""")
759
760 @property
761 def anchor(self):
762 """The concatenation of the drive and root, or ''."""
763 anchor = self._drv + self._root
764 return anchor
765
766 @property
767 def name(self):
768 """The final path component, if any."""
769 parts = self._parts
770 if len(parts) == (1 if (self._drv or self._root) else 0):
771 return ''
772 return parts[-1]
773
774 @property
775 def suffix(self):
776 """The final component's last suffix, if any."""
777 name = self.name
778 i = name.rfind('.')
779 if 0 < i < len(name) - 1:
780 return name[i:]
781 else:
782 return ''
783
784 @property
785 def suffixes(self):
786 """A list of the final component's suffixes, if any."""
787 name = self.name
788 if name.endswith('.'):
789 return []
790 name = name.lstrip('.')
791 return ['.' + suffix for suffix in name.split('.')[1:]]
792
793 @property
794 def stem(self):
795 """The final path component, minus its last suffix."""
796 name = self.name
797 i = name.rfind('.')
798 if 0 < i < len(name) - 1:
799 return name[:i]
800 else:
801 return name
802
803 def with_name(self, name):
804 """Return a new path with the file name changed."""
805 if not self.name:
806 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400807 drv, root, parts = self._flavour.parse_parts((name,))
808 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
809 or drv or root or len(parts) != 1):
810 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100811 return self._from_parsed_parts(self._drv, self._root,
812 self._parts[:-1] + [name])
813
814 def with_suffix(self, suffix):
815 """Return a new path with the file suffix changed (or added, if none)."""
816 # XXX if suffix is None, should the current suffix be removed?
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400817 f = self._flavour
818 if f.sep in suffix or f.altsep and f.altsep in suffix:
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100819 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400820 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100821 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100822 name = self.name
823 if not name:
824 raise ValueError("%r has an empty name" % (self,))
825 old_suffix = self.suffix
826 if not old_suffix:
827 name = name + suffix
828 else:
829 name = name[:-len(old_suffix)] + suffix
830 return self._from_parsed_parts(self._drv, self._root,
831 self._parts[:-1] + [name])
832
833 def relative_to(self, *other):
834 """Return the relative path to another path identified by the passed
835 arguments. If the operation is not possible (because this is not
836 a subpath of the other path), raise ValueError.
837 """
838 # For the purpose of this method, drive and root are considered
839 # separate parts, i.e.:
840 # Path('c:/').relative_to('c:') gives Path('/')
841 # Path('c:/').relative_to('/') raise ValueError
842 if not other:
843 raise TypeError("need at least one argument")
844 parts = self._parts
845 drv = self._drv
846 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100847 if root:
848 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100849 else:
850 abs_parts = parts
851 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100852 if to_root:
853 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100854 else:
855 to_abs_parts = to_parts
856 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100857 cf = self._flavour.casefold_parts
858 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100859 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
860 raise ValueError("{!r} does not start with {!r}"
861 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100862 return self._from_parsed_parts('', root if n == 1 else '',
863 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100864
865 @property
866 def parts(self):
867 """An object providing sequence-like access to the
868 components in the filesystem path."""
869 # We cache the tuple to avoid building a new one each time .parts
870 # is accessed. XXX is this necessary?
871 try:
872 return self._pparts
873 except AttributeError:
874 self._pparts = tuple(self._parts)
875 return self._pparts
876
877 def joinpath(self, *args):
878 """Combine this path with one or several arguments, and return a
879 new path representing either a subpath (if all arguments are relative
880 paths) or a totally different path (if one of the arguments is
881 anchored).
882 """
883 return self._make_child(args)
884
885 def __truediv__(self, key):
886 return self._make_child((key,))
887
888 def __rtruediv__(self, key):
889 return self._from_parts([key] + self._parts)
890
891 @property
892 def parent(self):
893 """The logical parent of the path."""
894 drv = self._drv
895 root = self._root
896 parts = self._parts
897 if len(parts) == 1 and (drv or root):
898 return self
899 return self._from_parsed_parts(drv, root, parts[:-1])
900
901 @property
902 def parents(self):
903 """A sequence of this path's logical parents."""
904 return _PathParents(self)
905
906 def is_absolute(self):
907 """True if the path is absolute (has both a root and, if applicable,
908 a drive)."""
909 if not self._root:
910 return False
911 return not self._flavour.has_drv or bool(self._drv)
912
913 def is_reserved(self):
914 """Return True if the path contains one of the special names reserved
915 by the system, if any."""
916 return self._flavour.is_reserved(self._parts)
917
918 def match(self, path_pattern):
919 """
920 Return True if this path matches the given pattern.
921 """
922 cf = self._flavour.casefold
923 path_pattern = cf(path_pattern)
924 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
925 if not pat_parts:
926 raise ValueError("empty pattern")
927 if drv and drv != cf(self._drv):
928 return False
929 if root and root != cf(self._root):
930 return False
931 parts = self._cparts
932 if drv or root:
933 if len(pat_parts) != len(parts):
934 return False
935 pat_parts = pat_parts[1:]
936 elif len(pat_parts) > len(parts):
937 return False
938 for part, pat in zip(reversed(parts), reversed(pat_parts)):
939 if not fnmatch.fnmatchcase(part, pat):
940 return False
941 return True
942
Brett Cannon568be632016-06-10 12:20:49 -0700943# Can't subclass os.PathLike from PurePath and keep the constructor
944# optimizations in PurePath._parse_args().
945os.PathLike.register(PurePath)
946
Antoine Pitrou31119e42013-11-22 17:38:12 +0100947
948class PurePosixPath(PurePath):
949 _flavour = _posix_flavour
950 __slots__ = ()
951
952
953class PureWindowsPath(PurePath):
954 _flavour = _windows_flavour
955 __slots__ = ()
956
957
958# Filesystem-accessing classes
959
960
961class Path(PurePath):
962 __slots__ = (
963 '_accessor',
964 '_closed',
965 )
966
967 def __new__(cls, *args, **kwargs):
968 if cls is Path:
969 cls = WindowsPath if os.name == 'nt' else PosixPath
970 self = cls._from_parts(args, init=False)
971 if not self._flavour.is_supported:
972 raise NotImplementedError("cannot instantiate %r on your system"
973 % (cls.__name__,))
974 self._init()
975 return self
976
977 def _init(self,
978 # Private non-constructor arguments
979 template=None,
980 ):
981 self._closed = False
982 if template is not None:
983 self._accessor = template._accessor
984 else:
985 self._accessor = _normal_accessor
986
987 def _make_child_relpath(self, part):
988 # This is an optimization used for dir walking. `part` must be
989 # a single part relative to this path.
990 parts = self._parts + [part]
991 return self._from_parsed_parts(self._drv, self._root, parts)
992
993 def __enter__(self):
994 if self._closed:
995 self._raise_closed()
996 return self
997
998 def __exit__(self, t, v, tb):
999 self._closed = True
1000
1001 def _raise_closed(self):
1002 raise ValueError("I/O operation on closed path")
1003
1004 def _opener(self, name, flags, mode=0o666):
1005 # A stub for the opener argument to built-in open()
1006 return self._accessor.open(self, flags, mode)
1007
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001008 def _raw_open(self, flags, mode=0o777):
1009 """
1010 Open the file pointed by this path and return a file descriptor,
1011 as os.open() does.
1012 """
1013 if self._closed:
1014 self._raise_closed()
1015 return self._accessor.open(self, flags, mode)
1016
Antoine Pitrou31119e42013-11-22 17:38:12 +01001017 # Public API
1018
1019 @classmethod
1020 def cwd(cls):
1021 """Return a new path pointing to the current working directory
1022 (as returned by os.getcwd()).
1023 """
1024 return cls(os.getcwd())
1025
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001026 @classmethod
1027 def home(cls):
1028 """Return a new path pointing to the user's home directory (as
1029 returned by os.path.expanduser('~')).
1030 """
1031 return cls(cls()._flavour.gethomedir(None))
1032
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001033 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001034 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001035 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001036 """
1037 st = self.stat()
1038 try:
1039 other_st = other_path.stat()
1040 except AttributeError:
1041 other_st = os.stat(other_path)
1042 return os.path.samestat(st, other_st)
1043
Antoine Pitrou31119e42013-11-22 17:38:12 +01001044 def iterdir(self):
1045 """Iterate over the files in this directory. Does not yield any
1046 result for the special paths '.' and '..'.
1047 """
1048 if self._closed:
1049 self._raise_closed()
1050 for name in self._accessor.listdir(self):
1051 if name in {'.', '..'}:
1052 # Yielding a path object for these makes little sense
1053 continue
1054 yield self._make_child_relpath(name)
1055 if self._closed:
1056 self._raise_closed()
1057
1058 def glob(self, pattern):
1059 """Iterate over this subtree and yield all existing files (of any
1060 kind, including directories) matching the given pattern.
1061 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001062 if not pattern:
1063 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001064 pattern = self._flavour.casefold(pattern)
1065 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1066 if drv or root:
1067 raise NotImplementedError("Non-relative patterns are unsupported")
1068 selector = _make_selector(tuple(pattern_parts))
1069 for p in selector.select_from(self):
1070 yield p
1071
1072 def rglob(self, pattern):
1073 """Recursively yield all existing files (of any kind, including
1074 directories) matching the given pattern, anywhere in this subtree.
1075 """
1076 pattern = self._flavour.casefold(pattern)
1077 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1078 if drv or root:
1079 raise NotImplementedError("Non-relative patterns are unsupported")
1080 selector = _make_selector(("**",) + tuple(pattern_parts))
1081 for p in selector.select_from(self):
1082 yield p
1083
1084 def absolute(self):
1085 """Return an absolute version of this path. This function works
1086 even if the path doesn't point to anything.
1087
1088 No normalization is done, i.e. all '.' and '..' will be kept along.
1089 Use resolve() to get the canonical path to a file.
1090 """
1091 # XXX untested yet!
1092 if self._closed:
1093 self._raise_closed()
1094 if self.is_absolute():
1095 return self
1096 # FIXME this must defer to the specific flavour (and, under Windows,
1097 # use nt._getfullpathname())
1098 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1099 obj._init(template=self)
1100 return obj
1101
Steve Dower98eb3602016-11-09 12:58:17 -08001102 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001103 """
1104 Make the path absolute, resolving all symlinks on the way and also
1105 normalizing it (for example turning slashes into backslashes under
1106 Windows).
1107 """
1108 if self._closed:
1109 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001110 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001111 if s is None:
1112 # No symlink resolution => for consistency, raise an error if
1113 # the path doesn't exist or is forbidden
1114 self.stat()
1115 s = str(self.absolute())
1116 # Now we have no symlinks in the path, it's safe to normalize it.
1117 normed = self._flavour.pathmod.normpath(s)
1118 obj = self._from_parts((normed,), init=False)
1119 obj._init(template=self)
1120 return obj
1121
1122 def stat(self):
1123 """
1124 Return the result of the stat() system call on this path, like
1125 os.stat() does.
1126 """
1127 return self._accessor.stat(self)
1128
1129 def owner(self):
1130 """
1131 Return the login name of the file owner.
1132 """
1133 import pwd
1134 return pwd.getpwuid(self.stat().st_uid).pw_name
1135
1136 def group(self):
1137 """
1138 Return the group name of the file gid.
1139 """
1140 import grp
1141 return grp.getgrgid(self.stat().st_gid).gr_name
1142
Antoine Pitrou31119e42013-11-22 17:38:12 +01001143 def open(self, mode='r', buffering=-1, encoding=None,
1144 errors=None, newline=None):
1145 """
1146 Open the file pointed by this path and return a file object, as
1147 the built-in open() function does.
1148 """
1149 if self._closed:
1150 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001151 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001152 opener=self._opener)
1153
Georg Brandlea683982014-10-01 19:12:33 +02001154 def read_bytes(self):
1155 """
1156 Open the file in bytes mode, read it, and close the file.
1157 """
1158 with self.open(mode='rb') as f:
1159 return f.read()
1160
1161 def read_text(self, encoding=None, errors=None):
1162 """
1163 Open the file in text mode, read it, and close the file.
1164 """
1165 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1166 return f.read()
1167
1168 def write_bytes(self, data):
1169 """
1170 Open the file in bytes mode, write to it, and close the file.
1171 """
1172 # type-check for the buffer interface before truncating the file
1173 view = memoryview(data)
1174 with self.open(mode='wb') as f:
1175 return f.write(view)
1176
1177 def write_text(self, data, encoding=None, errors=None):
1178 """
1179 Open the file in text mode, write to it, and close the file.
1180 """
1181 if not isinstance(data, str):
1182 raise TypeError('data must be str, not %s' %
1183 data.__class__.__name__)
1184 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1185 return f.write(data)
1186
Antoine Pitrou31119e42013-11-22 17:38:12 +01001187 def touch(self, mode=0o666, exist_ok=True):
1188 """
1189 Create this file with the given access mode, if it doesn't exist.
1190 """
1191 if self._closed:
1192 self._raise_closed()
1193 if exist_ok:
1194 # First try to bump modification time
1195 # Implementation note: GNU touch uses the UTIME_NOW option of
1196 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001197 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001198 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001199 except OSError:
1200 # Avoid exception chaining
1201 pass
1202 else:
1203 return
1204 flags = os.O_CREAT | os.O_WRONLY
1205 if not exist_ok:
1206 flags |= os.O_EXCL
1207 fd = self._raw_open(flags, mode)
1208 os.close(fd)
1209
Barry Warsaw7c549c42014-08-05 11:28:12 -04001210 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001211 """
1212 Create a new directory at this given path.
1213 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001214 if self._closed:
1215 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001216 try:
1217 self._accessor.mkdir(self, mode)
1218 except FileNotFoundError:
1219 if not parents or self.parent == self:
1220 raise
1221 self.parent.mkdir(parents=True)
1222 self._accessor.mkdir(self, mode)
1223 except OSError:
1224 # Cannot rely on checking for EEXIST, since the operating system
1225 # could give priority to other errors like EACCES or EROFS
1226 if not exist_ok or not self.is_dir():
1227 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001228
1229 def chmod(self, mode):
1230 """
1231 Change the permissions of the path, like os.chmod().
1232 """
1233 if self._closed:
1234 self._raise_closed()
1235 self._accessor.chmod(self, mode)
1236
1237 def lchmod(self, mode):
1238 """
1239 Like chmod(), except if the path points to a symlink, the symlink's
1240 permissions are changed, rather than its target's.
1241 """
1242 if self._closed:
1243 self._raise_closed()
1244 self._accessor.lchmod(self, mode)
1245
1246 def unlink(self):
1247 """
1248 Remove this file or link.
1249 If the path is a directory, use rmdir() instead.
1250 """
1251 if self._closed:
1252 self._raise_closed()
1253 self._accessor.unlink(self)
1254
1255 def rmdir(self):
1256 """
1257 Remove this directory. The directory must be empty.
1258 """
1259 if self._closed:
1260 self._raise_closed()
1261 self._accessor.rmdir(self)
1262
1263 def lstat(self):
1264 """
1265 Like stat(), except if the path points to a symlink, the symlink's
1266 status information is returned, rather than its target's.
1267 """
1268 if self._closed:
1269 self._raise_closed()
1270 return self._accessor.lstat(self)
1271
1272 def rename(self, target):
1273 """
1274 Rename this path to the given path.
1275 """
1276 if self._closed:
1277 self._raise_closed()
1278 self._accessor.rename(self, target)
1279
1280 def replace(self, target):
1281 """
1282 Rename this path to the given path, clobbering the existing
1283 destination if it exists.
1284 """
1285 if self._closed:
1286 self._raise_closed()
1287 self._accessor.replace(self, target)
1288
1289 def symlink_to(self, target, target_is_directory=False):
1290 """
1291 Make this path a symlink pointing to the given path.
1292 Note the order of arguments (self, target) is the reverse of os.symlink's.
1293 """
1294 if self._closed:
1295 self._raise_closed()
1296 self._accessor.symlink(target, self, target_is_directory)
1297
1298 # Convenience functions for querying the stat results
1299
1300 def exists(self):
1301 """
1302 Whether this path exists.
1303 """
1304 try:
1305 self.stat()
1306 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001307 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001308 raise
1309 return False
1310 return True
1311
1312 def is_dir(self):
1313 """
1314 Whether this path is a directory.
1315 """
1316 try:
1317 return S_ISDIR(self.stat().st_mode)
1318 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001319 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001320 raise
1321 # Path doesn't exist or is a broken symlink
1322 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1323 return False
1324
1325 def is_file(self):
1326 """
1327 Whether this path is a regular file (also True for symlinks pointing
1328 to regular files).
1329 """
1330 try:
1331 return S_ISREG(self.stat().st_mode)
1332 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001333 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001334 raise
1335 # Path doesn't exist or is a broken symlink
1336 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1337 return False
1338
1339 def is_symlink(self):
1340 """
1341 Whether this path is a symbolic link.
1342 """
1343 try:
1344 return S_ISLNK(self.lstat().st_mode)
1345 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001346 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001347 raise
1348 # Path doesn't exist
1349 return False
1350
1351 def is_block_device(self):
1352 """
1353 Whether this path is a block device.
1354 """
1355 try:
1356 return S_ISBLK(self.stat().st_mode)
1357 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001358 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001359 raise
1360 # Path doesn't exist or is a broken symlink
1361 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1362 return False
1363
1364 def is_char_device(self):
1365 """
1366 Whether this path is a character device.
1367 """
1368 try:
1369 return S_ISCHR(self.stat().st_mode)
1370 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001371 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001372 raise
1373 # Path doesn't exist or is a broken symlink
1374 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1375 return False
1376
1377 def is_fifo(self):
1378 """
1379 Whether this path is a FIFO.
1380 """
1381 try:
1382 return S_ISFIFO(self.stat().st_mode)
1383 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001384 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001385 raise
1386 # Path doesn't exist or is a broken symlink
1387 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1388 return False
1389
1390 def is_socket(self):
1391 """
1392 Whether this path is a socket.
1393 """
1394 try:
1395 return S_ISSOCK(self.stat().st_mode)
1396 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001397 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001398 raise
1399 # Path doesn't exist or is a broken symlink
1400 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1401 return False
1402
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001403 def expanduser(self):
1404 """ Return a new path with expanded ~ and ~user constructs
1405 (as returned by os.path.expanduser)
1406 """
1407 if (not (self._drv or self._root) and
1408 self._parts and self._parts[0][:1] == '~'):
1409 homedir = self._flavour.gethomedir(self._parts[0][1:])
1410 return self._from_parts([homedir] + self._parts[1:])
1411
1412 return self
1413
Antoine Pitrou31119e42013-11-22 17:38:12 +01001414
1415class PosixPath(Path, PurePosixPath):
1416 __slots__ = ()
1417
1418class WindowsPath(Path, PureWindowsPath):
1419 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001420
1421 def owner(self):
1422 raise NotImplementedError("Path.owner() is unsupported on this system")
1423
1424 def group(self):
1425 raise NotImplementedError("Path.group() is unsupported on this system")