blob: 69653938ef721cd0a9b2178d1cb8cae1ed245cbb [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
195 s = os.path.abspath(os.path.join(s, os.pardir))
196 else:
197 if previous_s is None:
198 return s
199 else:
200 return s + os.path.sep + os.path.basename(previous_s)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100201 # Means fallback on absolute
202 return None
203
204 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
205 prefix = ''
206 if s.startswith(ext_prefix):
207 prefix = s[:4]
208 s = s[4:]
209 if s.startswith('UNC\\'):
210 prefix += s[:3]
211 s = '\\' + s[3:]
212 return prefix, s
213
214 def _ext_to_normal(self, s):
215 # Turn back an extended path into a normal DOS-like path
216 return self._split_extended_path(s)[1]
217
218 def is_reserved(self, parts):
219 # NOTE: the rules for reserved names seem somewhat complicated
220 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
221 # We err on the side of caution and return True for paths which are
222 # not considered reserved by Windows.
223 if not parts:
224 return False
225 if parts[0].startswith('\\\\'):
226 # UNC paths are never reserved
227 return False
228 return parts[-1].partition('.')[0].upper() in self.reserved_names
229
230 def make_uri(self, path):
231 # Under Windows, file URIs use the UTF-8 encoding.
232 drive = path.drive
233 if len(drive) == 2 and drive[1] == ':':
234 # It's a path on a local drive => 'file:///c:/a/b'
235 rest = path.as_posix()[2:].lstrip('/')
236 return 'file:///%s/%s' % (
237 drive, urlquote_from_bytes(rest.encode('utf-8')))
238 else:
239 # It's a path on a network drive => 'file://host/share/a/b'
240 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
241
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100242 def gethomedir(self, username):
243 if 'HOME' in os.environ:
244 userhome = os.environ['HOME']
245 elif 'USERPROFILE' in os.environ:
246 userhome = os.environ['USERPROFILE']
247 elif 'HOMEPATH' in os.environ:
Antoine Pitrou5d4e27e2014-12-30 22:09:42 +0100248 try:
249 drv = os.environ['HOMEDRIVE']
250 except KeyError:
251 drv = ''
252 userhome = drv + os.environ['HOMEPATH']
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100253 else:
254 raise RuntimeError("Can't determine home directory")
255
256 if username:
257 # Try to guess user home directory. By default all users
258 # directories are located in the same place and are named by
259 # corresponding usernames. If current user home directory points
260 # to nonstandard place, this guess is likely wrong.
261 if os.environ['USERNAME'] != username:
262 drv, root, parts = self.parse_parts((userhome,))
263 if parts[-1] != os.environ['USERNAME']:
264 raise RuntimeError("Can't determine home directory "
265 "for %r" % username)
266 parts[-1] = username
267 if drv or root:
268 userhome = drv + root + self.join(parts[1:])
269 else:
270 userhome = self.join(parts)
271 return userhome
Antoine Pitrou31119e42013-11-22 17:38:12 +0100272
273class _PosixFlavour(_Flavour):
274 sep = '/'
275 altsep = ''
276 has_drv = False
277 pathmod = posixpath
278
279 is_supported = (os.name != 'nt')
280
281 def splitroot(self, part, sep=sep):
282 if part and part[0] == sep:
283 stripped_part = part.lstrip(sep)
284 # According to POSIX path resolution:
285 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
286 # "A pathname that begins with two successive slashes may be
287 # interpreted in an implementation-defined manner, although more
288 # than two leading slashes shall be treated as a single slash".
289 if len(part) - len(stripped_part) == 2:
290 return '', sep * 2, stripped_part
291 else:
292 return '', sep, stripped_part
293 else:
294 return '', '', part
295
296 def casefold(self, s):
297 return s
298
299 def casefold_parts(self, parts):
300 return parts
301
Steve Dower98eb3602016-11-09 12:58:17 -0800302 def resolve(self, path, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100303 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100304 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100305 seen = {}
306 def _resolve(path, rest):
307 if rest.startswith(sep):
308 path = ''
309
310 for name in rest.split(sep):
311 if not name or name == '.':
312 # current dir
313 continue
314 if name == '..':
315 # parent dir
316 path, _, _ = path.rpartition(sep)
317 continue
318 newpath = path + sep + name
319 if newpath in seen:
320 # Already seen this path
321 path = seen[newpath]
322 if path is not None:
323 # use cached value
324 continue
325 # The symlink is not resolved, so we must have a symlink loop.
326 raise RuntimeError("Symlink loop from %r" % newpath)
327 # Resolve the symbolic link
328 try:
329 target = accessor.readlink(newpath)
330 except OSError as e:
331 if e.errno != EINVAL:
Steve Dower98eb3602016-11-09 12:58:17 -0800332 if strict:
333 raise
334 else:
335 return newpath
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100336 # Not a symlink
337 path = newpath
338 else:
339 seen[newpath] = None # not resolved symlink
340 path = _resolve(path, target)
341 seen[newpath] = path # resolved symlink
342
343 return path
344 # NOTE: according to POSIX, getcwd() cannot contain path components
345 # which are symlinks.
346 base = '' if path.is_absolute() else os.getcwd()
347 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100348
349 def is_reserved(self, parts):
350 return False
351
352 def make_uri(self, path):
353 # We represent the path using the local filesystem encoding,
354 # for portability to other applications.
355 bpath = bytes(path)
356 return 'file://' + urlquote_from_bytes(bpath)
357
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100358 def gethomedir(self, username):
359 if not username:
360 try:
361 return os.environ['HOME']
362 except KeyError:
363 import pwd
364 return pwd.getpwuid(os.getuid()).pw_dir
365 else:
366 import pwd
367 try:
368 return pwd.getpwnam(username).pw_dir
369 except KeyError:
370 raise RuntimeError("Can't determine home directory "
371 "for %r" % username)
372
Antoine Pitrou31119e42013-11-22 17:38:12 +0100373
374_windows_flavour = _WindowsFlavour()
375_posix_flavour = _PosixFlavour()
376
377
378class _Accessor:
379 """An accessor implements a particular (system-specific or not) way of
380 accessing paths on the filesystem."""
381
382
383class _NormalAccessor(_Accessor):
384
385 def _wrap_strfunc(strfunc):
386 @functools.wraps(strfunc)
387 def wrapped(pathobj, *args):
388 return strfunc(str(pathobj), *args)
389 return staticmethod(wrapped)
390
391 def _wrap_binary_strfunc(strfunc):
392 @functools.wraps(strfunc)
393 def wrapped(pathobjA, pathobjB, *args):
394 return strfunc(str(pathobjA), str(pathobjB), *args)
395 return staticmethod(wrapped)
396
397 stat = _wrap_strfunc(os.stat)
398
399 lstat = _wrap_strfunc(os.lstat)
400
401 open = _wrap_strfunc(os.open)
402
403 listdir = _wrap_strfunc(os.listdir)
404
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300405 scandir = _wrap_strfunc(os.scandir)
406
Antoine Pitrou31119e42013-11-22 17:38:12 +0100407 chmod = _wrap_strfunc(os.chmod)
408
409 if hasattr(os, "lchmod"):
410 lchmod = _wrap_strfunc(os.lchmod)
411 else:
412 def lchmod(self, pathobj, mode):
413 raise NotImplementedError("lchmod() not available on this system")
414
415 mkdir = _wrap_strfunc(os.mkdir)
416
417 unlink = _wrap_strfunc(os.unlink)
418
419 rmdir = _wrap_strfunc(os.rmdir)
420
421 rename = _wrap_binary_strfunc(os.rename)
422
423 replace = _wrap_binary_strfunc(os.replace)
424
425 if nt:
426 if supports_symlinks:
427 symlink = _wrap_binary_strfunc(os.symlink)
428 else:
429 def symlink(a, b, target_is_directory):
430 raise NotImplementedError("symlink() not available on this system")
431 else:
432 # Under POSIX, os.symlink() takes two args
433 @staticmethod
434 def symlink(a, b, target_is_directory):
435 return os.symlink(str(a), str(b))
436
437 utime = _wrap_strfunc(os.utime)
438
439 # Helper for resolve()
440 def readlink(self, path):
441 return os.readlink(path)
442
443
444_normal_accessor = _NormalAccessor()
445
446
447#
448# Globbing helpers
449#
450
Antoine Pitrou31119e42013-11-22 17:38:12 +0100451def _make_selector(pattern_parts):
452 pat = pattern_parts[0]
453 child_parts = pattern_parts[1:]
454 if pat == '**':
455 cls = _RecursiveWildcardSelector
456 elif '**' in pat:
457 raise ValueError("Invalid pattern: '**' can only be an entire path component")
458 elif _is_wildcard_pattern(pat):
459 cls = _WildcardSelector
460 else:
461 cls = _PreciseSelector
462 return cls(pat, child_parts)
463
464if hasattr(functools, "lru_cache"):
465 _make_selector = functools.lru_cache()(_make_selector)
466
467
468class _Selector:
469 """A selector matches a specific glob pattern part against the children
470 of a given path."""
471
472 def __init__(self, child_parts):
473 self.child_parts = child_parts
474 if child_parts:
475 self.successor = _make_selector(child_parts)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300476 self.dironly = True
Antoine Pitrou31119e42013-11-22 17:38:12 +0100477 else:
478 self.successor = _TerminatingSelector()
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300479 self.dironly = False
Antoine Pitrou31119e42013-11-22 17:38:12 +0100480
481 def select_from(self, parent_path):
482 """Iterate over all child paths of `parent_path` matched by this
483 selector. This can contain parent_path itself."""
484 path_cls = type(parent_path)
485 is_dir = path_cls.is_dir
486 exists = path_cls.exists
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300487 scandir = parent_path._accessor.scandir
488 if not is_dir(parent_path):
489 return iter([])
490 return self._select_from(parent_path, is_dir, exists, scandir)
Antoine Pitrou31119e42013-11-22 17:38:12 +0100491
492
493class _TerminatingSelector:
494
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300495 def _select_from(self, parent_path, is_dir, exists, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100496 yield parent_path
497
498
499class _PreciseSelector(_Selector):
500
501 def __init__(self, name, child_parts):
502 self.name = name
503 _Selector.__init__(self, child_parts)
504
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300505 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800506 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800507 path = parent_path._make_child_relpath(self.name)
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300508 if (is_dir if self.dironly else exists)(path):
509 for p in self.successor._select_from(path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800510 yield p
511 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100512 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100513
514
515class _WildcardSelector(_Selector):
516
517 def __init__(self, pat, child_parts):
518 self.pat = re.compile(fnmatch.translate(pat))
519 _Selector.__init__(self, child_parts)
520
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300521 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800522 try:
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800523 cf = parent_path._flavour.casefold
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300524 entries = list(scandir(parent_path))
525 for entry in entries:
526 if not self.dironly or entry.is_dir():
527 name = entry.name
528 casefolded = cf(name)
529 if self.pat.match(casefolded):
530 path = parent_path._make_child_relpath(name)
531 for p in self.successor._select_from(path, is_dir, exists, scandir):
532 yield p
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800533 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100534 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800535
Antoine Pitrou31119e42013-11-22 17:38:12 +0100536
537
538class _RecursiveWildcardSelector(_Selector):
539
540 def __init__(self, pat, child_parts):
541 _Selector.__init__(self, child_parts)
542
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300543 def _iterate_directories(self, parent_path, is_dir, scandir):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100544 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800545 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300546 entries = list(scandir(parent_path))
547 for entry in entries:
548 if entry.is_dir() and not entry.is_symlink():
549 path = parent_path._make_child_relpath(entry.name)
550 for p in self._iterate_directories(path, is_dir, scandir):
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800551 yield p
552 except PermissionError:
553 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100554
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300555 def _select_from(self, parent_path, is_dir, exists, scandir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800556 try:
Serhiy Storchaka680cb152016-09-07 10:58:05 +0300557 yielded = set()
558 try:
559 successor_select = self.successor._select_from
560 for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
561 for p in successor_select(starting_point, is_dir, exists, scandir):
562 if p not in yielded:
563 yield p
564 yielded.add(p)
565 finally:
566 yielded.clear()
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800567 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100568 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100569
570
571#
572# Public API
573#
574
575class _PathParents(Sequence):
576 """This object provides sequence-like access to the logical ancestors
577 of a path. Don't try to construct it yourself."""
578 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
579
580 def __init__(self, path):
581 # We don't store the instance to avoid reference cycles
582 self._pathcls = type(path)
583 self._drv = path._drv
584 self._root = path._root
585 self._parts = path._parts
586
587 def __len__(self):
588 if self._drv or self._root:
589 return len(self._parts) - 1
590 else:
591 return len(self._parts)
592
593 def __getitem__(self, idx):
594 if idx < 0 or idx >= len(self):
595 raise IndexError(idx)
596 return self._pathcls._from_parsed_parts(self._drv, self._root,
597 self._parts[:-idx - 1])
598
599 def __repr__(self):
600 return "<{}.parents>".format(self._pathcls.__name__)
601
602
603class PurePath(object):
604 """PurePath represents a filesystem path and offers operations which
605 don't imply any actual filesystem I/O. Depending on your system,
606 instantiating a PurePath will return either a PurePosixPath or a
607 PureWindowsPath object. You can also instantiate either of these classes
608 directly, regardless of your system.
609 """
610 __slots__ = (
611 '_drv', '_root', '_parts',
612 '_str', '_hash', '_pparts', '_cached_cparts',
613 )
614
615 def __new__(cls, *args):
616 """Construct a PurePath from one or several strings and or existing
617 PurePath objects. The strings and path objects are combined so as
618 to yield a canonicalized path, which is incorporated into the
619 new PurePath object.
620 """
621 if cls is PurePath:
622 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
623 return cls._from_parts(args)
624
625 def __reduce__(self):
626 # Using the parts tuple helps share interned path parts
627 # when pickling related paths.
628 return (self.__class__, tuple(self._parts))
629
630 @classmethod
631 def _parse_args(cls, args):
632 # This is useful when you don't want to create an instance, just
633 # canonicalize some constructor arguments.
634 parts = []
635 for a in args:
636 if isinstance(a, PurePath):
637 parts += a._parts
Antoine Pitrou31119e42013-11-22 17:38:12 +0100638 else:
Brett Cannon568be632016-06-10 12:20:49 -0700639 a = os.fspath(a)
640 if isinstance(a, str):
641 # Force-cast str subclasses to str (issue #21127)
642 parts.append(str(a))
643 else:
644 raise TypeError(
645 "argument should be a str object or an os.PathLike "
646 "object returning str, not %r"
647 % type(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100648 return cls._flavour.parse_parts(parts)
649
650 @classmethod
651 def _from_parts(cls, args, init=True):
652 # We need to call _parse_args on the instance, so as to get the
653 # right flavour.
654 self = object.__new__(cls)
655 drv, root, parts = self._parse_args(args)
656 self._drv = drv
657 self._root = root
658 self._parts = parts
659 if init:
660 self._init()
661 return self
662
663 @classmethod
664 def _from_parsed_parts(cls, drv, root, parts, init=True):
665 self = object.__new__(cls)
666 self._drv = drv
667 self._root = root
668 self._parts = parts
669 if init:
670 self._init()
671 return self
672
673 @classmethod
674 def _format_parsed_parts(cls, drv, root, parts):
675 if drv or root:
676 return drv + root + cls._flavour.join(parts[1:])
677 else:
678 return cls._flavour.join(parts)
679
680 def _init(self):
Martin Pantere26da7c2016-06-02 10:07:09 +0000681 # Overridden in concrete Path
Antoine Pitrou31119e42013-11-22 17:38:12 +0100682 pass
683
684 def _make_child(self, args):
685 drv, root, parts = self._parse_args(args)
686 drv, root, parts = self._flavour.join_parsed_parts(
687 self._drv, self._root, self._parts, drv, root, parts)
688 return self._from_parsed_parts(drv, root, parts)
689
690 def __str__(self):
691 """Return the string representation of the path, suitable for
692 passing to system calls."""
693 try:
694 return self._str
695 except AttributeError:
696 self._str = self._format_parsed_parts(self._drv, self._root,
697 self._parts) or '.'
698 return self._str
699
Brett Cannon568be632016-06-10 12:20:49 -0700700 def __fspath__(self):
701 return str(self)
702
Antoine Pitrou31119e42013-11-22 17:38:12 +0100703 def as_posix(self):
704 """Return the string representation of the path with forward (/)
705 slashes."""
706 f = self._flavour
707 return str(self).replace(f.sep, '/')
708
709 def __bytes__(self):
710 """Return the bytes representation of the path. This is only
711 recommended to use under Unix."""
712 return os.fsencode(str(self))
713
714 def __repr__(self):
715 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
716
717 def as_uri(self):
718 """Return the path as a 'file' URI."""
719 if not self.is_absolute():
720 raise ValueError("relative path can't be expressed as a file URI")
721 return self._flavour.make_uri(self)
722
723 @property
724 def _cparts(self):
725 # Cached casefolded parts, for hashing and comparison
726 try:
727 return self._cached_cparts
728 except AttributeError:
729 self._cached_cparts = self._flavour.casefold_parts(self._parts)
730 return self._cached_cparts
731
732 def __eq__(self, other):
733 if not isinstance(other, PurePath):
734 return NotImplemented
735 return self._cparts == other._cparts and self._flavour is other._flavour
736
Antoine Pitrou31119e42013-11-22 17:38:12 +0100737 def __hash__(self):
738 try:
739 return self._hash
740 except AttributeError:
741 self._hash = hash(tuple(self._cparts))
742 return self._hash
743
744 def __lt__(self, other):
745 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
746 return NotImplemented
747 return self._cparts < other._cparts
748
749 def __le__(self, other):
750 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
751 return NotImplemented
752 return self._cparts <= other._cparts
753
754 def __gt__(self, other):
755 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
756 return NotImplemented
757 return self._cparts > other._cparts
758
759 def __ge__(self, other):
760 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
761 return NotImplemented
762 return self._cparts >= other._cparts
763
764 drive = property(attrgetter('_drv'),
765 doc="""The drive prefix (letter or UNC path), if any.""")
766
767 root = property(attrgetter('_root'),
768 doc="""The root of the path, if any.""")
769
770 @property
771 def anchor(self):
772 """The concatenation of the drive and root, or ''."""
773 anchor = self._drv + self._root
774 return anchor
775
776 @property
777 def name(self):
778 """The final path component, if any."""
779 parts = self._parts
780 if len(parts) == (1 if (self._drv or self._root) else 0):
781 return ''
782 return parts[-1]
783
784 @property
785 def suffix(self):
786 """The final component's last suffix, if any."""
787 name = self.name
788 i = name.rfind('.')
789 if 0 < i < len(name) - 1:
790 return name[i:]
791 else:
792 return ''
793
794 @property
795 def suffixes(self):
796 """A list of the final component's suffixes, if any."""
797 name = self.name
798 if name.endswith('.'):
799 return []
800 name = name.lstrip('.')
801 return ['.' + suffix for suffix in name.split('.')[1:]]
802
803 @property
804 def stem(self):
805 """The final path component, minus its last suffix."""
806 name = self.name
807 i = name.rfind('.')
808 if 0 < i < len(name) - 1:
809 return name[:i]
810 else:
811 return name
812
813 def with_name(self, name):
814 """Return a new path with the file name changed."""
815 if not self.name:
816 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400817 drv, root, parts = self._flavour.parse_parts((name,))
818 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
819 or drv or root or len(parts) != 1):
820 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100821 return self._from_parsed_parts(self._drv, self._root,
822 self._parts[:-1] + [name])
823
824 def with_suffix(self, suffix):
825 """Return a new path with the file suffix changed (or added, if none)."""
826 # XXX if suffix is None, should the current suffix be removed?
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400827 f = self._flavour
828 if f.sep in suffix or f.altsep and f.altsep in suffix:
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100829 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400830 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100831 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100832 name = self.name
833 if not name:
834 raise ValueError("%r has an empty name" % (self,))
835 old_suffix = self.suffix
836 if not old_suffix:
837 name = name + suffix
838 else:
839 name = name[:-len(old_suffix)] + suffix
840 return self._from_parsed_parts(self._drv, self._root,
841 self._parts[:-1] + [name])
842
843 def relative_to(self, *other):
844 """Return the relative path to another path identified by the passed
845 arguments. If the operation is not possible (because this is not
846 a subpath of the other path), raise ValueError.
847 """
848 # For the purpose of this method, drive and root are considered
849 # separate parts, i.e.:
850 # Path('c:/').relative_to('c:') gives Path('/')
851 # Path('c:/').relative_to('/') raise ValueError
852 if not other:
853 raise TypeError("need at least one argument")
854 parts = self._parts
855 drv = self._drv
856 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100857 if root:
858 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100859 else:
860 abs_parts = parts
861 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100862 if to_root:
863 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100864 else:
865 to_abs_parts = to_parts
866 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100867 cf = self._flavour.casefold_parts
868 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100869 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
870 raise ValueError("{!r} does not start with {!r}"
871 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100872 return self._from_parsed_parts('', root if n == 1 else '',
873 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100874
875 @property
876 def parts(self):
877 """An object providing sequence-like access to the
878 components in the filesystem path."""
879 # We cache the tuple to avoid building a new one each time .parts
880 # is accessed. XXX is this necessary?
881 try:
882 return self._pparts
883 except AttributeError:
884 self._pparts = tuple(self._parts)
885 return self._pparts
886
887 def joinpath(self, *args):
888 """Combine this path with one or several arguments, and return a
889 new path representing either a subpath (if all arguments are relative
890 paths) or a totally different path (if one of the arguments is
891 anchored).
892 """
893 return self._make_child(args)
894
895 def __truediv__(self, key):
896 return self._make_child((key,))
897
898 def __rtruediv__(self, key):
899 return self._from_parts([key] + self._parts)
900
901 @property
902 def parent(self):
903 """The logical parent of the path."""
904 drv = self._drv
905 root = self._root
906 parts = self._parts
907 if len(parts) == 1 and (drv or root):
908 return self
909 return self._from_parsed_parts(drv, root, parts[:-1])
910
911 @property
912 def parents(self):
913 """A sequence of this path's logical parents."""
914 return _PathParents(self)
915
916 def is_absolute(self):
917 """True if the path is absolute (has both a root and, if applicable,
918 a drive)."""
919 if not self._root:
920 return False
921 return not self._flavour.has_drv or bool(self._drv)
922
923 def is_reserved(self):
924 """Return True if the path contains one of the special names reserved
925 by the system, if any."""
926 return self._flavour.is_reserved(self._parts)
927
928 def match(self, path_pattern):
929 """
930 Return True if this path matches the given pattern.
931 """
932 cf = self._flavour.casefold
933 path_pattern = cf(path_pattern)
934 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
935 if not pat_parts:
936 raise ValueError("empty pattern")
937 if drv and drv != cf(self._drv):
938 return False
939 if root and root != cf(self._root):
940 return False
941 parts = self._cparts
942 if drv or root:
943 if len(pat_parts) != len(parts):
944 return False
945 pat_parts = pat_parts[1:]
946 elif len(pat_parts) > len(parts):
947 return False
948 for part, pat in zip(reversed(parts), reversed(pat_parts)):
949 if not fnmatch.fnmatchcase(part, pat):
950 return False
951 return True
952
Brett Cannon568be632016-06-10 12:20:49 -0700953# Can't subclass os.PathLike from PurePath and keep the constructor
954# optimizations in PurePath._parse_args().
955os.PathLike.register(PurePath)
956
Antoine Pitrou31119e42013-11-22 17:38:12 +0100957
958class PurePosixPath(PurePath):
959 _flavour = _posix_flavour
960 __slots__ = ()
961
962
963class PureWindowsPath(PurePath):
964 _flavour = _windows_flavour
965 __slots__ = ()
966
967
968# Filesystem-accessing classes
969
970
971class Path(PurePath):
972 __slots__ = (
973 '_accessor',
974 '_closed',
975 )
976
977 def __new__(cls, *args, **kwargs):
978 if cls is Path:
979 cls = WindowsPath if os.name == 'nt' else PosixPath
980 self = cls._from_parts(args, init=False)
981 if not self._flavour.is_supported:
982 raise NotImplementedError("cannot instantiate %r on your system"
983 % (cls.__name__,))
984 self._init()
985 return self
986
987 def _init(self,
988 # Private non-constructor arguments
989 template=None,
990 ):
991 self._closed = False
992 if template is not None:
993 self._accessor = template._accessor
994 else:
995 self._accessor = _normal_accessor
996
997 def _make_child_relpath(self, part):
998 # This is an optimization used for dir walking. `part` must be
999 # a single part relative to this path.
1000 parts = self._parts + [part]
1001 return self._from_parsed_parts(self._drv, self._root, parts)
1002
1003 def __enter__(self):
1004 if self._closed:
1005 self._raise_closed()
1006 return self
1007
1008 def __exit__(self, t, v, tb):
1009 self._closed = True
1010
1011 def _raise_closed(self):
1012 raise ValueError("I/O operation on closed path")
1013
1014 def _opener(self, name, flags, mode=0o666):
1015 # A stub for the opener argument to built-in open()
1016 return self._accessor.open(self, flags, mode)
1017
Antoine Pitrou4a60d422013-12-02 21:25:18 +01001018 def _raw_open(self, flags, mode=0o777):
1019 """
1020 Open the file pointed by this path and return a file descriptor,
1021 as os.open() does.
1022 """
1023 if self._closed:
1024 self._raise_closed()
1025 return self._accessor.open(self, flags, mode)
1026
Antoine Pitrou31119e42013-11-22 17:38:12 +01001027 # Public API
1028
1029 @classmethod
1030 def cwd(cls):
1031 """Return a new path pointing to the current working directory
1032 (as returned by os.getcwd()).
1033 """
1034 return cls(os.getcwd())
1035
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001036 @classmethod
1037 def home(cls):
1038 """Return a new path pointing to the user's home directory (as
1039 returned by os.path.expanduser('~')).
1040 """
1041 return cls(cls()._flavour.gethomedir(None))
1042
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001043 def samefile(self, other_path):
Berker Peksag05492b82015-10-22 03:34:16 +03001044 """Return whether other_path is the same or not as this file
Berker Peksag267597f2015-10-21 20:10:24 +03001045 (as returned by os.path.samefile()).
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001046 """
1047 st = self.stat()
1048 try:
1049 other_st = other_path.stat()
1050 except AttributeError:
1051 other_st = os.stat(other_path)
1052 return os.path.samestat(st, other_st)
1053
Antoine Pitrou31119e42013-11-22 17:38:12 +01001054 def iterdir(self):
1055 """Iterate over the files in this directory. Does not yield any
1056 result for the special paths '.' and '..'.
1057 """
1058 if self._closed:
1059 self._raise_closed()
1060 for name in self._accessor.listdir(self):
1061 if name in {'.', '..'}:
1062 # Yielding a path object for these makes little sense
1063 continue
1064 yield self._make_child_relpath(name)
1065 if self._closed:
1066 self._raise_closed()
1067
1068 def glob(self, pattern):
1069 """Iterate over this subtree and yield all existing files (of any
1070 kind, including directories) matching the given pattern.
1071 """
Berker Peksag4a208e42016-01-30 17:50:48 +02001072 if not pattern:
1073 raise ValueError("Unacceptable pattern: {!r}".format(pattern))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001074 pattern = self._flavour.casefold(pattern)
1075 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1076 if drv or root:
1077 raise NotImplementedError("Non-relative patterns are unsupported")
1078 selector = _make_selector(tuple(pattern_parts))
1079 for p in selector.select_from(self):
1080 yield p
1081
1082 def rglob(self, pattern):
1083 """Recursively yield all existing files (of any kind, including
1084 directories) matching the given pattern, anywhere in this subtree.
1085 """
1086 pattern = self._flavour.casefold(pattern)
1087 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1088 if drv or root:
1089 raise NotImplementedError("Non-relative patterns are unsupported")
1090 selector = _make_selector(("**",) + tuple(pattern_parts))
1091 for p in selector.select_from(self):
1092 yield p
1093
1094 def absolute(self):
1095 """Return an absolute version of this path. This function works
1096 even if the path doesn't point to anything.
1097
1098 No normalization is done, i.e. all '.' and '..' will be kept along.
1099 Use resolve() to get the canonical path to a file.
1100 """
1101 # XXX untested yet!
1102 if self._closed:
1103 self._raise_closed()
1104 if self.is_absolute():
1105 return self
1106 # FIXME this must defer to the specific flavour (and, under Windows,
1107 # use nt._getfullpathname())
1108 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1109 obj._init(template=self)
1110 return obj
1111
Steve Dower98eb3602016-11-09 12:58:17 -08001112 def resolve(self, strict=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001113 """
1114 Make the path absolute, resolving all symlinks on the way and also
1115 normalizing it (for example turning slashes into backslashes under
1116 Windows).
1117 """
1118 if self._closed:
1119 self._raise_closed()
Steve Dower98eb3602016-11-09 12:58:17 -08001120 s = self._flavour.resolve(self, strict=strict)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001121 if s is None:
1122 # No symlink resolution => for consistency, raise an error if
1123 # the path doesn't exist or is forbidden
1124 self.stat()
1125 s = str(self.absolute())
1126 # Now we have no symlinks in the path, it's safe to normalize it.
1127 normed = self._flavour.pathmod.normpath(s)
1128 obj = self._from_parts((normed,), init=False)
1129 obj._init(template=self)
1130 return obj
1131
1132 def stat(self):
1133 """
1134 Return the result of the stat() system call on this path, like
1135 os.stat() does.
1136 """
1137 return self._accessor.stat(self)
1138
1139 def owner(self):
1140 """
1141 Return the login name of the file owner.
1142 """
1143 import pwd
1144 return pwd.getpwuid(self.stat().st_uid).pw_name
1145
1146 def group(self):
1147 """
1148 Return the group name of the file gid.
1149 """
1150 import grp
1151 return grp.getgrgid(self.stat().st_gid).gr_name
1152
Antoine Pitrou31119e42013-11-22 17:38:12 +01001153 def open(self, mode='r', buffering=-1, encoding=None,
1154 errors=None, newline=None):
1155 """
1156 Open the file pointed by this path and return a file object, as
1157 the built-in open() function does.
1158 """
1159 if self._closed:
1160 self._raise_closed()
1161 return io.open(str(self), mode, buffering, encoding, errors, newline,
1162 opener=self._opener)
1163
Georg Brandlea683982014-10-01 19:12:33 +02001164 def read_bytes(self):
1165 """
1166 Open the file in bytes mode, read it, and close the file.
1167 """
1168 with self.open(mode='rb') as f:
1169 return f.read()
1170
1171 def read_text(self, encoding=None, errors=None):
1172 """
1173 Open the file in text mode, read it, and close the file.
1174 """
1175 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1176 return f.read()
1177
1178 def write_bytes(self, data):
1179 """
1180 Open the file in bytes mode, write to it, and close the file.
1181 """
1182 # type-check for the buffer interface before truncating the file
1183 view = memoryview(data)
1184 with self.open(mode='wb') as f:
1185 return f.write(view)
1186
1187 def write_text(self, data, encoding=None, errors=None):
1188 """
1189 Open the file in text mode, write to it, and close the file.
1190 """
1191 if not isinstance(data, str):
1192 raise TypeError('data must be str, not %s' %
1193 data.__class__.__name__)
1194 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1195 return f.write(data)
1196
Antoine Pitrou31119e42013-11-22 17:38:12 +01001197 def touch(self, mode=0o666, exist_ok=True):
1198 """
1199 Create this file with the given access mode, if it doesn't exist.
1200 """
1201 if self._closed:
1202 self._raise_closed()
1203 if exist_ok:
1204 # First try to bump modification time
1205 # Implementation note: GNU touch uses the UTIME_NOW option of
1206 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001207 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001208 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001209 except OSError:
1210 # Avoid exception chaining
1211 pass
1212 else:
1213 return
1214 flags = os.O_CREAT | os.O_WRONLY
1215 if not exist_ok:
1216 flags |= os.O_EXCL
1217 fd = self._raw_open(flags, mode)
1218 os.close(fd)
1219
Barry Warsaw7c549c42014-08-05 11:28:12 -04001220 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001221 if self._closed:
1222 self._raise_closed()
1223 if not parents:
Barry Warsaw7c549c42014-08-05 11:28:12 -04001224 try:
1225 self._accessor.mkdir(self, mode)
1226 except FileExistsError:
1227 if not exist_ok or not self.is_dir():
1228 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001229 else:
1230 try:
1231 self._accessor.mkdir(self, mode)
Barry Warsaw7c549c42014-08-05 11:28:12 -04001232 except FileExistsError:
1233 if not exist_ok or not self.is_dir():
1234 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001235 except OSError as e:
1236 if e.errno != ENOENT:
1237 raise
Antoine Pitrou0048c982013-12-16 20:22:37 +01001238 self.parent.mkdir(parents=True)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001239 self._accessor.mkdir(self, mode)
1240
1241 def chmod(self, mode):
1242 """
1243 Change the permissions of the path, like os.chmod().
1244 """
1245 if self._closed:
1246 self._raise_closed()
1247 self._accessor.chmod(self, mode)
1248
1249 def lchmod(self, mode):
1250 """
1251 Like chmod(), except if the path points to a symlink, the symlink's
1252 permissions are changed, rather than its target's.
1253 """
1254 if self._closed:
1255 self._raise_closed()
1256 self._accessor.lchmod(self, mode)
1257
1258 def unlink(self):
1259 """
1260 Remove this file or link.
1261 If the path is a directory, use rmdir() instead.
1262 """
1263 if self._closed:
1264 self._raise_closed()
1265 self._accessor.unlink(self)
1266
1267 def rmdir(self):
1268 """
1269 Remove this directory. The directory must be empty.
1270 """
1271 if self._closed:
1272 self._raise_closed()
1273 self._accessor.rmdir(self)
1274
1275 def lstat(self):
1276 """
1277 Like stat(), except if the path points to a symlink, the symlink's
1278 status information is returned, rather than its target's.
1279 """
1280 if self._closed:
1281 self._raise_closed()
1282 return self._accessor.lstat(self)
1283
1284 def rename(self, target):
1285 """
1286 Rename this path to the given path.
1287 """
1288 if self._closed:
1289 self._raise_closed()
1290 self._accessor.rename(self, target)
1291
1292 def replace(self, target):
1293 """
1294 Rename this path to the given path, clobbering the existing
1295 destination if it exists.
1296 """
1297 if self._closed:
1298 self._raise_closed()
1299 self._accessor.replace(self, target)
1300
1301 def symlink_to(self, target, target_is_directory=False):
1302 """
1303 Make this path a symlink pointing to the given path.
1304 Note the order of arguments (self, target) is the reverse of os.symlink's.
1305 """
1306 if self._closed:
1307 self._raise_closed()
1308 self._accessor.symlink(target, self, target_is_directory)
1309
1310 # Convenience functions for querying the stat results
1311
1312 def exists(self):
1313 """
1314 Whether this path exists.
1315 """
1316 try:
1317 self.stat()
1318 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001319 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001320 raise
1321 return False
1322 return True
1323
1324 def is_dir(self):
1325 """
1326 Whether this path is a directory.
1327 """
1328 try:
1329 return S_ISDIR(self.stat().st_mode)
1330 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001331 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001332 raise
1333 # Path doesn't exist or is a broken symlink
1334 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1335 return False
1336
1337 def is_file(self):
1338 """
1339 Whether this path is a regular file (also True for symlinks pointing
1340 to regular files).
1341 """
1342 try:
1343 return S_ISREG(self.stat().st_mode)
1344 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001345 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001346 raise
1347 # Path doesn't exist or is a broken symlink
1348 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1349 return False
1350
1351 def is_symlink(self):
1352 """
1353 Whether this path is a symbolic link.
1354 """
1355 try:
1356 return S_ISLNK(self.lstat().st_mode)
1357 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001358 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001359 raise
1360 # Path doesn't exist
1361 return False
1362
1363 def is_block_device(self):
1364 """
1365 Whether this path is a block device.
1366 """
1367 try:
1368 return S_ISBLK(self.stat().st_mode)
1369 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001370 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001371 raise
1372 # Path doesn't exist or is a broken symlink
1373 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1374 return False
1375
1376 def is_char_device(self):
1377 """
1378 Whether this path is a character device.
1379 """
1380 try:
1381 return S_ISCHR(self.stat().st_mode)
1382 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001383 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001384 raise
1385 # Path doesn't exist or is a broken symlink
1386 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1387 return False
1388
1389 def is_fifo(self):
1390 """
1391 Whether this path is a FIFO.
1392 """
1393 try:
1394 return S_ISFIFO(self.stat().st_mode)
1395 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001396 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001397 raise
1398 # Path doesn't exist or is a broken symlink
1399 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1400 return False
1401
1402 def is_socket(self):
1403 """
1404 Whether this path is a socket.
1405 """
1406 try:
1407 return S_ISSOCK(self.stat().st_mode)
1408 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001409 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001410 raise
1411 # Path doesn't exist or is a broken symlink
1412 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1413 return False
1414
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001415 def expanduser(self):
1416 """ Return a new path with expanded ~ and ~user constructs
1417 (as returned by os.path.expanduser)
1418 """
1419 if (not (self._drv or self._root) and
1420 self._parts and self._parts[0][:1] == '~'):
1421 homedir = self._flavour.gethomedir(self._parts[0][1:])
1422 return self._from_parts([homedir] + self._parts[1:])
1423
1424 return self
1425
Antoine Pitrou31119e42013-11-22 17:38:12 +01001426
1427class PosixPath(Path, PurePosixPath):
1428 __slots__ = ()
1429
1430class WindowsPath(Path, PureWindowsPath):
1431 __slots__ = ()
Berker Peksag04d42292016-03-11 23:07:27 +02001432
1433 def owner(self):
1434 raise NotImplementedError("Path.owner() is unsupported on this system")
1435
1436 def group(self):
1437 raise NotImplementedError("Path.group() is unsupported on this system")