blob: 5d3636443eb82fac9cb73ba4c0da4e00a564b398 [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
668 def __ne__(self, other):
669 return not self == other
670
671 def __hash__(self):
672 try:
673 return self._hash
674 except AttributeError:
675 self._hash = hash(tuple(self._cparts))
676 return self._hash
677
678 def __lt__(self, other):
679 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
680 return NotImplemented
681 return self._cparts < other._cparts
682
683 def __le__(self, other):
684 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
685 return NotImplemented
686 return self._cparts <= other._cparts
687
688 def __gt__(self, other):
689 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
690 return NotImplemented
691 return self._cparts > other._cparts
692
693 def __ge__(self, other):
694 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
695 return NotImplemented
696 return self._cparts >= other._cparts
697
698 drive = property(attrgetter('_drv'),
699 doc="""The drive prefix (letter or UNC path), if any.""")
700
701 root = property(attrgetter('_root'),
702 doc="""The root of the path, if any.""")
703
704 @property
705 def anchor(self):
706 """The concatenation of the drive and root, or ''."""
707 anchor = self._drv + self._root
708 return anchor
709
710 @property
711 def name(self):
712 """The final path component, if any."""
713 parts = self._parts
714 if len(parts) == (1 if (self._drv or self._root) else 0):
715 return ''
716 return parts[-1]
717
718 @property
719 def suffix(self):
720 """The final component's last suffix, if any."""
721 name = self.name
722 i = name.rfind('.')
723 if 0 < i < len(name) - 1:
724 return name[i:]
725 else:
726 return ''
727
728 @property
729 def suffixes(self):
730 """A list of the final component's suffixes, if any."""
731 name = self.name
732 if name.endswith('.'):
733 return []
734 name = name.lstrip('.')
735 return ['.' + suffix for suffix in name.split('.')[1:]]
736
737 @property
738 def stem(self):
739 """The final path component, minus its last suffix."""
740 name = self.name
741 i = name.rfind('.')
742 if 0 < i < len(name) - 1:
743 return name[:i]
744 else:
745 return name
746
747 def with_name(self, name):
748 """Return a new path with the file name changed."""
749 if not self.name:
750 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400751 drv, root, parts = self._flavour.parse_parts((name,))
752 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
753 or drv or root or len(parts) != 1):
754 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100755 return self._from_parsed_parts(self._drv, self._root,
756 self._parts[:-1] + [name])
757
758 def with_suffix(self, suffix):
759 """Return a new path with the file suffix changed (or added, if none)."""
760 # XXX if suffix is None, should the current suffix be removed?
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400761 f = self._flavour
762 if f.sep in suffix or f.altsep and f.altsep in suffix:
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100763 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400764 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100765 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100766 name = self.name
767 if not name:
768 raise ValueError("%r has an empty name" % (self,))
769 old_suffix = self.suffix
770 if not old_suffix:
771 name = name + suffix
772 else:
773 name = name[:-len(old_suffix)] + suffix
774 return self._from_parsed_parts(self._drv, self._root,
775 self._parts[:-1] + [name])
776
777 def relative_to(self, *other):
778 """Return the relative path to another path identified by the passed
779 arguments. If the operation is not possible (because this is not
780 a subpath of the other path), raise ValueError.
781 """
782 # For the purpose of this method, drive and root are considered
783 # separate parts, i.e.:
784 # Path('c:/').relative_to('c:') gives Path('/')
785 # Path('c:/').relative_to('/') raise ValueError
786 if not other:
787 raise TypeError("need at least one argument")
788 parts = self._parts
789 drv = self._drv
790 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100791 if root:
792 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100793 else:
794 abs_parts = parts
795 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100796 if to_root:
797 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100798 else:
799 to_abs_parts = to_parts
800 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100801 cf = self._flavour.casefold_parts
802 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100803 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
804 raise ValueError("{!r} does not start with {!r}"
805 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100806 return self._from_parsed_parts('', root if n == 1 else '',
807 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100808
809 @property
810 def parts(self):
811 """An object providing sequence-like access to the
812 components in the filesystem path."""
813 # We cache the tuple to avoid building a new one each time .parts
814 # is accessed. XXX is this necessary?
815 try:
816 return self._pparts
817 except AttributeError:
818 self._pparts = tuple(self._parts)
819 return self._pparts
820
821 def joinpath(self, *args):
822 """Combine this path with one or several arguments, and return a
823 new path representing either a subpath (if all arguments are relative
824 paths) or a totally different path (if one of the arguments is
825 anchored).
826 """
827 return self._make_child(args)
828
829 def __truediv__(self, key):
830 return self._make_child((key,))
831
832 def __rtruediv__(self, key):
833 return self._from_parts([key] + self._parts)
834
835 @property
836 def parent(self):
837 """The logical parent of the path."""
838 drv = self._drv
839 root = self._root
840 parts = self._parts
841 if len(parts) == 1 and (drv or root):
842 return self
843 return self._from_parsed_parts(drv, root, parts[:-1])
844
845 @property
846 def parents(self):
847 """A sequence of this path's logical parents."""
848 return _PathParents(self)
849
850 def is_absolute(self):
851 """True if the path is absolute (has both a root and, if applicable,
852 a drive)."""
853 if not self._root:
854 return False
855 return not self._flavour.has_drv or bool(self._drv)
856
857 def is_reserved(self):
858 """Return True if the path contains one of the special names reserved
859 by the system, if any."""
860 return self._flavour.is_reserved(self._parts)
861
862 def match(self, path_pattern):
863 """
864 Return True if this path matches the given pattern.
865 """
866 cf = self._flavour.casefold
867 path_pattern = cf(path_pattern)
868 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
869 if not pat_parts:
870 raise ValueError("empty pattern")
871 if drv and drv != cf(self._drv):
872 return False
873 if root and root != cf(self._root):
874 return False
875 parts = self._cparts
876 if drv or root:
877 if len(pat_parts) != len(parts):
878 return False
879 pat_parts = pat_parts[1:]
880 elif len(pat_parts) > len(parts):
881 return False
882 for part, pat in zip(reversed(parts), reversed(pat_parts)):
883 if not fnmatch.fnmatchcase(part, pat):
884 return False
885 return True
886
887
888class PurePosixPath(PurePath):
889 _flavour = _posix_flavour
890 __slots__ = ()
891
892
893class PureWindowsPath(PurePath):
894 _flavour = _windows_flavour
895 __slots__ = ()
896
897
898# Filesystem-accessing classes
899
900
901class Path(PurePath):
902 __slots__ = (
903 '_accessor',
904 '_closed',
905 )
906
907 def __new__(cls, *args, **kwargs):
908 if cls is Path:
909 cls = WindowsPath if os.name == 'nt' else PosixPath
910 self = cls._from_parts(args, init=False)
911 if not self._flavour.is_supported:
912 raise NotImplementedError("cannot instantiate %r on your system"
913 % (cls.__name__,))
914 self._init()
915 return self
916
917 def _init(self,
918 # Private non-constructor arguments
919 template=None,
920 ):
921 self._closed = False
922 if template is not None:
923 self._accessor = template._accessor
924 else:
925 self._accessor = _normal_accessor
926
927 def _make_child_relpath(self, part):
928 # This is an optimization used for dir walking. `part` must be
929 # a single part relative to this path.
930 parts = self._parts + [part]
931 return self._from_parsed_parts(self._drv, self._root, parts)
932
933 def __enter__(self):
934 if self._closed:
935 self._raise_closed()
936 return self
937
938 def __exit__(self, t, v, tb):
939 self._closed = True
940
941 def _raise_closed(self):
942 raise ValueError("I/O operation on closed path")
943
944 def _opener(self, name, flags, mode=0o666):
945 # A stub for the opener argument to built-in open()
946 return self._accessor.open(self, flags, mode)
947
Antoine Pitrou4a60d422013-12-02 21:25:18 +0100948 def _raw_open(self, flags, mode=0o777):
949 """
950 Open the file pointed by this path and return a file descriptor,
951 as os.open() does.
952 """
953 if self._closed:
954 self._raise_closed()
955 return self._accessor.open(self, flags, mode)
956
Antoine Pitrou31119e42013-11-22 17:38:12 +0100957 # Public API
958
959 @classmethod
960 def cwd(cls):
961 """Return a new path pointing to the current working directory
962 (as returned by os.getcwd()).
963 """
964 return cls(os.getcwd())
965
966 def iterdir(self):
967 """Iterate over the files in this directory. Does not yield any
968 result for the special paths '.' and '..'.
969 """
970 if self._closed:
971 self._raise_closed()
972 for name in self._accessor.listdir(self):
973 if name in {'.', '..'}:
974 # Yielding a path object for these makes little sense
975 continue
976 yield self._make_child_relpath(name)
977 if self._closed:
978 self._raise_closed()
979
980 def glob(self, pattern):
981 """Iterate over this subtree and yield all existing files (of any
982 kind, including directories) matching the given pattern.
983 """
984 pattern = self._flavour.casefold(pattern)
985 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
986 if drv or root:
987 raise NotImplementedError("Non-relative patterns are unsupported")
988 selector = _make_selector(tuple(pattern_parts))
989 for p in selector.select_from(self):
990 yield p
991
992 def rglob(self, pattern):
993 """Recursively yield all existing files (of any kind, including
994 directories) matching the given pattern, anywhere in this subtree.
995 """
996 pattern = self._flavour.casefold(pattern)
997 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
998 if drv or root:
999 raise NotImplementedError("Non-relative patterns are unsupported")
1000 selector = _make_selector(("**",) + tuple(pattern_parts))
1001 for p in selector.select_from(self):
1002 yield p
1003
1004 def absolute(self):
1005 """Return an absolute version of this path. This function works
1006 even if the path doesn't point to anything.
1007
1008 No normalization is done, i.e. all '.' and '..' will be kept along.
1009 Use resolve() to get the canonical path to a file.
1010 """
1011 # XXX untested yet!
1012 if self._closed:
1013 self._raise_closed()
1014 if self.is_absolute():
1015 return self
1016 # FIXME this must defer to the specific flavour (and, under Windows,
1017 # use nt._getfullpathname())
1018 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1019 obj._init(template=self)
1020 return obj
1021
1022 def resolve(self):
1023 """
1024 Make the path absolute, resolving all symlinks on the way and also
1025 normalizing it (for example turning slashes into backslashes under
1026 Windows).
1027 """
1028 if self._closed:
1029 self._raise_closed()
1030 s = self._flavour.resolve(self)
1031 if s is None:
1032 # No symlink resolution => for consistency, raise an error if
1033 # the path doesn't exist or is forbidden
1034 self.stat()
1035 s = str(self.absolute())
1036 # Now we have no symlinks in the path, it's safe to normalize it.
1037 normed = self._flavour.pathmod.normpath(s)
1038 obj = self._from_parts((normed,), init=False)
1039 obj._init(template=self)
1040 return obj
1041
1042 def stat(self):
1043 """
1044 Return the result of the stat() system call on this path, like
1045 os.stat() does.
1046 """
1047 return self._accessor.stat(self)
1048
1049 def owner(self):
1050 """
1051 Return the login name of the file owner.
1052 """
1053 import pwd
1054 return pwd.getpwuid(self.stat().st_uid).pw_name
1055
1056 def group(self):
1057 """
1058 Return the group name of the file gid.
1059 """
1060 import grp
1061 return grp.getgrgid(self.stat().st_gid).gr_name
1062
Antoine Pitrou31119e42013-11-22 17:38:12 +01001063 def open(self, mode='r', buffering=-1, encoding=None,
1064 errors=None, newline=None):
1065 """
1066 Open the file pointed by this path and return a file object, as
1067 the built-in open() function does.
1068 """
1069 if self._closed:
1070 self._raise_closed()
1071 return io.open(str(self), mode, buffering, encoding, errors, newline,
1072 opener=self._opener)
1073
1074 def touch(self, mode=0o666, exist_ok=True):
1075 """
1076 Create this file with the given access mode, if it doesn't exist.
1077 """
1078 if self._closed:
1079 self._raise_closed()
1080 if exist_ok:
1081 # First try to bump modification time
1082 # Implementation note: GNU touch uses the UTIME_NOW option of
1083 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001084 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001085 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001086 except OSError:
1087 # Avoid exception chaining
1088 pass
1089 else:
1090 return
1091 flags = os.O_CREAT | os.O_WRONLY
1092 if not exist_ok:
1093 flags |= os.O_EXCL
1094 fd = self._raw_open(flags, mode)
1095 os.close(fd)
1096
1097 def mkdir(self, mode=0o777, parents=False):
1098 if self._closed:
1099 self._raise_closed()
1100 if not parents:
1101 self._accessor.mkdir(self, mode)
1102 else:
1103 try:
1104 self._accessor.mkdir(self, mode)
1105 except OSError as e:
1106 if e.errno != ENOENT:
1107 raise
Antoine Pitrou0048c982013-12-16 20:22:37 +01001108 self.parent.mkdir(parents=True)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001109 self._accessor.mkdir(self, mode)
1110
1111 def chmod(self, mode):
1112 """
1113 Change the permissions of the path, like os.chmod().
1114 """
1115 if self._closed:
1116 self._raise_closed()
1117 self._accessor.chmod(self, mode)
1118
1119 def lchmod(self, mode):
1120 """
1121 Like chmod(), except if the path points to a symlink, the symlink's
1122 permissions are changed, rather than its target's.
1123 """
1124 if self._closed:
1125 self._raise_closed()
1126 self._accessor.lchmod(self, mode)
1127
1128 def unlink(self):
1129 """
1130 Remove this file or link.
1131 If the path is a directory, use rmdir() instead.
1132 """
1133 if self._closed:
1134 self._raise_closed()
1135 self._accessor.unlink(self)
1136
1137 def rmdir(self):
1138 """
1139 Remove this directory. The directory must be empty.
1140 """
1141 if self._closed:
1142 self._raise_closed()
1143 self._accessor.rmdir(self)
1144
1145 def lstat(self):
1146 """
1147 Like stat(), except if the path points to a symlink, the symlink's
1148 status information is returned, rather than its target's.
1149 """
1150 if self._closed:
1151 self._raise_closed()
1152 return self._accessor.lstat(self)
1153
1154 def rename(self, target):
1155 """
1156 Rename this path to the given path.
1157 """
1158 if self._closed:
1159 self._raise_closed()
1160 self._accessor.rename(self, target)
1161
1162 def replace(self, target):
1163 """
1164 Rename this path to the given path, clobbering the existing
1165 destination if it exists.
1166 """
1167 if self._closed:
1168 self._raise_closed()
1169 self._accessor.replace(self, target)
1170
1171 def symlink_to(self, target, target_is_directory=False):
1172 """
1173 Make this path a symlink pointing to the given path.
1174 Note the order of arguments (self, target) is the reverse of os.symlink's.
1175 """
1176 if self._closed:
1177 self._raise_closed()
1178 self._accessor.symlink(target, self, target_is_directory)
1179
1180 # Convenience functions for querying the stat results
1181
1182 def exists(self):
1183 """
1184 Whether this path exists.
1185 """
1186 try:
1187 self.stat()
1188 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001189 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001190 raise
1191 return False
1192 return True
1193
1194 def is_dir(self):
1195 """
1196 Whether this path is a directory.
1197 """
1198 try:
1199 return S_ISDIR(self.stat().st_mode)
1200 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001201 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001202 raise
1203 # Path doesn't exist or is a broken symlink
1204 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1205 return False
1206
1207 def is_file(self):
1208 """
1209 Whether this path is a regular file (also True for symlinks pointing
1210 to regular files).
1211 """
1212 try:
1213 return S_ISREG(self.stat().st_mode)
1214 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001215 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001216 raise
1217 # Path doesn't exist or is a broken symlink
1218 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1219 return False
1220
1221 def is_symlink(self):
1222 """
1223 Whether this path is a symbolic link.
1224 """
1225 try:
1226 return S_ISLNK(self.lstat().st_mode)
1227 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001228 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001229 raise
1230 # Path doesn't exist
1231 return False
1232
1233 def is_block_device(self):
1234 """
1235 Whether this path is a block device.
1236 """
1237 try:
1238 return S_ISBLK(self.stat().st_mode)
1239 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001240 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001241 raise
1242 # Path doesn't exist or is a broken symlink
1243 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1244 return False
1245
1246 def is_char_device(self):
1247 """
1248 Whether this path is a character device.
1249 """
1250 try:
1251 return S_ISCHR(self.stat().st_mode)
1252 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001253 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001254 raise
1255 # Path doesn't exist or is a broken symlink
1256 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1257 return False
1258
1259 def is_fifo(self):
1260 """
1261 Whether this path is a FIFO.
1262 """
1263 try:
1264 return S_ISFIFO(self.stat().st_mode)
1265 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001266 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001267 raise
1268 # Path doesn't exist or is a broken symlink
1269 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1270 return False
1271
1272 def is_socket(self):
1273 """
1274 Whether this path is a socket.
1275 """
1276 try:
1277 return S_ISSOCK(self.stat().st_mode)
1278 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001279 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001280 raise
1281 # Path doesn't exist or is a broken symlink
1282 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1283 return False
1284
1285
1286class PosixPath(Path, PurePosixPath):
1287 __slots__ = ()
1288
1289class WindowsPath(Path, PureWindowsPath):
1290 __slots__ = ()