blob: e1b07ca955667c28f0f04ffa1b7ce5bced13bb51 [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
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100224 def gethomedir(self, username):
225 if 'HOME' in os.environ:
226 userhome = os.environ['HOME']
227 elif 'USERPROFILE' in os.environ:
228 userhome = os.environ['USERPROFILE']
229 elif 'HOMEPATH' in os.environ:
Antoine Pitrou5d4e27e2014-12-30 22:09:42 +0100230 try:
231 drv = os.environ['HOMEDRIVE']
232 except KeyError:
233 drv = ''
234 userhome = drv + os.environ['HOMEPATH']
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100235 else:
236 raise RuntimeError("Can't determine home directory")
237
238 if username:
239 # Try to guess user home directory. By default all users
240 # directories are located in the same place and are named by
241 # corresponding usernames. If current user home directory points
242 # to nonstandard place, this guess is likely wrong.
243 if os.environ['USERNAME'] != username:
244 drv, root, parts = self.parse_parts((userhome,))
245 if parts[-1] != os.environ['USERNAME']:
246 raise RuntimeError("Can't determine home directory "
247 "for %r" % username)
248 parts[-1] = username
249 if drv or root:
250 userhome = drv + root + self.join(parts[1:])
251 else:
252 userhome = self.join(parts)
253 return userhome
Antoine Pitrou31119e42013-11-22 17:38:12 +0100254
255class _PosixFlavour(_Flavour):
256 sep = '/'
257 altsep = ''
258 has_drv = False
259 pathmod = posixpath
260
261 is_supported = (os.name != 'nt')
262
263 def splitroot(self, part, sep=sep):
264 if part and part[0] == sep:
265 stripped_part = part.lstrip(sep)
266 # According to POSIX path resolution:
267 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
268 # "A pathname that begins with two successive slashes may be
269 # interpreted in an implementation-defined manner, although more
270 # than two leading slashes shall be treated as a single slash".
271 if len(part) - len(stripped_part) == 2:
272 return '', sep * 2, stripped_part
273 else:
274 return '', sep, stripped_part
275 else:
276 return '', '', part
277
278 def casefold(self, s):
279 return s
280
281 def casefold_parts(self, parts):
282 return parts
283
284 def resolve(self, path):
285 sep = self.sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100286 accessor = path._accessor
Antoine Pitrouc274fd22013-12-16 19:57:41 +0100287 seen = {}
288 def _resolve(path, rest):
289 if rest.startswith(sep):
290 path = ''
291
292 for name in rest.split(sep):
293 if not name or name == '.':
294 # current dir
295 continue
296 if name == '..':
297 # parent dir
298 path, _, _ = path.rpartition(sep)
299 continue
300 newpath = path + sep + name
301 if newpath in seen:
302 # Already seen this path
303 path = seen[newpath]
304 if path is not None:
305 # use cached value
306 continue
307 # The symlink is not resolved, so we must have a symlink loop.
308 raise RuntimeError("Symlink loop from %r" % newpath)
309 # Resolve the symbolic link
310 try:
311 target = accessor.readlink(newpath)
312 except OSError as e:
313 if e.errno != EINVAL:
314 raise
315 # Not a symlink
316 path = newpath
317 else:
318 seen[newpath] = None # not resolved symlink
319 path = _resolve(path, target)
320 seen[newpath] = path # resolved symlink
321
322 return path
323 # NOTE: according to POSIX, getcwd() cannot contain path components
324 # which are symlinks.
325 base = '' if path.is_absolute() else os.getcwd()
326 return _resolve(base, str(path)) or sep
Antoine Pitrou31119e42013-11-22 17:38:12 +0100327
328 def is_reserved(self, parts):
329 return False
330
331 def make_uri(self, path):
332 # We represent the path using the local filesystem encoding,
333 # for portability to other applications.
334 bpath = bytes(path)
335 return 'file://' + urlquote_from_bytes(bpath)
336
Antoine Pitrou8477ed62014-12-30 20:54:45 +0100337 def gethomedir(self, username):
338 if not username:
339 try:
340 return os.environ['HOME']
341 except KeyError:
342 import pwd
343 return pwd.getpwuid(os.getuid()).pw_dir
344 else:
345 import pwd
346 try:
347 return pwd.getpwnam(username).pw_dir
348 except KeyError:
349 raise RuntimeError("Can't determine home directory "
350 "for %r" % username)
351
Antoine Pitrou31119e42013-11-22 17:38:12 +0100352
353_windows_flavour = _WindowsFlavour()
354_posix_flavour = _PosixFlavour()
355
356
357class _Accessor:
358 """An accessor implements a particular (system-specific or not) way of
359 accessing paths on the filesystem."""
360
361
362class _NormalAccessor(_Accessor):
363
364 def _wrap_strfunc(strfunc):
365 @functools.wraps(strfunc)
366 def wrapped(pathobj, *args):
367 return strfunc(str(pathobj), *args)
368 return staticmethod(wrapped)
369
370 def _wrap_binary_strfunc(strfunc):
371 @functools.wraps(strfunc)
372 def wrapped(pathobjA, pathobjB, *args):
373 return strfunc(str(pathobjA), str(pathobjB), *args)
374 return staticmethod(wrapped)
375
376 stat = _wrap_strfunc(os.stat)
377
378 lstat = _wrap_strfunc(os.lstat)
379
380 open = _wrap_strfunc(os.open)
381
382 listdir = _wrap_strfunc(os.listdir)
383
384 chmod = _wrap_strfunc(os.chmod)
385
386 if hasattr(os, "lchmod"):
387 lchmod = _wrap_strfunc(os.lchmod)
388 else:
389 def lchmod(self, pathobj, mode):
390 raise NotImplementedError("lchmod() not available on this system")
391
392 mkdir = _wrap_strfunc(os.mkdir)
393
394 unlink = _wrap_strfunc(os.unlink)
395
396 rmdir = _wrap_strfunc(os.rmdir)
397
398 rename = _wrap_binary_strfunc(os.rename)
399
400 replace = _wrap_binary_strfunc(os.replace)
401
402 if nt:
403 if supports_symlinks:
404 symlink = _wrap_binary_strfunc(os.symlink)
405 else:
406 def symlink(a, b, target_is_directory):
407 raise NotImplementedError("symlink() not available on this system")
408 else:
409 # Under POSIX, os.symlink() takes two args
410 @staticmethod
411 def symlink(a, b, target_is_directory):
412 return os.symlink(str(a), str(b))
413
414 utime = _wrap_strfunc(os.utime)
415
416 # Helper for resolve()
417 def readlink(self, path):
418 return os.readlink(path)
419
420
421_normal_accessor = _NormalAccessor()
422
423
424#
425# Globbing helpers
426#
427
428@contextmanager
429def _cached(func):
430 try:
431 func.__cached__
432 yield func
433 except AttributeError:
434 cache = {}
435 def wrapper(*args):
436 try:
437 return cache[args]
438 except KeyError:
439 value = cache[args] = func(*args)
440 return value
441 wrapper.__cached__ = True
442 try:
443 yield wrapper
444 finally:
445 cache.clear()
446
447def _make_selector(pattern_parts):
448 pat = pattern_parts[0]
449 child_parts = pattern_parts[1:]
450 if pat == '**':
451 cls = _RecursiveWildcardSelector
452 elif '**' in pat:
453 raise ValueError("Invalid pattern: '**' can only be an entire path component")
454 elif _is_wildcard_pattern(pat):
455 cls = _WildcardSelector
456 else:
457 cls = _PreciseSelector
458 return cls(pat, child_parts)
459
460if hasattr(functools, "lru_cache"):
461 _make_selector = functools.lru_cache()(_make_selector)
462
463
464class _Selector:
465 """A selector matches a specific glob pattern part against the children
466 of a given path."""
467
468 def __init__(self, child_parts):
469 self.child_parts = child_parts
470 if child_parts:
471 self.successor = _make_selector(child_parts)
472 else:
473 self.successor = _TerminatingSelector()
474
475 def select_from(self, parent_path):
476 """Iterate over all child paths of `parent_path` matched by this
477 selector. This can contain parent_path itself."""
478 path_cls = type(parent_path)
479 is_dir = path_cls.is_dir
480 exists = path_cls.exists
481 listdir = parent_path._accessor.listdir
482 return self._select_from(parent_path, is_dir, exists, listdir)
483
484
485class _TerminatingSelector:
486
487 def _select_from(self, parent_path, is_dir, exists, listdir):
488 yield parent_path
489
490
491class _PreciseSelector(_Selector):
492
493 def __init__(self, name, child_parts):
494 self.name = name
495 _Selector.__init__(self, child_parts)
496
497 def _select_from(self, parent_path, is_dir, exists, listdir):
498 if not is_dir(parent_path):
499 return
500 path = parent_path._make_child_relpath(self.name)
501 if exists(path):
502 for p in self.successor._select_from(path, is_dir, exists, listdir):
503 yield p
504
505
506class _WildcardSelector(_Selector):
507
508 def __init__(self, pat, child_parts):
509 self.pat = re.compile(fnmatch.translate(pat))
510 _Selector.__init__(self, child_parts)
511
512 def _select_from(self, parent_path, is_dir, exists, listdir):
513 if not is_dir(parent_path):
514 return
515 cf = parent_path._flavour.casefold
516 for name in listdir(parent_path):
517 casefolded = cf(name)
518 if self.pat.match(casefolded):
519 path = parent_path._make_child_relpath(name)
520 for p in self.successor._select_from(path, is_dir, exists, listdir):
521 yield p
522
523
524class _RecursiveWildcardSelector(_Selector):
525
526 def __init__(self, pat, child_parts):
527 _Selector.__init__(self, child_parts)
528
529 def _iterate_directories(self, parent_path, is_dir, listdir):
530 yield parent_path
531 for name in listdir(parent_path):
532 path = parent_path._make_child_relpath(name)
533 if is_dir(path):
534 for p in self._iterate_directories(path, is_dir, listdir):
535 yield p
536
537 def _select_from(self, parent_path, is_dir, exists, listdir):
538 if not is_dir(parent_path):
539 return
540 with _cached(listdir) as listdir:
541 yielded = set()
542 try:
543 successor_select = self.successor._select_from
544 for starting_point in self._iterate_directories(parent_path, is_dir, listdir):
545 for p in successor_select(starting_point, is_dir, exists, listdir):
546 if p not in yielded:
547 yield p
548 yielded.add(p)
549 finally:
550 yielded.clear()
551
552
553#
554# Public API
555#
556
557class _PathParents(Sequence):
558 """This object provides sequence-like access to the logical ancestors
559 of a path. Don't try to construct it yourself."""
560 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
561
562 def __init__(self, path):
563 # We don't store the instance to avoid reference cycles
564 self._pathcls = type(path)
565 self._drv = path._drv
566 self._root = path._root
567 self._parts = path._parts
568
569 def __len__(self):
570 if self._drv or self._root:
571 return len(self._parts) - 1
572 else:
573 return len(self._parts)
574
575 def __getitem__(self, idx):
576 if idx < 0 or idx >= len(self):
577 raise IndexError(idx)
578 return self._pathcls._from_parsed_parts(self._drv, self._root,
579 self._parts[:-idx - 1])
580
581 def __repr__(self):
582 return "<{}.parents>".format(self._pathcls.__name__)
583
584
585class PurePath(object):
586 """PurePath represents a filesystem path and offers operations which
587 don't imply any actual filesystem I/O. Depending on your system,
588 instantiating a PurePath will return either a PurePosixPath or a
589 PureWindowsPath object. You can also instantiate either of these classes
590 directly, regardless of your system.
591 """
592 __slots__ = (
593 '_drv', '_root', '_parts',
594 '_str', '_hash', '_pparts', '_cached_cparts',
595 )
596
597 def __new__(cls, *args):
598 """Construct a PurePath from one or several strings and or existing
599 PurePath objects. The strings and path objects are combined so as
600 to yield a canonicalized path, which is incorporated into the
601 new PurePath object.
602 """
603 if cls is PurePath:
604 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
605 return cls._from_parts(args)
606
607 def __reduce__(self):
608 # Using the parts tuple helps share interned path parts
609 # when pickling related paths.
610 return (self.__class__, tuple(self._parts))
611
612 @classmethod
613 def _parse_args(cls, args):
614 # This is useful when you don't want to create an instance, just
615 # canonicalize some constructor arguments.
616 parts = []
617 for a in args:
618 if isinstance(a, PurePath):
619 parts += a._parts
620 elif isinstance(a, str):
Antoine Pitroucb5ec772014-04-23 00:34:15 +0200621 # Force-cast str subclasses to str (issue #21127)
622 parts.append(str(a))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100623 else:
624 raise TypeError(
625 "argument should be a path or str object, not %r"
626 % type(a))
627 return cls._flavour.parse_parts(parts)
628
629 @classmethod
630 def _from_parts(cls, args, init=True):
631 # We need to call _parse_args on the instance, so as to get the
632 # right flavour.
633 self = object.__new__(cls)
634 drv, root, parts = self._parse_args(args)
635 self._drv = drv
636 self._root = root
637 self._parts = parts
638 if init:
639 self._init()
640 return self
641
642 @classmethod
643 def _from_parsed_parts(cls, drv, root, parts, init=True):
644 self = object.__new__(cls)
645 self._drv = drv
646 self._root = root
647 self._parts = parts
648 if init:
649 self._init()
650 return self
651
652 @classmethod
653 def _format_parsed_parts(cls, drv, root, parts):
654 if drv or root:
655 return drv + root + cls._flavour.join(parts[1:])
656 else:
657 return cls._flavour.join(parts)
658
659 def _init(self):
660 # Overriden in concrete Path
661 pass
662
663 def _make_child(self, args):
664 drv, root, parts = self._parse_args(args)
665 drv, root, parts = self._flavour.join_parsed_parts(
666 self._drv, self._root, self._parts, drv, root, parts)
667 return self._from_parsed_parts(drv, root, parts)
668
669 def __str__(self):
670 """Return the string representation of the path, suitable for
671 passing to system calls."""
672 try:
673 return self._str
674 except AttributeError:
675 self._str = self._format_parsed_parts(self._drv, self._root,
676 self._parts) or '.'
677 return self._str
678
679 def as_posix(self):
680 """Return the string representation of the path with forward (/)
681 slashes."""
682 f = self._flavour
683 return str(self).replace(f.sep, '/')
684
685 def __bytes__(self):
686 """Return the bytes representation of the path. This is only
687 recommended to use under Unix."""
688 return os.fsencode(str(self))
689
690 def __repr__(self):
691 return "{}({!r})".format(self.__class__.__name__, self.as_posix())
692
693 def as_uri(self):
694 """Return the path as a 'file' URI."""
695 if not self.is_absolute():
696 raise ValueError("relative path can't be expressed as a file URI")
697 return self._flavour.make_uri(self)
698
699 @property
700 def _cparts(self):
701 # Cached casefolded parts, for hashing and comparison
702 try:
703 return self._cached_cparts
704 except AttributeError:
705 self._cached_cparts = self._flavour.casefold_parts(self._parts)
706 return self._cached_cparts
707
708 def __eq__(self, other):
709 if not isinstance(other, PurePath):
710 return NotImplemented
711 return self._cparts == other._cparts and self._flavour is other._flavour
712
Antoine Pitrou31119e42013-11-22 17:38:12 +0100713 def __hash__(self):
714 try:
715 return self._hash
716 except AttributeError:
717 self._hash = hash(tuple(self._cparts))
718 return self._hash
719
720 def __lt__(self, other):
721 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
722 return NotImplemented
723 return self._cparts < other._cparts
724
725 def __le__(self, other):
726 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
727 return NotImplemented
728 return self._cparts <= other._cparts
729
730 def __gt__(self, other):
731 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
732 return NotImplemented
733 return self._cparts > other._cparts
734
735 def __ge__(self, other):
736 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
737 return NotImplemented
738 return self._cparts >= other._cparts
739
740 drive = property(attrgetter('_drv'),
741 doc="""The drive prefix (letter or UNC path), if any.""")
742
743 root = property(attrgetter('_root'),
744 doc="""The root of the path, if any.""")
745
746 @property
747 def anchor(self):
748 """The concatenation of the drive and root, or ''."""
749 anchor = self._drv + self._root
750 return anchor
751
752 @property
753 def name(self):
754 """The final path component, if any."""
755 parts = self._parts
756 if len(parts) == (1 if (self._drv or self._root) else 0):
757 return ''
758 return parts[-1]
759
760 @property
761 def suffix(self):
762 """The final component's last suffix, if any."""
763 name = self.name
764 i = name.rfind('.')
765 if 0 < i < len(name) - 1:
766 return name[i:]
767 else:
768 return ''
769
770 @property
771 def suffixes(self):
772 """A list of the final component's suffixes, if any."""
773 name = self.name
774 if name.endswith('.'):
775 return []
776 name = name.lstrip('.')
777 return ['.' + suffix for suffix in name.split('.')[1:]]
778
779 @property
780 def stem(self):
781 """The final path component, minus its last suffix."""
782 name = self.name
783 i = name.rfind('.')
784 if 0 < i < len(name) - 1:
785 return name[:i]
786 else:
787 return name
788
789 def with_name(self, name):
790 """Return a new path with the file name changed."""
791 if not self.name:
792 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400793 drv, root, parts = self._flavour.parse_parts((name,))
794 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
795 or drv or root or len(parts) != 1):
796 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100797 return self._from_parsed_parts(self._drv, self._root,
798 self._parts[:-1] + [name])
799
800 def with_suffix(self, suffix):
801 """Return a new path with the file suffix changed (or added, if none)."""
802 # XXX if suffix is None, should the current suffix be removed?
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400803 f = self._flavour
804 if f.sep in suffix or f.altsep and f.altsep in suffix:
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100805 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400806 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100807 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100808 name = self.name
809 if not name:
810 raise ValueError("%r has an empty name" % (self,))
811 old_suffix = self.suffix
812 if not old_suffix:
813 name = name + suffix
814 else:
815 name = name[:-len(old_suffix)] + suffix
816 return self._from_parsed_parts(self._drv, self._root,
817 self._parts[:-1] + [name])
818
819 def relative_to(self, *other):
820 """Return the relative path to another path identified by the passed
821 arguments. If the operation is not possible (because this is not
822 a subpath of the other path), raise ValueError.
823 """
824 # For the purpose of this method, drive and root are considered
825 # separate parts, i.e.:
826 # Path('c:/').relative_to('c:') gives Path('/')
827 # Path('c:/').relative_to('/') raise ValueError
828 if not other:
829 raise TypeError("need at least one argument")
830 parts = self._parts
831 drv = self._drv
832 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100833 if root:
834 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100835 else:
836 abs_parts = parts
837 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100838 if to_root:
839 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100840 else:
841 to_abs_parts = to_parts
842 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100843 cf = self._flavour.casefold_parts
844 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100845 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
846 raise ValueError("{!r} does not start with {!r}"
847 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100848 return self._from_parsed_parts('', root if n == 1 else '',
849 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100850
851 @property
852 def parts(self):
853 """An object providing sequence-like access to the
854 components in the filesystem path."""
855 # We cache the tuple to avoid building a new one each time .parts
856 # is accessed. XXX is this necessary?
857 try:
858 return self._pparts
859 except AttributeError:
860 self._pparts = tuple(self._parts)
861 return self._pparts
862
863 def joinpath(self, *args):
864 """Combine this path with one or several arguments, and return a
865 new path representing either a subpath (if all arguments are relative
866 paths) or a totally different path (if one of the arguments is
867 anchored).
868 """
869 return self._make_child(args)
870
871 def __truediv__(self, key):
872 return self._make_child((key,))
873
874 def __rtruediv__(self, key):
875 return self._from_parts([key] + self._parts)
876
877 @property
878 def parent(self):
879 """The logical parent of the path."""
880 drv = self._drv
881 root = self._root
882 parts = self._parts
883 if len(parts) == 1 and (drv or root):
884 return self
885 return self._from_parsed_parts(drv, root, parts[:-1])
886
887 @property
888 def parents(self):
889 """A sequence of this path's logical parents."""
890 return _PathParents(self)
891
892 def is_absolute(self):
893 """True if the path is absolute (has both a root and, if applicable,
894 a drive)."""
895 if not self._root:
896 return False
897 return not self._flavour.has_drv or bool(self._drv)
898
899 def is_reserved(self):
900 """Return True if the path contains one of the special names reserved
901 by the system, if any."""
902 return self._flavour.is_reserved(self._parts)
903
904 def match(self, path_pattern):
905 """
906 Return True if this path matches the given pattern.
907 """
908 cf = self._flavour.casefold
909 path_pattern = cf(path_pattern)
910 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
911 if not pat_parts:
912 raise ValueError("empty pattern")
913 if drv and drv != cf(self._drv):
914 return False
915 if root and root != cf(self._root):
916 return False
917 parts = self._cparts
918 if drv or root:
919 if len(pat_parts) != len(parts):
920 return False
921 pat_parts = pat_parts[1:]
922 elif len(pat_parts) > len(parts):
923 return False
924 for part, pat in zip(reversed(parts), reversed(pat_parts)):
925 if not fnmatch.fnmatchcase(part, pat):
926 return False
927 return True
928
929
930class PurePosixPath(PurePath):
931 _flavour = _posix_flavour
932 __slots__ = ()
933
934
935class PureWindowsPath(PurePath):
936 _flavour = _windows_flavour
937 __slots__ = ()
938
939
940# Filesystem-accessing classes
941
942
943class Path(PurePath):
944 __slots__ = (
945 '_accessor',
946 '_closed',
947 )
948
949 def __new__(cls, *args, **kwargs):
950 if cls is Path:
951 cls = WindowsPath if os.name == 'nt' else PosixPath
952 self = cls._from_parts(args, init=False)
953 if not self._flavour.is_supported:
954 raise NotImplementedError("cannot instantiate %r on your system"
955 % (cls.__name__,))
956 self._init()
957 return self
958
959 def _init(self,
960 # Private non-constructor arguments
961 template=None,
962 ):
963 self._closed = False
964 if template is not None:
965 self._accessor = template._accessor
966 else:
967 self._accessor = _normal_accessor
968
969 def _make_child_relpath(self, part):
970 # This is an optimization used for dir walking. `part` must be
971 # a single part relative to this path.
972 parts = self._parts + [part]
973 return self._from_parsed_parts(self._drv, self._root, parts)
974
975 def __enter__(self):
976 if self._closed:
977 self._raise_closed()
978 return self
979
980 def __exit__(self, t, v, tb):
981 self._closed = True
982
983 def _raise_closed(self):
984 raise ValueError("I/O operation on closed path")
985
986 def _opener(self, name, flags, mode=0o666):
987 # A stub for the opener argument to built-in open()
988 return self._accessor.open(self, flags, mode)
989
Antoine Pitrou4a60d422013-12-02 21:25:18 +0100990 def _raw_open(self, flags, mode=0o777):
991 """
992 Open the file pointed by this path and return a file descriptor,
993 as os.open() does.
994 """
995 if self._closed:
996 self._raise_closed()
997 return self._accessor.open(self, flags, mode)
998
Antoine Pitrou31119e42013-11-22 17:38:12 +0100999 # Public API
1000
1001 @classmethod
1002 def cwd(cls):
1003 """Return a new path pointing to the current working directory
1004 (as returned by os.getcwd()).
1005 """
1006 return cls(os.getcwd())
1007
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001008 @classmethod
1009 def home(cls):
1010 """Return a new path pointing to the user's home directory (as
1011 returned by os.path.expanduser('~')).
1012 """
1013 return cls(cls()._flavour.gethomedir(None))
1014
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001015 def samefile(self, other_path):
1016 """Return whether `other_file` is the same or not as this file.
1017 (as returned by os.path.samefile(file, other_file)).
1018 """
1019 st = self.stat()
1020 try:
1021 other_st = other_path.stat()
1022 except AttributeError:
1023 other_st = os.stat(other_path)
1024 return os.path.samestat(st, other_st)
1025
Antoine Pitrou31119e42013-11-22 17:38:12 +01001026 def iterdir(self):
1027 """Iterate over the files in this directory. Does not yield any
1028 result for the special paths '.' and '..'.
1029 """
1030 if self._closed:
1031 self._raise_closed()
1032 for name in self._accessor.listdir(self):
1033 if name in {'.', '..'}:
1034 # Yielding a path object for these makes little sense
1035 continue
1036 yield self._make_child_relpath(name)
1037 if self._closed:
1038 self._raise_closed()
1039
1040 def glob(self, pattern):
1041 """Iterate over this subtree and yield all existing files (of any
1042 kind, including directories) matching the given pattern.
1043 """
1044 pattern = self._flavour.casefold(pattern)
1045 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1046 if drv or root:
1047 raise NotImplementedError("Non-relative patterns are unsupported")
1048 selector = _make_selector(tuple(pattern_parts))
1049 for p in selector.select_from(self):
1050 yield p
1051
1052 def rglob(self, pattern):
1053 """Recursively yield all existing files (of any kind, including
1054 directories) matching the given pattern, anywhere in this subtree.
1055 """
1056 pattern = self._flavour.casefold(pattern)
1057 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1058 if drv or root:
1059 raise NotImplementedError("Non-relative patterns are unsupported")
1060 selector = _make_selector(("**",) + tuple(pattern_parts))
1061 for p in selector.select_from(self):
1062 yield p
1063
1064 def absolute(self):
1065 """Return an absolute version of this path. This function works
1066 even if the path doesn't point to anything.
1067
1068 No normalization is done, i.e. all '.' and '..' will be kept along.
1069 Use resolve() to get the canonical path to a file.
1070 """
1071 # XXX untested yet!
1072 if self._closed:
1073 self._raise_closed()
1074 if self.is_absolute():
1075 return self
1076 # FIXME this must defer to the specific flavour (and, under Windows,
1077 # use nt._getfullpathname())
1078 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1079 obj._init(template=self)
1080 return obj
1081
1082 def resolve(self):
1083 """
1084 Make the path absolute, resolving all symlinks on the way and also
1085 normalizing it (for example turning slashes into backslashes under
1086 Windows).
1087 """
1088 if self._closed:
1089 self._raise_closed()
1090 s = self._flavour.resolve(self)
1091 if s is None:
1092 # No symlink resolution => for consistency, raise an error if
1093 # the path doesn't exist or is forbidden
1094 self.stat()
1095 s = str(self.absolute())
1096 # Now we have no symlinks in the path, it's safe to normalize it.
1097 normed = self._flavour.pathmod.normpath(s)
1098 obj = self._from_parts((normed,), init=False)
1099 obj._init(template=self)
1100 return obj
1101
1102 def stat(self):
1103 """
1104 Return the result of the stat() system call on this path, like
1105 os.stat() does.
1106 """
1107 return self._accessor.stat(self)
1108
1109 def owner(self):
1110 """
1111 Return the login name of the file owner.
1112 """
1113 import pwd
1114 return pwd.getpwuid(self.stat().st_uid).pw_name
1115
1116 def group(self):
1117 """
1118 Return the group name of the file gid.
1119 """
1120 import grp
1121 return grp.getgrgid(self.stat().st_gid).gr_name
1122
Antoine Pitrou31119e42013-11-22 17:38:12 +01001123 def open(self, mode='r', buffering=-1, encoding=None,
1124 errors=None, newline=None):
1125 """
1126 Open the file pointed by this path and return a file object, as
1127 the built-in open() function does.
1128 """
1129 if self._closed:
1130 self._raise_closed()
1131 return io.open(str(self), mode, buffering, encoding, errors, newline,
1132 opener=self._opener)
1133
Georg Brandlea683982014-10-01 19:12:33 +02001134 def read_bytes(self):
1135 """
1136 Open the file in bytes mode, read it, and close the file.
1137 """
1138 with self.open(mode='rb') as f:
1139 return f.read()
1140
1141 def read_text(self, encoding=None, errors=None):
1142 """
1143 Open the file in text mode, read it, and close the file.
1144 """
1145 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1146 return f.read()
1147
1148 def write_bytes(self, data):
1149 """
1150 Open the file in bytes mode, write to it, and close the file.
1151 """
1152 # type-check for the buffer interface before truncating the file
1153 view = memoryview(data)
1154 with self.open(mode='wb') as f:
1155 return f.write(view)
1156
1157 def write_text(self, data, encoding=None, errors=None):
1158 """
1159 Open the file in text mode, write to it, and close the file.
1160 """
1161 if not isinstance(data, str):
1162 raise TypeError('data must be str, not %s' %
1163 data.__class__.__name__)
1164 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1165 return f.write(data)
1166
Antoine Pitrou31119e42013-11-22 17:38:12 +01001167 def touch(self, mode=0o666, exist_ok=True):
1168 """
1169 Create this file with the given access mode, if it doesn't exist.
1170 """
1171 if self._closed:
1172 self._raise_closed()
1173 if exist_ok:
1174 # First try to bump modification time
1175 # Implementation note: GNU touch uses the UTIME_NOW option of
1176 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001177 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001178 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001179 except OSError:
1180 # Avoid exception chaining
1181 pass
1182 else:
1183 return
1184 flags = os.O_CREAT | os.O_WRONLY
1185 if not exist_ok:
1186 flags |= os.O_EXCL
1187 fd = self._raw_open(flags, mode)
1188 os.close(fd)
1189
Barry Warsaw7c549c42014-08-05 11:28:12 -04001190 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001191 if self._closed:
1192 self._raise_closed()
1193 if not parents:
Barry Warsaw7c549c42014-08-05 11:28:12 -04001194 try:
1195 self._accessor.mkdir(self, mode)
1196 except FileExistsError:
1197 if not exist_ok or not self.is_dir():
1198 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001199 else:
1200 try:
1201 self._accessor.mkdir(self, mode)
Barry Warsaw7c549c42014-08-05 11:28:12 -04001202 except FileExistsError:
1203 if not exist_ok or not self.is_dir():
1204 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001205 except OSError as e:
1206 if e.errno != ENOENT:
1207 raise
Antoine Pitrou0048c982013-12-16 20:22:37 +01001208 self.parent.mkdir(parents=True)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001209 self._accessor.mkdir(self, mode)
1210
1211 def chmod(self, mode):
1212 """
1213 Change the permissions of the path, like os.chmod().
1214 """
1215 if self._closed:
1216 self._raise_closed()
1217 self._accessor.chmod(self, mode)
1218
1219 def lchmod(self, mode):
1220 """
1221 Like chmod(), except if the path points to a symlink, the symlink's
1222 permissions are changed, rather than its target's.
1223 """
1224 if self._closed:
1225 self._raise_closed()
1226 self._accessor.lchmod(self, mode)
1227
1228 def unlink(self):
1229 """
1230 Remove this file or link.
1231 If the path is a directory, use rmdir() instead.
1232 """
1233 if self._closed:
1234 self._raise_closed()
1235 self._accessor.unlink(self)
1236
1237 def rmdir(self):
1238 """
1239 Remove this directory. The directory must be empty.
1240 """
1241 if self._closed:
1242 self._raise_closed()
1243 self._accessor.rmdir(self)
1244
1245 def lstat(self):
1246 """
1247 Like stat(), except if the path points to a symlink, the symlink's
1248 status information is returned, rather than its target's.
1249 """
1250 if self._closed:
1251 self._raise_closed()
1252 return self._accessor.lstat(self)
1253
1254 def rename(self, target):
1255 """
1256 Rename this path to the given path.
1257 """
1258 if self._closed:
1259 self._raise_closed()
1260 self._accessor.rename(self, target)
1261
1262 def replace(self, target):
1263 """
1264 Rename this path to the given path, clobbering the existing
1265 destination if it exists.
1266 """
1267 if self._closed:
1268 self._raise_closed()
1269 self._accessor.replace(self, target)
1270
1271 def symlink_to(self, target, target_is_directory=False):
1272 """
1273 Make this path a symlink pointing to the given path.
1274 Note the order of arguments (self, target) is the reverse of os.symlink's.
1275 """
1276 if self._closed:
1277 self._raise_closed()
1278 self._accessor.symlink(target, self, target_is_directory)
1279
1280 # Convenience functions for querying the stat results
1281
1282 def exists(self):
1283 """
1284 Whether this path exists.
1285 """
1286 try:
1287 self.stat()
1288 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001289 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001290 raise
1291 return False
1292 return True
1293
1294 def is_dir(self):
1295 """
1296 Whether this path is a directory.
1297 """
1298 try:
1299 return S_ISDIR(self.stat().st_mode)
1300 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001301 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001302 raise
1303 # Path doesn't exist or is a broken symlink
1304 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1305 return False
1306
1307 def is_file(self):
1308 """
1309 Whether this path is a regular file (also True for symlinks pointing
1310 to regular files).
1311 """
1312 try:
1313 return S_ISREG(self.stat().st_mode)
1314 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001315 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001316 raise
1317 # Path doesn't exist or is a broken symlink
1318 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1319 return False
1320
1321 def is_symlink(self):
1322 """
1323 Whether this path is a symbolic link.
1324 """
1325 try:
1326 return S_ISLNK(self.lstat().st_mode)
1327 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001328 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001329 raise
1330 # Path doesn't exist
1331 return False
1332
1333 def is_block_device(self):
1334 """
1335 Whether this path is a block device.
1336 """
1337 try:
1338 return S_ISBLK(self.stat().st_mode)
1339 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001340 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001341 raise
1342 # Path doesn't exist or is a broken symlink
1343 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1344 return False
1345
1346 def is_char_device(self):
1347 """
1348 Whether this path is a character device.
1349 """
1350 try:
1351 return S_ISCHR(self.stat().st_mode)
1352 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001353 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001354 raise
1355 # Path doesn't exist or is a broken symlink
1356 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1357 return False
1358
1359 def is_fifo(self):
1360 """
1361 Whether this path is a FIFO.
1362 """
1363 try:
1364 return S_ISFIFO(self.stat().st_mode)
1365 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001366 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001367 raise
1368 # Path doesn't exist or is a broken symlink
1369 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1370 return False
1371
1372 def is_socket(self):
1373 """
1374 Whether this path is a socket.
1375 """
1376 try:
1377 return S_ISSOCK(self.stat().st_mode)
1378 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001379 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001380 raise
1381 # Path doesn't exist or is a broken symlink
1382 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1383 return False
1384
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001385 def expanduser(self):
1386 """ Return a new path with expanded ~ and ~user constructs
1387 (as returned by os.path.expanduser)
1388 """
1389 if (not (self._drv or self._root) and
1390 self._parts and self._parts[0][:1] == '~'):
1391 homedir = self._flavour.gethomedir(self._parts[0][1:])
1392 return self._from_parts([homedir] + self._parts[1:])
1393
1394 return self
1395
Antoine Pitrou31119e42013-11-22 17:38:12 +01001396
1397class PosixPath(Path, PurePosixPath):
1398 __slots__ = ()
1399
1400class WindowsPath(Path, PureWindowsPath):
1401 __slots__ = ()