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