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