blob: 48b566d9295f8fecbf1a2afbd1e6ee47ec1fedbd [file] [log] [blame]
Antoine Pitrou31119e42013-11-22 17:38:12 +01001import fnmatch
2import functools
3import io
4import ntpath
5import os
6import posixpath
7import re
8import sys
Antoine Pitrou069a5e12013-12-03 09:41:35 +01009from collections import Sequence
Antoine Pitrou31119e42013-11-22 17:38:12 +010010from contextlib import contextmanager
Antoine Pitrou2b2852b2014-10-30 23:14:03 +010011from errno import EINVAL, ENOENT, ENOTDIR
Antoine Pitrou31119e42013-11-22 17:38:12 +010012from operator import attrgetter
13from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
Antoine Pitrou069a5e12013-12-03 09:41:35 +010014from urllib.parse import quote_from_bytes as urlquote_from_bytes
Antoine Pitrou31119e42013-11-22 17:38:12 +010015
16
17supports_symlinks = True
Antoine Pitroudb118f52014-11-19 00:32:08 +010018if os.name == 'nt':
Antoine Pitrou31119e42013-11-22 17:38:12 +010019 import nt
Antoine Pitrou31119e42013-11-22 17:38:12 +010020 if sys.getwindowsversion()[:2] >= (6, 0):
21 from nt import _getfinalpathname
22 else:
23 supports_symlinks = False
24 _getfinalpathname = None
Antoine Pitroudb118f52014-11-19 00:32:08 +010025else:
26 nt = None
Antoine Pitrou31119e42013-11-22 17:38:12 +010027
28
29__all__ = [
30 "PurePath", "PurePosixPath", "PureWindowsPath",
31 "Path", "PosixPath", "WindowsPath",
32 ]
33
34#
35# Internals
36#
37
38def _is_wildcard_pattern(pat):
39 # Whether this pattern needs actual matching using fnmatch, or can
40 # be looked up directly as a file.
41 return "*" in pat or "?" in pat or "[" in pat
42
43
44class _Flavour(object):
45 """A flavour implements a particular (platform-specific) set of path
46 semantics."""
47
48 def __init__(self):
49 self.join = self.sep.join
50
51 def parse_parts(self, parts):
52 parsed = []
53 sep = self.sep
54 altsep = self.altsep
55 drv = root = ''
56 it = reversed(parts)
57 for part in it:
58 if not part:
59 continue
60 if altsep:
61 part = part.replace(altsep, sep)
62 drv, root, rel = self.splitroot(part)
63 if sep in rel:
64 for x in reversed(rel.split(sep)):
65 if x and x != '.':
66 parsed.append(sys.intern(x))
67 else:
68 if rel and rel != '.':
69 parsed.append(sys.intern(rel))
70 if drv or root:
71 if not drv:
72 # If no drive is present, try to find one in the previous
73 # parts. This makes the result of parsing e.g.
74 # ("C:", "/", "a") reasonably intuitive.
75 for part in it:
Antoine Pitrou57fffd62015-02-15 18:03:59 +010076 if not part:
77 continue
78 if altsep:
79 part = part.replace(altsep, sep)
Antoine Pitrou31119e42013-11-22 17:38:12 +010080 drv = self.splitroot(part)[0]
81 if drv:
82 break
83 break
84 if drv or root:
85 parsed.append(drv + root)
86 parsed.reverse()
87 return drv, root, parsed
88
89 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
90 """
91 Join the two paths represented by the respective
92 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple.
93 """
94 if root2:
Serhiy Storchakaa9939022013-12-06 17:14:12 +020095 if not drv2 and drv:
96 return drv, root2, [drv + root2] + parts2[1:]
97 elif drv2:
98 if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
99 # Same drive => second path is relative to the first
100 return drv, root, parts + parts2[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100101 else:
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200102 # Second path is non-anchored (common case)
103 return drv, root, parts + parts2
104 return drv2, root2, parts2
Antoine Pitrou31119e42013-11-22 17:38:12 +0100105
106
107class _WindowsFlavour(_Flavour):
108 # Reference for Windows paths can be found at
109 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
110
111 sep = '\\'
112 altsep = '/'
113 has_drv = True
114 pathmod = ntpath
115
Antoine Pitroudb118f52014-11-19 00:32:08 +0100116 is_supported = (os.name == 'nt')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100117
118 drive_letters = (
119 set(chr(x) for x in range(ord('a'), ord('z') + 1)) |
120 set(chr(x) for x in range(ord('A'), ord('Z') + 1))
121 )
122 ext_namespace_prefix = '\\\\?\\'
123
124 reserved_names = (
125 {'CON', 'PRN', 'AUX', 'NUL'} |
126 {'COM%d' % i for i in range(1, 10)} |
127 {'LPT%d' % i for i in range(1, 10)}
128 )
129
130 # Interesting findings about extended paths:
131 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
132 # but '\\?\c:/a' is not
133 # - extended paths are always absolute; "relative" extended paths will
134 # fail.
135
136 def splitroot(self, part, sep=sep):
137 first = part[0:1]
138 second = part[1:2]
139 if (second == sep and first == sep):
140 # XXX extended paths should also disable the collapsing of "."
141 # components (according to MSDN docs).
142 prefix, part = self._split_extended_path(part)
143 first = part[0:1]
144 second = part[1:2]
145 else:
146 prefix = ''
147 third = part[2:3]
148 if (second == sep and first == sep and third != sep):
149 # is a UNC path:
150 # vvvvvvvvvvvvvvvvvvvvv root
151 # \\machine\mountpoint\directory\etc\...
152 # directory ^^^^^^^^^^^^^^
153 index = part.find(sep, 2)
154 if index != -1:
155 index2 = part.find(sep, index + 1)
156 # a UNC path can't have two slashes in a row
157 # (after the initial two)
158 if index2 != index + 1:
159 if index2 == -1:
160 index2 = len(part)
161 if prefix:
162 return prefix + part[1:index2], sep, part[index2+1:]
163 else:
164 return part[:index2], sep, part[index2+1:]
165 drv = root = ''
166 if second == ':' and first in self.drive_letters:
167 drv = part[:2]
168 part = part[2:]
169 first = third
170 if first == sep:
171 root = first
172 part = part.lstrip(sep)
173 return prefix + drv, root, part
174
175 def casefold(self, s):
176 return s.lower()
177
178 def casefold_parts(self, parts):
179 return [p.lower() for p in parts]
180
Steve Dower98eb3602016-11-09 12:58:17 -0800181 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100182 s = str(path)
183 if not s:
184 return os.getcwd()
Steve Dower98eb3602016-11-09 12:58:17 -0800185 previous_s = None
Antoine Pitrou31119e42013-11-22 17:38:12 +0100186 if _getfinalpathname is not None:
Steve Dower98eb3602016-11-09 12:58:17 -0800187 if strict:
188 return self._ext_to_normal(_getfinalpathname(s))
189 else:
Antoine Pietriceabf9a2017-06-07 19:18:56 +0200190 tail_parts = [] # End of the path after the first one not found
Steve Dower98eb3602016-11-09 12:58:17 -0800191 while True:
192 try:
193 s = self._ext_to_normal(_getfinalpathname(s))
194 except FileNotFoundError:
195 previous_s = s
Antoine Pietriceabf9a2017-06-07 19:18:56 +0200196 s, tail = os.path.split(s)
197 tail_parts.append(tail)
Steve Dower4b1e98b2016-12-28 16:02:59 -0800198 if previous_s == s:
199 return path
Steve Dower98eb3602016-11-09 12:58:17 -0800200 else:
Antoine Pietriceabf9a2017-06-07 19:18:56 +0200201 return os.path.join(s, *reversed(tail_parts))
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:
Antoine Pietriceabf9a2017-06-07 19:18:56 +0200332 if e.errno != EINVAL and strict:
333 raise
334 # Not a symlink, or non-strict mode. We just leave the path
335 # untouched.
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100336 path = newpath
337 else:
338 seen[newpath] = None # not resolved symlink
339 path = _resolve(path, target)
340 seen[newpath] = path # resolved symlink
341
342 return path
343 # NOTE: according to POSIX, getcwd() cannot contain path components
344 # which are symlinks.
345 base = '' if path.is_absolute() else os.getcwd()
346 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100347
348 def is_reserved(self, parts):
349 return False
350
351 def make_uri(self, path):
352 # We represent the path using the local filesystem encoding,
353 # for portability to other applications.
354 bpath = bytes(path)
355 return 'file://' + urlquote_from_bytes(bpath)
356
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100357 def gethomedir(self, username):
358 if not username:
359 try:
360 return os.environ['HOME']
361 except KeyError:
362 import pwd
363 return pwd.getpwuid(os.getuid()).pw_dir
364 else:
365 import pwd
366 try:
367 return pwd.getpwnam(username).pw_dir
368 except KeyError:
369 raise RuntimeError("Can't determine home directory "
370 "for %r" % username)
371
Antoine Pitrou31119e42013-11-22 17:38:12 +0100372
373_windows_flavour = _WindowsFlavour()
374_posix_flavour = _PosixFlavour()
375
376
377class _Accessor:
378 """An accessor implements a particular (system-specific or not) way of
379 accessing paths on the filesystem."""
380
381
382class _NormalAccessor(_Accessor):
383
384 def _wrap_strfunc(strfunc):
385 @functools.wraps(strfunc)
386 def wrapped(pathobj, *args):
387 return strfunc(str(pathobj), *args)
388 return staticmethod(wrapped)
389
390 def _wrap_binary_strfunc(strfunc):
391 @functools.wraps(strfunc)
392 def wrapped(pathobjA, pathobjB, *args):
393 return strfunc(str(pathobjA), str(pathobjB), *args)
394 return staticmethod(wrapped)
395
396 stat = _wrap_strfunc(os.stat)
397
398 lstat = _wrap_strfunc(os.lstat)
399
400 open = _wrap_strfunc(os.open)
401
402 listdir = _wrap_strfunc(os.listdir)
403
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300404 scandir = _wrap_strfunc(os.scandir)
405
Antoine Pitrou31119e42013-11-22 17:38:12 +0100406 chmod = _wrap_strfunc(os.chmod)
407
408 if hasattr(os, "lchmod"):
409 lchmod = _wrap_strfunc(os.lchmod)
410 else:
411 def lchmod(self, pathobj, mode):
412 raise NotImplementedError("lchmod() not available on this system")
413
414 mkdir = _wrap_strfunc(os.mkdir)
415
416 unlink = _wrap_strfunc(os.unlink)
417
418 rmdir = _wrap_strfunc(os.rmdir)
419
420 rename = _wrap_binary_strfunc(os.rename)
421
422 replace = _wrap_binary_strfunc(os.replace)
423
424 if nt:
425 if supports_symlinks:
426 symlink = _wrap_binary_strfunc(os.symlink)
427 else:
428 def symlink(a, b, target_is_directory):
429 raise NotImplementedError("symlink() not available on this system")
430 else:
431 # Under POSIX, os.symlink() takes two args
432 @staticmethod
433 def symlink(a, b, target_is_directory):
434 return os.symlink(str(a), str(b))
435
436 utime = _wrap_strfunc(os.utime)
437
438 # Helper for resolve()
439 def readlink(self, path):
440 return os.readlink(path)
441
442
443_normal_accessor = _NormalAccessor()
444
445
446#
447# Globbing helpers
448#
449
Antoine Pitrou31119e42013-11-22 17:38:12 +0100450def _make_selector(pattern_parts):
451 pat = pattern_parts[0]
452 child_parts = pattern_parts[1:]
453 if pat == '**':
454 cls = _RecursiveWildcardSelector
455 elif '**' in pat:
456 raise ValueError("Invalid pattern: '**' can only be an entire path component")
457 elif _is_wildcard_pattern(pat):
458 cls = _WildcardSelector
459 else:
460 cls = _PreciseSelector
461 return cls(pat, child_parts)
462
463if hasattr(functools, "lru_cache"):
464 _make_selector = functools.lru_cache()(_make_selector)
465
466
467class _Selector:
468 """A selector matches a specific glob pattern part against the children
469 of a given path."""
470
471 def __init__(self, child_parts):
472 self.child_parts = child_parts
473 if child_parts:
474 self.successor = _make_selector(child_parts)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300475 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100476 else:
477 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300478 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100479
480 def select_from(self, parent_path):
481 """Iterate over all child paths of `parent_path` matched by this
482 selector. This can contain parent_path itself."""
483 path_cls = type(parent_path)
484 is_dir = path_cls.is_dir
485 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300486 scandir = parent_path._accessor.scandir
487 if not is_dir(parent_path):
488 return iter([])
489 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100490
491
492class _TerminatingSelector:
493
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300494 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100495 yield parent_path
496
497
498class _PreciseSelector(_Selector):
499
500 def __init__(self, name, child_parts):
501 self.name = name
502 _Selector.__init__(self, child_parts)
503
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300504 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800505 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800506 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300507 if (is_dir if self.dironly else exists)(path):
508 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800509 yield p
510 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100511 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100512
513
514class _WildcardSelector(_Selector):
515
516 def __init__(self, pat, child_parts):
517 self.pat = re.compile(fnmatch.translate(pat))
518 _Selector.__init__(self, child_parts)
519
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300520 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800521 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800522 cf = parent_path._flavour.casefold
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300523 entries = list(scandir(parent_path))
524 for entry in entries:
525 if not self.dironly or entry.is_dir():
526 name = entry.name
527 casefolded = cf(name)
528 if self.pat.match(casefolded):
529 path = parent_path._make_child_relpath(name)
530 for p in self.successor._select_from(path, is_dir, exists, scandir):
531 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800532 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100533 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800534
Antoine Pitrou31119e42013-11-22 17:38:12 +0100535
536
537class _RecursiveWildcardSelector(_Selector):
538
539 def __init__(self, pat, child_parts):
540 _Selector.__init__(self, child_parts)
541
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300542 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100543 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800544 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300545 entries = list(scandir(parent_path))
546 for entry in entries:
547 if entry.is_dir() and not entry.is_symlink():
548 path = parent_path._make_child_relpath(entry.name)
549 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800550 yield p
551 except PermissionError:
552 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100553
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300554 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800555 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300556 yielded = set()
557 try:
558 successor_select = self.successor._select_from
559 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
560 for p in successor_select(starting_point, is_dir, exists, scandir):
561 if p not in yielded:
562 yield p
563 yielded.add(p)
564 finally:
565 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800566 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100567 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100568
569
570#
571# Public API
572#
573
574class _PathParents(Sequence):
575 """This object provides sequence-like access to the logical ancestors
576 of a path. Don't try to construct it yourself."""
577 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
578
579 def __init__(self, path):
580 # We don't store the instance to avoid reference cycles
581 self._pathcls = type(path)
582 self._drv = path._drv
583 self._root = path._root
584 self._parts = path._parts
585
586 def __len__(self):
587 if self._drv or self._root:
588 return len(self._parts) - 1
589 else:
590 return len(self._parts)
591
592 def __getitem__(self, idx):
593 if idx < 0 or idx >= len(self):
594 raise IndexError(idx)
595 return self._pathcls._from_parsed_parts(self._drv, self._root,
596 self._parts[:-idx - 1])
597
598 def __repr__(self):
599 return "<{}.parents>".format(self._pathcls.__name__)
600
601
602class PurePath(object):
603 """PurePath represents a filesystem path and offers operations which
604 don't imply any actual filesystem I/O. Depending on your system,
605 instantiating a PurePath will return either a PurePosixPath or a
606 PureWindowsPath object. You can also instantiate either of these classes
607 directly, regardless of your system.
608 """
609 __slots__ = (
610 '_drv', '_root', '_parts',
611 '_str', '_hash', '_pparts', '_cached_cparts',
612 )
613
614 def __new__(cls, *args):
615 """Construct a PurePath from one or several strings and or existing
616 PurePath objects. The strings and path objects are combined so as
617 to yield a canonicalized path, which is incorporated into the
618 new PurePath object.
619 """
620 if cls is PurePath:
621 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
622 return cls._from_parts(args)
623
624 def __reduce__(self):
625 # Using the parts tuple helps share interned path parts
626 # when pickling related paths.
627 return (self.__class__, tuple(self._parts))
628
629 @classmethod
630 def _parse_args(cls, args):
631 # This is useful when you don't want to create an instance, just
632 # canonicalize some constructor arguments.
633 parts = []
634 for a in args:
635 if isinstance(a, PurePath):
636 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100637 else:
Brett Cannon568be632016-06-10 12:20:49 -0700638 a = os.fspath(a)
639 if isinstance(a, str):
640 # Force-cast str subclasses to str (issue #21127)
641 parts.append(str(a))
642 else:
643 raise TypeError(
644 "argument should be a str object or an os.PathLike "
645 "object returning str, not %r"
646 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100647 return cls._flavour.parse_parts(parts)
648
649 @classmethod
650 def _from_parts(cls, args, init=True):
651 # We need to call _parse_args on the instance, so as to get the
652 # right flavour.
653 self = object.__new__(cls)
654 drv, root, parts = self._parse_args(args)
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 _from_parsed_parts(cls, drv, root, parts, init=True):
664 self = object.__new__(cls)
665 self._drv = drv
666 self._root = root
667 self._parts = parts
668 if init:
669 self._init()
670 return self
671
672 @classmethod
673 def _format_parsed_parts(cls, drv, root, parts):
674 if drv or root:
675 return drv + root + cls._flavour.join(parts[1:])
676 else:
677 return cls._flavour.join(parts)
678
679 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000680 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100681 pass
682
683 def _make_child(self, args):
684 drv, root, parts = self._parse_args(args)
685 drv, root, parts = self._flavour.join_parsed_parts(
686 self._drv, self._root, self._parts, drv, root, parts)
687 return self._from_parsed_parts(drv, root, parts)
688
689 def __str__(self):
690 """Return the string representation of the path, suitable for
691 passing to system calls."""
692 try:
693 return self._str
694 except AttributeError:
695 self._str = self._format_parsed_parts(self._drv, self._root,
696 self._parts) or '.'
697 return self._str
698
Brett Cannon568be632016-06-10 12:20:49 -0700699 def __fspath__(self):
700 return str(self)
701
Antoine Pitrou31119e42013-11-22 17:38:12 +0100702 def as_posix(self):
703 """Return the string representation of the path with forward (/)
704 slashes."""
705 f = self._flavour
706 return str(self).replace(f.sep, '/')
707
708 def __bytes__(self):
709 """Return the bytes representation of the path. This is only
710 recommended to use under Unix."""
711 return os.fsencode(str(self))
712
713 def __repr__(self):
714 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
715
716 def as_uri(self):
717 """Return the path as a 'file' URI."""
718 if not self.is_absolute():
719 raise ValueError("relative path can't be expressed as a file URI")
720 return self._flavour.make_uri(self)
721
722 @property
723 def _cparts(self):
724 # Cached casefolded parts, for hashing and comparison
725 try:
726 return self._cached_cparts
727 except AttributeError:
728 self._cached_cparts = self._flavour.casefold_parts(self._parts)
729 return self._cached_cparts
730
731 def __eq__(self, other):
732 if not isinstance(other, PurePath):
733 return NotImplemented
734 return self._cparts == other._cparts and self._flavour is other._flavour
735
Antoine Pitrou31119e42013-11-22 17:38:12 +0100736 def __hash__(self):
737 try:
738 return self._hash
739 except AttributeError:
740 self._hash = hash(tuple(self._cparts))
741 return self._hash
742
743 def __lt__(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 __le__(self, other):
749 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
750 return NotImplemented
751 return self._cparts <= other._cparts
752
753 def __gt__(self, other):
754 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
755 return NotImplemented
756 return self._cparts > other._cparts
757
758 def __ge__(self, other):
759 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
760 return NotImplemented
761 return self._cparts >= other._cparts
762
763 drive = property(attrgetter('_drv'),
764 doc="""The drive prefix (letter or UNC path), if any.""")
765
766 root = property(attrgetter('_root'),
767 doc="""The root of the path, if any.""")
768
769 @property
770 def anchor(self):
771 """The concatenation of the drive and root, or ''."""
772 anchor = self._drv + self._root
773 return anchor
774
775 @property
776 def name(self):
777 """The final path component, if any."""
778 parts = self._parts
779 if len(parts) == (1 if (self._drv or self._root) else 0):
780 return ''
781 return parts[-1]
782
783 @property
784 def suffix(self):
785 """The final component's last suffix, if any."""
786 name = self.name
787 i = name.rfind('.')
788 if 0 < i < len(name) - 1:
789 return name[i:]
790 else:
791 return ''
792
793 @property
794 def suffixes(self):
795 """A list of the final component's suffixes, if any."""
796 name = self.name
797 if name.endswith('.'):
798 return []
799 name = name.lstrip('.')
800 return ['.' + suffix for suffix in name.split('.')[1:]]
801
802 @property
803 def stem(self):
804 """The final path component, minus its last suffix."""
805 name = self.name
806 i = name.rfind('.')
807 if 0 < i < len(name) - 1:
808 return name[:i]
809 else:
810 return name
811
812 def with_name(self, name):
813 """Return a new path with the file name changed."""
814 if not self.name:
815 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400816 drv, root, parts = self._flavour.parse_parts((name,))
817 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
818 or drv or root or len(parts) != 1):
819 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100820 return self._from_parsed_parts(self._drv, self._root,
821 self._parts[:-1] + [name])
822
823 def with_suffix(self, suffix):
824 """Return a new path with the file suffix changed (or added, if none)."""
825 # XXX if suffix is None, should the current suffix be removed?
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400826 f = self._flavour
827 if f.sep in suffix or f.altsep and f.altsep in suffix:
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100828 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400829 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100830 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100831 name = self.name
832 if not name:
833 raise ValueError("%r has an empty name" % (self,))
834 old_suffix = self.suffix
835 if not old_suffix:
836 name = name + suffix
837 else:
838 name = name[:-len(old_suffix)] + suffix
839 return self._from_parsed_parts(self._drv, self._root,
840 self._parts[:-1] + [name])
841
842 def relative_to(self, *other):
843 """Return the relative path to another path identified by the passed
844 arguments. If the operation is not possible (because this is not
845 a subpath of the other path), raise ValueError.
846 """
847 # For the purpose of this method, drive and root are considered
848 # separate parts, i.e.:
849 # Path('c:/').relative_to('c:') gives Path('/')
850 # Path('c:/').relative_to('/') raise ValueError
851 if not other:
852 raise TypeError("need at least one argument")
853 parts = self._parts
854 drv = self._drv
855 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100856 if root:
857 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100858 else:
859 abs_parts = parts
860 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100861 if to_root:
862 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100863 else:
864 to_abs_parts = to_parts
865 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100866 cf = self._flavour.casefold_parts
867 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100868 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
869 raise ValueError("{!r} does not start with {!r}"
870 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100871 return self._from_parsed_parts('', root if n == 1 else '',
872 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100873
874 @property
875 def parts(self):
876 """An object providing sequence-like access to the
877 components in the filesystem path."""
878 # We cache the tuple to avoid building a new one each time .parts
879 # is accessed. XXX is this necessary?
880 try:
881 return self._pparts
882 except AttributeError:
883 self._pparts = tuple(self._parts)
884 return self._pparts
885
886 def joinpath(self, *args):
887 """Combine this path with one or several arguments, and return a
888 new path representing either a subpath (if all arguments are relative
889 paths) or a totally different path (if one of the arguments is
890 anchored).
891 """
892 return self._make_child(args)
893
894 def __truediv__(self, key):
895 return self._make_child((key,))
896
897 def __rtruediv__(self, key):
898 return self._from_parts([key] + self._parts)
899
900 @property
901 def parent(self):
902 """The logical parent of the path."""
903 drv = self._drv
904 root = self._root
905 parts = self._parts
906 if len(parts) == 1 and (drv or root):
907 return self
908 return self._from_parsed_parts(drv, root, parts[:-1])
909
910 @property
911 def parents(self):
912 """A sequence of this path's logical parents."""
913 return _PathParents(self)
914
915 def is_absolute(self):
916 """True if the path is absolute (has both a root and, if applicable,
917 a drive)."""
918 if not self._root:
919 return False
920 return not self._flavour.has_drv or bool(self._drv)
921
922 def is_reserved(self):
923 """Return True if the path contains one of the special names reserved
924 by the system, if any."""
925 return self._flavour.is_reserved(self._parts)
926
927 def match(self, path_pattern):
928 """
929 Return True if this path matches the given pattern.
930 """
931 cf = self._flavour.casefold
932 path_pattern = cf(path_pattern)
933 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
934 if not pat_parts:
935 raise ValueError("empty pattern")
936 if drv and drv != cf(self._drv):
937 return False
938 if root and root != cf(self._root):
939 return False
940 parts = self._cparts
941 if drv or root:
942 if len(pat_parts) != len(parts):
943 return False
944 pat_parts = pat_parts[1:]
945 elif len(pat_parts) > len(parts):
946 return False
947 for part, pat in zip(reversed(parts), reversed(pat_parts)):
948 if not fnmatch.fnmatchcase(part, pat):
949 return False
950 return True
951
Brett Cannon568be632016-06-10 12:20:49 -0700952# Can't subclass os.PathLike from PurePath and keep the constructor
953# optimizations in PurePath._parse_args().
954os.PathLike.register(PurePath)
955
Antoine Pitrou31119e42013-11-22 17:38:12 +0100956
957class PurePosixPath(PurePath):
958 _flavour = _posix_flavour
959 __slots__ = ()
960
961
962class PureWindowsPath(PurePath):
963 _flavour = _windows_flavour
964 __slots__ = ()
965
966
967# Filesystem-accessing classes
968
969
970class Path(PurePath):
971 __slots__ = (
972 '_accessor',
973 '_closed',
974 )
975
976 def __new__(cls, *args, **kwargs):
977 if cls is Path:
978 cls = WindowsPath if os.name == 'nt' else PosixPath
979 self = cls._from_parts(args, init=False)
980 if not self._flavour.is_supported:
981 raise NotImplementedError("cannot instantiate %r on your system"
982 % (cls.__name__,))
983 self._init()
984 return self
985
986 def _init(self,
987 # Private non-constructor arguments
988 template=None,
989 ):
990 self._closed = False
991 if template is not None:
992 self._accessor = template._accessor
993 else:
994 self._accessor = _normal_accessor
995
996 def _make_child_relpath(self, part):
997 # This is an optimization used for dir walking. `part` must be
998 # a single part relative to this path.
999 parts = self._parts + [part]
1000 return self._from_parsed_parts(self._drv, self._root, parts)
1001
1002 def __enter__(self):
1003 if self._closed:
1004 self._raise_closed()
1005 return self
1006
1007 def __exit__(self, t, v, tb):
1008 self._closed = True
1009
1010 def _raise_closed(self):
1011 raise ValueError("I/O operation on closed path")
1012
1013 def _opener(self, name, flags, mode=0o666):
1014 # A stub for the opener argument to built-in open()
1015 return self._accessor.open(self, flags, mode)
1016
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001017 def _raw_open(self, flags, mode=0o777):
1018 """
1019 Open the file pointed by this path and return a file descriptor,
1020 as os.open() does.
1021 """
1022 if self._closed:
1023 self._raise_closed()
1024 return self._accessor.open(self, flags, mode)
1025
Antoine Pitrou31119e42013-11-22 17:38:12 +01001026 # Public API
1027
1028 @classmethod
1029 def cwd(cls):
1030 """Return a new path pointing to the current working directory
1031 (as returned by os.getcwd()).
1032 """
1033 return cls(os.getcwd())
1034
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001035 @classmethod
1036 def home(cls):
1037 """Return a new path pointing to the user's home directory (as
1038 returned by os.path.expanduser('~')).
1039 """
1040 return cls(cls()._flavour.gethomedir(None))
1041
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001042 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001043 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001044 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001045 """
1046 st = self.stat()
1047 try:
1048 other_st = other_path.stat()
1049 except AttributeError:
1050 other_st = os.stat(other_path)
1051 return os.path.samestat(st, other_st)
1052
Antoine Pitrou31119e42013-11-22 17:38:12 +01001053 def iterdir(self):
1054 """Iterate over the files in this directory. Does not yield any
1055 result for the special paths '.' and '..'.
1056 """
1057 if self._closed:
1058 self._raise_closed()
1059 for name in self._accessor.listdir(self):
1060 if name in {'.', '..'}:
1061 # Yielding a path object for these makes little sense
1062 continue
1063 yield self._make_child_relpath(name)
1064 if self._closed:
1065 self._raise_closed()
1066
1067 def glob(self, pattern):
1068 """Iterate over this subtree and yield all existing files (of any
1069 kind, including directories) matching the given pattern.
1070 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001071 if not pattern:
1072 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001073 pattern = self._flavour.casefold(pattern)
1074 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1075 if drv or root:
1076 raise NotImplementedError("Non-relative patterns are unsupported")
1077 selector = _make_selector(tuple(pattern_parts))
1078 for p in selector.select_from(self):
1079 yield p
1080
1081 def rglob(self, pattern):
1082 """Recursively yield all existing files (of any kind, including
1083 directories) matching the given pattern, anywhere in this subtree.
1084 """
1085 pattern = self._flavour.casefold(pattern)
1086 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1087 if drv or root:
1088 raise NotImplementedError("Non-relative patterns are unsupported")
1089 selector = _make_selector(("**",) + tuple(pattern_parts))
1090 for p in selector.select_from(self):
1091 yield p
1092
1093 def absolute(self):
1094 """Return an absolute version of this path. This function works
1095 even if the path doesn't point to anything.
1096
1097 No normalization is done, i.e. all '.' and '..' will be kept along.
1098 Use resolve() to get the canonical path to a file.
1099 """
1100 # XXX untested yet!
1101 if self._closed:
1102 self._raise_closed()
1103 if self.is_absolute():
1104 return self
1105 # FIXME this must defer to the specific flavour (and, under Windows,
1106 # use nt._getfullpathname())
1107 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1108 obj._init(template=self)
1109 return obj
1110
Steve Dower98eb3602016-11-09 12:58:17 -08001111 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001112 """
1113 Make the path absolute, resolving all symlinks on the way and also
1114 normalizing it (for example turning slashes into backslashes under
1115 Windows).
1116 """
1117 if self._closed:
1118 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001119 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001120 if s is None:
1121 # No symlink resolution => for consistency, raise an error if
1122 # the path doesn't exist or is forbidden
1123 self.stat()
1124 s = str(self.absolute())
1125 # Now we have no symlinks in the path, it's safe to normalize it.
1126 normed = self._flavour.pathmod.normpath(s)
1127 obj = self._from_parts((normed,), init=False)
1128 obj._init(template=self)
1129 return obj
1130
1131 def stat(self):
1132 """
1133 Return the result of the stat() system call on this path, like
1134 os.stat() does.
1135 """
1136 return self._accessor.stat(self)
1137
1138 def owner(self):
1139 """
1140 Return the login name of the file owner.
1141 """
1142 import pwd
1143 return pwd.getpwuid(self.stat().st_uid).pw_name
1144
1145 def group(self):
1146 """
1147 Return the group name of the file gid.
1148 """
1149 import grp
1150 return grp.getgrgid(self.stat().st_gid).gr_name
1151
Antoine Pitrou31119e42013-11-22 17:38:12 +01001152 def open(self, mode='r', buffering=-1, encoding=None,
1153 errors=None, newline=None):
1154 """
1155 Open the file pointed by this path and return a file object, as
1156 the built-in open() function does.
1157 """
1158 if self._closed:
1159 self._raise_closed()
1160 return io.open(str(self), mode, buffering, encoding, errors, newline,
1161 opener=self._opener)
1162
Georg Brandlea683982014-10-01 19:12:33 +02001163 def read_bytes(self):
1164 """
1165 Open the file in bytes mode, read it, and close the file.
1166 """
1167 with self.open(mode='rb') as f:
1168 return f.read()
1169
1170 def read_text(self, encoding=None, errors=None):
1171 """
1172 Open the file in text mode, read it, and close the file.
1173 """
1174 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1175 return f.read()
1176
1177 def write_bytes(self, data):
1178 """
1179 Open the file in bytes mode, write to it, and close the file.
1180 """
1181 # type-check for the buffer interface before truncating the file
1182 view = memoryview(data)
1183 with self.open(mode='wb') as f:
1184 return f.write(view)
1185
1186 def write_text(self, data, encoding=None, errors=None):
1187 """
1188 Open the file in text mode, write to it, and close the file.
1189 """
1190 if not isinstance(data, str):
1191 raise TypeError('data must be str, not %s' %
1192 data.__class__.__name__)
1193 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1194 return f.write(data)
1195
Antoine Pitrou31119e42013-11-22 17:38:12 +01001196 def touch(self, mode=0o666, exist_ok=True):
1197 """
1198 Create this file with the given access mode, if it doesn't exist.
1199 """
1200 if self._closed:
1201 self._raise_closed()
1202 if exist_ok:
1203 # First try to bump modification time
1204 # Implementation note: GNU touch uses the UTIME_NOW option of
1205 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001206 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001207 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001208 except OSError:
1209 # Avoid exception chaining
1210 pass
1211 else:
1212 return
1213 flags = os.O_CREAT | os.O_WRONLY
1214 if not exist_ok:
1215 flags |= os.O_EXCL
1216 fd = self._raw_open(flags, mode)
1217 os.close(fd)
1218
Barry Warsaw7c549c42014-08-05 11:28:12 -04001219 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Serhiy Storchaka8c8785b2017-03-24 21:46:25 +02001220 """
1221 Create a new directory at this given path.
1222 """
Antoine Pitrou31119e42013-11-22 17:38:12 +01001223 if self._closed:
1224 self._raise_closed()
Serhiy Storchaka8c8785b2017-03-24 21:46:25 +02001225 try:
1226 self._accessor.mkdir(self, mode)
1227 except FileNotFoundError:
1228 if not parents or self.parent == self:
1229 raise
Mariattacbc46af2017-04-13 19:26:16 -07001230 self.parent.mkdir(parents=True, exist_ok=True)
1231 self.mkdir(mode, parents=False, exist_ok=exist_ok)
Serhiy Storchaka8c8785b2017-03-24 21:46:25 +02001232 except OSError:
1233 # Cannot rely on checking for EEXIST, since the operating system
1234 # could give priority to other errors like EACCES or EROFS
1235 if not exist_ok or not self.is_dir():
1236 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001237
1238 def chmod(self, mode):
1239 """
1240 Change the permissions of the path, like os.chmod().
1241 """
1242 if self._closed:
1243 self._raise_closed()
1244 self._accessor.chmod(self, mode)
1245
1246 def lchmod(self, mode):
1247 """
1248 Like chmod(), except if the path points to a symlink, the symlink's
1249 permissions are changed, rather than its target's.
1250 """
1251 if self._closed:
1252 self._raise_closed()
1253 self._accessor.lchmod(self, mode)
1254
1255 def unlink(self):
1256 """
1257 Remove this file or link.
1258 If the path is a directory, use rmdir() instead.
1259 """
1260 if self._closed:
1261 self._raise_closed()
1262 self._accessor.unlink(self)
1263
1264 def rmdir(self):
1265 """
1266 Remove this directory. The directory must be empty.
1267 """
1268 if self._closed:
1269 self._raise_closed()
1270 self._accessor.rmdir(self)
1271
1272 def lstat(self):
1273 """
1274 Like stat(), except if the path points to a symlink, the symlink's
1275 status information is returned, rather than its target's.
1276 """
1277 if self._closed:
1278 self._raise_closed()
1279 return self._accessor.lstat(self)
1280
1281 def rename(self, target):
1282 """
1283 Rename this path to the given path.
1284 """
1285 if self._closed:
1286 self._raise_closed()
1287 self._accessor.rename(self, target)
1288
1289 def replace(self, target):
1290 """
1291 Rename this path to the given path, clobbering the existing
1292 destination if it exists.
1293 """
1294 if self._closed:
1295 self._raise_closed()
1296 self._accessor.replace(self, target)
1297
1298 def symlink_to(self, target, target_is_directory=False):
1299 """
1300 Make this path a symlink pointing to the given path.
1301 Note the order of arguments (self, target) is the reverse of os.symlink's.
1302 """
1303 if self._closed:
1304 self._raise_closed()
1305 self._accessor.symlink(target, self, target_is_directory)
1306
1307 # Convenience functions for querying the stat results
1308
1309 def exists(self):
1310 """
1311 Whether this path exists.
1312 """
1313 try:
1314 self.stat()
1315 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001316 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001317 raise
1318 return False
1319 return True
1320
1321 def is_dir(self):
1322 """
1323 Whether this path is a directory.
1324 """
1325 try:
1326 return S_ISDIR(self.stat().st_mode)
1327 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001328 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001329 raise
1330 # Path doesn't exist or is a broken symlink
1331 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1332 return False
1333
1334 def is_file(self):
1335 """
1336 Whether this path is a regular file (also True for symlinks pointing
1337 to regular files).
1338 """
1339 try:
1340 return S_ISREG(self.stat().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 or is a broken symlink
1345 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1346 return False
1347
1348 def is_symlink(self):
1349 """
1350 Whether this path is a symbolic link.
1351 """
1352 try:
1353 return S_ISLNK(self.lstat().st_mode)
1354 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001355 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001356 raise
1357 # Path doesn't exist
1358 return False
1359
1360 def is_block_device(self):
1361 """
1362 Whether this path is a block device.
1363 """
1364 try:
1365 return S_ISBLK(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_char_device(self):
1374 """
1375 Whether this path is a character device.
1376 """
1377 try:
1378 return S_ISCHR(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_fifo(self):
1387 """
1388 Whether this path is a FIFO.
1389 """
1390 try:
1391 return S_ISFIFO(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
1399 def is_socket(self):
1400 """
1401 Whether this path is a socket.
1402 """
1403 try:
1404 return S_ISSOCK(self.stat().st_mode)
1405 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001406 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001407 raise
1408 # Path doesn't exist or is a broken symlink
1409 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1410 return False
1411
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001412 def expanduser(self):
1413 """ Return a new path with expanded ~ and ~user constructs
1414 (as returned by os.path.expanduser)
1415 """
1416 if (not (self._drv or self._root) and
1417 self._parts and self._parts[0][:1] == '~'):
1418 homedir = self._flavour.gethomedir(self._parts[0][1:])
1419 return self._from_parts([homedir] + self._parts[1:])
1420
1421 return self
1422
Antoine Pitrou31119e42013-11-22 17:38:12 +01001423
1424class PosixPath(Path, PurePosixPath):
1425 __slots__ = ()
1426
1427class WindowsPath(Path, PureWindowsPath):
1428 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001429
1430 def owner(self):
1431 raise NotImplementedError("Path.owner() is unsupported on this system")
1432
1433 def group(self):
1434 raise NotImplementedError("Path.group() is unsupported on this system")