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