blob: 4368eba8a0efe056a1750dc736abf0fffe813797 [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 Storchaka2e576f52017-04-24 09:05:00 +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
117 drive_letters = (
118 set(chr(x) for x in range(ord('a'), ord('z') + 1)) |
119 set(chr(x) for x in range(ord('A'), ord('Z') + 1))
120 )
121 ext_namespace_prefix = '\\\\?\\'
122
123 reserved_names = (
124 {'CON', 'PRN', 'AUX', 'NUL'} |
125 {'COM%d' % i for i in range(1, 10)} |
126 {'LPT%d' % i for i in range(1, 10)}
127 )
128
129 # Interesting findings about extended paths:
130 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
131 # but '\\?\c:/a' is not
132 # - extended paths are always absolute; "relative" extended paths will
133 # fail.
134
135 def splitroot(self, part, sep=sep):
136 first = part[0:1]
137 second = part[1:2]
138 if (second == sep and first == sep):
139 # XXX extended paths should also disable the collapsing of "."
140 # components (according to MSDN docs).
141 prefix, part = self._split_extended_path(part)
142 first = part[0:1]
143 second = part[1:2]
144 else:
145 prefix = ''
146 third = part[2:3]
147 if (second == sep and first == sep and third != sep):
148 # is a UNC path:
149 # vvvvvvvvvvvvvvvvvvvvv root
150 # \\machine\mountpoint\directory\etc\...
151 # directory ^^^^^^^^^^^^^^
152 index = part.find(sep, 2)
153 if index != -1:
154 index2 = part.find(sep, index + 1)
155 # a UNC path can't have two slashes in a row
156 # (after the initial two)
157 if index2 != index + 1:
158 if index2 == -1:
159 index2 = len(part)
160 if prefix:
161 return prefix + part[1:index2], sep, part[index2+1:]
162 else:
163 return part[:index2], sep, part[index2+1:]
164 drv = root = ''
165 if second == ':' and first in self.drive_letters:
166 drv = part[:2]
167 part = part[2:]
168 first = third
169 if first == sep:
170 root = first
171 part = part.lstrip(sep)
172 return prefix + drv, root, part
173
174 def casefold(self, s):
175 return s.lower()
176
177 def casefold_parts(self, parts):
178 return [p.lower() for p in parts]
179
Steve Dower98eb3602016-11-09 12:58:17 -0800180 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100181 s = str(path)
182 if not s:
183 return os.getcwd()
Steve Dower98eb3602016-11-09 12:58:17 -0800184 previous_s = None
Antoine Pitrou31119e42013-11-22 17:38:12 +0100185 if _getfinalpathname is not None:
Steve Dower98eb3602016-11-09 12:58:17 -0800186 if strict:
187 return self._ext_to_normal(_getfinalpathname(s))
188 else:
189 while True:
190 try:
191 s = self._ext_to_normal(_getfinalpathname(s))
192 except FileNotFoundError:
193 previous_s = s
Steve Dower4b1e98b2016-12-28 16:02:59 -0800194 s = os.path.dirname(s)
195 if previous_s == s:
196 return path
Steve Dower98eb3602016-11-09 12:58:17 -0800197 else:
198 if previous_s is None:
199 return s
200 else:
201 return s + os.path.sep + os.path.basename(previous_s)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100202 # Means fallback on absolute
203 return None
204
205 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
206 prefix = ''
207 if s.startswith(ext_prefix):
208 prefix = s[:4]
209 s = s[4:]
210 if s.startswith('UNC\\'):
211 prefix += s[:3]
212 s = '\\' + s[3:]
213 return prefix, s
214
215 def _ext_to_normal(self, s):
216 # Turn back an extended path into a normal DOS-like path
217 return self._split_extended_path(s)[1]
218
219 def is_reserved(self, parts):
220 # NOTE: the rules for reserved names seem somewhat complicated
221 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
222 # We err on the side of caution and return True for paths which are
223 # not considered reserved by Windows.
224 if not parts:
225 return False
226 if parts[0].startswith('\\\\'):
227 # UNC paths are never reserved
228 return False
229 return parts[-1].partition('.')[0].upper() in self.reserved_names
230
231 def make_uri(self, path):
232 # Under Windows, file URIs use the UTF-8 encoding.
233 drive = path.drive
234 if len(drive) == 2 and drive[1] == ':':
235 # It's a path on a local drive => 'file:///c:/a/b'
236 rest = path.as_posix()[2:].lstrip('/')
237 return 'file:///%s/%s' % (
238 drive, urlquote_from_bytes(rest.encode('utf-8')))
239 else:
240 # It's a path on a network drive => 'file://host/share/a/b'
241 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
242
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100243 def gethomedir(self, username):
244 if 'HOME' in os.environ:
245 userhome = os.environ['HOME']
246 elif 'USERPROFILE' in os.environ:
247 userhome = os.environ['USERPROFILE']
248 elif 'HOMEPATH' in os.environ:
Antoine Pitrou5d4e27e2014-12-30 22:09:42 +0100249 try:
250 drv = os.environ['HOMEDRIVE']
251 except KeyError:
252 drv = ''
253 userhome = drv + os.environ['HOMEPATH']
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100254 else:
255 raise RuntimeError("Can't determine home directory")
256
257 if username:
258 # Try to guess user home directory. By default all users
259 # directories are located in the same place and are named by
260 # corresponding usernames. If current user home directory points
261 # to nonstandard place, this guess is likely wrong.
262 if os.environ['USERNAME'] != username:
263 drv, root, parts = self.parse_parts((userhome,))
264 if parts[-1] != os.environ['USERNAME']:
265 raise RuntimeError("Can't determine home directory "
266 "for %r" % username)
267 parts[-1] = username
268 if drv or root:
269 userhome = drv + root + self.join(parts[1:])
270 else:
271 userhome = self.join(parts)
272 return userhome
Antoine Pitrou31119e42013-11-22 17:38:12 +0100273
274class _PosixFlavour(_Flavour):
275 sep = '/'
276 altsep = ''
277 has_drv = False
278 pathmod = posixpath
279
280 is_supported = (os.name != 'nt')
281
282 def splitroot(self, part, sep=sep):
283 if part and part[0] == sep:
284 stripped_part = part.lstrip(sep)
285 # According to POSIX path resolution:
286 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
287 # "A pathname that begins with two successive slashes may be
288 # interpreted in an implementation-defined manner, although more
289 # than two leading slashes shall be treated as a single slash".
290 if len(part) - len(stripped_part) == 2:
291 return '', sep * 2, stripped_part
292 else:
293 return '', sep, stripped_part
294 else:
295 return '', '', part
296
297 def casefold(self, s):
298 return s
299
300 def casefold_parts(self, parts):
301 return parts
302
Steve Dower98eb3602016-11-09 12:58:17 -0800303 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100304 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100305 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100306 seen = {}
307 def _resolve(path, rest):
308 if rest.startswith(sep):
309 path = ''
310
311 for name in rest.split(sep):
312 if not name or name == '.':
313 # current dir
314 continue
315 if name == '..':
316 # parent dir
317 path, _, _ = path.rpartition(sep)
318 continue
319 newpath = path + sep + name
320 if newpath in seen:
321 # Already seen this path
322 path = seen[newpath]
323 if path is not None:
324 # use cached value
325 continue
326 # The symlink is not resolved, so we must have a symlink loop.
327 raise RuntimeError("Symlink loop from %r" % newpath)
328 # Resolve the symbolic link
329 try:
330 target = accessor.readlink(newpath)
331 except OSError as e:
332 if e.errno != EINVAL:
Steve Dower98eb3602016-11-09 12:58:17 -0800333 if strict:
334 raise
335 else:
336 return newpath
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100337 # Not a symlink
338 path = newpath
339 else:
340 seen[newpath] = None # not resolved symlink
341 path = _resolve(path, target)
342 seen[newpath] = path # resolved symlink
343
344 return path
345 # NOTE: according to POSIX, getcwd() cannot contain path components
346 # which are symlinks.
347 base = '' if path.is_absolute() else os.getcwd()
348 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100349
350 def is_reserved(self, parts):
351 return False
352
353 def make_uri(self, path):
354 # We represent the path using the local filesystem encoding,
355 # for portability to other applications.
356 bpath = bytes(path)
357 return 'file://' + urlquote_from_bytes(bpath)
358
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100359 def gethomedir(self, username):
360 if not username:
361 try:
362 return os.environ['HOME']
363 except KeyError:
364 import pwd
365 return pwd.getpwuid(os.getuid()).pw_dir
366 else:
367 import pwd
368 try:
369 return pwd.getpwnam(username).pw_dir
370 except KeyError:
371 raise RuntimeError("Can't determine home directory "
372 "for %r" % username)
373
Antoine Pitrou31119e42013-11-22 17:38:12 +0100374
375_windows_flavour = _WindowsFlavour()
376_posix_flavour = _PosixFlavour()
377
378
379class _Accessor:
380 """An accessor implements a particular (system-specific or not) way of
381 accessing paths on the filesystem."""
382
383
384class _NormalAccessor(_Accessor):
385
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200386 stat = os.stat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100387
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200388 lstat = os.lstat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100389
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200390 open = os.open
Antoine Pitrou31119e42013-11-22 17:38:12 +0100391
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200392 listdir = os.listdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100393
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200394 scandir = os.scandir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100395
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200396 chmod = os.chmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100397
398 if hasattr(os, "lchmod"):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200399 lchmod = os.lchmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100400 else:
401 def lchmod(self, pathobj, mode):
402 raise NotImplementedError("lchmod() not available on this system")
403
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200404 mkdir = os.mkdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100405
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200406 unlink = os.unlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100407
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200408 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100409
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200410 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100411
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200412 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100413
414 if nt:
415 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200416 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100417 else:
418 def symlink(a, b, target_is_directory):
419 raise NotImplementedError("symlink() not available on this system")
420 else:
421 # Under POSIX, os.symlink() takes two args
422 @staticmethod
423 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200424 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100425
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200426 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100427
428 # Helper for resolve()
429 def readlink(self, path):
430 return os.readlink(path)
431
432
433_normal_accessor = _NormalAccessor()
434
435
436#
437# Globbing helpers
438#
439
Antoine Pitrou31119e42013-11-22 17:38:12 +0100440def _make_selector(pattern_parts):
441 pat = pattern_parts[0]
442 child_parts = pattern_parts[1:]
443 if pat == '**':
444 cls = _RecursiveWildcardSelector
445 elif '**' in pat:
446 raise ValueError("Invalid pattern: '**' can only be an entire path component")
447 elif _is_wildcard_pattern(pat):
448 cls = _WildcardSelector
449 else:
450 cls = _PreciseSelector
451 return cls(pat, child_parts)
452
453if hasattr(functools, "lru_cache"):
454 _make_selector = functools.lru_cache()(_make_selector)
455
456
457class _Selector:
458 """A selector matches a specific glob pattern part against the children
459 of a given path."""
460
461 def __init__(self, child_parts):
462 self.child_parts = child_parts
463 if child_parts:
464 self.successor = _make_selector(child_parts)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300465 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100466 else:
467 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300468 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100469
470 def select_from(self, parent_path):
471 """Iterate over all child paths of `parent_path` matched by this
472 selector. This can contain parent_path itself."""
473 path_cls = type(parent_path)
474 is_dir = path_cls.is_dir
475 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300476 scandir = parent_path._accessor.scandir
477 if not is_dir(parent_path):
478 return iter([])
479 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100480
481
482class _TerminatingSelector:
483
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300484 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100485 yield parent_path
486
487
488class _PreciseSelector(_Selector):
489
490 def __init__(self, name, child_parts):
491 self.name = name
492 _Selector.__init__(self, child_parts)
493
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300494 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800495 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800496 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300497 if (is_dir if self.dironly else exists)(path):
498 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800499 yield p
500 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100501 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100502
503
504class _WildcardSelector(_Selector):
505
506 def __init__(self, pat, child_parts):
507 self.pat = re.compile(fnmatch.translate(pat))
508 _Selector.__init__(self, child_parts)
509
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300510 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800511 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800512 cf = parent_path._flavour.casefold
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300513 entries = list(scandir(parent_path))
514 for entry in entries:
515 if not self.dironly or entry.is_dir():
516 name = entry.name
517 casefolded = cf(name)
518 if self.pat.match(casefolded):
519 path = parent_path._make_child_relpath(name)
520 for p in self.successor._select_from(path, is_dir, exists, scandir):
521 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800522 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100523 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800524
Antoine Pitrou31119e42013-11-22 17:38:12 +0100525
526
527class _RecursiveWildcardSelector(_Selector):
528
529 def __init__(self, pat, child_parts):
530 _Selector.__init__(self, child_parts)
531
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300532 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100533 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800534 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300535 entries = list(scandir(parent_path))
536 for entry in entries:
537 if entry.is_dir() and not entry.is_symlink():
538 path = parent_path._make_child_relpath(entry.name)
539 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800540 yield p
541 except PermissionError:
542 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100543
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300544 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800545 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300546 yielded = set()
547 try:
548 successor_select = self.successor._select_from
549 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
550 for p in successor_select(starting_point, is_dir, exists, scandir):
551 if p not in yielded:
552 yield p
553 yielded.add(p)
554 finally:
555 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800556 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100557 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100558
559
560#
561# Public API
562#
563
564class _PathParents(Sequence):
565 """This object provides sequence-like access to the logical ancestors
566 of a path. Don't try to construct it yourself."""
567 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
568
569 def __init__(self, path):
570 # We don't store the instance to avoid reference cycles
571 self._pathcls = type(path)
572 self._drv = path._drv
573 self._root = path._root
574 self._parts = path._parts
575
576 def __len__(self):
577 if self._drv or self._root:
578 return len(self._parts) - 1
579 else:
580 return len(self._parts)
581
582 def __getitem__(self, idx):
583 if idx < 0 or idx >= len(self):
584 raise IndexError(idx)
585 return self._pathcls._from_parsed_parts(self._drv, self._root,
586 self._parts[:-idx - 1])
587
588 def __repr__(self):
589 return "<{}.parents>".format(self._pathcls.__name__)
590
591
592class PurePath(object):
593 """PurePath represents a filesystem path and offers operations which
594 don't imply any actual filesystem I/O. Depending on your system,
595 instantiating a PurePath will return either a PurePosixPath or a
596 PureWindowsPath object. You can also instantiate either of these classes
597 directly, regardless of your system.
598 """
599 __slots__ = (
600 '_drv', '_root', '_parts',
601 '_str', '_hash', '_pparts', '_cached_cparts',
602 )
603
604 def __new__(cls, *args):
605 """Construct a PurePath from one or several strings and or existing
606 PurePath objects. The strings and path objects are combined so as
607 to yield a canonicalized path, which is incorporated into the
608 new PurePath object.
609 """
610 if cls is PurePath:
611 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
612 return cls._from_parts(args)
613
614 def __reduce__(self):
615 # Using the parts tuple helps share interned path parts
616 # when pickling related paths.
617 return (self.__class__, tuple(self._parts))
618
619 @classmethod
620 def _parse_args(cls, args):
621 # This is useful when you don't want to create an instance, just
622 # canonicalize some constructor arguments.
623 parts = []
624 for a in args:
625 if isinstance(a, PurePath):
626 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100627 else:
Brett Cannon568be632016-06-10 12:20:49 -0700628 a = os.fspath(a)
629 if isinstance(a, str):
630 # Force-cast str subclasses to str (issue #21127)
631 parts.append(str(a))
632 else:
633 raise TypeError(
634 "argument should be a str object or an os.PathLike "
635 "object returning str, not %r"
636 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100637 return cls._flavour.parse_parts(parts)
638
639 @classmethod
640 def _from_parts(cls, args, init=True):
641 # We need to call _parse_args on the instance, so as to get the
642 # right flavour.
643 self = object.__new__(cls)
644 drv, root, parts = self._parse_args(args)
645 self._drv = drv
646 self._root = root
647 self._parts = parts
648 if init:
649 self._init()
650 return self
651
652 @classmethod
653 def _from_parsed_parts(cls, drv, root, parts, init=True):
654 self = object.__new__(cls)
655 self._drv = drv
656 self._root = root
657 self._parts = parts
658 if init:
659 self._init()
660 return self
661
662 @classmethod
663 def _format_parsed_parts(cls, drv, root, parts):
664 if drv or root:
665 return drv + root + cls._flavour.join(parts[1:])
666 else:
667 return cls._flavour.join(parts)
668
669 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000670 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100671 pass
672
673 def _make_child(self, args):
674 drv, root, parts = self._parse_args(args)
675 drv, root, parts = self._flavour.join_parsed_parts(
676 self._drv, self._root, self._parts, drv, root, parts)
677 return self._from_parsed_parts(drv, root, parts)
678
679 def __str__(self):
680 """Return the string representation of the path, suitable for
681 passing to system calls."""
682 try:
683 return self._str
684 except AttributeError:
685 self._str = self._format_parsed_parts(self._drv, self._root,
686 self._parts) or '.'
687 return self._str
688
Brett Cannon568be632016-06-10 12:20:49 -0700689 def __fspath__(self):
690 return str(self)
691
Antoine Pitrou31119e42013-11-22 17:38:12 +0100692 def as_posix(self):
693 """Return the string representation of the path with forward (/)
694 slashes."""
695 f = self._flavour
696 return str(self).replace(f.sep, '/')
697
698 def __bytes__(self):
699 """Return the bytes representation of the path. This is only
700 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200701 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100702
703 def __repr__(self):
704 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
705
706 def as_uri(self):
707 """Return the path as a 'file' URI."""
708 if not self.is_absolute():
709 raise ValueError("relative path can't be expressed as a file URI")
710 return self._flavour.make_uri(self)
711
712 @property
713 def _cparts(self):
714 # Cached casefolded parts, for hashing and comparison
715 try:
716 return self._cached_cparts
717 except AttributeError:
718 self._cached_cparts = self._flavour.casefold_parts(self._parts)
719 return self._cached_cparts
720
721 def __eq__(self, other):
722 if not isinstance(other, PurePath):
723 return NotImplemented
724 return self._cparts == other._cparts and self._flavour is other._flavour
725
Antoine Pitrou31119e42013-11-22 17:38:12 +0100726 def __hash__(self):
727 try:
728 return self._hash
729 except AttributeError:
730 self._hash = hash(tuple(self._cparts))
731 return self._hash
732
733 def __lt__(self, other):
734 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
735 return NotImplemented
736 return self._cparts < other._cparts
737
738 def __le__(self, other):
739 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
740 return NotImplemented
741 return self._cparts <= other._cparts
742
743 def __gt__(self, other):
744 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
745 return NotImplemented
746 return self._cparts > other._cparts
747
748 def __ge__(self, other):
749 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
750 return NotImplemented
751 return self._cparts >= other._cparts
752
753 drive = property(attrgetter('_drv'),
754 doc="""The drive prefix (letter or UNC path), if any.""")
755
756 root = property(attrgetter('_root'),
757 doc="""The root of the path, if any.""")
758
759 @property
760 def anchor(self):
761 """The concatenation of the drive and root, or ''."""
762 anchor = self._drv + self._root
763 return anchor
764
765 @property
766 def name(self):
767 """The final path component, if any."""
768 parts = self._parts
769 if len(parts) == (1 if (self._drv or self._root) else 0):
770 return ''
771 return parts[-1]
772
773 @property
774 def suffix(self):
775 """The final component's last suffix, if any."""
776 name = self.name
777 i = name.rfind('.')
778 if 0 < i < len(name) - 1:
779 return name[i:]
780 else:
781 return ''
782
783 @property
784 def suffixes(self):
785 """A list of the final component's suffixes, if any."""
786 name = self.name
787 if name.endswith('.'):
788 return []
789 name = name.lstrip('.')
790 return ['.' + suffix for suffix in name.split('.')[1:]]
791
792 @property
793 def stem(self):
794 """The final path component, minus its last suffix."""
795 name = self.name
796 i = name.rfind('.')
797 if 0 < i < len(name) - 1:
798 return name[:i]
799 else:
800 return name
801
802 def with_name(self, name):
803 """Return a new path with the file name changed."""
804 if not self.name:
805 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400806 drv, root, parts = self._flavour.parse_parts((name,))
807 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
808 or drv or root or len(parts) != 1):
809 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100810 return self._from_parsed_parts(self._drv, self._root,
811 self._parts[:-1] + [name])
812
813 def with_suffix(self, suffix):
814 """Return a new path with the file suffix changed (or added, if none)."""
815 # XXX if suffix is None, should the current suffix be removed?
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400816 f = self._flavour
817 if f.sep in suffix or f.altsep and f.altsep in suffix:
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100818 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400819 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100820 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100821 name = self.name
822 if not name:
823 raise ValueError("%r has an empty name" % (self,))
824 old_suffix = self.suffix
825 if not old_suffix:
826 name = name + suffix
827 else:
828 name = name[:-len(old_suffix)] + suffix
829 return self._from_parsed_parts(self._drv, self._root,
830 self._parts[:-1] + [name])
831
832 def relative_to(self, *other):
833 """Return the relative path to another path identified by the passed
834 arguments. If the operation is not possible (because this is not
835 a subpath of the other path), raise ValueError.
836 """
837 # For the purpose of this method, drive and root are considered
838 # separate parts, i.e.:
839 # Path('c:/').relative_to('c:') gives Path('/')
840 # Path('c:/').relative_to('/') raise ValueError
841 if not other:
842 raise TypeError("need at least one argument")
843 parts = self._parts
844 drv = self._drv
845 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100846 if root:
847 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100848 else:
849 abs_parts = parts
850 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100851 if to_root:
852 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100853 else:
854 to_abs_parts = to_parts
855 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100856 cf = self._flavour.casefold_parts
857 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100858 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
859 raise ValueError("{!r} does not start with {!r}"
860 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100861 return self._from_parsed_parts('', root if n == 1 else '',
862 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100863
864 @property
865 def parts(self):
866 """An object providing sequence-like access to the
867 components in the filesystem path."""
868 # We cache the tuple to avoid building a new one each time .parts
869 # is accessed. XXX is this necessary?
870 try:
871 return self._pparts
872 except AttributeError:
873 self._pparts = tuple(self._parts)
874 return self._pparts
875
876 def joinpath(self, *args):
877 """Combine this path with one or several arguments, and return a
878 new path representing either a subpath (if all arguments are relative
879 paths) or a totally different path (if one of the arguments is
880 anchored).
881 """
882 return self._make_child(args)
883
884 def __truediv__(self, key):
885 return self._make_child((key,))
886
887 def __rtruediv__(self, key):
888 return self._from_parts([key] + self._parts)
889
890 @property
891 def parent(self):
892 """The logical parent of the path."""
893 drv = self._drv
894 root = self._root
895 parts = self._parts
896 if len(parts) == 1 and (drv or root):
897 return self
898 return self._from_parsed_parts(drv, root, parts[:-1])
899
900 @property
901 def parents(self):
902 """A sequence of this path's logical parents."""
903 return _PathParents(self)
904
905 def is_absolute(self):
906 """True if the path is absolute (has both a root and, if applicable,
907 a drive)."""
908 if not self._root:
909 return False
910 return not self._flavour.has_drv or bool(self._drv)
911
912 def is_reserved(self):
913 """Return True if the path contains one of the special names reserved
914 by the system, if any."""
915 return self._flavour.is_reserved(self._parts)
916
917 def match(self, path_pattern):
918 """
919 Return True if this path matches the given pattern.
920 """
921 cf = self._flavour.casefold
922 path_pattern = cf(path_pattern)
923 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
924 if not pat_parts:
925 raise ValueError("empty pattern")
926 if drv and drv != cf(self._drv):
927 return False
928 if root and root != cf(self._root):
929 return False
930 parts = self._cparts
931 if drv or root:
932 if len(pat_parts) != len(parts):
933 return False
934 pat_parts = pat_parts[1:]
935 elif len(pat_parts) > len(parts):
936 return False
937 for part, pat in zip(reversed(parts), reversed(pat_parts)):
938 if not fnmatch.fnmatchcase(part, pat):
939 return False
940 return True
941
Brett Cannon568be632016-06-10 12:20:49 -0700942# Can't subclass os.PathLike from PurePath and keep the constructor
943# optimizations in PurePath._parse_args().
944os.PathLike.register(PurePath)
945
Antoine Pitrou31119e42013-11-22 17:38:12 +0100946
947class PurePosixPath(PurePath):
948 _flavour = _posix_flavour
949 __slots__ = ()
950
951
952class PureWindowsPath(PurePath):
953 _flavour = _windows_flavour
954 __slots__ = ()
955
956
957# Filesystem-accessing classes
958
959
960class Path(PurePath):
961 __slots__ = (
962 '_accessor',
963 '_closed',
964 )
965
966 def __new__(cls, *args, **kwargs):
967 if cls is Path:
968 cls = WindowsPath if os.name == 'nt' else PosixPath
969 self = cls._from_parts(args, init=False)
970 if not self._flavour.is_supported:
971 raise NotImplementedError("cannot instantiate %r on your system"
972 % (cls.__name__,))
973 self._init()
974 return self
975
976 def _init(self,
977 # Private non-constructor arguments
978 template=None,
979 ):
980 self._closed = False
981 if template is not None:
982 self._accessor = template._accessor
983 else:
984 self._accessor = _normal_accessor
985
986 def _make_child_relpath(self, part):
987 # This is an optimization used for dir walking. `part` must be
988 # a single part relative to this path.
989 parts = self._parts + [part]
990 return self._from_parsed_parts(self._drv, self._root, parts)
991
992 def __enter__(self):
993 if self._closed:
994 self._raise_closed()
995 return self
996
997 def __exit__(self, t, v, tb):
998 self._closed = True
999
1000 def _raise_closed(self):
1001 raise ValueError("I/O operation on closed path")
1002
1003 def _opener(self, name, flags, mode=0o666):
1004 # A stub for the opener argument to built-in open()
1005 return self._accessor.open(self, flags, mode)
1006
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001007 def _raw_open(self, flags, mode=0o777):
1008 """
1009 Open the file pointed by this path and return a file descriptor,
1010 as os.open() does.
1011 """
1012 if self._closed:
1013 self._raise_closed()
1014 return self._accessor.open(self, flags, mode)
1015
Antoine Pitrou31119e42013-11-22 17:38:12 +01001016 # Public API
1017
1018 @classmethod
1019 def cwd(cls):
1020 """Return a new path pointing to the current working directory
1021 (as returned by os.getcwd()).
1022 """
1023 return cls(os.getcwd())
1024
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001025 @classmethod
1026 def home(cls):
1027 """Return a new path pointing to the user's home directory (as
1028 returned by os.path.expanduser('~')).
1029 """
1030 return cls(cls()._flavour.gethomedir(None))
1031
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001032 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001033 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001034 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001035 """
1036 st = self.stat()
1037 try:
1038 other_st = other_path.stat()
1039 except AttributeError:
1040 other_st = os.stat(other_path)
1041 return os.path.samestat(st, other_st)
1042
Antoine Pitrou31119e42013-11-22 17:38:12 +01001043 def iterdir(self):
1044 """Iterate over the files in this directory. Does not yield any
1045 result for the special paths '.' and '..'.
1046 """
1047 if self._closed:
1048 self._raise_closed()
1049 for name in self._accessor.listdir(self):
1050 if name in {'.', '..'}:
1051 # Yielding a path object for these makes little sense
1052 continue
1053 yield self._make_child_relpath(name)
1054 if self._closed:
1055 self._raise_closed()
1056
1057 def glob(self, pattern):
1058 """Iterate over this subtree and yield all existing files (of any
1059 kind, including directories) matching the given pattern.
1060 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001061 if not pattern:
1062 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001063 pattern = self._flavour.casefold(pattern)
1064 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1065 if drv or root:
1066 raise NotImplementedError("Non-relative patterns are unsupported")
1067 selector = _make_selector(tuple(pattern_parts))
1068 for p in selector.select_from(self):
1069 yield p
1070
1071 def rglob(self, pattern):
1072 """Recursively yield all existing files (of any kind, including
1073 directories) matching the given pattern, anywhere in this subtree.
1074 """
1075 pattern = self._flavour.casefold(pattern)
1076 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1077 if drv or root:
1078 raise NotImplementedError("Non-relative patterns are unsupported")
1079 selector = _make_selector(("**",) + tuple(pattern_parts))
1080 for p in selector.select_from(self):
1081 yield p
1082
1083 def absolute(self):
1084 """Return an absolute version of this path. This function works
1085 even if the path doesn't point to anything.
1086
1087 No normalization is done, i.e. all '.' and '..' will be kept along.
1088 Use resolve() to get the canonical path to a file.
1089 """
1090 # XXX untested yet!
1091 if self._closed:
1092 self._raise_closed()
1093 if self.is_absolute():
1094 return self
1095 # FIXME this must defer to the specific flavour (and, under Windows,
1096 # use nt._getfullpathname())
1097 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1098 obj._init(template=self)
1099 return obj
1100
Steve Dower98eb3602016-11-09 12:58:17 -08001101 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001102 """
1103 Make the path absolute, resolving all symlinks on the way and also
1104 normalizing it (for example turning slashes into backslashes under
1105 Windows).
1106 """
1107 if self._closed:
1108 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001109 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001110 if s is None:
1111 # No symlink resolution => for consistency, raise an error if
1112 # the path doesn't exist or is forbidden
1113 self.stat()
1114 s = str(self.absolute())
1115 # Now we have no symlinks in the path, it's safe to normalize it.
1116 normed = self._flavour.pathmod.normpath(s)
1117 obj = self._from_parts((normed,), init=False)
1118 obj._init(template=self)
1119 return obj
1120
1121 def stat(self):
1122 """
1123 Return the result of the stat() system call on this path, like
1124 os.stat() does.
1125 """
1126 return self._accessor.stat(self)
1127
1128 def owner(self):
1129 """
1130 Return the login name of the file owner.
1131 """
1132 import pwd
1133 return pwd.getpwuid(self.stat().st_uid).pw_name
1134
1135 def group(self):
1136 """
1137 Return the group name of the file gid.
1138 """
1139 import grp
1140 return grp.getgrgid(self.stat().st_gid).gr_name
1141
Antoine Pitrou31119e42013-11-22 17:38:12 +01001142 def open(self, mode='r', buffering=-1, encoding=None,
1143 errors=None, newline=None):
1144 """
1145 Open the file pointed by this path and return a file object, as
1146 the built-in open() function does.
1147 """
1148 if self._closed:
1149 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001150 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001151 opener=self._opener)
1152
Georg Brandlea683982014-10-01 19:12:33 +02001153 def read_bytes(self):
1154 """
1155 Open the file in bytes mode, read it, and close the file.
1156 """
1157 with self.open(mode='rb') as f:
1158 return f.read()
1159
1160 def read_text(self, encoding=None, errors=None):
1161 """
1162 Open the file in text mode, read it, and close the file.
1163 """
1164 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1165 return f.read()
1166
1167 def write_bytes(self, data):
1168 """
1169 Open the file in bytes mode, write to it, and close the file.
1170 """
1171 # type-check for the buffer interface before truncating the file
1172 view = memoryview(data)
1173 with self.open(mode='wb') as f:
1174 return f.write(view)
1175
1176 def write_text(self, data, encoding=None, errors=None):
1177 """
1178 Open the file in text mode, write to it, and close the file.
1179 """
1180 if not isinstance(data, str):
1181 raise TypeError('data must be str, not %s' %
1182 data.__class__.__name__)
1183 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1184 return f.write(data)
1185
Antoine Pitrou31119e42013-11-22 17:38:12 +01001186 def touch(self, mode=0o666, exist_ok=True):
1187 """
1188 Create this file with the given access mode, if it doesn't exist.
1189 """
1190 if self._closed:
1191 self._raise_closed()
1192 if exist_ok:
1193 # First try to bump modification time
1194 # Implementation note: GNU touch uses the UTIME_NOW option of
1195 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001196 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001197 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001198 except OSError:
1199 # Avoid exception chaining
1200 pass
1201 else:
1202 return
1203 flags = os.O_CREAT | os.O_WRONLY
1204 if not exist_ok:
1205 flags |= os.O_EXCL
1206 fd = self._raw_open(flags, mode)
1207 os.close(fd)
1208
Barry Warsaw7c549c42014-08-05 11:28:12 -04001209 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001210 """
1211 Create a new directory at this given path.
1212 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001213 if self._closed:
1214 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001215 try:
1216 self._accessor.mkdir(self, mode)
1217 except FileNotFoundError:
1218 if not parents or self.parent == self:
1219 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001220 self.parent.mkdir(parents=True, exist_ok=True)
1221 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001222 except OSError:
1223 # Cannot rely on checking for EEXIST, since the operating system
1224 # could give priority to other errors like EACCES or EROFS
1225 if not exist_ok or not self.is_dir():
1226 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001227
1228 def chmod(self, mode):
1229 """
1230 Change the permissions of the path, like os.chmod().
1231 """
1232 if self._closed:
1233 self._raise_closed()
1234 self._accessor.chmod(self, mode)
1235
1236 def lchmod(self, mode):
1237 """
1238 Like chmod(), except if the path points to a symlink, the symlink's
1239 permissions are changed, rather than its target's.
1240 """
1241 if self._closed:
1242 self._raise_closed()
1243 self._accessor.lchmod(self, mode)
1244
1245 def unlink(self):
1246 """
1247 Remove this file or link.
1248 If the path is a directory, use rmdir() instead.
1249 """
1250 if self._closed:
1251 self._raise_closed()
1252 self._accessor.unlink(self)
1253
1254 def rmdir(self):
1255 """
1256 Remove this directory. The directory must be empty.
1257 """
1258 if self._closed:
1259 self._raise_closed()
1260 self._accessor.rmdir(self)
1261
1262 def lstat(self):
1263 """
1264 Like stat(), except if the path points to a symlink, the symlink's
1265 status information is returned, rather than its target's.
1266 """
1267 if self._closed:
1268 self._raise_closed()
1269 return self._accessor.lstat(self)
1270
1271 def rename(self, target):
1272 """
1273 Rename this path to the given path.
1274 """
1275 if self._closed:
1276 self._raise_closed()
1277 self._accessor.rename(self, target)
1278
1279 def replace(self, target):
1280 """
1281 Rename this path to the given path, clobbering the existing
1282 destination if it exists.
1283 """
1284 if self._closed:
1285 self._raise_closed()
1286 self._accessor.replace(self, target)
1287
1288 def symlink_to(self, target, target_is_directory=False):
1289 """
1290 Make this path a symlink pointing to the given path.
1291 Note the order of arguments (self, target) is the reverse of os.symlink's.
1292 """
1293 if self._closed:
1294 self._raise_closed()
1295 self._accessor.symlink(target, self, target_is_directory)
1296
1297 # Convenience functions for querying the stat results
1298
1299 def exists(self):
1300 """
1301 Whether this path exists.
1302 """
1303 try:
1304 self.stat()
1305 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001306 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001307 raise
1308 return False
1309 return True
1310
1311 def is_dir(self):
1312 """
1313 Whether this path is a directory.
1314 """
1315 try:
1316 return S_ISDIR(self.stat().st_mode)
1317 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001318 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001319 raise
1320 # Path doesn't exist or is a broken symlink
1321 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1322 return False
1323
1324 def is_file(self):
1325 """
1326 Whether this path is a regular file (also True for symlinks pointing
1327 to regular files).
1328 """
1329 try:
1330 return S_ISREG(self.stat().st_mode)
1331 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001332 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001333 raise
1334 # Path doesn't exist or is a broken symlink
1335 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1336 return False
1337
1338 def is_symlink(self):
1339 """
1340 Whether this path is a symbolic link.
1341 """
1342 try:
1343 return S_ISLNK(self.lstat().st_mode)
1344 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001345 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001346 raise
1347 # Path doesn't exist
1348 return False
1349
1350 def is_block_device(self):
1351 """
1352 Whether this path is a block device.
1353 """
1354 try:
1355 return S_ISBLK(self.stat().st_mode)
1356 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001357 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001358 raise
1359 # Path doesn't exist or is a broken symlink
1360 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1361 return False
1362
1363 def is_char_device(self):
1364 """
1365 Whether this path is a character device.
1366 """
1367 try:
1368 return S_ISCHR(self.stat().st_mode)
1369 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001370 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001371 raise
1372 # Path doesn't exist or is a broken symlink
1373 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1374 return False
1375
1376 def is_fifo(self):
1377 """
1378 Whether this path is a FIFO.
1379 """
1380 try:
1381 return S_ISFIFO(self.stat().st_mode)
1382 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001383 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001384 raise
1385 # Path doesn't exist or is a broken symlink
1386 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1387 return False
1388
1389 def is_socket(self):
1390 """
1391 Whether this path is a socket.
1392 """
1393 try:
1394 return S_ISSOCK(self.stat().st_mode)
1395 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001396 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001397 raise
1398 # Path doesn't exist or is a broken symlink
1399 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1400 return False
1401
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001402 def expanduser(self):
1403 """ Return a new path with expanded ~ and ~user constructs
1404 (as returned by os.path.expanduser)
1405 """
1406 if (not (self._drv or self._root) and
1407 self._parts and self._parts[0][:1] == '~'):
1408 homedir = self._flavour.gethomedir(self._parts[0][1:])
1409 return self._from_parts([homedir] + self._parts[1:])
1410
1411 return self
1412
Antoine Pitrou31119e42013-11-22 17:38:12 +01001413
1414class PosixPath(Path, PurePosixPath):
1415 __slots__ = ()
1416
1417class WindowsPath(Path, PureWindowsPath):
1418 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001419
1420 def owner(self):
1421 raise NotImplementedError("Path.owner() is unsupported on this system")
1422
1423 def group(self):
1424 raise NotImplementedError("Path.group() is unsupported on this system")