blob: dd2ccba1a61d72349df68629884a0e9e4f217cc7 [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
713 def __ne__(self, other):
714 return not self == other
715
716 def __hash__(self):
717 try:
718 return self._hash
719 except AttributeError:
720 self._hash = hash(tuple(self._cparts))
721 return self._hash
722
723 def __lt__(self, other):
724 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
725 return NotImplemented
726 return self._cparts < other._cparts
727
728 def __le__(self, other):
729 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
730 return NotImplemented
731 return self._cparts <= other._cparts
732
733 def __gt__(self, other):
734 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
735 return NotImplemented
736 return self._cparts > other._cparts
737
738 def __ge__(self, other):
739 if not isinstance(other, PurePath) or self._flavour is not other._flavour:
740 return NotImplemented
741 return self._cparts >= other._cparts
742
743 drive = property(attrgetter('_drv'),
744 doc="""The drive prefix (letter or UNC path), if any.""")
745
746 root = property(attrgetter('_root'),
747 doc="""The root of the path, if any.""")
748
749 @property
750 def anchor(self):
751 """The concatenation of the drive and root, or ''."""
752 anchor = self._drv + self._root
753 return anchor
754
755 @property
756 def name(self):
757 """The final path component, if any."""
758 parts = self._parts
759 if len(parts) == (1 if (self._drv or self._root) else 0):
760 return ''
761 return parts[-1]
762
763 @property
764 def suffix(self):
765 """The final component's last suffix, if any."""
766 name = self.name
767 i = name.rfind('.')
768 if 0 < i < len(name) - 1:
769 return name[i:]
770 else:
771 return ''
772
773 @property
774 def suffixes(self):
775 """A list of the final component's suffixes, if any."""
776 name = self.name
777 if name.endswith('.'):
778 return []
779 name = name.lstrip('.')
780 return ['.' + suffix for suffix in name.split('.')[1:]]
781
782 @property
783 def stem(self):
784 """The final path component, minus its last suffix."""
785 name = self.name
786 i = name.rfind('.')
787 if 0 < i < len(name) - 1:
788 return name[:i]
789 else:
790 return name
791
792 def with_name(self, name):
793 """Return a new path with the file name changed."""
794 if not self.name:
795 raise ValueError("%r has an empty name" % (self,))
Antoine Pitrou7084e732014-07-06 21:31:12 -0400796 drv, root, parts = self._flavour.parse_parts((name,))
797 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
798 or drv or root or len(parts) != 1):
799 raise ValueError("Invalid name %r" % (name))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100800 return self._from_parsed_parts(self._drv, self._root,
801 self._parts[:-1] + [name])
802
803 def with_suffix(self, suffix):
804 """Return a new path with the file suffix changed (or added, if none)."""
805 # XXX if suffix is None, should the current suffix be removed?
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400806 f = self._flavour
807 if f.sep in suffix or f.altsep and f.altsep in suffix:
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100808 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400809 if suffix and not suffix.startswith('.') or suffix == '.':
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100810 raise ValueError("Invalid suffix %r" % (suffix))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100811 name = self.name
812 if not name:
813 raise ValueError("%r has an empty name" % (self,))
814 old_suffix = self.suffix
815 if not old_suffix:
816 name = name + suffix
817 else:
818 name = name[:-len(old_suffix)] + suffix
819 return self._from_parsed_parts(self._drv, self._root,
820 self._parts[:-1] + [name])
821
822 def relative_to(self, *other):
823 """Return the relative path to another path identified by the passed
824 arguments. If the operation is not possible (because this is not
825 a subpath of the other path), raise ValueError.
826 """
827 # For the purpose of this method, drive and root are considered
828 # separate parts, i.e.:
829 # Path('c:/').relative_to('c:') gives Path('/')
830 # Path('c:/').relative_to('/') raise ValueError
831 if not other:
832 raise TypeError("need at least one argument")
833 parts = self._parts
834 drv = self._drv
835 root = self._root
Antoine Pitrou156b3612013-12-28 19:49:04 +0100836 if root:
837 abs_parts = [drv, root] + parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100838 else:
839 abs_parts = parts
840 to_drv, to_root, to_parts = self._parse_args(other)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100841 if to_root:
842 to_abs_parts = [to_drv, to_root] + to_parts[1:]
Antoine Pitrou31119e42013-11-22 17:38:12 +0100843 else:
844 to_abs_parts = to_parts
845 n = len(to_abs_parts)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100846 cf = self._flavour.casefold_parts
847 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100848 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
849 raise ValueError("{!r} does not start with {!r}"
850 .format(str(self), str(formatted)))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100851 return self._from_parsed_parts('', root if n == 1 else '',
852 abs_parts[n:])
Antoine Pitrou31119e42013-11-22 17:38:12 +0100853
854 @property
855 def parts(self):
856 """An object providing sequence-like access to the
857 components in the filesystem path."""
858 # We cache the tuple to avoid building a new one each time .parts
859 # is accessed. XXX is this necessary?
860 try:
861 return self._pparts
862 except AttributeError:
863 self._pparts = tuple(self._parts)
864 return self._pparts
865
866 def joinpath(self, *args):
867 """Combine this path with one or several arguments, and return a
868 new path representing either a subpath (if all arguments are relative
869 paths) or a totally different path (if one of the arguments is
870 anchored).
871 """
872 return self._make_child(args)
873
874 def __truediv__(self, key):
875 return self._make_child((key,))
876
877 def __rtruediv__(self, key):
878 return self._from_parts([key] + self._parts)
879
880 @property
881 def parent(self):
882 """The logical parent of the path."""
883 drv = self._drv
884 root = self._root
885 parts = self._parts
886 if len(parts) == 1 and (drv or root):
887 return self
888 return self._from_parsed_parts(drv, root, parts[:-1])
889
890 @property
891 def parents(self):
892 """A sequence of this path's logical parents."""
893 return _PathParents(self)
894
895 def is_absolute(self):
896 """True if the path is absolute (has both a root and, if applicable,
897 a drive)."""
898 if not self._root:
899 return False
900 return not self._flavour.has_drv or bool(self._drv)
901
902 def is_reserved(self):
903 """Return True if the path contains one of the special names reserved
904 by the system, if any."""
905 return self._flavour.is_reserved(self._parts)
906
907 def match(self, path_pattern):
908 """
909 Return True if this path matches the given pattern.
910 """
911 cf = self._flavour.casefold
912 path_pattern = cf(path_pattern)
913 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
914 if not pat_parts:
915 raise ValueError("empty pattern")
916 if drv and drv != cf(self._drv):
917 return False
918 if root and root != cf(self._root):
919 return False
920 parts = self._cparts
921 if drv or root:
922 if len(pat_parts) != len(parts):
923 return False
924 pat_parts = pat_parts[1:]
925 elif len(pat_parts) > len(parts):
926 return False
927 for part, pat in zip(reversed(parts), reversed(pat_parts)):
928 if not fnmatch.fnmatchcase(part, pat):
929 return False
930 return True
931
932
933class PurePosixPath(PurePath):
934 _flavour = _posix_flavour
935 __slots__ = ()
936
937
938class PureWindowsPath(PurePath):
939 _flavour = _windows_flavour
940 __slots__ = ()
941
942
943# Filesystem-accessing classes
944
945
946class Path(PurePath):
947 __slots__ = (
948 '_accessor',
949 '_closed',
950 )
951
952 def __new__(cls, *args, **kwargs):
953 if cls is Path:
954 cls = WindowsPath if os.name == 'nt' else PosixPath
955 self = cls._from_parts(args, init=False)
956 if not self._flavour.is_supported:
957 raise NotImplementedError("cannot instantiate %r on your system"
958 % (cls.__name__,))
959 self._init()
960 return self
961
962 def _init(self,
963 # Private non-constructor arguments
964 template=None,
965 ):
966 self._closed = False
967 if template is not None:
968 self._accessor = template._accessor
969 else:
970 self._accessor = _normal_accessor
971
972 def _make_child_relpath(self, part):
973 # This is an optimization used for dir walking. `part` must be
974 # a single part relative to this path.
975 parts = self._parts + [part]
976 return self._from_parsed_parts(self._drv, self._root, parts)
977
978 def __enter__(self):
979 if self._closed:
980 self._raise_closed()
981 return self
982
983 def __exit__(self, t, v, tb):
984 self._closed = True
985
986 def _raise_closed(self):
987 raise ValueError("I/O operation on closed path")
988
989 def _opener(self, name, flags, mode=0o666):
990 # A stub for the opener argument to built-in open()
991 return self._accessor.open(self, flags, mode)
992
Antoine Pitrou4a60d422013-12-02 21:25:18 +0100993 def _raw_open(self, flags, mode=0o777):
994 """
995 Open the file pointed by this path and return a file descriptor,
996 as os.open() does.
997 """
998 if self._closed:
999 self._raise_closed()
1000 return self._accessor.open(self, flags, mode)
1001
Antoine Pitrou31119e42013-11-22 17:38:12 +01001002 # Public API
1003
1004 @classmethod
1005 def cwd(cls):
1006 """Return a new path pointing to the current working directory
1007 (as returned by os.getcwd()).
1008 """
1009 return cls(os.getcwd())
1010
Antoine Pitrou17cba7d2015-01-12 21:03:41 +01001011 @classmethod
1012 def home(cls):
1013 """Return a new path pointing to the user's home directory (as
1014 returned by os.path.expanduser('~')).
1015 """
1016 return cls(cls()._flavour.gethomedir(None))
1017
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001018 def samefile(self, other_path):
1019 """Return whether `other_file` is the same or not as this file.
1020 (as returned by os.path.samefile(file, other_file)).
1021 """
1022 st = self.stat()
1023 try:
1024 other_st = other_path.stat()
1025 except AttributeError:
1026 other_st = os.stat(other_path)
1027 return os.path.samestat(st, other_st)
1028
Antoine Pitrou31119e42013-11-22 17:38:12 +01001029 def iterdir(self):
1030 """Iterate over the files in this directory. Does not yield any
1031 result for the special paths '.' and '..'.
1032 """
1033 if self._closed:
1034 self._raise_closed()
1035 for name in self._accessor.listdir(self):
1036 if name in {'.', '..'}:
1037 # Yielding a path object for these makes little sense
1038 continue
1039 yield self._make_child_relpath(name)
1040 if self._closed:
1041 self._raise_closed()
1042
1043 def glob(self, pattern):
1044 """Iterate over this subtree and yield all existing files (of any
1045 kind, including directories) matching the given pattern.
1046 """
1047 pattern = self._flavour.casefold(pattern)
1048 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1049 if drv or root:
1050 raise NotImplementedError("Non-relative patterns are unsupported")
1051 selector = _make_selector(tuple(pattern_parts))
1052 for p in selector.select_from(self):
1053 yield p
1054
1055 def rglob(self, pattern):
1056 """Recursively yield all existing files (of any kind, including
1057 directories) matching the given pattern, anywhere in this subtree.
1058 """
1059 pattern = self._flavour.casefold(pattern)
1060 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1061 if drv or root:
1062 raise NotImplementedError("Non-relative patterns are unsupported")
1063 selector = _make_selector(("**",) + tuple(pattern_parts))
1064 for p in selector.select_from(self):
1065 yield p
1066
1067 def absolute(self):
1068 """Return an absolute version of this path. This function works
1069 even if the path doesn't point to anything.
1070
1071 No normalization is done, i.e. all '.' and '..' will be kept along.
1072 Use resolve() to get the canonical path to a file.
1073 """
1074 # XXX untested yet!
1075 if self._closed:
1076 self._raise_closed()
1077 if self.is_absolute():
1078 return self
1079 # FIXME this must defer to the specific flavour (and, under Windows,
1080 # use nt._getfullpathname())
1081 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1082 obj._init(template=self)
1083 return obj
1084
1085 def resolve(self):
1086 """
1087 Make the path absolute, resolving all symlinks on the way and also
1088 normalizing it (for example turning slashes into backslashes under
1089 Windows).
1090 """
1091 if self._closed:
1092 self._raise_closed()
1093 s = self._flavour.resolve(self)
1094 if s is None:
1095 # No symlink resolution => for consistency, raise an error if
1096 # the path doesn't exist or is forbidden
1097 self.stat()
1098 s = str(self.absolute())
1099 # Now we have no symlinks in the path, it's safe to normalize it.
1100 normed = self._flavour.pathmod.normpath(s)
1101 obj = self._from_parts((normed,), init=False)
1102 obj._init(template=self)
1103 return obj
1104
1105 def stat(self):
1106 """
1107 Return the result of the stat() system call on this path, like
1108 os.stat() does.
1109 """
1110 return self._accessor.stat(self)
1111
1112 def owner(self):
1113 """
1114 Return the login name of the file owner.
1115 """
1116 import pwd
1117 return pwd.getpwuid(self.stat().st_uid).pw_name
1118
1119 def group(self):
1120 """
1121 Return the group name of the file gid.
1122 """
1123 import grp
1124 return grp.getgrgid(self.stat().st_gid).gr_name
1125
Antoine Pitrou31119e42013-11-22 17:38:12 +01001126 def open(self, mode='r', buffering=-1, encoding=None,
1127 errors=None, newline=None):
1128 """
1129 Open the file pointed by this path and return a file object, as
1130 the built-in open() function does.
1131 """
1132 if self._closed:
1133 self._raise_closed()
1134 return io.open(str(self), mode, buffering, encoding, errors, newline,
1135 opener=self._opener)
1136
Georg Brandlea683982014-10-01 19:12:33 +02001137 def read_bytes(self):
1138 """
1139 Open the file in bytes mode, read it, and close the file.
1140 """
1141 with self.open(mode='rb') as f:
1142 return f.read()
1143
1144 def read_text(self, encoding=None, errors=None):
1145 """
1146 Open the file in text mode, read it, and close the file.
1147 """
1148 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1149 return f.read()
1150
1151 def write_bytes(self, data):
1152 """
1153 Open the file in bytes mode, write to it, and close the file.
1154 """
1155 # type-check for the buffer interface before truncating the file
1156 view = memoryview(data)
1157 with self.open(mode='wb') as f:
1158 return f.write(view)
1159
1160 def write_text(self, data, encoding=None, errors=None):
1161 """
1162 Open the file in text mode, write to it, and close the file.
1163 """
1164 if not isinstance(data, str):
1165 raise TypeError('data must be str, not %s' %
1166 data.__class__.__name__)
1167 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1168 return f.write(data)
1169
Antoine Pitrou31119e42013-11-22 17:38:12 +01001170 def touch(self, mode=0o666, exist_ok=True):
1171 """
1172 Create this file with the given access mode, if it doesn't exist.
1173 """
1174 if self._closed:
1175 self._raise_closed()
1176 if exist_ok:
1177 # First try to bump modification time
1178 # Implementation note: GNU touch uses the UTIME_NOW option of
1179 # the utimensat() / futimens() functions.
Antoine Pitrou31119e42013-11-22 17:38:12 +01001180 try:
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001181 self._accessor.utime(self, None)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001182 except OSError:
1183 # Avoid exception chaining
1184 pass
1185 else:
1186 return
1187 flags = os.O_CREAT | os.O_WRONLY
1188 if not exist_ok:
1189 flags |= os.O_EXCL
1190 fd = self._raw_open(flags, mode)
1191 os.close(fd)
1192
Barry Warsaw7c549c42014-08-05 11:28:12 -04001193 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001194 if self._closed:
1195 self._raise_closed()
1196 if not parents:
Barry Warsaw7c549c42014-08-05 11:28:12 -04001197 try:
1198 self._accessor.mkdir(self, mode)
1199 except FileExistsError:
1200 if not exist_ok or not self.is_dir():
1201 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001202 else:
1203 try:
1204 self._accessor.mkdir(self, mode)
Barry Warsaw7c549c42014-08-05 11:28:12 -04001205 except FileExistsError:
1206 if not exist_ok or not self.is_dir():
1207 raise
Antoine Pitrou31119e42013-11-22 17:38:12 +01001208 except OSError as e:
1209 if e.errno != ENOENT:
1210 raise
Antoine Pitrou0048c982013-12-16 20:22:37 +01001211 self.parent.mkdir(parents=True)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001212 self._accessor.mkdir(self, mode)
1213
1214 def chmod(self, mode):
1215 """
1216 Change the permissions of the path, like os.chmod().
1217 """
1218 if self._closed:
1219 self._raise_closed()
1220 self._accessor.chmod(self, mode)
1221
1222 def lchmod(self, mode):
1223 """
1224 Like chmod(), except if the path points to a symlink, the symlink's
1225 permissions are changed, rather than its target's.
1226 """
1227 if self._closed:
1228 self._raise_closed()
1229 self._accessor.lchmod(self, mode)
1230
1231 def unlink(self):
1232 """
1233 Remove this file or link.
1234 If the path is a directory, use rmdir() instead.
1235 """
1236 if self._closed:
1237 self._raise_closed()
1238 self._accessor.unlink(self)
1239
1240 def rmdir(self):
1241 """
1242 Remove this directory. The directory must be empty.
1243 """
1244 if self._closed:
1245 self._raise_closed()
1246 self._accessor.rmdir(self)
1247
1248 def lstat(self):
1249 """
1250 Like stat(), except if the path points to a symlink, the symlink's
1251 status information is returned, rather than its target's.
1252 """
1253 if self._closed:
1254 self._raise_closed()
1255 return self._accessor.lstat(self)
1256
1257 def rename(self, target):
1258 """
1259 Rename this path to the given path.
1260 """
1261 if self._closed:
1262 self._raise_closed()
1263 self._accessor.rename(self, target)
1264
1265 def replace(self, target):
1266 """
1267 Rename this path to the given path, clobbering the existing
1268 destination if it exists.
1269 """
1270 if self._closed:
1271 self._raise_closed()
1272 self._accessor.replace(self, target)
1273
1274 def symlink_to(self, target, target_is_directory=False):
1275 """
1276 Make this path a symlink pointing to the given path.
1277 Note the order of arguments (self, target) is the reverse of os.symlink's.
1278 """
1279 if self._closed:
1280 self._raise_closed()
1281 self._accessor.symlink(target, self, target_is_directory)
1282
1283 # Convenience functions for querying the stat results
1284
1285 def exists(self):
1286 """
1287 Whether this path exists.
1288 """
1289 try:
1290 self.stat()
1291 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001292 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001293 raise
1294 return False
1295 return True
1296
1297 def is_dir(self):
1298 """
1299 Whether this path is a directory.
1300 """
1301 try:
1302 return S_ISDIR(self.stat().st_mode)
1303 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001304 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001305 raise
1306 # Path doesn't exist or is a broken symlink
1307 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1308 return False
1309
1310 def is_file(self):
1311 """
1312 Whether this path is a regular file (also True for symlinks pointing
1313 to regular files).
1314 """
1315 try:
1316 return S_ISREG(self.stat().st_mode)
1317 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001318 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001319 raise
1320 # Path doesn't exist or is a broken symlink
1321 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1322 return False
1323
1324 def is_symlink(self):
1325 """
1326 Whether this path is a symbolic link.
1327 """
1328 try:
1329 return S_ISLNK(self.lstat().st_mode)
1330 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001331 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001332 raise
1333 # Path doesn't exist
1334 return False
1335
1336 def is_block_device(self):
1337 """
1338 Whether this path is a block device.
1339 """
1340 try:
1341 return S_ISBLK(self.stat().st_mode)
1342 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001343 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001344 raise
1345 # Path doesn't exist or is a broken symlink
1346 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1347 return False
1348
1349 def is_char_device(self):
1350 """
1351 Whether this path is a character device.
1352 """
1353 try:
1354 return S_ISCHR(self.stat().st_mode)
1355 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001356 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001357 raise
1358 # Path doesn't exist or is a broken symlink
1359 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1360 return False
1361
1362 def is_fifo(self):
1363 """
1364 Whether this path is a FIFO.
1365 """
1366 try:
1367 return S_ISFIFO(self.stat().st_mode)
1368 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001369 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001370 raise
1371 # Path doesn't exist or is a broken symlink
1372 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1373 return False
1374
1375 def is_socket(self):
1376 """
1377 Whether this path is a socket.
1378 """
1379 try:
1380 return S_ISSOCK(self.stat().st_mode)
1381 except OSError as e:
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001382 if e.errno not in (ENOENT, ENOTDIR):
Antoine Pitrou31119e42013-11-22 17:38:12 +01001383 raise
1384 # Path doesn't exist or is a broken symlink
1385 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1386 return False
1387
Antoine Pitrou8477ed62014-12-30 20:54:45 +01001388 def expanduser(self):
1389 """ Return a new path with expanded ~ and ~user constructs
1390 (as returned by os.path.expanduser)
1391 """
1392 if (not (self._drv or self._root) and
1393 self._parts and self._parts[0][:1] == '~'):
1394 homedir = self._flavour.gethomedir(self._parts[0][1:])
1395 return self._from_parts([homedir] + self._parts[1:])
1396
1397 return self
1398
Antoine Pitrou31119e42013-11-22 17:38:12 +01001399
1400class PosixPath(Path, PurePosixPath):
1401 __slots__ = ()
1402
1403class WindowsPath(Path, PureWindowsPath):
1404 __slots__ = ()