blob: 918ac8d563703785b9e0eacfdd88510deac58db4 [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):
457 if not is_dir(parent_path):
458 return
459 path = parent_path._make_child_relpath(self.name)
460 if exists(path):
461 for p in self.successor._select_from(path, is_dir, exists, listdir):
462 yield p
463
464
465class _WildcardSelector(_Selector):
466
467 def __init__(self, pat, child_parts):
468 self.pat = re.compile(fnmatch.translate(pat))
469 _Selector.__init__(self, child_parts)
470
471 def _select_from(self, parent_path, is_dir, exists, listdir):
472 if not is_dir(parent_path):
473 return
474 cf = parent_path._flavour.casefold
475 for name in listdir(parent_path):
476 casefolded = cf(name)
477 if self.pat.match(casefolded):
478 path = parent_path._make_child_relpath(name)
479 for p in self.successor._select_from(path, is_dir, exists, listdir):
480 yield p
481
482
483class _RecursiveWildcardSelector(_Selector):
484
485 def __init__(self, pat, child_parts):
486 _Selector.__init__(self, child_parts)
487
488 def _iterate_directories(self, parent_path, is_dir, listdir):
489 yield parent_path
490 for name in listdir(parent_path):
491 path = parent_path._make_child_relpath(name)
492 if is_dir(path):
493 for p in self._iterate_directories(path, is_dir, listdir):
494 yield p
495
496 def _select_from(self, parent_path, is_dir, exists, listdir):
497 if not is_dir(parent_path):
498 return
499 with _cached(listdir) as listdir:
500 yielded = set()
501 try:
502 successor_select = self.successor._select_from
503 for starting_point in self._iterate_directories(parent_path, is_dir, listdir):
504 for p in successor_select(starting_point, is_dir, exists, listdir):
505 if p not in yielded:
506 yield p
507 yielded.add(p)
508 finally:
509 yielded.clear()
510
511
512#
513# Public API
514#
515
516class _PathParents(Sequence):
517 """This object provides sequence-like access to the logical ancestors
518 of a path. Don't try to construct it yourself."""
519 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
520
521 def __init__(self, path):
522 # We don't store the instance to avoid reference cycles
523 self._pathcls = type(path)
524 self._drv = path._drv
525 self._root = path._root
526 self._parts = path._parts
527
528 def __len__(self):
529 if self._drv or self._root:
530 return len(self._parts) - 1
531 else:
532 return len(self._parts)
533
534 def __getitem__(self, idx):
535 if idx < 0 or idx >= len(self):
536 raise IndexError(idx)
537 return self._pathcls._from_parsed_parts(self._drv, self._root,
538 self._parts[:-idx - 1])
539
540 def __repr__(self):
541 return "<{}.parents>".format(self._pathcls.__name__)
542
543
544class PurePath(object):
545 """PurePath represents a filesystem path and offers operations which
546 don't imply any actual filesystem I/O. Depending on your system,
547 instantiating a PurePath will return either a PurePosixPath or a
548 PureWindowsPath object. You can also instantiate either of these classes
549 directly, regardless of your system.
550 """
551 __slots__ = (
552 '_drv', '_root', '_parts',
553 '_str', '_hash', '_pparts', '_cached_cparts',
554 )
555
556 def __new__(cls, *args):
557 """Construct a PurePath from one or several strings and or existing
558 PurePath objects. The strings and path objects are combined so as
559 to yield a canonicalized path, which is incorporated into the
560 new PurePath object.
561 """
562 if cls is PurePath:
563 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
564 return cls._from_parts(args)
565
566 def __reduce__(self):
567 # Using the parts tuple helps share interned path parts
568 # when pickling related paths.
569 return (self.__class__, tuple(self._parts))
570
571 @classmethod
572 def _parse_args(cls, args):
573 # This is useful when you don't want to create an instance, just
574 # canonicalize some constructor arguments.
575 parts = []
576 for a in args:
577 if isinstance(a, PurePath):
578 parts += a._parts
579 elif isinstance(a, str):
Antoine Pitroucb5ec772014-04-23 00:34:15 +0200580 # Force-cast str subclasses to str (issue #21127)
581 parts.append(str(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100582 else:
583 raise TypeError(
584 "argument should be a path or str object, not %r"
585 % type(a))
586 return cls._flavour.parse_parts(parts)
587
588 @classmethod
589 def _from_parts(cls, args, init=True):
590 # We need to call _parse_args on the instance, so as to get the
591 # right flavour.
592 self = object.__new__(cls)
593 drv, root, parts = self._parse_args(args)
594 self._drv = drv
595 self._root = root
596 self._parts = parts
597 if init:
598 self._init()
599 return self
600
601 @classmethod
602 def _from_parsed_parts(cls, drv, root, parts, init=True):
603 self = object.__new__(cls)
604 self._drv = drv
605 self._root = root
606 self._parts = parts
607 if init:
608 self._init()
609 return self
610
611 @classmethod
612 def _format_parsed_parts(cls, drv, root, parts):
613 if drv or root:
614 return drv + root + cls._flavour.join(parts[1:])
615 else:
616 return cls._flavour.join(parts)
617
618 def _init(self):
619 # Overriden in concrete Path
620 pass
621
622 def _make_child(self, args):
623 drv, root, parts = self._parse_args(args)
624 drv, root, parts = self._flavour.join_parsed_parts(
625 self._drv, self._root, self._parts, drv, root, parts)
626 return self._from_parsed_parts(drv, root, parts)
627
628 def __str__(self):
629 """Return the string representation of the path, suitable for
630 passing to system calls."""
631 try:
632 return self._str
633 except AttributeError:
634 self._str = self._format_parsed_parts(self._drv, self._root,
635 self._parts) or '.'
636 return self._str
637
638 def as_posix(self):
639 """Return the string representation of the path with forward (/)
640 slashes."""
641 f = self._flavour
642 return str(self).replace(f.sep, '/')
643
644 def __bytes__(self):
645 """Return the bytes representation of the path. This is only
646 recommended to use under Unix."""
647 return os.fsencode(str(self))
648
649 def __repr__(self):
650 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
651
652 def as_uri(self):
653 """Return the path as a 'file' URI."""
654 if not self.is_absolute():
655 raise ValueError("relative path can't be expressed as a file URI")
656 return self._flavour.make_uri(self)
657
658 @property
659 def _cparts(self):
660 # Cached casefolded parts, for hashing and comparison
661 try:
662 return self._cached_cparts
663 except AttributeError:
664 self._cached_cparts = self._flavour.casefold_parts(self._parts)
665 return self._cached_cparts
666
667 def __eq__(self, other):
668 if not isinstance(other, PurePath):
669 return NotImplemented
670 return self._cparts == other._cparts and self._flavour is other._flavour
671
Antoine Pitrou31119e42013-11-22 17:38:12 +0100672 def __hash__(self):
673 try:
674 return self._hash
675 except AttributeError:
676 self._hash = hash(tuple(self._cparts))
677 return self._hash
678
679 def __lt__(self, other):
680 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
681 return NotImplemented
682 return self._cparts < other._cparts
683
684 def __le__(self, other):
685 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
686 return NotImplemented
687 return self._cparts <= other._cparts
688
689 def __gt__(self, other):
690 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
691 return NotImplemented
692 return self._cparts > other._cparts
693
694 def __ge__(self, other):
695 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
696 return NotImplemented
697 return self._cparts >= other._cparts
698
699 drive = property(attrgetter('_drv'),
700 doc="""The drive prefix (letter or UNC path), if any.""")
701
702 root = property(attrgetter('_root'),
703 doc="""The root of the path, if any.""")
704
705 @property
706 def anchor(self):
707 """The concatenation of the drive and root, or ''."""
708 anchor = self._drv + self._root
709 return anchor
710
711 @property
712 def name(self):
713 """The final path component, if any."""
714 parts = self._parts
715 if len(parts) == (1 if (self._drv or self._root) else 0):
716 return ''
717 return parts[-1]
718
719 @property
720 def suffix(self):
721 """The final component's last suffix, if any."""
722 name = self.name
723 i = name.rfind('.')
724 if 0 < i < len(name) - 1:
725 return name[i:]
726 else:
727 return ''
728
729 @property
730 def suffixes(self):
731 """A list of the final component's suffixes, if any."""
732 name = self.name
733 if name.endswith('.'):
734 return []
735 name = name.lstrip('.')
736 return ['.' + suffix for suffix in name.split('.')[1:]]
737
738 @property
739 def stem(self):
740 """The final path component, minus its last suffix."""
741 name = self.name
742 i = name.rfind('.')
743 if 0 < i < len(name) - 1:
744 return name[:i]
745 else:
746 return name
747
748 def with_name(self, name):
749 """Return a new path with the file name changed."""
750 if not self.name:
751 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400752 drv, root, parts = self._flavour.parse_parts((name,))
753 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
754 or drv or root or len(parts) != 1):
755 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100756 return self._from_parsed_parts(self._drv, self._root,
757 self._parts[:-1] + [name])
758
759 def with_suffix(self, suffix):
760 """Return a new path with the file suffix changed (or added, if none)."""
761 # XXX if suffix is None, should the current suffix be removed?
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400762 f = self._flavour
763 if f.sep in suffix or f.altsep and f.altsep in suffix:
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100764 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400765 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100766 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100767 name = self.name
768 if not name:
769 raise ValueError("%r has an empty name" % (self,))
770 old_suffix = self.suffix
771 if not old_suffix:
772 name = name + suffix
773 else:
774 name = name[:-len(old_suffix)] + suffix
775 return self._from_parsed_parts(self._drv, self._root,
776 self._parts[:-1] + [name])
777
778 def relative_to(self, *other):
779 """Return the relative path to another path identified by the passed
780 arguments. If the operation is not possible (because this is not
781 a subpath of the other path), raise ValueError.
782 """
783 # For the purpose of this method, drive and root are considered
784 # separate parts, i.e.:
785 # Path('c:/').relative_to('c:') gives Path('/')
786 # Path('c:/').relative_to('/') raise ValueError
787 if not other:
788 raise TypeError("need at least one argument")
789 parts = self._parts
790 drv = self._drv
791 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100792 if root:
793 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100794 else:
795 abs_parts = parts
796 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100797 if to_root:
798 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100799 else:
800 to_abs_parts = to_parts
801 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100802 cf = self._flavour.casefold_parts
803 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100804 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
805 raise ValueError("{!r} does not start with {!r}"
806 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100807 return self._from_parsed_parts('', root if n == 1 else '',
808 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100809
810 @property
811 def parts(self):
812 """An object providing sequence-like access to the
813 components in the filesystem path."""
814 # We cache the tuple to avoid building a new one each time .parts
815 # is accessed. XXX is this necessary?
816 try:
817 return self._pparts
818 except AttributeError:
819 self._pparts = tuple(self._parts)
820 return self._pparts
821
822 def joinpath(self, *args):
823 """Combine this path with one or several arguments, and return a
824 new path representing either a subpath (if all arguments are relative
825 paths) or a totally different path (if one of the arguments is
826 anchored).
827 """
828 return self._make_child(args)
829
830 def __truediv__(self, key):
831 return self._make_child((key,))
832
833 def __rtruediv__(self, key):
834 return self._from_parts([key] + self._parts)
835
836 @property
837 def parent(self):
838 """The logical parent of the path."""
839 drv = self._drv
840 root = self._root
841 parts = self._parts
842 if len(parts) == 1 and (drv or root):
843 return self
844 return self._from_parsed_parts(drv, root, parts[:-1])
845
846 @property
847 def parents(self):
848 """A sequence of this path's logical parents."""
849 return _PathParents(self)
850
851 def is_absolute(self):
852 """True if the path is absolute (has both a root and, if applicable,
853 a drive)."""
854 if not self._root:
855 return False
856 return not self._flavour.has_drv or bool(self._drv)
857
858 def is_reserved(self):
859 """Return True if the path contains one of the special names reserved
860 by the system, if any."""
861 return self._flavour.is_reserved(self._parts)
862
863 def match(self, path_pattern):
864 """
865 Return True if this path matches the given pattern.
866 """
867 cf = self._flavour.casefold
868 path_pattern = cf(path_pattern)
869 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
870 if not pat_parts:
871 raise ValueError("empty pattern")
872 if drv and drv != cf(self._drv):
873 return False
874 if root and root != cf(self._root):
875 return False
876 parts = self._cparts
877 if drv or root:
878 if len(pat_parts) != len(parts):
879 return False
880 pat_parts = pat_parts[1:]
881 elif len(pat_parts) > len(parts):
882 return False
883 for part, pat in zip(reversed(parts), reversed(pat_parts)):
884 if not fnmatch.fnmatchcase(part, pat):
885 return False
886 return True
887
888
889class PurePosixPath(PurePath):
890 _flavour = _posix_flavour
891 __slots__ = ()
892
893
894class PureWindowsPath(PurePath):
895 _flavour = _windows_flavour
896 __slots__ = ()
897
898
899# Filesystem-accessing classes
900
901
902class Path(PurePath):
903 __slots__ = (
904 '_accessor',
905 '_closed',
906 )
907
908 def __new__(cls, *args, **kwargs):
909 if cls is Path:
910 cls = WindowsPath if os.name == 'nt' else PosixPath
911 self = cls._from_parts(args, init=False)
912 if not self._flavour.is_supported:
913 raise NotImplementedError("cannot instantiate %r on your system"
914 % (cls.__name__,))
915 self._init()
916 return self
917
918 def _init(self,
919 # Private non-constructor arguments
920 template=None,
921 ):
922 self._closed = False
923 if template is not None:
924 self._accessor = template._accessor
925 else:
926 self._accessor = _normal_accessor
927
928 def _make_child_relpath(self, part):
929 # This is an optimization used for dir walking. `part` must be
930 # a single part relative to this path.
931 parts = self._parts + [part]
932 return self._from_parsed_parts(self._drv, self._root, parts)
933
934 def __enter__(self):
935 if self._closed:
936 self._raise_closed()
937 return self
938
939 def __exit__(self, t, v, tb):
940 self._closed = True
941
942 def _raise_closed(self):
943 raise ValueError("I/O operation on closed path")
944
945 def _opener(self, name, flags, mode=0o666):
946 # A stub for the opener argument to built-in open()
947 return self._accessor.open(self, flags, mode)
948
Antoine Pitrou4a60d422013-12-02 21:25:18 +0100949 def _raw_open(self, flags, mode=0o777):
950 """
951 Open the file pointed by this path and return a file descriptor,
952 as os.open() does.
953 """
954 if self._closed:
955 self._raise_closed()
956 return self._accessor.open(self, flags, mode)
957
Antoine Pitrou31119e42013-11-22 17:38:12 +0100958 # Public API
959
960 @classmethod
961 def cwd(cls):
962 """Return a new path pointing to the current working directory
963 (as returned by os.getcwd()).
964 """
965 return cls(os.getcwd())
966
967 def iterdir(self):
968 """Iterate over the files in this directory. Does not yield any
969 result for the special paths '.' and '..'.
970 """
971 if self._closed:
972 self._raise_closed()
973 for name in self._accessor.listdir(self):
974 if name in {'.', '..'}:
975 # Yielding a path object for these makes little sense
976 continue
977 yield self._make_child_relpath(name)
978 if self._closed:
979 self._raise_closed()
980
981 def glob(self, pattern):
982 """Iterate over this subtree and yield all existing files (of any
983 kind, including directories) matching the given pattern.
984 """
985 pattern = self._flavour.casefold(pattern)
986 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
987 if drv or root:
988 raise NotImplementedError("Non-relative patterns are unsupported")
989 selector = _make_selector(tuple(pattern_parts))
990 for p in selector.select_from(self):
991 yield p
992
993 def rglob(self, pattern):
994 """Recursively yield all existing files (of any kind, including
995 directories) matching the given pattern, anywhere in this subtree.
996 """
997 pattern = self._flavour.casefold(pattern)
998 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
999 if drv or root:
1000 raise NotImplementedError("Non-relative patterns are unsupported")
1001 selector = _make_selector(("**",) + tuple(pattern_parts))
1002 for p in selector.select_from(self):
1003 yield p
1004
1005 def absolute(self):
1006 """Return an absolute version of this path. This function works
1007 even if the path doesn't point to anything.
1008
1009 No normalization is done, i.e. all '.' and '..' will be kept along.
1010 Use resolve() to get the canonical path to a file.
1011 """
1012 # XXX untested yet!
1013 if self._closed:
1014 self._raise_closed()
1015 if self.is_absolute():
1016 return self
1017 # FIXME this must defer to the specific flavour (and, under Windows,
1018 # use nt._getfullpathname())
1019 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1020 obj._init(template=self)
1021 return obj
1022
1023 def resolve(self):
1024 """
1025 Make the path absolute, resolving all symlinks on the way and also
1026 normalizing it (for example turning slashes into backslashes under
1027 Windows).
1028 """
1029 if self._closed:
1030 self._raise_closed()
1031 s = self._flavour.resolve(self)
1032 if s is None:
1033 # No symlink resolution => for consistency, raise an error if
1034 # the path doesn't exist or is forbidden
1035 self.stat()
1036 s = str(self.absolute())
1037 # Now we have no symlinks in the path, it's safe to normalize it.
1038 normed = self._flavour.pathmod.normpath(s)
1039 obj = self._from_parts((normed,), init=False)
1040 obj._init(template=self)
1041 return obj
1042
1043 def stat(self):
1044 """
1045 Return the result of the stat() system call on this path, like
1046 os.stat() does.
1047 """
1048 return self._accessor.stat(self)
1049
1050 def owner(self):
1051 """
1052 Return the login name of the file owner.
1053 """
1054 import pwd
1055 return pwd.getpwuid(self.stat().st_uid).pw_name
1056
1057 def group(self):
1058 """
1059 Return the group name of the file gid.
1060 """
1061 import grp
1062 return grp.getgrgid(self.stat().st_gid).gr_name
1063
Antoine Pitrou31119e42013-11-22 17:38:12 +01001064 def open(self, mode='r', buffering=-1, encoding=None,
1065 errors=None, newline=None):
1066 """
1067 Open the file pointed by this path and return a file object, as
1068 the built-in open() function does.
1069 """
1070 if self._closed:
1071 self._raise_closed()
1072 return io.open(str(self), mode, buffering, encoding, errors, newline,
1073 opener=self._opener)
1074
1075 def touch(self, mode=0o666, exist_ok=True):
1076 """
1077 Create this file with the given access mode, if it doesn't exist.
1078 """
1079 if self._closed:
1080 self._raise_closed()
1081 if exist_ok:
1082 # First try to bump modification time
1083 # Implementation note: GNU touch uses the UTIME_NOW option of
1084 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001085 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001086 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001087 except OSError:
1088 # Avoid exception chaining
1089 pass
1090 else:
1091 return
1092 flags = os.O_CREAT | os.O_WRONLY
1093 if not exist_ok:
1094 flags |= os.O_EXCL
1095 fd = self._raw_open(flags, mode)
1096 os.close(fd)
1097
1098 def mkdir(self, mode=0o777, parents=False):
1099 if self._closed:
1100 self._raise_closed()
1101 if not parents:
1102 self._accessor.mkdir(self, mode)
1103 else:
1104 try:
1105 self._accessor.mkdir(self, mode)
1106 except OSError as e:
1107 if e.errno != ENOENT:
1108 raise
Antoine Pitrou0048c982013-12-16 20:22:37 +01001109 self.parent.mkdir(parents=True)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001110 self._accessor.mkdir(self, mode)
1111
1112 def chmod(self, mode):
1113 """
1114 Change the permissions of the path, like os.chmod().
1115 """
1116 if self._closed:
1117 self._raise_closed()
1118 self._accessor.chmod(self, mode)
1119
1120 def lchmod(self, mode):
1121 """
1122 Like chmod(), except if the path points to a symlink, the symlink's
1123 permissions are changed, rather than its target's.
1124 """
1125 if self._closed:
1126 self._raise_closed()
1127 self._accessor.lchmod(self, mode)
1128
1129 def unlink(self):
1130 """
1131 Remove this file or link.
1132 If the path is a directory, use rmdir() instead.
1133 """
1134 if self._closed:
1135 self._raise_closed()
1136 self._accessor.unlink(self)
1137
1138 def rmdir(self):
1139 """
1140 Remove this directory. The directory must be empty.
1141 """
1142 if self._closed:
1143 self._raise_closed()
1144 self._accessor.rmdir(self)
1145
1146 def lstat(self):
1147 """
1148 Like stat(), except if the path points to a symlink, the symlink's
1149 status information is returned, rather than its target's.
1150 """
1151 if self._closed:
1152 self._raise_closed()
1153 return self._accessor.lstat(self)
1154
1155 def rename(self, target):
1156 """
1157 Rename this path to the given path.
1158 """
1159 if self._closed:
1160 self._raise_closed()
1161 self._accessor.rename(self, target)
1162
1163 def replace(self, target):
1164 """
1165 Rename this path to the given path, clobbering the existing
1166 destination if it exists.
1167 """
1168 if self._closed:
1169 self._raise_closed()
1170 self._accessor.replace(self, target)
1171
1172 def symlink_to(self, target, target_is_directory=False):
1173 """
1174 Make this path a symlink pointing to the given path.
1175 Note the order of arguments (self, target) is the reverse of os.symlink's.
1176 """
1177 if self._closed:
1178 self._raise_closed()
1179 self._accessor.symlink(target, self, target_is_directory)
1180
1181 # Convenience functions for querying the stat results
1182
1183 def exists(self):
1184 """
1185 Whether this path exists.
1186 """
1187 try:
1188 self.stat()
1189 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001190 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001191 raise
1192 return False
1193 return True
1194
1195 def is_dir(self):
1196 """
1197 Whether this path is a directory.
1198 """
1199 try:
1200 return S_ISDIR(self.stat().st_mode)
1201 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001202 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001203 raise
1204 # Path doesn't exist or is a broken symlink
1205 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1206 return False
1207
1208 def is_file(self):
1209 """
1210 Whether this path is a regular file (also True for symlinks pointing
1211 to regular files).
1212 """
1213 try:
1214 return S_ISREG(self.stat().st_mode)
1215 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001216 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001217 raise
1218 # Path doesn't exist or is a broken symlink
1219 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1220 return False
1221
1222 def is_symlink(self):
1223 """
1224 Whether this path is a symbolic link.
1225 """
1226 try:
1227 return S_ISLNK(self.lstat().st_mode)
1228 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001229 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001230 raise
1231 # Path doesn't exist
1232 return False
1233
1234 def is_block_device(self):
1235 """
1236 Whether this path is a block device.
1237 """
1238 try:
1239 return S_ISBLK(self.stat().st_mode)
1240 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001241 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001242 raise
1243 # Path doesn't exist or is a broken symlink
1244 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1245 return False
1246
1247 def is_char_device(self):
1248 """
1249 Whether this path is a character device.
1250 """
1251 try:
1252 return S_ISCHR(self.stat().st_mode)
1253 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001254 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001255 raise
1256 # Path doesn't exist or is a broken symlink
1257 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1258 return False
1259
1260 def is_fifo(self):
1261 """
1262 Whether this path is a FIFO.
1263 """
1264 try:
1265 return S_ISFIFO(self.stat().st_mode)
1266 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001267 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001268 raise
1269 # Path doesn't exist or is a broken symlink
1270 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1271 return False
1272
1273 def is_socket(self):
1274 """
1275 Whether this path is a socket.
1276 """
1277 try:
1278 return S_ISSOCK(self.stat().st_mode)
1279 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001280 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001281 raise
1282 # Path doesn't exist or is a broken symlink
1283 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1284 return False
1285
1286
1287class PosixPath(Path, PurePosixPath):
1288 __slots__ = ()
1289
1290class WindowsPath(Path, PureWindowsPath):
1291 __slots__ = ()