blob: 8fef2590b85a4c8435408e27aec564261ec55cdb [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
181 def resolve(self, path):
182 s = str(path)
183 if not s:
184 return os.getcwd()
185 if _getfinalpathname is not None:
186 return self._ext_to_normal(_getfinalpathname(s))
187 # Means fallback on absolute
188 return None
189
190 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
191 prefix = ''
192 if s.startswith(ext_prefix):
193 prefix = s[:4]
194 s = s[4:]
195 if s.startswith('UNC\\'):
196 prefix += s[:3]
197 s = '\\' + s[3:]
198 return prefix, s
199
200 def _ext_to_normal(self, s):
201 # Turn back an extended path into a normal DOS-like path
202 return self._split_extended_path(s)[1]
203
204 def is_reserved(self, parts):
205 # NOTE: the rules for reserved names seem somewhat complicated
206 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
207 # We err on the side of caution and return True for paths which are
208 # not considered reserved by Windows.
209 if not parts:
210 return False
211 if parts[0].startswith('\\\\'):
212 # UNC paths are never reserved
213 return False
214 return parts[-1].partition('.')[0].upper() in self.reserved_names
215
216 def make_uri(self, path):
217 # Under Windows, file URIs use the UTF-8 encoding.
218 drive = path.drive
219 if len(drive) == 2 and drive[1] == ':':
220 # It's a path on a local drive => 'file:///c:/a/b'
221 rest = path.as_posix()[2:].lstrip('/')
222 return 'file:///%s/%s' % (
223 drive, urlquote_from_bytes(rest.encode('utf-8')))
224 else:
225 # It's a path on a network drive => 'file://host/share/a/b'
226 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
227
228
229class _PosixFlavour(_Flavour):
230 sep = '/'
231 altsep = ''
232 has_drv = False
233 pathmod = posixpath
234
235 is_supported = (os.name != 'nt')
236
237 def splitroot(self, part, sep=sep):
238 if part and part[0] == sep:
239 stripped_part = part.lstrip(sep)
240 # According to POSIX path resolution:
241 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
242 # "A pathname that begins with two successive slashes may be
243 # interpreted in an implementation-defined manner, although more
244 # than two leading slashes shall be treated as a single slash".
245 if len(part) - len(stripped_part) == 2:
246 return '', sep * 2, stripped_part
247 else:
248 return '', sep, stripped_part
249 else:
250 return '', '', part
251
252 def casefold(self, s):
253 return s
254
255 def casefold_parts(self, parts):
256 return parts
257
258 def resolve(self, path):
259 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100260 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100261 seen = {}
262 def _resolve(path, rest):
263 if rest.startswith(sep):
264 path = ''
265
266 for name in rest.split(sep):
267 if not name or name == '.':
268 # current dir
269 continue
270 if name == '..':
271 # parent dir
272 path, _, _ = path.rpartition(sep)
273 continue
274 newpath = path + sep + name
275 if newpath in seen:
276 # Already seen this path
277 path = seen[newpath]
278 if path is not None:
279 # use cached value
280 continue
281 # The symlink is not resolved, so we must have a symlink loop.
282 raise RuntimeError("Symlink loop from %r" % newpath)
283 # Resolve the symbolic link
284 try:
285 target = accessor.readlink(newpath)
286 except OSError as e:
287 if e.errno != EINVAL:
288 raise
289 # Not a symlink
290 path = newpath
291 else:
292 seen[newpath] = None # not resolved symlink
293 path = _resolve(path, target)
294 seen[newpath] = path # resolved symlink
295
296 return path
297 # NOTE: according to POSIX, getcwd() cannot contain path components
298 # which are symlinks.
299 base = '' if path.is_absolute() else os.getcwd()
300 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100301
302 def is_reserved(self, parts):
303 return False
304
305 def make_uri(self, path):
306 # We represent the path using the local filesystem encoding,
307 # for portability to other applications.
308 bpath = bytes(path)
309 return 'file://' + urlquote_from_bytes(bpath)
310
311
312_windows_flavour = _WindowsFlavour()
313_posix_flavour = _PosixFlavour()
314
315
316class _Accessor:
317 """An accessor implements a particular (system-specific or not) way of
318 accessing paths on the filesystem."""
319
320
321class _NormalAccessor(_Accessor):
322
323 def _wrap_strfunc(strfunc):
324 @functools.wraps(strfunc)
325 def wrapped(pathobj, *args):
326 return strfunc(str(pathobj), *args)
327 return staticmethod(wrapped)
328
329 def _wrap_binary_strfunc(strfunc):
330 @functools.wraps(strfunc)
331 def wrapped(pathobjA, pathobjB, *args):
332 return strfunc(str(pathobjA), str(pathobjB), *args)
333 return staticmethod(wrapped)
334
335 stat = _wrap_strfunc(os.stat)
336
337 lstat = _wrap_strfunc(os.lstat)
338
339 open = _wrap_strfunc(os.open)
340
341 listdir = _wrap_strfunc(os.listdir)
342
343 chmod = _wrap_strfunc(os.chmod)
344
345 if hasattr(os, "lchmod"):
346 lchmod = _wrap_strfunc(os.lchmod)
347 else:
348 def lchmod(self, pathobj, mode):
349 raise NotImplementedError("lchmod() not available on this system")
350
351 mkdir = _wrap_strfunc(os.mkdir)
352
353 unlink = _wrap_strfunc(os.unlink)
354
355 rmdir = _wrap_strfunc(os.rmdir)
356
357 rename = _wrap_binary_strfunc(os.rename)
358
359 replace = _wrap_binary_strfunc(os.replace)
360
361 if nt:
362 if supports_symlinks:
363 symlink = _wrap_binary_strfunc(os.symlink)
364 else:
365 def symlink(a, b, target_is_directory):
366 raise NotImplementedError("symlink() not available on this system")
367 else:
368 # Under POSIX, os.symlink() takes two args
369 @staticmethod
370 def symlink(a, b, target_is_directory):
371 return os.symlink(str(a), str(b))
372
373 utime = _wrap_strfunc(os.utime)
374
375 # Helper for resolve()
376 def readlink(self, path):
377 return os.readlink(path)
378
379
380_normal_accessor = _NormalAccessor()
381
382
383#
384# Globbing helpers
385#
386
387@contextmanager
388def _cached(func):
389 try:
390 func.__cached__
391 yield func
392 except AttributeError:
393 cache = {}
394 def wrapper(*args):
395 try:
396 return cache[args]
397 except KeyError:
398 value = cache[args] = func(*args)
399 return value
400 wrapper.__cached__ = True
401 try:
402 yield wrapper
403 finally:
404 cache.clear()
405
406def _make_selector(pattern_parts):
407 pat = pattern_parts[0]
408 child_parts = pattern_parts[1:]
409 if pat == '**':
410 cls = _RecursiveWildcardSelector
411 elif '**' in pat:
412 raise ValueError("Invalid pattern: '**' can only be an entire path component")
413 elif _is_wildcard_pattern(pat):
414 cls = _WildcardSelector
415 else:
416 cls = _PreciseSelector
417 return cls(pat, child_parts)
418
419if hasattr(functools, "lru_cache"):
420 _make_selector = functools.lru_cache()(_make_selector)
421
422
423class _Selector:
424 """A selector matches a specific glob pattern part against the children
425 of a given path."""
426
427 def __init__(self, child_parts):
428 self.child_parts = child_parts
429 if child_parts:
430 self.successor = _make_selector(child_parts)
431 else:
432 self.successor = _TerminatingSelector()
433
434 def select_from(self, parent_path):
435 """Iterate over all child paths of `parent_path` matched by this
436 selector. This can contain parent_path itself."""
437 path_cls = type(parent_path)
438 is_dir = path_cls.is_dir
439 exists = path_cls.exists
440 listdir = parent_path._accessor.listdir
441 return self._select_from(parent_path, is_dir, exists, listdir)
442
443
444class _TerminatingSelector:
445
446 def _select_from(self, parent_path, is_dir, exists, listdir):
447 yield parent_path
448
449
450class _PreciseSelector(_Selector):
451
452 def __init__(self, name, child_parts):
453 self.name = name
454 _Selector.__init__(self, child_parts)
455
456 def _select_from(self, parent_path, is_dir, exists, listdir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800457 try:
458 if not is_dir(parent_path):
459 return
460 path = parent_path._make_child_relpath(self.name)
461 if exists(path):
462 for p in self.successor._select_from(path, is_dir, exists, listdir):
463 yield p
464 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100465 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100466
467
468class _WildcardSelector(_Selector):
469
470 def __init__(self, pat, child_parts):
471 self.pat = re.compile(fnmatch.translate(pat))
472 _Selector.__init__(self, child_parts)
473
474 def _select_from(self, parent_path, is_dir, exists, listdir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800475 try:
476 if not is_dir(parent_path):
477 return
478 cf = parent_path._flavour.casefold
479 for name in listdir(parent_path):
480 casefolded = cf(name)
481 if self.pat.match(casefolded):
482 path = parent_path._make_child_relpath(name)
483 for p in self.successor._select_from(path, is_dir, exists, listdir):
484 yield p
485 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100486 return
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800487
Antoine Pitrou31119e42013-11-22 17:38:12 +0100488
489
490class _RecursiveWildcardSelector(_Selector):
491
492 def __init__(self, pat, child_parts):
493 _Selector.__init__(self, child_parts)
494
495 def _iterate_directories(self, parent_path, is_dir, listdir):
496 yield parent_path
Guido van Rossumbc9fdda2016-01-07 10:56:36 -0800497 try:
498 for name in listdir(parent_path):
499 path = parent_path._make_child_relpath(name)
500 if is_dir(path) and not path.is_symlink():
501 for p in self._iterate_directories(path, is_dir, listdir):
502 yield p
503 except PermissionError:
504 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100505
506 def _select_from(self, parent_path, is_dir, exists, listdir):
Guido van Rossum6c2d33a2016-01-06 09:42:07 -0800507 try:
508 if not is_dir(parent_path):
509 return
510 with _cached(listdir) as listdir:
511 yielded = set()
512 try:
513 successor_select = self.successor._select_from
514 for starting_point in self._iterate_directories(parent_path, is_dir, listdir):
515 for p in successor_select(starting_point, is_dir, exists, listdir):
516 if p not in yielded:
517 yield p
518 yielded.add(p)
519 finally:
520 yielded.clear()
521 except PermissionError:
Antoine Pitrou31119e42013-11-22 17:38:12 +0100522 return
Antoine Pitrou31119e42013-11-22 17:38:12 +0100523
524
525#
526# Public API
527#
528
529class _PathParents(Sequence):
530 """This object provides sequence-like access to the logical ancestors
531 of a path. Don't try to construct it yourself."""
532 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
533
534 def __init__(self, path):
535 # We don't store the instance to avoid reference cycles
536 self._pathcls = type(path)
537 self._drv = path._drv
538 self._root = path._root
539 self._parts = path._parts
540
541 def __len__(self):
542 if self._drv or self._root:
543 return len(self._parts) - 1
544 else:
545 return len(self._parts)
546
547 def __getitem__(self, idx):
548 if idx < 0 or idx >= len(self):
549 raise IndexError(idx)
550 return self._pathcls._from_parsed_parts(self._drv, self._root,
551 self._parts[:-idx - 1])
552
553 def __repr__(self):
554 return "<{}.parents>".format(self._pathcls.__name__)
555
556
557class PurePath(object):
558 """PurePath represents a filesystem path and offers operations which
559 don't imply any actual filesystem I/O. Depending on your system,
560 instantiating a PurePath will return either a PurePosixPath or a
561 PureWindowsPath object. You can also instantiate either of these classes
562 directly, regardless of your system.
563 """
564 __slots__ = (
565 '_drv', '_root', '_parts',
566 '_str', '_hash', '_pparts', '_cached_cparts',
567 )
568
569 def __new__(cls, *args):
570 """Construct a PurePath from one or several strings and or existing
571 PurePath objects. The strings and path objects are combined so as
572 to yield a canonicalized path, which is incorporated into the
573 new PurePath object.
574 """
575 if cls is PurePath:
576 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
577 return cls._from_parts(args)
578
579 def __reduce__(self):
580 # Using the parts tuple helps share interned path parts
581 # when pickling related paths.
582 return (self.__class__, tuple(self._parts))
583
584 @classmethod
585 def _parse_args(cls, args):
586 # This is useful when you don't want to create an instance, just
587 # canonicalize some constructor arguments.
588 parts = []
589 for a in args:
590 if isinstance(a, PurePath):
591 parts += a._parts
592 elif isinstance(a, str):
Antoine Pitroucb5ec772014-04-23 00:34:15 +0200593 # Force-cast str subclasses to str (issue #21127)
594 parts.append(str(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100595 else:
596 raise TypeError(
597 "argument should be a path or str object, not %r"
598 % type(a))
599 return cls._flavour.parse_parts(parts)
600
601 @classmethod
602 def _from_parts(cls, args, init=True):
603 # We need to call _parse_args on the instance, so as to get the
604 # right flavour.
605 self = object.__new__(cls)
606 drv, root, parts = self._parse_args(args)
607 self._drv = drv
608 self._root = root
609 self._parts = parts
610 if init:
611 self._init()
612 return self
613
614 @classmethod
615 def _from_parsed_parts(cls, drv, root, parts, init=True):
616 self = object.__new__(cls)
617 self._drv = drv
618 self._root = root
619 self._parts = parts
620 if init:
621 self._init()
622 return self
623
624 @classmethod
625 def _format_parsed_parts(cls, drv, root, parts):
626 if drv or root:
627 return drv + root + cls._flavour.join(parts[1:])
628 else:
629 return cls._flavour.join(parts)
630
631 def _init(self):
632 # Overriden in concrete Path
633 pass
634
635 def _make_child(self, args):
636 drv, root, parts = self._parse_args(args)
637 drv, root, parts = self._flavour.join_parsed_parts(
638 self._drv, self._root, self._parts, drv, root, parts)
639 return self._from_parsed_parts(drv, root, parts)
640
641 def __str__(self):
642 """Return the string representation of the path, suitable for
643 passing to system calls."""
644 try:
645 return self._str
646 except AttributeError:
647 self._str = self._format_parsed_parts(self._drv, self._root,
648 self._parts) or '.'
649 return self._str
650
Guido van Rossume4282312016-01-06 11:01:42 -0800651 @property
652 def path(self):
653 try:
654 return self._str
655 except AttributeError:
656 return str(self)
657
Antoine Pitrou31119e42013-11-22 17:38:12 +0100658 def as_posix(self):
659 """Return the string representation of the path with forward (/)
660 slashes."""
661 f = self._flavour
662 return str(self).replace(f.sep, '/')
663
664 def __bytes__(self):
665 """Return the bytes representation of the path. This is only
666 recommended to use under Unix."""
667 return os.fsencode(str(self))
668
669 def __repr__(self):
670 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
671
672 def as_uri(self):
673 """Return the path as a 'file' URI."""
674 if not self.is_absolute():
675 raise ValueError("relative path can't be expressed as a file URI")
676 return self._flavour.make_uri(self)
677
678 @property
679 def _cparts(self):
680 # Cached casefolded parts, for hashing and comparison
681 try:
682 return self._cached_cparts
683 except AttributeError:
684 self._cached_cparts = self._flavour.casefold_parts(self._parts)
685 return self._cached_cparts
686
687 def __eq__(self, other):
688 if not isinstance(other, PurePath):
689 return NotImplemented
690 return self._cparts == other._cparts and self._flavour is other._flavour
691
Antoine Pitrou31119e42013-11-22 17:38:12 +0100692 def __hash__(self):
693 try:
694 return self._hash
695 except AttributeError:
696 self._hash = hash(tuple(self._cparts))
697 return self._hash
698
699 def __lt__(self, other):
700 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
701 return NotImplemented
702 return self._cparts < other._cparts
703
704 def __le__(self, other):
705 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
706 return NotImplemented
707 return self._cparts <= other._cparts
708
709 def __gt__(self, other):
710 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
711 return NotImplemented
712 return self._cparts > other._cparts
713
714 def __ge__(self, other):
715 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
716 return NotImplemented
717 return self._cparts >= other._cparts
718
719 drive = property(attrgetter('_drv'),
720 doc="""The drive prefix (letter or UNC path), if any.""")
721
722 root = property(attrgetter('_root'),
723 doc="""The root of the path, if any.""")
724
725 @property
726 def anchor(self):
727 """The concatenation of the drive and root, or ''."""
728 anchor = self._drv + self._root
729 return anchor
730
731 @property
732 def name(self):
733 """The final path component, if any."""
734 parts = self._parts
735 if len(parts) == (1 if (self._drv or self._root) else 0):
736 return ''
737 return parts[-1]
738
739 @property
740 def suffix(self):
741 """The final component's last suffix, if any."""
742 name = self.name
743 i = name.rfind('.')
744 if 0 < i < len(name) - 1:
745 return name[i:]
746 else:
747 return ''
748
749 @property
750 def suffixes(self):
751 """A list of the final component's suffixes, if any."""
752 name = self.name
753 if name.endswith('.'):
754 return []
755 name = name.lstrip('.')
756 return ['.' + suffix for suffix in name.split('.')[1:]]
757
758 @property
759 def stem(self):
760 """The final path component, minus its last suffix."""
761 name = self.name
762 i = name.rfind('.')
763 if 0 < i < len(name) - 1:
764 return name[:i]
765 else:
766 return name
767
768 def with_name(self, name):
769 """Return a new path with the file name changed."""
770 if not self.name:
771 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400772 drv, root, parts = self._flavour.parse_parts((name,))
773 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
774 or drv or root or len(parts) != 1):
775 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100776 return self._from_parsed_parts(self._drv, self._root,
777 self._parts[:-1] + [name])
778
779 def with_suffix(self, suffix):
780 """Return a new path with the file suffix changed (or added, if none)."""
781 # XXX if suffix is None, should the current suffix be removed?
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400782 f = self._flavour
783 if f.sep in suffix or f.altsep and f.altsep in suffix:
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100784 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400785 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100786 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100787 name = self.name
788 if not name:
789 raise ValueError("%r has an empty name" % (self,))
790 old_suffix = self.suffix
791 if not old_suffix:
792 name = name + suffix
793 else:
794 name = name[:-len(old_suffix)] + suffix
795 return self._from_parsed_parts(self._drv, self._root,
796 self._parts[:-1] + [name])
797
798 def relative_to(self, *other):
799 """Return the relative path to another path identified by the passed
800 arguments. If the operation is not possible (because this is not
801 a subpath of the other path), raise ValueError.
802 """
803 # For the purpose of this method, drive and root are considered
804 # separate parts, i.e.:
805 # Path('c:/').relative_to('c:') gives Path('/')
806 # Path('c:/').relative_to('/') raise ValueError
807 if not other:
808 raise TypeError("need at least one argument")
809 parts = self._parts
810 drv = self._drv
811 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100812 if root:
813 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100814 else:
815 abs_parts = parts
816 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100817 if to_root:
818 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100819 else:
820 to_abs_parts = to_parts
821 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100822 cf = self._flavour.casefold_parts
823 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100824 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
825 raise ValueError("{!r} does not start with {!r}"
826 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100827 return self._from_parsed_parts('', root if n == 1 else '',
828 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100829
830 @property
831 def parts(self):
832 """An object providing sequence-like access to the
833 components in the filesystem path."""
834 # We cache the tuple to avoid building a new one each time .parts
835 # is accessed. XXX is this necessary?
836 try:
837 return self._pparts
838 except AttributeError:
839 self._pparts = tuple(self._parts)
840 return self._pparts
841
842 def joinpath(self, *args):
843 """Combine this path with one or several arguments, and return a
844 new path representing either a subpath (if all arguments are relative
845 paths) or a totally different path (if one of the arguments is
846 anchored).
847 """
848 return self._make_child(args)
849
850 def __truediv__(self, key):
851 return self._make_child((key,))
852
853 def __rtruediv__(self, key):
854 return self._from_parts([key] + self._parts)
855
856 @property
857 def parent(self):
858 """The logical parent of the path."""
859 drv = self._drv
860 root = self._root
861 parts = self._parts
862 if len(parts) == 1 and (drv or root):
863 return self
864 return self._from_parsed_parts(drv, root, parts[:-1])
865
866 @property
867 def parents(self):
868 """A sequence of this path's logical parents."""
869 return _PathParents(self)
870
871 def is_absolute(self):
872 """True if the path is absolute (has both a root and, if applicable,
873 a drive)."""
874 if not self._root:
875 return False
876 return not self._flavour.has_drv or bool(self._drv)
877
878 def is_reserved(self):
879 """Return True if the path contains one of the special names reserved
880 by the system, if any."""
881 return self._flavour.is_reserved(self._parts)
882
883 def match(self, path_pattern):
884 """
885 Return True if this path matches the given pattern.
886 """
887 cf = self._flavour.casefold
888 path_pattern = cf(path_pattern)
889 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
890 if not pat_parts:
891 raise ValueError("empty pattern")
892 if drv and drv != cf(self._drv):
893 return False
894 if root and root != cf(self._root):
895 return False
896 parts = self._cparts
897 if drv or root:
898 if len(pat_parts) != len(parts):
899 return False
900 pat_parts = pat_parts[1:]
901 elif len(pat_parts) > len(parts):
902 return False
903 for part, pat in zip(reversed(parts), reversed(pat_parts)):
904 if not fnmatch.fnmatchcase(part, pat):
905 return False
906 return True
907
908
909class PurePosixPath(PurePath):
910 _flavour = _posix_flavour
911 __slots__ = ()
912
913
914class PureWindowsPath(PurePath):
915 _flavour = _windows_flavour
916 __slots__ = ()
917
918
919# Filesystem-accessing classes
920
921
922class Path(PurePath):
923 __slots__ = (
924 '_accessor',
925 '_closed',
926 )
927
928 def __new__(cls, *args, **kwargs):
929 if cls is Path:
930 cls = WindowsPath if os.name == 'nt' else PosixPath
931 self = cls._from_parts(args, init=False)
932 if not self._flavour.is_supported:
933 raise NotImplementedError("cannot instantiate %r on your system"
934 % (cls.__name__,))
935 self._init()
936 return self
937
938 def _init(self,
939 # Private non-constructor arguments
940 template=None,
941 ):
942 self._closed = False
943 if template is not None:
944 self._accessor = template._accessor
945 else:
946 self._accessor = _normal_accessor
947
948 def _make_child_relpath(self, part):
949 # This is an optimization used for dir walking. `part` must be
950 # a single part relative to this path.
951 parts = self._parts + [part]
952 return self._from_parsed_parts(self._drv, self._root, parts)
953
954 def __enter__(self):
955 if self._closed:
956 self._raise_closed()
957 return self
958
959 def __exit__(self, t, v, tb):
960 self._closed = True
961
962 def _raise_closed(self):
963 raise ValueError("I/O operation on closed path")
964
965 def _opener(self, name, flags, mode=0o666):
966 # A stub for the opener argument to built-in open()
967 return self._accessor.open(self, flags, mode)
968
Antoine Pitrou4a60d422013-12-02 21:25:18 +0100969 def _raw_open(self, flags, mode=0o777):
970 """
971 Open the file pointed by this path and return a file descriptor,
972 as os.open() does.
973 """
974 if self._closed:
975 self._raise_closed()
976 return self._accessor.open(self, flags, mode)
977
Antoine Pitrou31119e42013-11-22 17:38:12 +0100978 # Public API
979
980 @classmethod
981 def cwd(cls):
982 """Return a new path pointing to the current working directory
983 (as returned by os.getcwd()).
984 """
985 return cls(os.getcwd())
986
987 def iterdir(self):
988 """Iterate over the files in this directory. Does not yield any
989 result for the special paths '.' and '..'.
990 """
991 if self._closed:
992 self._raise_closed()
993 for name in self._accessor.listdir(self):
994 if name in {'.', '..'}:
995 # Yielding a path object for these makes little sense
996 continue
997 yield self._make_child_relpath(name)
998 if self._closed:
999 self._raise_closed()
1000
1001 def glob(self, pattern):
1002 """Iterate over this subtree and yield all existing files (of any
1003 kind, including directories) matching the given pattern.
1004 """
1005 pattern = self._flavour.casefold(pattern)
1006 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1007 if drv or root:
1008 raise NotImplementedError("Non-relative patterns are unsupported")
1009 selector = _make_selector(tuple(pattern_parts))
1010 for p in selector.select_from(self):
1011 yield p
1012
1013 def rglob(self, pattern):
1014 """Recursively yield all existing files (of any kind, including
1015 directories) matching the given pattern, anywhere in this subtree.
1016 """
1017 pattern = self._flavour.casefold(pattern)
1018 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1019 if drv or root:
1020 raise NotImplementedError("Non-relative patterns are unsupported")
1021 selector = _make_selector(("**",) + tuple(pattern_parts))
1022 for p in selector.select_from(self):
1023 yield p
1024
1025 def absolute(self):
1026 """Return an absolute version of this path. This function works
1027 even if the path doesn't point to anything.
1028
1029 No normalization is done, i.e. all '.' and '..' will be kept along.
1030 Use resolve() to get the canonical path to a file.
1031 """
1032 # XXX untested yet!
1033 if self._closed:
1034 self._raise_closed()
1035 if self.is_absolute():
1036 return self
1037 # FIXME this must defer to the specific flavour (and, under Windows,
1038 # use nt._getfullpathname())
1039 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1040 obj._init(template=self)
1041 return obj
1042
1043 def resolve(self):
1044 """
1045 Make the path absolute, resolving all symlinks on the way and also
1046 normalizing it (for example turning slashes into backslashes under
1047 Windows).
1048 """
1049 if self._closed:
1050 self._raise_closed()
1051 s = self._flavour.resolve(self)
1052 if s is None:
1053 # No symlink resolution => for consistency, raise an error if
1054 # the path doesn't exist or is forbidden
1055 self.stat()
1056 s = str(self.absolute())
1057 # Now we have no symlinks in the path, it's safe to normalize it.
1058 normed = self._flavour.pathmod.normpath(s)
1059 obj = self._from_parts((normed,), init=False)
1060 obj._init(template=self)
1061 return obj
1062
1063 def stat(self):
1064 """
1065 Return the result of the stat() system call on this path, like
1066 os.stat() does.
1067 """
1068 return self._accessor.stat(self)
1069
1070 def owner(self):
1071 """
1072 Return the login name of the file owner.
1073 """
1074 import pwd
1075 return pwd.getpwuid(self.stat().st_uid).pw_name
1076
1077 def group(self):
1078 """
1079 Return the group name of the file gid.
1080 """
1081 import grp
1082 return grp.getgrgid(self.stat().st_gid).gr_name
1083
Antoine Pitrou31119e42013-11-22 17:38:12 +01001084 def open(self, mode='r', buffering=-1, encoding=None,
1085 errors=None, newline=None):
1086 """
1087 Open the file pointed by this path and return a file object, as
1088 the built-in open() function does.
1089 """
1090 if self._closed:
1091 self._raise_closed()
1092 return io.open(str(self), mode, buffering, encoding, errors, newline,
1093 opener=self._opener)
1094
1095 def touch(self, mode=0o666, exist_ok=True):
1096 """
1097 Create this file with the given access mode, if it doesn't exist.
1098 """
1099 if self._closed:
1100 self._raise_closed()
1101 if exist_ok:
1102 # First try to bump modification time
1103 # Implementation note: GNU touch uses the UTIME_NOW option of
1104 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001105 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001106 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001107 except OSError:
1108 # Avoid exception chaining
1109 pass
1110 else:
1111 return
1112 flags = os.O_CREAT | os.O_WRONLY
1113 if not exist_ok:
1114 flags |= os.O_EXCL
1115 fd = self._raw_open(flags, mode)
1116 os.close(fd)
1117
1118 def mkdir(self, mode=0o777, parents=False):
1119 if self._closed:
1120 self._raise_closed()
1121 if not parents:
1122 self._accessor.mkdir(self, mode)
1123 else:
1124 try:
1125 self._accessor.mkdir(self, mode)
1126 except OSError as e:
1127 if e.errno != ENOENT:
1128 raise
Antoine Pitrou0048c982013-12-16 20:22:37 +01001129 self.parent.mkdir(parents=True)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001130 self._accessor.mkdir(self, mode)
1131
1132 def chmod(self, mode):
1133 """
1134 Change the permissions of the path, like os.chmod().
1135 """
1136 if self._closed:
1137 self._raise_closed()
1138 self._accessor.chmod(self, mode)
1139
1140 def lchmod(self, mode):
1141 """
1142 Like chmod(), except if the path points to a symlink, the symlink's
1143 permissions are changed, rather than its target's.
1144 """
1145 if self._closed:
1146 self._raise_closed()
1147 self._accessor.lchmod(self, mode)
1148
1149 def unlink(self):
1150 """
1151 Remove this file or link.
1152 If the path is a directory, use rmdir() instead.
1153 """
1154 if self._closed:
1155 self._raise_closed()
1156 self._accessor.unlink(self)
1157
1158 def rmdir(self):
1159 """
1160 Remove this directory. The directory must be empty.
1161 """
1162 if self._closed:
1163 self._raise_closed()
1164 self._accessor.rmdir(self)
1165
1166 def lstat(self):
1167 """
1168 Like stat(), except if the path points to a symlink, the symlink's
1169 status information is returned, rather than its target's.
1170 """
1171 if self._closed:
1172 self._raise_closed()
1173 return self._accessor.lstat(self)
1174
1175 def rename(self, target):
1176 """
1177 Rename this path to the given path.
1178 """
1179 if self._closed:
1180 self._raise_closed()
1181 self._accessor.rename(self, target)
1182
1183 def replace(self, target):
1184 """
1185 Rename this path to the given path, clobbering the existing
1186 destination if it exists.
1187 """
1188 if self._closed:
1189 self._raise_closed()
1190 self._accessor.replace(self, target)
1191
1192 def symlink_to(self, target, target_is_directory=False):
1193 """
1194 Make this path a symlink pointing to the given path.
1195 Note the order of arguments (self, target) is the reverse of os.symlink's.
1196 """
1197 if self._closed:
1198 self._raise_closed()
1199 self._accessor.symlink(target, self, target_is_directory)
1200
1201 # Convenience functions for querying the stat results
1202
1203 def exists(self):
1204 """
1205 Whether this path exists.
1206 """
1207 try:
1208 self.stat()
1209 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001210 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001211 raise
1212 return False
1213 return True
1214
1215 def is_dir(self):
1216 """
1217 Whether this path is a directory.
1218 """
1219 try:
1220 return S_ISDIR(self.stat().st_mode)
1221 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001222 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001223 raise
1224 # Path doesn't exist or is a broken symlink
1225 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1226 return False
1227
1228 def is_file(self):
1229 """
1230 Whether this path is a regular file (also True for symlinks pointing
1231 to regular files).
1232 """
1233 try:
1234 return S_ISREG(self.stat().st_mode)
1235 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001236 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001237 raise
1238 # Path doesn't exist or is a broken symlink
1239 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1240 return False
1241
1242 def is_symlink(self):
1243 """
1244 Whether this path is a symbolic link.
1245 """
1246 try:
1247 return S_ISLNK(self.lstat().st_mode)
1248 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001249 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001250 raise
1251 # Path doesn't exist
1252 return False
1253
1254 def is_block_device(self):
1255 """
1256 Whether this path is a block device.
1257 """
1258 try:
1259 return S_ISBLK(self.stat().st_mode)
1260 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001261 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001262 raise
1263 # Path doesn't exist or is a broken symlink
1264 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1265 return False
1266
1267 def is_char_device(self):
1268 """
1269 Whether this path is a character device.
1270 """
1271 try:
1272 return S_ISCHR(self.stat().st_mode)
1273 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001274 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001275 raise
1276 # Path doesn't exist or is a broken symlink
1277 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1278 return False
1279
1280 def is_fifo(self):
1281 """
1282 Whether this path is a FIFO.
1283 """
1284 try:
1285 return S_ISFIFO(self.stat().st_mode)
1286 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001287 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001288 raise
1289 # Path doesn't exist or is a broken symlink
1290 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1291 return False
1292
1293 def is_socket(self):
1294 """
1295 Whether this path is a socket.
1296 """
1297 try:
1298 return S_ISSOCK(self.stat().st_mode)
1299 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001300 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001301 raise
1302 # Path doesn't exist or is a broken symlink
1303 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1304 return False
1305
1306
1307class PosixPath(Path, PurePosixPath):
1308 __slots__ = ()
1309
1310class WindowsPath(Path, PureWindowsPath):
1311 __slots__ = ()