blob: 4d89436f775c757fa1719d55df28d818087f2d43 [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
Jon Dufresne39726282017-05-18 07:35:54 -0700117 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100118 ext_namespace_prefix = '\\\\?\\'
119
120 reserved_names = (
121 {'CON', 'PRN', 'AUX', 'NUL'} |
122 {'COM%d' % i for i in range(1, 10)} |
123 {'LPT%d' % i for i in range(1, 10)}
124 )
125
126 # Interesting findings about extended paths:
127 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
128 # but '\\?\c:/a' is not
129 # - extended paths are always absolute; "relative" extended paths will
130 # fail.
131
132 def splitroot(self, part, sep=sep):
133 first = part[0:1]
134 second = part[1:2]
135 if (second == sep and first == sep):
136 # XXX extended paths should also disable the collapsing of "."
137 # components (according to MSDN docs).
138 prefix, part = self._split_extended_path(part)
139 first = part[0:1]
140 second = part[1:2]
141 else:
142 prefix = ''
143 third = part[2:3]
144 if (second == sep and first == sep and third != sep):
145 # is a UNC path:
146 # vvvvvvvvvvvvvvvvvvvvv root
147 # \\machine\mountpoint\directory\etc\...
148 # directory ^^^^^^^^^^^^^^
149 index = part.find(sep, 2)
150 if index != -1:
151 index2 = part.find(sep, index + 1)
152 # a UNC path can't have two slashes in a row
153 # (after the initial two)
154 if index2 != index + 1:
155 if index2 == -1:
156 index2 = len(part)
157 if prefix:
158 return prefix + part[1:index2], sep, part[index2+1:]
159 else:
160 return part[:index2], sep, part[index2+1:]
161 drv = root = ''
162 if second == ':' and first in self.drive_letters:
163 drv = part[:2]
164 part = part[2:]
165 first = third
166 if first == sep:
167 root = first
168 part = part.lstrip(sep)
169 return prefix + drv, root, part
170
171 def casefold(self, s):
172 return s.lower()
173
174 def casefold_parts(self, parts):
175 return [p.lower() for p in parts]
176
Steve Dower98eb3602016-11-09 12:58:17 -0800177 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100178 s = str(path)
179 if not s:
180 return os.getcwd()
Steve Dower98eb3602016-11-09 12:58:17 -0800181 previous_s = None
Antoine Pitrou31119e42013-11-22 17:38:12 +0100182 if _getfinalpathname is not None:
Steve Dower98eb3602016-11-09 12:58:17 -0800183 if strict:
184 return self._ext_to_normal(_getfinalpathname(s))
185 else:
186 while True:
187 try:
188 s = self._ext_to_normal(_getfinalpathname(s))
189 except FileNotFoundError:
190 previous_s = s
Steve Dower4b1e98b2016-12-28 16:02:59 -0800191 s = os.path.dirname(s)
192 if previous_s == s:
193 return path
Steve Dower98eb3602016-11-09 12:58:17 -0800194 else:
195 if previous_s is None:
196 return s
197 else:
198 return s + os.path.sep + os.path.basename(previous_s)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100199 # Means fallback on absolute
200 return None
201
202 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
203 prefix = ''
204 if s.startswith(ext_prefix):
205 prefix = s[:4]
206 s = s[4:]
207 if s.startswith('UNC\\'):
208 prefix += s[:3]
209 s = '\\' + s[3:]
210 return prefix, s
211
212 def _ext_to_normal(self, s):
213 # Turn back an extended path into a normal DOS-like path
214 return self._split_extended_path(s)[1]
215
216 def is_reserved(self, parts):
217 # NOTE: the rules for reserved names seem somewhat complicated
218 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
219 # We err on the side of caution and return True for paths which are
220 # not considered reserved by Windows.
221 if not parts:
222 return False
223 if parts[0].startswith('\\\\'):
224 # UNC paths are never reserved
225 return False
226 return parts[-1].partition('.')[0].upper() in self.reserved_names
227
228 def make_uri(self, path):
229 # Under Windows, file URIs use the UTF-8 encoding.
230 drive = path.drive
231 if len(drive) == 2 and drive[1] == ':':
232 # It's a path on a local drive => 'file:///c:/a/b'
233 rest = path.as_posix()[2:].lstrip('/')
234 return 'file:///%s/%s' % (
235 drive, urlquote_from_bytes(rest.encode('utf-8')))
236 else:
237 # It's a path on a network drive => 'file://host/share/a/b'
238 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
239
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100240 def gethomedir(self, username):
241 if 'HOME' in os.environ:
242 userhome = os.environ['HOME']
243 elif 'USERPROFILE' in os.environ:
244 userhome = os.environ['USERPROFILE']
245 elif 'HOMEPATH' in os.environ:
Antoine Pitrou5d4e27e2014-12-30 22:09:42 +0100246 try:
247 drv = os.environ['HOMEDRIVE']
248 except KeyError:
249 drv = ''
250 userhome = drv + os.environ['HOMEPATH']
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100251 else:
252 raise RuntimeError("Can't determine home directory")
253
254 if username:
255 # Try to guess user home directory. By default all users
256 # directories are located in the same place and are named by
257 # corresponding usernames. If current user home directory points
258 # to nonstandard place, this guess is likely wrong.
259 if os.environ['USERNAME'] != username:
260 drv, root, parts = self.parse_parts((userhome,))
261 if parts[-1] != os.environ['USERNAME']:
262 raise RuntimeError("Can't determine home directory "
263 "for %r" % username)
264 parts[-1] = username
265 if drv or root:
266 userhome = drv + root + self.join(parts[1:])
267 else:
268 userhome = self.join(parts)
269 return userhome
Antoine Pitrou31119e42013-11-22 17:38:12 +0100270
271class _PosixFlavour(_Flavour):
272 sep = '/'
273 altsep = ''
274 has_drv = False
275 pathmod = posixpath
276
277 is_supported = (os.name != 'nt')
278
279 def splitroot(self, part, sep=sep):
280 if part and part[0] == sep:
281 stripped_part = part.lstrip(sep)
282 # According to POSIX path resolution:
283 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
284 # "A pathname that begins with two successive slashes may be
285 # interpreted in an implementation-defined manner, although more
286 # than two leading slashes shall be treated as a single slash".
287 if len(part) - len(stripped_part) == 2:
288 return '', sep * 2, stripped_part
289 else:
290 return '', sep, stripped_part
291 else:
292 return '', '', part
293
294 def casefold(self, s):
295 return s
296
297 def casefold_parts(self, parts):
298 return parts
299
Steve Dower98eb3602016-11-09 12:58:17 -0800300 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100301 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100302 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100303 seen = {}
304 def _resolve(path, rest):
305 if rest.startswith(sep):
306 path = ''
307
308 for name in rest.split(sep):
309 if not name or name == '.':
310 # current dir
311 continue
312 if name == '..':
313 # parent dir
314 path, _, _ = path.rpartition(sep)
315 continue
316 newpath = path + sep + name
317 if newpath in seen:
318 # Already seen this path
319 path = seen[newpath]
320 if path is not None:
321 # use cached value
322 continue
323 # The symlink is not resolved, so we must have a symlink loop.
324 raise RuntimeError("Symlink loop from %r" % newpath)
325 # Resolve the symbolic link
326 try:
327 target = accessor.readlink(newpath)
328 except OSError as e:
329 if e.errno != EINVAL:
Steve Dower98eb3602016-11-09 12:58:17 -0800330 if strict:
331 raise
332 else:
333 return newpath
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100334 # Not a symlink
335 path = newpath
336 else:
337 seen[newpath] = None # not resolved symlink
338 path = _resolve(path, target)
339 seen[newpath] = path # resolved symlink
340
341 return path
342 # NOTE: according to POSIX, getcwd() cannot contain path components
343 # which are symlinks.
344 base = '' if path.is_absolute() else os.getcwd()
345 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100346
347 def is_reserved(self, parts):
348 return False
349
350 def make_uri(self, path):
351 # We represent the path using the local filesystem encoding,
352 # for portability to other applications.
353 bpath = bytes(path)
354 return 'file://' + urlquote_from_bytes(bpath)
355
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100356 def gethomedir(self, username):
357 if not username:
358 try:
359 return os.environ['HOME']
360 except KeyError:
361 import pwd
362 return pwd.getpwuid(os.getuid()).pw_dir
363 else:
364 import pwd
365 try:
366 return pwd.getpwnam(username).pw_dir
367 except KeyError:
368 raise RuntimeError("Can't determine home directory "
369 "for %r" % username)
370
Antoine Pitrou31119e42013-11-22 17:38:12 +0100371
372_windows_flavour = _WindowsFlavour()
373_posix_flavour = _PosixFlavour()
374
375
376class _Accessor:
377 """An accessor implements a particular (system-specific or not) way of
378 accessing paths on the filesystem."""
379
380
381class _NormalAccessor(_Accessor):
382
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200383 stat = os.stat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100384
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200385 lstat = os.lstat
Antoine Pitrou31119e42013-11-22 17:38:12 +0100386
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200387 open = os.open
Antoine Pitrou31119e42013-11-22 17:38:12 +0100388
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200389 listdir = os.listdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100390
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200391 scandir = os.scandir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100392
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200393 chmod = os.chmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100394
395 if hasattr(os, "lchmod"):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200396 lchmod = os.lchmod
Antoine Pitrou31119e42013-11-22 17:38:12 +0100397 else:
398 def lchmod(self, pathobj, mode):
399 raise NotImplementedError("lchmod() not available on this system")
400
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200401 mkdir = os.mkdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100402
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200403 unlink = os.unlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100404
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200405 rmdir = os.rmdir
Antoine Pitrou31119e42013-11-22 17:38:12 +0100406
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200407 rename = os.rename
Antoine Pitrou31119e42013-11-22 17:38:12 +0100408
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200409 replace = os.replace
Antoine Pitrou31119e42013-11-22 17:38:12 +0100410
411 if nt:
412 if supports_symlinks:
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200413 symlink = os.symlink
Antoine Pitrou31119e42013-11-22 17:38:12 +0100414 else:
415 def symlink(a, b, target_is_directory):
416 raise NotImplementedError("symlink() not available on this system")
417 else:
418 # Under POSIX, os.symlink() takes two args
419 @staticmethod
420 def symlink(a, b, target_is_directory):
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200421 return os.symlink(a, b)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100422
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200423 utime = os.utime
Antoine Pitrou31119e42013-11-22 17:38:12 +0100424
425 # Helper for resolve()
426 def readlink(self, path):
427 return os.readlink(path)
428
429
430_normal_accessor = _NormalAccessor()
431
432
433#
434# Globbing helpers
435#
436
Antoine Pitrou31119e42013-11-22 17:38:12 +0100437def _make_selector(pattern_parts):
438 pat = pattern_parts[0]
439 child_parts = pattern_parts[1:]
440 if pat == '**':
441 cls = _RecursiveWildcardSelector
442 elif '**' in pat:
443 raise ValueError("Invalid pattern: '**' can only be an entire path component")
444 elif _is_wildcard_pattern(pat):
445 cls = _WildcardSelector
446 else:
447 cls = _PreciseSelector
448 return cls(pat, child_parts)
449
450if hasattr(functools, "lru_cache"):
451 _make_selector = functools.lru_cache()(_make_selector)
452
453
454class _Selector:
455 """A selector matches a specific glob pattern part against the children
456 of a given path."""
457
458 def __init__(self, child_parts):
459 self.child_parts = child_parts
460 if child_parts:
461 self.successor = _make_selector(child_parts)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300462 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100463 else:
464 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300465 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100466
467 def select_from(self, parent_path):
468 """Iterate over all child paths of `parent_path` matched by this
469 selector. This can contain parent_path itself."""
470 path_cls = type(parent_path)
471 is_dir = path_cls.is_dir
472 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300473 scandir = parent_path._accessor.scandir
474 if not is_dir(parent_path):
475 return iter([])
476 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100477
478
479class _TerminatingSelector:
480
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300481 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100482 yield parent_path
483
484
485class _PreciseSelector(_Selector):
486
487 def __init__(self, name, child_parts):
488 self.name = name
489 _Selector.__init__(self, child_parts)
490
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300491 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800492 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800493 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300494 if (is_dir if self.dironly else exists)(path):
495 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800496 yield p
497 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100498 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100499
500
501class _WildcardSelector(_Selector):
502
503 def __init__(self, pat, child_parts):
504 self.pat = re.compile(fnmatch.translate(pat))
505 _Selector.__init__(self, child_parts)
506
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300507 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800508 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800509 cf = parent_path._flavour.casefold
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300510 entries = list(scandir(parent_path))
511 for entry in entries:
512 if not self.dironly or entry.is_dir():
513 name = entry.name
514 casefolded = cf(name)
515 if self.pat.match(casefolded):
516 path = parent_path._make_child_relpath(name)
517 for p in self.successor._select_from(path, is_dir, exists, scandir):
518 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800519 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100520 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800521
Antoine Pitrou31119e42013-11-22 17:38:12 +0100522
523
524class _RecursiveWildcardSelector(_Selector):
525
526 def __init__(self, pat, child_parts):
527 _Selector.__init__(self, child_parts)
528
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300529 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100530 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800531 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300532 entries = list(scandir(parent_path))
533 for entry in entries:
534 if entry.is_dir() and not entry.is_symlink():
535 path = parent_path._make_child_relpath(entry.name)
536 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800537 yield p
538 except PermissionError:
539 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100540
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300541 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800542 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300543 yielded = set()
544 try:
545 successor_select = self.successor._select_from
546 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
547 for p in successor_select(starting_point, is_dir, exists, scandir):
548 if p not in yielded:
549 yield p
550 yielded.add(p)
551 finally:
552 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800553 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100554 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100555
556
557#
558# Public API
559#
560
561class _PathParents(Sequence):
562 """This object provides sequence-like access to the logical ancestors
563 of a path. Don't try to construct it yourself."""
564 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
565
566 def __init__(self, path):
567 # We don't store the instance to avoid reference cycles
568 self._pathcls = type(path)
569 self._drv = path._drv
570 self._root = path._root
571 self._parts = path._parts
572
573 def __len__(self):
574 if self._drv or self._root:
575 return len(self._parts) - 1
576 else:
577 return len(self._parts)
578
579 def __getitem__(self, idx):
580 if idx < 0 or idx >= len(self):
581 raise IndexError(idx)
582 return self._pathcls._from_parsed_parts(self._drv, self._root,
583 self._parts[:-idx - 1])
584
585 def __repr__(self):
586 return "<{}.parents>".format(self._pathcls.__name__)
587
588
589class PurePath(object):
590 """PurePath represents a filesystem path and offers operations which
591 don't imply any actual filesystem I/O. Depending on your system,
592 instantiating a PurePath will return either a PurePosixPath or a
593 PureWindowsPath object. You can also instantiate either of these classes
594 directly, regardless of your system.
595 """
596 __slots__ = (
597 '_drv', '_root', '_parts',
598 '_str', '_hash', '_pparts', '_cached_cparts',
599 )
600
601 def __new__(cls, *args):
602 """Construct a PurePath from one or several strings and or existing
603 PurePath objects. The strings and path objects are combined so as
604 to yield a canonicalized path, which is incorporated into the
605 new PurePath object.
606 """
607 if cls is PurePath:
608 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
609 return cls._from_parts(args)
610
611 def __reduce__(self):
612 # Using the parts tuple helps share interned path parts
613 # when pickling related paths.
614 return (self.__class__, tuple(self._parts))
615
616 @classmethod
617 def _parse_args(cls, args):
618 # This is useful when you don't want to create an instance, just
619 # canonicalize some constructor arguments.
620 parts = []
621 for a in args:
622 if isinstance(a, PurePath):
623 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100624 else:
Brett Cannon568be632016-06-10 12:20:49 -0700625 a = os.fspath(a)
626 if isinstance(a, str):
627 # Force-cast str subclasses to str (issue #21127)
628 parts.append(str(a))
629 else:
630 raise TypeError(
631 "argument should be a str object or an os.PathLike "
632 "object returning str, not %r"
633 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100634 return cls._flavour.parse_parts(parts)
635
636 @classmethod
637 def _from_parts(cls, args, init=True):
638 # We need to call _parse_args on the instance, so as to get the
639 # right flavour.
640 self = object.__new__(cls)
641 drv, root, parts = self._parse_args(args)
642 self._drv = drv
643 self._root = root
644 self._parts = parts
645 if init:
646 self._init()
647 return self
648
649 @classmethod
650 def _from_parsed_parts(cls, drv, root, parts, init=True):
651 self = object.__new__(cls)
652 self._drv = drv
653 self._root = root
654 self._parts = parts
655 if init:
656 self._init()
657 return self
658
659 @classmethod
660 def _format_parsed_parts(cls, drv, root, parts):
661 if drv or root:
662 return drv + root + cls._flavour.join(parts[1:])
663 else:
664 return cls._flavour.join(parts)
665
666 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000667 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100668 pass
669
670 def _make_child(self, args):
671 drv, root, parts = self._parse_args(args)
672 drv, root, parts = self._flavour.join_parsed_parts(
673 self._drv, self._root, self._parts, drv, root, parts)
674 return self._from_parsed_parts(drv, root, parts)
675
676 def __str__(self):
677 """Return the string representation of the path, suitable for
678 passing to system calls."""
679 try:
680 return self._str
681 except AttributeError:
682 self._str = self._format_parsed_parts(self._drv, self._root,
683 self._parts) or '.'
684 return self._str
685
Brett Cannon568be632016-06-10 12:20:49 -0700686 def __fspath__(self):
687 return str(self)
688
Antoine Pitrou31119e42013-11-22 17:38:12 +0100689 def as_posix(self):
690 """Return the string representation of the path with forward (/)
691 slashes."""
692 f = self._flavour
693 return str(self).replace(f.sep, '/')
694
695 def __bytes__(self):
696 """Return the bytes representation of the path. This is only
697 recommended to use under Unix."""
Serhiy Storchaka62a99512017-03-25 13:42:11 +0200698 return os.fsencode(self)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100699
700 def __repr__(self):
701 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
702
703 def as_uri(self):
704 """Return the path as a 'file' URI."""
705 if not self.is_absolute():
706 raise ValueError("relative path can't be expressed as a file URI")
707 return self._flavour.make_uri(self)
708
709 @property
710 def _cparts(self):
711 # Cached casefolded parts, for hashing and comparison
712 try:
713 return self._cached_cparts
714 except AttributeError:
715 self._cached_cparts = self._flavour.casefold_parts(self._parts)
716 return self._cached_cparts
717
718 def __eq__(self, other):
719 if not isinstance(other, PurePath):
720 return NotImplemented
721 return self._cparts == other._cparts and self._flavour is other._flavour
722
Antoine Pitrou31119e42013-11-22 17:38:12 +0100723 def __hash__(self):
724 try:
725 return self._hash
726 except AttributeError:
727 self._hash = hash(tuple(self._cparts))
728 return self._hash
729
730 def __lt__(self, other):
731 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
732 return NotImplemented
733 return self._cparts < other._cparts
734
735 def __le__(self, other):
736 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
737 return NotImplemented
738 return self._cparts <= other._cparts
739
740 def __gt__(self, other):
741 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
742 return NotImplemented
743 return self._cparts > other._cparts
744
745 def __ge__(self, other):
746 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
747 return NotImplemented
748 return self._cparts >= other._cparts
749
750 drive = property(attrgetter('_drv'),
751 doc="""The drive prefix (letter or UNC path), if any.""")
752
753 root = property(attrgetter('_root'),
754 doc="""The root of the path, if any.""")
755
756 @property
757 def anchor(self):
758 """The concatenation of the drive and root, or ''."""
759 anchor = self._drv + self._root
760 return anchor
761
762 @property
763 def name(self):
764 """The final path component, if any."""
765 parts = self._parts
766 if len(parts) == (1 if (self._drv or self._root) else 0):
767 return ''
768 return parts[-1]
769
770 @property
771 def suffix(self):
772 """The final component's last suffix, if any."""
773 name = self.name
774 i = name.rfind('.')
775 if 0 < i < len(name) - 1:
776 return name[i:]
777 else:
778 return ''
779
780 @property
781 def suffixes(self):
782 """A list of the final component's suffixes, if any."""
783 name = self.name
784 if name.endswith('.'):
785 return []
786 name = name.lstrip('.')
787 return ['.' + suffix for suffix in name.split('.')[1:]]
788
789 @property
790 def stem(self):
791 """The final path component, minus its last suffix."""
792 name = self.name
793 i = name.rfind('.')
794 if 0 < i < len(name) - 1:
795 return name[:i]
796 else:
797 return name
798
799 def with_name(self, name):
800 """Return a new path with the file name changed."""
801 if not self.name:
802 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400803 drv, root, parts = self._flavour.parse_parts((name,))
804 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
805 or drv or root or len(parts) != 1):
806 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100807 return self._from_parsed_parts(self._drv, self._root,
808 self._parts[:-1] + [name])
809
810 def with_suffix(self, suffix):
811 """Return a new path with the file suffix changed (or added, if none)."""
812 # XXX if suffix is None, should the current suffix be removed?
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400813 f = self._flavour
814 if f.sep in suffix or f.altsep and f.altsep in suffix:
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100815 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400816 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100817 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100818 name = self.name
819 if not name:
820 raise ValueError("%r has an empty name" % (self,))
821 old_suffix = self.suffix
822 if not old_suffix:
823 name = name + suffix
824 else:
825 name = name[:-len(old_suffix)] + suffix
826 return self._from_parsed_parts(self._drv, self._root,
827 self._parts[:-1] + [name])
828
829 def relative_to(self, *other):
830 """Return the relative path to another path identified by the passed
831 arguments. If the operation is not possible (because this is not
832 a subpath of the other path), raise ValueError.
833 """
834 # For the purpose of this method, drive and root are considered
835 # separate parts, i.e.:
836 # Path('c:/').relative_to('c:') gives Path('/')
837 # Path('c:/').relative_to('/') raise ValueError
838 if not other:
839 raise TypeError("need at least one argument")
840 parts = self._parts
841 drv = self._drv
842 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100843 if root:
844 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100845 else:
846 abs_parts = parts
847 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100848 if to_root:
849 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100850 else:
851 to_abs_parts = to_parts
852 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100853 cf = self._flavour.casefold_parts
854 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100855 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
856 raise ValueError("{!r} does not start with {!r}"
857 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100858 return self._from_parsed_parts('', root if n == 1 else '',
859 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100860
861 @property
862 def parts(self):
863 """An object providing sequence-like access to the
864 components in the filesystem path."""
865 # We cache the tuple to avoid building a new one each time .parts
866 # is accessed. XXX is this necessary?
867 try:
868 return self._pparts
869 except AttributeError:
870 self._pparts = tuple(self._parts)
871 return self._pparts
872
873 def joinpath(self, *args):
874 """Combine this path with one or several arguments, and return a
875 new path representing either a subpath (if all arguments are relative
876 paths) or a totally different path (if one of the arguments is
877 anchored).
878 """
879 return self._make_child(args)
880
881 def __truediv__(self, key):
882 return self._make_child((key,))
883
884 def __rtruediv__(self, key):
885 return self._from_parts([key] + self._parts)
886
887 @property
888 def parent(self):
889 """The logical parent of the path."""
890 drv = self._drv
891 root = self._root
892 parts = self._parts
893 if len(parts) == 1 and (drv or root):
894 return self
895 return self._from_parsed_parts(drv, root, parts[:-1])
896
897 @property
898 def parents(self):
899 """A sequence of this path's logical parents."""
900 return _PathParents(self)
901
902 def is_absolute(self):
903 """True if the path is absolute (has both a root and, if applicable,
904 a drive)."""
905 if not self._root:
906 return False
907 return not self._flavour.has_drv or bool(self._drv)
908
909 def is_reserved(self):
910 """Return True if the path contains one of the special names reserved
911 by the system, if any."""
912 return self._flavour.is_reserved(self._parts)
913
914 def match(self, path_pattern):
915 """
916 Return True if this path matches the given pattern.
917 """
918 cf = self._flavour.casefold
919 path_pattern = cf(path_pattern)
920 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
921 if not pat_parts:
922 raise ValueError("empty pattern")
923 if drv and drv != cf(self._drv):
924 return False
925 if root and root != cf(self._root):
926 return False
927 parts = self._cparts
928 if drv or root:
929 if len(pat_parts) != len(parts):
930 return False
931 pat_parts = pat_parts[1:]
932 elif len(pat_parts) > len(parts):
933 return False
934 for part, pat in zip(reversed(parts), reversed(pat_parts)):
935 if not fnmatch.fnmatchcase(part, pat):
936 return False
937 return True
938
Brett Cannon568be632016-06-10 12:20:49 -0700939# Can't subclass os.PathLike from PurePath and keep the constructor
940# optimizations in PurePath._parse_args().
941os.PathLike.register(PurePath)
942
Antoine Pitrou31119e42013-11-22 17:38:12 +0100943
944class PurePosixPath(PurePath):
945 _flavour = _posix_flavour
946 __slots__ = ()
947
948
949class PureWindowsPath(PurePath):
950 _flavour = _windows_flavour
951 __slots__ = ()
952
953
954# Filesystem-accessing classes
955
956
957class Path(PurePath):
958 __slots__ = (
959 '_accessor',
960 '_closed',
961 )
962
963 def __new__(cls, *args, **kwargs):
964 if cls is Path:
965 cls = WindowsPath if os.name == 'nt' else PosixPath
966 self = cls._from_parts(args, init=False)
967 if not self._flavour.is_supported:
968 raise NotImplementedError("cannot instantiate %r on your system"
969 % (cls.__name__,))
970 self._init()
971 return self
972
973 def _init(self,
974 # Private non-constructor arguments
975 template=None,
976 ):
977 self._closed = False
978 if template is not None:
979 self._accessor = template._accessor
980 else:
981 self._accessor = _normal_accessor
982
983 def _make_child_relpath(self, part):
984 # This is an optimization used for dir walking. `part` must be
985 # a single part relative to this path.
986 parts = self._parts + [part]
987 return self._from_parsed_parts(self._drv, self._root, parts)
988
989 def __enter__(self):
990 if self._closed:
991 self._raise_closed()
992 return self
993
994 def __exit__(self, t, v, tb):
995 self._closed = True
996
997 def _raise_closed(self):
998 raise ValueError("I/O operation on closed path")
999
1000 def _opener(self, name, flags, mode=0o666):
1001 # A stub for the opener argument to built-in open()
1002 return self._accessor.open(self, flags, mode)
1003
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001004 def _raw_open(self, flags, mode=0o777):
1005 """
1006 Open the file pointed by this path and return a file descriptor,
1007 as os.open() does.
1008 """
1009 if self._closed:
1010 self._raise_closed()
1011 return self._accessor.open(self, flags, mode)
1012
Antoine Pitrou31119e42013-11-22 17:38:12 +01001013 # Public API
1014
1015 @classmethod
1016 def cwd(cls):
1017 """Return a new path pointing to the current working directory
1018 (as returned by os.getcwd()).
1019 """
1020 return cls(os.getcwd())
1021
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001022 @classmethod
1023 def home(cls):
1024 """Return a new path pointing to the user's home directory (as
1025 returned by os.path.expanduser('~')).
1026 """
1027 return cls(cls()._flavour.gethomedir(None))
1028
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001029 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001030 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001031 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001032 """
1033 st = self.stat()
1034 try:
1035 other_st = other_path.stat()
1036 except AttributeError:
1037 other_st = os.stat(other_path)
1038 return os.path.samestat(st, other_st)
1039
Antoine Pitrou31119e42013-11-22 17:38:12 +01001040 def iterdir(self):
1041 """Iterate over the files in this directory. Does not yield any
1042 result for the special paths '.' and '..'.
1043 """
1044 if self._closed:
1045 self._raise_closed()
1046 for name in self._accessor.listdir(self):
1047 if name in {'.', '..'}:
1048 # Yielding a path object for these makes little sense
1049 continue
1050 yield self._make_child_relpath(name)
1051 if self._closed:
1052 self._raise_closed()
1053
1054 def glob(self, pattern):
1055 """Iterate over this subtree and yield all existing files (of any
1056 kind, including directories) matching the given pattern.
1057 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001058 if not pattern:
1059 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001060 pattern = self._flavour.casefold(pattern)
1061 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1062 if drv or root:
1063 raise NotImplementedError("Non-relative patterns are unsupported")
1064 selector = _make_selector(tuple(pattern_parts))
1065 for p in selector.select_from(self):
1066 yield p
1067
1068 def rglob(self, pattern):
1069 """Recursively yield all existing files (of any kind, including
1070 directories) matching the given pattern, anywhere in this subtree.
1071 """
1072 pattern = self._flavour.casefold(pattern)
1073 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1074 if drv or root:
1075 raise NotImplementedError("Non-relative patterns are unsupported")
1076 selector = _make_selector(("**",) + tuple(pattern_parts))
1077 for p in selector.select_from(self):
1078 yield p
1079
1080 def absolute(self):
1081 """Return an absolute version of this path. This function works
1082 even if the path doesn't point to anything.
1083
1084 No normalization is done, i.e. all '.' and '..' will be kept along.
1085 Use resolve() to get the canonical path to a file.
1086 """
1087 # XXX untested yet!
1088 if self._closed:
1089 self._raise_closed()
1090 if self.is_absolute():
1091 return self
1092 # FIXME this must defer to the specific flavour (and, under Windows,
1093 # use nt._getfullpathname())
1094 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1095 obj._init(template=self)
1096 return obj
1097
Steve Dower98eb3602016-11-09 12:58:17 -08001098 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001099 """
1100 Make the path absolute, resolving all symlinks on the way and also
1101 normalizing it (for example turning slashes into backslashes under
1102 Windows).
1103 """
1104 if self._closed:
1105 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001106 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001107 if s is None:
1108 # No symlink resolution => for consistency, raise an error if
1109 # the path doesn't exist or is forbidden
1110 self.stat()
1111 s = str(self.absolute())
1112 # Now we have no symlinks in the path, it's safe to normalize it.
1113 normed = self._flavour.pathmod.normpath(s)
1114 obj = self._from_parts((normed,), init=False)
1115 obj._init(template=self)
1116 return obj
1117
1118 def stat(self):
1119 """
1120 Return the result of the stat() system call on this path, like
1121 os.stat() does.
1122 """
1123 return self._accessor.stat(self)
1124
1125 def owner(self):
1126 """
1127 Return the login name of the file owner.
1128 """
1129 import pwd
1130 return pwd.getpwuid(self.stat().st_uid).pw_name
1131
1132 def group(self):
1133 """
1134 Return the group name of the file gid.
1135 """
1136 import grp
1137 return grp.getgrgid(self.stat().st_gid).gr_name
1138
Antoine Pitrou31119e42013-11-22 17:38:12 +01001139 def open(self, mode='r', buffering=-1, encoding=None,
1140 errors=None, newline=None):
1141 """
1142 Open the file pointed by this path and return a file object, as
1143 the built-in open() function does.
1144 """
1145 if self._closed:
1146 self._raise_closed()
Serhiy Storchaka62a99512017-03-25 13:42:11 +02001147 return io.open(self, mode, buffering, encoding, errors, newline,
Antoine Pitrou31119e42013-11-22 17:38:12 +01001148 opener=self._opener)
1149
Georg Brandlea683982014-10-01 19:12:33 +02001150 def read_bytes(self):
1151 """
1152 Open the file in bytes mode, read it, and close the file.
1153 """
1154 with self.open(mode='rb') as f:
1155 return f.read()
1156
1157 def read_text(self, encoding=None, errors=None):
1158 """
1159 Open the file in text mode, read it, and close the file.
1160 """
1161 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1162 return f.read()
1163
1164 def write_bytes(self, data):
1165 """
1166 Open the file in bytes mode, write to it, and close the file.
1167 """
1168 # type-check for the buffer interface before truncating the file
1169 view = memoryview(data)
1170 with self.open(mode='wb') as f:
1171 return f.write(view)
1172
1173 def write_text(self, data, encoding=None, errors=None):
1174 """
1175 Open the file in text mode, write to it, and close the file.
1176 """
1177 if not isinstance(data, str):
1178 raise TypeError('data must be str, not %s' %
1179 data.__class__.__name__)
1180 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1181 return f.write(data)
1182
Antoine Pitrou31119e42013-11-22 17:38:12 +01001183 def touch(self, mode=0o666, exist_ok=True):
1184 """
1185 Create this file with the given access mode, if it doesn't exist.
1186 """
1187 if self._closed:
1188 self._raise_closed()
1189 if exist_ok:
1190 # First try to bump modification time
1191 # Implementation note: GNU touch uses the UTIME_NOW option of
1192 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001193 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001194 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001195 except OSError:
1196 # Avoid exception chaining
1197 pass
1198 else:
1199 return
1200 flags = os.O_CREAT | os.O_WRONLY
1201 if not exist_ok:
1202 flags |= os.O_EXCL
1203 fd = self._raw_open(flags, mode)
1204 os.close(fd)
1205
Barry Warsaw7c549c42014-08-05 11:28:12 -04001206 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001207 """
1208 Create a new directory at this given path.
1209 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001210 if self._closed:
1211 self._raise_closed()
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001212 try:
1213 self._accessor.mkdir(self, mode)
1214 except FileNotFoundError:
1215 if not parents or self.parent == self:
1216 raise
Armin Rigo22a594a2017-04-13 20:08:15 +02001217 self.parent.mkdir(parents=True, exist_ok=True)
1218 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchakaaf7b9ec2017-03-24 20:51:53 +02001219 except OSError:
1220 # Cannot rely on checking for EEXIST, since the operating system
1221 # could give priority to other errors like EACCES or EROFS
1222 if not exist_ok or not self.is_dir():
1223 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001224
1225 def chmod(self, mode):
1226 """
1227 Change the permissions of the path, like os.chmod().
1228 """
1229 if self._closed:
1230 self._raise_closed()
1231 self._accessor.chmod(self, mode)
1232
1233 def lchmod(self, mode):
1234 """
1235 Like chmod(), except if the path points to a symlink, the symlink's
1236 permissions are changed, rather than its target's.
1237 """
1238 if self._closed:
1239 self._raise_closed()
1240 self._accessor.lchmod(self, mode)
1241
1242 def unlink(self):
1243 """
1244 Remove this file or link.
1245 If the path is a directory, use rmdir() instead.
1246 """
1247 if self._closed:
1248 self._raise_closed()
1249 self._accessor.unlink(self)
1250
1251 def rmdir(self):
1252 """
1253 Remove this directory. The directory must be empty.
1254 """
1255 if self._closed:
1256 self._raise_closed()
1257 self._accessor.rmdir(self)
1258
1259 def lstat(self):
1260 """
1261 Like stat(), except if the path points to a symlink, the symlink's
1262 status information is returned, rather than its target's.
1263 """
1264 if self._closed:
1265 self._raise_closed()
1266 return self._accessor.lstat(self)
1267
1268 def rename(self, target):
1269 """
1270 Rename this path to the given path.
1271 """
1272 if self._closed:
1273 self._raise_closed()
1274 self._accessor.rename(self, target)
1275
1276 def replace(self, target):
1277 """
1278 Rename this path to the given path, clobbering the existing
1279 destination if it exists.
1280 """
1281 if self._closed:
1282 self._raise_closed()
1283 self._accessor.replace(self, target)
1284
1285 def symlink_to(self, target, target_is_directory=False):
1286 """
1287 Make this path a symlink pointing to the given path.
1288 Note the order of arguments (self, target) is the reverse of os.symlink's.
1289 """
1290 if self._closed:
1291 self._raise_closed()
1292 self._accessor.symlink(target, self, target_is_directory)
1293
1294 # Convenience functions for querying the stat results
1295
1296 def exists(self):
1297 """
1298 Whether this path exists.
1299 """
1300 try:
1301 self.stat()
1302 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001303 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001304 raise
1305 return False
1306 return True
1307
1308 def is_dir(self):
1309 """
1310 Whether this path is a directory.
1311 """
1312 try:
1313 return S_ISDIR(self.stat().st_mode)
1314 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001315 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001316 raise
1317 # Path doesn't exist or is a broken symlink
1318 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1319 return False
1320
1321 def is_file(self):
1322 """
1323 Whether this path is a regular file (also True for symlinks pointing
1324 to regular files).
1325 """
1326 try:
1327 return S_ISREG(self.stat().st_mode)
1328 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001329 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001330 raise
1331 # Path doesn't exist or is a broken symlink
1332 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1333 return False
1334
1335 def is_symlink(self):
1336 """
1337 Whether this path is a symbolic link.
1338 """
1339 try:
1340 return S_ISLNK(self.lstat().st_mode)
1341 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001342 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001343 raise
1344 # Path doesn't exist
1345 return False
1346
1347 def is_block_device(self):
1348 """
1349 Whether this path is a block device.
1350 """
1351 try:
1352 return S_ISBLK(self.stat().st_mode)
1353 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001354 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001355 raise
1356 # Path doesn't exist or is a broken symlink
1357 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1358 return False
1359
1360 def is_char_device(self):
1361 """
1362 Whether this path is a character device.
1363 """
1364 try:
1365 return S_ISCHR(self.stat().st_mode)
1366 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001367 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001368 raise
1369 # Path doesn't exist or is a broken symlink
1370 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1371 return False
1372
1373 def is_fifo(self):
1374 """
1375 Whether this path is a FIFO.
1376 """
1377 try:
1378 return S_ISFIFO(self.stat().st_mode)
1379 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001380 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001381 raise
1382 # Path doesn't exist or is a broken symlink
1383 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1384 return False
1385
1386 def is_socket(self):
1387 """
1388 Whether this path is a socket.
1389 """
1390 try:
1391 return S_ISSOCK(self.stat().st_mode)
1392 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001393 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001394 raise
1395 # Path doesn't exist or is a broken symlink
1396 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1397 return False
1398
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001399 def expanduser(self):
1400 """ Return a new path with expanded ~ and ~user constructs
1401 (as returned by os.path.expanduser)
1402 """
1403 if (not (self._drv or self._root) and
1404 self._parts and self._parts[0][:1] == '~'):
1405 homedir = self._flavour.gethomedir(self._parts[0][1:])
1406 return self._from_parts([homedir] + self._parts[1:])
1407
1408 return self
1409
Antoine Pitrou31119e42013-11-22 17:38:12 +01001410
1411class PosixPath(Path, PurePosixPath):
1412 __slots__ = ()
1413
1414class WindowsPath(Path, PureWindowsPath):
1415 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001416
1417 def owner(self):
1418 raise NotImplementedError("Path.owner() is unsupported on this system")
1419
1420 def group(self):
1421 raise NotImplementedError("Path.group() is unsupported on this system")