comparison env/lib/python3.7/site-packages/pathlib2/__init__.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
1 # Copyright (c) 2014-2017 Matthias C. M. Troffaes
2 # Copyright (c) 2012-2014 Antoine Pitrou and contributors
3 # Distributed under the terms of the MIT License.
4
5 import ctypes
6 import fnmatch
7 import functools
8 import io
9 import ntpath
10 import os
11 import posixpath
12 import re
13 import six
14 import sys
15
16 from errno import EINVAL, ENOENT, ENOTDIR, EBADF
17 from errno import EEXIST, EPERM, EACCES
18 from operator import attrgetter
19 from stat import (
20 S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO)
21
22 try:
23 from collections.abc import Sequence
24 except ImportError:
25 from collections import Sequence
26
27 try:
28 from urllib import quote as urlquote_from_bytes
29 except ImportError:
30 from urllib.parse import quote_from_bytes as urlquote_from_bytes
31
32
33 try:
34 intern = intern
35 except NameError:
36 intern = sys.intern
37
38 supports_symlinks = True
39 if os.name == 'nt':
40 import nt
41 if sys.getwindowsversion()[:2] >= (6, 0) and sys.version_info >= (3, 2):
42 from nt import _getfinalpathname
43 else:
44 supports_symlinks = False
45 _getfinalpathname = None
46 else:
47 nt = None
48
49 try:
50 from os import scandir as os_scandir
51 except ImportError:
52 from scandir import scandir as os_scandir
53
54 __all__ = [
55 "PurePath", "PurePosixPath", "PureWindowsPath",
56 "Path", "PosixPath", "WindowsPath",
57 ]
58
59 #
60 # Internals
61 #
62
63 # EBADF - guard agains macOS `stat` throwing EBADF
64 _IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF)
65
66 _IGNORED_WINERRORS = (
67 21, # ERROR_NOT_READY - drive exists but is not accessible
68 )
69
70
71 def _ignore_error(exception):
72 return (getattr(exception, 'errno', None) in _IGNORED_ERROS or
73 getattr(exception, 'winerror', None) in _IGNORED_WINERRORS)
74
75
76 def _py2_fsencode(parts):
77 # py2 => minimal unicode support
78 assert six.PY2
79 return [part.encode('ascii') if isinstance(part, six.text_type)
80 else part for part in parts]
81
82
83 def _try_except_fileexistserror(try_func, except_func, else_func=None):
84 if sys.version_info >= (3, 3):
85 try:
86 try_func()
87 except FileExistsError as exc:
88 except_func(exc)
89 else:
90 if else_func is not None:
91 else_func()
92 else:
93 try:
94 try_func()
95 except EnvironmentError as exc:
96 if exc.errno != EEXIST:
97 raise
98 else:
99 except_func(exc)
100 else:
101 if else_func is not None:
102 else_func()
103
104
105 def _try_except_filenotfounderror(try_func, except_func):
106 if sys.version_info >= (3, 3):
107 try:
108 try_func()
109 except FileNotFoundError as exc:
110 except_func(exc)
111 elif os.name != 'nt':
112 try:
113 try_func()
114 except EnvironmentError as exc:
115 if exc.errno != ENOENT:
116 raise
117 else:
118 except_func(exc)
119 else:
120 try:
121 try_func()
122 except WindowsError as exc:
123 # errno contains winerror
124 # 2 = file not found
125 # 3 = path not found
126 if exc.errno not in (2, 3):
127 raise
128 else:
129 except_func(exc)
130 except EnvironmentError as exc:
131 if exc.errno != ENOENT:
132 raise
133 else:
134 except_func(exc)
135
136
137 def _try_except_permissionerror_iter(try_iter, except_iter):
138 if sys.version_info >= (3, 3):
139 try:
140 for x in try_iter():
141 yield x
142 except PermissionError as exc:
143 for x in except_iter(exc):
144 yield x
145 else:
146 try:
147 for x in try_iter():
148 yield x
149 except EnvironmentError as exc:
150 if exc.errno not in (EPERM, EACCES):
151 raise
152 else:
153 for x in except_iter(exc):
154 yield x
155
156
157 def _win32_get_unique_path_id(path):
158 # get file information, needed for samefile on older Python versions
159 # see http://timgolden.me.uk/python/win32_how_do_i/
160 # see_if_two_files_are_the_same_file.html
161 from ctypes import POINTER, Structure, WinError
162 from ctypes.wintypes import DWORD, HANDLE, BOOL
163
164 class FILETIME(Structure):
165 _fields_ = [("datetime_lo", DWORD),
166 ("datetime_hi", DWORD),
167 ]
168
169 class BY_HANDLE_FILE_INFORMATION(Structure):
170 _fields_ = [("attributes", DWORD),
171 ("created_at", FILETIME),
172 ("accessed_at", FILETIME),
173 ("written_at", FILETIME),
174 ("volume", DWORD),
175 ("file_hi", DWORD),
176 ("file_lo", DWORD),
177 ("n_links", DWORD),
178 ("index_hi", DWORD),
179 ("index_lo", DWORD),
180 ]
181
182 CreateFile = ctypes.windll.kernel32.CreateFileW
183 CreateFile.argtypes = [ctypes.c_wchar_p, DWORD, DWORD, ctypes.c_void_p,
184 DWORD, DWORD, HANDLE]
185 CreateFile.restype = HANDLE
186 GetFileInformationByHandle = (
187 ctypes.windll.kernel32.GetFileInformationByHandle)
188 GetFileInformationByHandle.argtypes = [
189 HANDLE, POINTER(BY_HANDLE_FILE_INFORMATION)]
190 GetFileInformationByHandle.restype = BOOL
191 CloseHandle = ctypes.windll.kernel32.CloseHandle
192 CloseHandle.argtypes = [HANDLE]
193 CloseHandle.restype = BOOL
194 GENERIC_READ = 0x80000000
195 FILE_SHARE_READ = 0x00000001
196 FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
197 OPEN_EXISTING = 3
198 if os.path.isdir(path):
199 flags = FILE_FLAG_BACKUP_SEMANTICS
200 else:
201 flags = 0
202 hfile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ,
203 None, OPEN_EXISTING, flags, None)
204 if hfile == 0xffffffff:
205 if sys.version_info >= (3, 3):
206 raise FileNotFoundError(path)
207 else:
208 exc = OSError("file not found: path")
209 exc.errno = ENOENT
210 raise exc
211 info = BY_HANDLE_FILE_INFORMATION()
212 success = GetFileInformationByHandle(hfile, info)
213 CloseHandle(hfile)
214 if success == 0:
215 raise WinError()
216 return info.volume, info.index_hi, info.index_lo
217
218
219 def _is_wildcard_pattern(pat):
220 # Whether this pattern needs actual matching using fnmatch, or can
221 # be looked up directly as a file.
222 return "*" in pat or "?" in pat or "[" in pat
223
224
225 class _Flavour(object):
226
227 """A flavour implements a particular (platform-specific) set of path
228 semantics."""
229
230 def __init__(self):
231 self.join = self.sep.join
232
233 def parse_parts(self, parts):
234 if six.PY2:
235 parts = _py2_fsencode(parts)
236 parsed = []
237 sep = self.sep
238 altsep = self.altsep
239 drv = root = ''
240 it = reversed(parts)
241 for part in it:
242 if not part:
243 continue
244 if altsep:
245 part = part.replace(altsep, sep)
246 drv, root, rel = self.splitroot(part)
247 if sep in rel:
248 for x in reversed(rel.split(sep)):
249 if x and x != '.':
250 parsed.append(intern(x))
251 else:
252 if rel and rel != '.':
253 parsed.append(intern(rel))
254 if drv or root:
255 if not drv:
256 # If no drive is present, try to find one in the previous
257 # parts. This makes the result of parsing e.g.
258 # ("C:", "/", "a") reasonably intuitive.
259 for part in it:
260 if not part:
261 continue
262 if altsep:
263 part = part.replace(altsep, sep)
264 drv = self.splitroot(part)[0]
265 if drv:
266 break
267 break
268 if drv or root:
269 parsed.append(drv + root)
270 parsed.reverse()
271 return drv, root, parsed
272
273 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
274 """
275 Join the two paths represented by the respective
276 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple.
277 """
278 if root2:
279 if not drv2 and drv:
280 return drv, root2, [drv + root2] + parts2[1:]
281 elif drv2:
282 if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
283 # Same drive => second path is relative to the first
284 return drv, root, parts + parts2[1:]
285 else:
286 # Second path is non-anchored (common case)
287 return drv, root, parts + parts2
288 return drv2, root2, parts2
289
290
291 class _WindowsFlavour(_Flavour):
292 # Reference for Windows paths can be found at
293 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
294
295 sep = '\\'
296 altsep = '/'
297 has_drv = True
298 pathmod = ntpath
299
300 is_supported = (os.name == 'nt')
301
302 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
303 ext_namespace_prefix = '\\\\?\\'
304
305 reserved_names = (
306 set(['CON', 'PRN', 'AUX', 'NUL']) |
307 set(['COM%d' % i for i in range(1, 10)]) |
308 set(['LPT%d' % i for i in range(1, 10)])
309 )
310
311 # Interesting findings about extended paths:
312 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
313 # but '\\?\c:/a' is not
314 # - extended paths are always absolute; "relative" extended paths will
315 # fail.
316
317 def splitroot(self, part, sep=sep):
318 first = part[0:1]
319 second = part[1:2]
320 if (second == sep and first == sep):
321 # XXX extended paths should also disable the collapsing of "."
322 # components (according to MSDN docs).
323 prefix, part = self._split_extended_path(part)
324 first = part[0:1]
325 second = part[1:2]
326 else:
327 prefix = ''
328 third = part[2:3]
329 if (second == sep and first == sep and third != sep):
330 # is a UNC path:
331 # vvvvvvvvvvvvvvvvvvvvv root
332 # \\machine\mountpoint\directory\etc\...
333 # directory ^^^^^^^^^^^^^^
334 index = part.find(sep, 2)
335 if index != -1:
336 index2 = part.find(sep, index + 1)
337 # a UNC path can't have two slashes in a row
338 # (after the initial two)
339 if index2 != index + 1:
340 if index2 == -1:
341 index2 = len(part)
342 if prefix:
343 return prefix + part[1:index2], sep, part[index2 + 1:]
344 else:
345 return part[:index2], sep, part[index2 + 1:]
346 drv = root = ''
347 if second == ':' and first in self.drive_letters:
348 drv = part[:2]
349 part = part[2:]
350 first = third
351 if first == sep:
352 root = first
353 part = part.lstrip(sep)
354 return prefix + drv, root, part
355
356 def casefold(self, s):
357 return s.lower()
358
359 def casefold_parts(self, parts):
360 return [p.lower() for p in parts]
361
362 def resolve(self, path, strict=False):
363 s = str(path)
364 if not s:
365 return os.getcwd()
366 previous_s = None
367 if _getfinalpathname is not None:
368 if strict:
369 return self._ext_to_normal(_getfinalpathname(s))
370 else:
371 # End of the path after the first one not found
372 tail_parts = []
373
374 def _try_func():
375 result[0] = self._ext_to_normal(_getfinalpathname(s))
376 # if there was no exception, set flag to 0
377 result[1] = 0
378
379 def _exc_func(exc):
380 pass
381
382 while True:
383 result = [None, 1]
384 _try_except_filenotfounderror(_try_func, _exc_func)
385 if result[1] == 1: # file not found exception raised
386 previous_s = s
387 s, tail = os.path.split(s)
388 tail_parts.append(tail)
389 if previous_s == s:
390 return path
391 else:
392 s = result[0]
393 return os.path.join(s, *reversed(tail_parts))
394 # Means fallback on absolute
395 return None
396
397 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
398 prefix = ''
399 if s.startswith(ext_prefix):
400 prefix = s[:4]
401 s = s[4:]
402 if s.startswith('UNC\\'):
403 prefix += s[:3]
404 s = '\\' + s[3:]
405 return prefix, s
406
407 def _ext_to_normal(self, s):
408 # Turn back an extended path into a normal DOS-like path
409 return self._split_extended_path(s)[1]
410
411 def is_reserved(self, parts):
412 # NOTE: the rules for reserved names seem somewhat complicated
413 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
414 # We err on the side of caution and return True for paths which are
415 # not considered reserved by Windows.
416 if not parts:
417 return False
418 if parts[0].startswith('\\\\'):
419 # UNC paths are never reserved
420 return False
421 return parts[-1].partition('.')[0].upper() in self.reserved_names
422
423 def make_uri(self, path):
424 # Under Windows, file URIs use the UTF-8 encoding.
425 drive = path.drive
426 if len(drive) == 2 and drive[1] == ':':
427 # It's a path on a local drive => 'file:///c:/a/b'
428 rest = path.as_posix()[2:].lstrip('/')
429 return 'file:///%s/%s' % (
430 drive, urlquote_from_bytes(rest.encode('utf-8')))
431 else:
432 # It's a path on a network drive => 'file://host/share/a/b'
433 return 'file:' + urlquote_from_bytes(
434 path.as_posix().encode('utf-8'))
435
436 def gethomedir(self, username):
437 if 'HOME' in os.environ:
438 userhome = os.environ['HOME']
439 elif 'USERPROFILE' in os.environ:
440 userhome = os.environ['USERPROFILE']
441 elif 'HOMEPATH' in os.environ:
442 try:
443 drv = os.environ['HOMEDRIVE']
444 except KeyError:
445 drv = ''
446 userhome = drv + os.environ['HOMEPATH']
447 else:
448 raise RuntimeError("Can't determine home directory")
449
450 if username:
451 # Try to guess user home directory. By default all users
452 # directories are located in the same place and are named by
453 # corresponding usernames. If current user home directory points
454 # to nonstandard place, this guess is likely wrong.
455 if os.environ['USERNAME'] != username:
456 drv, root, parts = self.parse_parts((userhome,))
457 if parts[-1] != os.environ['USERNAME']:
458 raise RuntimeError("Can't determine home directory "
459 "for %r" % username)
460 parts[-1] = username
461 if drv or root:
462 userhome = drv + root + self.join(parts[1:])
463 else:
464 userhome = self.join(parts)
465 return userhome
466
467
468 class _PosixFlavour(_Flavour):
469 sep = '/'
470 altsep = ''
471 has_drv = False
472 pathmod = posixpath
473
474 is_supported = (os.name != 'nt')
475
476 def splitroot(self, part, sep=sep):
477 if part and part[0] == sep:
478 stripped_part = part.lstrip(sep)
479 # According to POSIX path resolution:
480 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/
481 # xbd_chap04.html#tag_04_11
482 # "A pathname that begins with two successive slashes may be
483 # interpreted in an implementation-defined manner, although more
484 # than two leading slashes shall be treated as a single slash".
485 if len(part) - len(stripped_part) == 2:
486 return '', sep * 2, stripped_part
487 else:
488 return '', sep, stripped_part
489 else:
490 return '', '', part
491
492 def casefold(self, s):
493 return s
494
495 def casefold_parts(self, parts):
496 return parts
497
498 def resolve(self, path, strict=False):
499 sep = self.sep
500 accessor = path._accessor
501 seen = {}
502
503 def _resolve(path, rest):
504 if rest.startswith(sep):
505 path = ''
506
507 for name in rest.split(sep):
508 if not name or name == '.':
509 # current dir
510 continue
511 if name == '..':
512 # parent dir
513 path, _, _ = path.rpartition(sep)
514 continue
515 newpath = path + sep + name
516 if newpath in seen:
517 # Already seen this path
518 path = seen[newpath]
519 if path is not None:
520 # use cached value
521 continue
522 # The symlink is not resolved, so we must have a symlink
523 # loop.
524 raise RuntimeError("Symlink loop from %r" % newpath)
525 # Resolve the symbolic link
526 try:
527 target = accessor.readlink(newpath)
528 except OSError as e:
529 if e.errno != EINVAL and strict:
530 raise
531 # Not a symlink, or non-strict mode. We just leave the path
532 # untouched.
533 path = newpath
534 else:
535 seen[newpath] = None # not resolved symlink
536 path = _resolve(path, target)
537 seen[newpath] = path # resolved symlink
538
539 return path
540 # NOTE: according to POSIX, getcwd() cannot contain path components
541 # which are symlinks.
542 base = '' if path.is_absolute() else os.getcwd()
543 return _resolve(base, str(path)) or sep
544
545 def is_reserved(self, parts):
546 return False
547
548 def make_uri(self, path):
549 # We represent the path using the local filesystem encoding,
550 # for portability to other applications.
551 bpath = bytes(path)
552 return 'file://' + urlquote_from_bytes(bpath)
553
554 def gethomedir(self, username):
555 if not username:
556 try:
557 return os.environ['HOME']
558 except KeyError:
559 import pwd
560 return pwd.getpwuid(os.getuid()).pw_dir
561 else:
562 import pwd
563 try:
564 return pwd.getpwnam(username).pw_dir
565 except KeyError:
566 raise RuntimeError("Can't determine home directory "
567 "for %r" % username)
568
569
570 _windows_flavour = _WindowsFlavour()
571 _posix_flavour = _PosixFlavour()
572
573
574 class _Accessor:
575
576 """An accessor implements a particular (system-specific or not) way of
577 accessing paths on the filesystem."""
578
579
580 class _NormalAccessor(_Accessor):
581
582 def _wrap_strfunc(strfunc):
583 @functools.wraps(strfunc)
584 def wrapped(pathobj, *args):
585 return strfunc(str(pathobj), *args)
586 return staticmethod(wrapped)
587
588 def _wrap_binary_strfunc(strfunc):
589 @functools.wraps(strfunc)
590 def wrapped(pathobjA, pathobjB, *args):
591 return strfunc(str(pathobjA), str(pathobjB), *args)
592 return staticmethod(wrapped)
593
594 stat = _wrap_strfunc(os.stat)
595
596 lstat = _wrap_strfunc(os.lstat)
597
598 open = _wrap_strfunc(os.open)
599
600 listdir = _wrap_strfunc(os.listdir)
601
602 scandir = _wrap_strfunc(os_scandir)
603
604 chmod = _wrap_strfunc(os.chmod)
605
606 if hasattr(os, "lchmod"):
607 lchmod = _wrap_strfunc(os.lchmod)
608 else:
609 def lchmod(self, pathobj, mode):
610 raise NotImplementedError("lchmod() not available on this system")
611
612 mkdir = _wrap_strfunc(os.mkdir)
613
614 unlink = _wrap_strfunc(os.unlink)
615
616 rmdir = _wrap_strfunc(os.rmdir)
617
618 rename = _wrap_binary_strfunc(os.rename)
619
620 if sys.version_info >= (3, 3):
621 replace = _wrap_binary_strfunc(os.replace)
622
623 if nt:
624 if supports_symlinks:
625 symlink = _wrap_binary_strfunc(os.symlink)
626 else:
627 def symlink(a, b, target_is_directory):
628 raise NotImplementedError(
629 "symlink() not available on this system")
630 else:
631 # Under POSIX, os.symlink() takes two args
632 @staticmethod
633 def symlink(a, b, target_is_directory):
634 return os.symlink(str(a), str(b))
635
636 utime = _wrap_strfunc(os.utime)
637
638 # Helper for resolve()
639 def readlink(self, path):
640 return os.readlink(path)
641
642
643 _normal_accessor = _NormalAccessor()
644
645
646 #
647 # Globbing helpers
648 #
649
650 def _make_selector(pattern_parts):
651 pat = pattern_parts[0]
652 child_parts = pattern_parts[1:]
653 if pat == '**':
654 cls = _RecursiveWildcardSelector
655 elif '**' in pat:
656 raise ValueError(
657 "Invalid pattern: '**' can only be an entire path component")
658 elif _is_wildcard_pattern(pat):
659 cls = _WildcardSelector
660 else:
661 cls = _PreciseSelector
662 return cls(pat, child_parts)
663
664
665 if hasattr(functools, "lru_cache"):
666 _make_selector = functools.lru_cache()(_make_selector)
667
668
669 class _Selector:
670
671 """A selector matches a specific glob pattern part against the children
672 of a given path."""
673
674 def __init__(self, child_parts):
675 self.child_parts = child_parts
676 if child_parts:
677 self.successor = _make_selector(child_parts)
678 self.dironly = True
679 else:
680 self.successor = _TerminatingSelector()
681 self.dironly = False
682
683 def select_from(self, parent_path):
684 """Iterate over all child paths of `parent_path` matched by this
685 selector. This can contain parent_path itself."""
686 path_cls = type(parent_path)
687 is_dir = path_cls.is_dir
688 exists = path_cls.exists
689 scandir = parent_path._accessor.scandir
690 if not is_dir(parent_path):
691 return iter([])
692 return self._select_from(parent_path, is_dir, exists, scandir)
693
694
695 class _TerminatingSelector:
696
697 def _select_from(self, parent_path, is_dir, exists, scandir):
698 yield parent_path
699
700
701 class _PreciseSelector(_Selector):
702
703 def __init__(self, name, child_parts):
704 self.name = name
705 _Selector.__init__(self, child_parts)
706
707 def _select_from(self, parent_path, is_dir, exists, scandir):
708 def try_iter():
709 path = parent_path._make_child_relpath(self.name)
710 if (is_dir if self.dironly else exists)(path):
711 for p in self.successor._select_from(
712 path, is_dir, exists, scandir):
713 yield p
714
715 def except_iter(exc):
716 return
717 yield
718
719 for x in _try_except_permissionerror_iter(try_iter, except_iter):
720 yield x
721
722
723 class _WildcardSelector(_Selector):
724
725 def __init__(self, pat, child_parts):
726 self.pat = re.compile(fnmatch.translate(pat))
727 _Selector.__init__(self, child_parts)
728
729 def _select_from(self, parent_path, is_dir, exists, scandir):
730 def try_iter():
731 cf = parent_path._flavour.casefold
732 entries = list(scandir(parent_path))
733 for entry in entries:
734 if not self.dironly or entry.is_dir():
735 name = entry.name
736 casefolded = cf(name)
737 if self.pat.match(casefolded):
738 path = parent_path._make_child_relpath(name)
739 for p in self.successor._select_from(
740 path, is_dir, exists, scandir):
741 yield p
742
743 def except_iter(exc):
744 return
745 yield
746
747 for x in _try_except_permissionerror_iter(try_iter, except_iter):
748 yield x
749
750
751 class _RecursiveWildcardSelector(_Selector):
752
753 def __init__(self, pat, child_parts):
754 _Selector.__init__(self, child_parts)
755
756 def _iterate_directories(self, parent_path, is_dir, scandir):
757 yield parent_path
758
759 def try_iter():
760 entries = list(scandir(parent_path))
761 for entry in entries:
762 entry_is_dir = False
763 try:
764 entry_is_dir = entry.is_dir()
765 except OSError as e:
766 if not _ignore_error(e):
767 raise
768 if entry_is_dir and not entry.is_symlink():
769 path = parent_path._make_child_relpath(entry.name)
770 for p in self._iterate_directories(path, is_dir, scandir):
771 yield p
772
773 def except_iter(exc):
774 return
775 yield
776
777 for x in _try_except_permissionerror_iter(try_iter, except_iter):
778 yield x
779
780 def _select_from(self, parent_path, is_dir, exists, scandir):
781 def try_iter():
782 yielded = set()
783 try:
784 successor_select = self.successor._select_from
785 for starting_point in self._iterate_directories(
786 parent_path, is_dir, scandir):
787 for p in successor_select(
788 starting_point, is_dir, exists, scandir):
789 if p not in yielded:
790 yield p
791 yielded.add(p)
792 finally:
793 yielded.clear()
794
795 def except_iter(exc):
796 return
797 yield
798
799 for x in _try_except_permissionerror_iter(try_iter, except_iter):
800 yield x
801
802
803 #
804 # Public API
805 #
806
807 class _PathParents(Sequence):
808
809 """This object provides sequence-like access to the logical ancestors
810 of a path. Don't try to construct it yourself."""
811 __slots__ = ('_pathcls', '_drv', '_root', '_parts')
812
813 def __init__(self, path):
814 # We don't store the instance to avoid reference cycles
815 self._pathcls = type(path)
816 self._drv = path._drv
817 self._root = path._root
818 self._parts = path._parts
819
820 def __len__(self):
821 if self._drv or self._root:
822 return len(self._parts) - 1
823 else:
824 return len(self._parts)
825
826 def __getitem__(self, idx):
827 if idx < 0 or idx >= len(self):
828 raise IndexError(idx)
829 return self._pathcls._from_parsed_parts(self._drv, self._root,
830 self._parts[:-idx - 1])
831
832 def __repr__(self):
833 return "<{0}.parents>".format(self._pathcls.__name__)
834
835
836 class PurePath(object):
837
838 """PurePath represents a filesystem path and offers operations which
839 don't imply any actual filesystem I/O. Depending on your system,
840 instantiating a PurePath will return either a PurePosixPath or a
841 PureWindowsPath object. You can also instantiate either of these classes
842 directly, regardless of your system.
843 """
844 __slots__ = (
845 '_drv', '_root', '_parts',
846 '_str', '_hash', '_pparts', '_cached_cparts',
847 )
848
849 def __new__(cls, *args):
850 """Construct a PurePath from one or several strings and or existing
851 PurePath objects. The strings and path objects are combined so as
852 to yield a canonicalized path, which is incorporated into the
853 new PurePath object.
854 """
855 if cls is PurePath:
856 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
857 return cls._from_parts(args)
858
859 def __reduce__(self):
860 # Using the parts tuple helps share interned path parts
861 # when pickling related paths.
862 return (self.__class__, tuple(self._parts))
863
864 @classmethod
865 def _parse_args(cls, args):
866 # This is useful when you don't want to create an instance, just
867 # canonicalize some constructor arguments.
868 parts = []
869 for a in args:
870 if isinstance(a, PurePath):
871 parts += a._parts
872 else:
873 if sys.version_info >= (3, 6):
874 a = os.fspath(a)
875 else:
876 # duck typing for older Python versions
877 if hasattr(a, "__fspath__"):
878 a = a.__fspath__()
879 if isinstance(a, str):
880 # Force-cast str subclasses to str (issue #21127)
881 parts.append(str(a))
882 # also handle unicode for PY2 (six.text_type = unicode)
883 elif six.PY2 and isinstance(a, six.text_type):
884 # cast to str using filesystem encoding
885 # note: in rare circumstances, on Python < 3.2,
886 # getfilesystemencoding can return None, in that
887 # case fall back to ascii
888 parts.append(a.encode(
889 sys.getfilesystemencoding() or "ascii"))
890 else:
891 raise TypeError(
892 "argument should be a str object or an os.PathLike "
893 "object returning str, not %r"
894 % type(a))
895 return cls._flavour.parse_parts(parts)
896
897 @classmethod
898 def _from_parts(cls, args, init=True):
899 # We need to call _parse_args on the instance, so as to get the
900 # right flavour.
901 self = object.__new__(cls)
902 drv, root, parts = self._parse_args(args)
903 self._drv = drv
904 self._root = root
905 self._parts = parts
906 if init:
907 self._init()
908 return self
909
910 @classmethod
911 def _from_parsed_parts(cls, drv, root, parts, init=True):
912 self = object.__new__(cls)
913 self._drv = drv
914 self._root = root
915 self._parts = parts
916 if init:
917 self._init()
918 return self
919
920 @classmethod
921 def _format_parsed_parts(cls, drv, root, parts):
922 if drv or root:
923 return drv + root + cls._flavour.join(parts[1:])
924 else:
925 return cls._flavour.join(parts)
926
927 def _init(self):
928 # Overridden in concrete Path
929 pass
930
931 def _make_child(self, args):
932 drv, root, parts = self._parse_args(args)
933 drv, root, parts = self._flavour.join_parsed_parts(
934 self._drv, self._root, self._parts, drv, root, parts)
935 return self._from_parsed_parts(drv, root, parts)
936
937 def __str__(self):
938 """Return the string representation of the path, suitable for
939 passing to system calls."""
940 try:
941 return self._str
942 except AttributeError:
943 self._str = self._format_parsed_parts(self._drv, self._root,
944 self._parts) or '.'
945 return self._str
946
947 def __fspath__(self):
948 return str(self)
949
950 def as_posix(self):
951 """Return the string representation of the path with forward (/)
952 slashes."""
953 f = self._flavour
954 return str(self).replace(f.sep, '/')
955
956 def __bytes__(self):
957 """Return the bytes representation of the path. This is only
958 recommended to use under Unix."""
959 if sys.version_info < (3, 2):
960 raise NotImplementedError("needs Python 3.2 or later")
961 return os.fsencode(str(self))
962
963 def __repr__(self):
964 return "{0}({1!r})".format(self.__class__.__name__, self.as_posix())
965
966 def as_uri(self):
967 """Return the path as a 'file' URI."""
968 if not self.is_absolute():
969 raise ValueError("relative path can't be expressed as a file URI")
970 return self._flavour.make_uri(self)
971
972 @property
973 def _cparts(self):
974 # Cached casefolded parts, for hashing and comparison
975 try:
976 return self._cached_cparts
977 except AttributeError:
978 self._cached_cparts = self._flavour.casefold_parts(self._parts)
979 return self._cached_cparts
980
981 def __eq__(self, other):
982 if not isinstance(other, PurePath):
983 return NotImplemented
984 return (
985 self._cparts == other._cparts
986 and self._flavour is other._flavour)
987
988 def __ne__(self, other):
989 return not self == other
990
991 def __hash__(self):
992 try:
993 return self._hash
994 except AttributeError:
995 self._hash = hash(tuple(self._cparts))
996 return self._hash
997
998 def __lt__(self, other):
999 if (not isinstance(other, PurePath)
1000 or self._flavour is not other._flavour):
1001 return NotImplemented
1002 return self._cparts < other._cparts
1003
1004 def __le__(self, other):
1005 if (not isinstance(other, PurePath)
1006 or self._flavour is not other._flavour):
1007 return NotImplemented
1008 return self._cparts <= other._cparts
1009
1010 def __gt__(self, other):
1011 if (not isinstance(other, PurePath)
1012 or self._flavour is not other._flavour):
1013 return NotImplemented
1014 return self._cparts > other._cparts
1015
1016 def __ge__(self, other):
1017 if (not isinstance(other, PurePath)
1018 or self._flavour is not other._flavour):
1019 return NotImplemented
1020 return self._cparts >= other._cparts
1021
1022 drive = property(attrgetter('_drv'),
1023 doc="""The drive prefix (letter or UNC path), if any.""")
1024
1025 root = property(attrgetter('_root'),
1026 doc="""The root of the path, if any.""")
1027
1028 @property
1029 def anchor(self):
1030 """The concatenation of the drive and root, or ''."""
1031 anchor = self._drv + self._root
1032 return anchor
1033
1034 @property
1035 def name(self):
1036 """The final path component, if any."""
1037 parts = self._parts
1038 if len(parts) == (1 if (self._drv or self._root) else 0):
1039 return ''
1040 return parts[-1]
1041
1042 @property
1043 def suffix(self):
1044 """The final component's last suffix, if any."""
1045 name = self.name
1046 i = name.rfind('.')
1047 if 0 < i < len(name) - 1:
1048 return name[i:]
1049 else:
1050 return ''
1051
1052 @property
1053 def suffixes(self):
1054 """A list of the final component's suffixes, if any."""
1055 name = self.name
1056 if name.endswith('.'):
1057 return []
1058 name = name.lstrip('.')
1059 return ['.' + suffix for suffix in name.split('.')[1:]]
1060
1061 @property
1062 def stem(self):
1063 """The final path component, minus its last suffix."""
1064 name = self.name
1065 i = name.rfind('.')
1066 if 0 < i < len(name) - 1:
1067 return name[:i]
1068 else:
1069 return name
1070
1071 def with_name(self, name):
1072 """Return a new path with the file name changed."""
1073 if not self.name:
1074 raise ValueError("%r has an empty name" % (self,))
1075 drv, root, parts = self._flavour.parse_parts((name,))
1076 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep]
1077 or drv or root or len(parts) != 1):
1078 raise ValueError("Invalid name %r" % (name))
1079 return self._from_parsed_parts(self._drv, self._root,
1080 self._parts[:-1] + [name])
1081
1082 def with_suffix(self, suffix):
1083 """Return a new path with the file suffix changed. If the path
1084 has no suffix, add given suffix. If the given suffix is an empty
1085 string, remove the suffix from the path.
1086 """
1087 # XXX if suffix is None, should the current suffix be removed?
1088 f = self._flavour
1089 if f.sep in suffix or f.altsep and f.altsep in suffix:
1090 raise ValueError("Invalid suffix %r" % (suffix))
1091 if suffix and not suffix.startswith('.') or suffix == '.':
1092 raise ValueError("Invalid suffix %r" % (suffix))
1093 name = self.name
1094 if not name:
1095 raise ValueError("%r has an empty name" % (self,))
1096 old_suffix = self.suffix
1097 if not old_suffix:
1098 name = name + suffix
1099 else:
1100 name = name[:-len(old_suffix)] + suffix
1101 return self._from_parsed_parts(self._drv, self._root,
1102 self._parts[:-1] + [name])
1103
1104 def relative_to(self, *other):
1105 """Return the relative path to another path identified by the passed
1106 arguments. If the operation is not possible (because this is not
1107 a subpath of the other path), raise ValueError.
1108 """
1109 # For the purpose of this method, drive and root are considered
1110 # separate parts, i.e.:
1111 # Path('c:/').relative_to('c:') gives Path('/')
1112 # Path('c:/').relative_to('/') raise ValueError
1113 if not other:
1114 raise TypeError("need at least one argument")
1115 parts = self._parts
1116 drv = self._drv
1117 root = self._root
1118 if root:
1119 abs_parts = [drv, root] + parts[1:]
1120 else:
1121 abs_parts = parts
1122 to_drv, to_root, to_parts = self._parse_args(other)
1123 if to_root:
1124 to_abs_parts = [to_drv, to_root] + to_parts[1:]
1125 else:
1126 to_abs_parts = to_parts
1127 n = len(to_abs_parts)
1128 cf = self._flavour.casefold_parts
1129 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
1130 formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
1131 raise ValueError("{0!r} does not start with {1!r}"
1132 .format(str(self), str(formatted)))
1133 return self._from_parsed_parts('', root if n == 1 else '',
1134 abs_parts[n:])
1135
1136 @property
1137 def parts(self):
1138 """An object providing sequence-like access to the
1139 components in the filesystem path."""
1140 # We cache the tuple to avoid building a new one each time .parts
1141 # is accessed. XXX is this necessary?
1142 try:
1143 return self._pparts
1144 except AttributeError:
1145 self._pparts = tuple(self._parts)
1146 return self._pparts
1147
1148 def joinpath(self, *args):
1149 """Combine this path with one or several arguments, and return a
1150 new path representing either a subpath (if all arguments are relative
1151 paths) or a totally different path (if one of the arguments is
1152 anchored).
1153 """
1154 return self._make_child(args)
1155
1156 def __truediv__(self, key):
1157 return self._make_child((key,))
1158
1159 def __rtruediv__(self, key):
1160 return self._from_parts([key] + self._parts)
1161
1162 if six.PY2:
1163 __div__ = __truediv__
1164 __rdiv__ = __rtruediv__
1165
1166 @property
1167 def parent(self):
1168 """The logical parent of the path."""
1169 drv = self._drv
1170 root = self._root
1171 parts = self._parts
1172 if len(parts) == 1 and (drv or root):
1173 return self
1174 return self._from_parsed_parts(drv, root, parts[:-1])
1175
1176 @property
1177 def parents(self):
1178 """A sequence of this path's logical parents."""
1179 return _PathParents(self)
1180
1181 def is_absolute(self):
1182 """True if the path is absolute (has both a root and, if applicable,
1183 a drive)."""
1184 if not self._root:
1185 return False
1186 return not self._flavour.has_drv or bool(self._drv)
1187
1188 def is_reserved(self):
1189 """Return True if the path contains one of the special names reserved
1190 by the system, if any."""
1191 return self._flavour.is_reserved(self._parts)
1192
1193 def match(self, path_pattern):
1194 """
1195 Return True if this path matches the given pattern.
1196 """
1197 cf = self._flavour.casefold
1198 path_pattern = cf(path_pattern)
1199 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
1200 if not pat_parts:
1201 raise ValueError("empty pattern")
1202 if drv and drv != cf(self._drv):
1203 return False
1204 if root and root != cf(self._root):
1205 return False
1206 parts = self._cparts
1207 if drv or root:
1208 if len(pat_parts) != len(parts):
1209 return False
1210 pat_parts = pat_parts[1:]
1211 elif len(pat_parts) > len(parts):
1212 return False
1213 for part, pat in zip(reversed(parts), reversed(pat_parts)):
1214 if not fnmatch.fnmatchcase(part, pat):
1215 return False
1216 return True
1217
1218
1219 # Can't subclass os.PathLike from PurePath and keep the constructor
1220 # optimizations in PurePath._parse_args().
1221 if sys.version_info >= (3, 6):
1222 os.PathLike.register(PurePath)
1223
1224
1225 class PurePosixPath(PurePath):
1226 _flavour = _posix_flavour
1227 __slots__ = ()
1228
1229
1230 class PureWindowsPath(PurePath):
1231 """PurePath subclass for Windows systems.
1232
1233 On a Windows system, instantiating a PurePath should return this object.
1234 However, you can also instantiate it directly on any system.
1235 """
1236 _flavour = _windows_flavour
1237 __slots__ = ()
1238
1239
1240 # Filesystem-accessing classes
1241
1242
1243 class Path(PurePath):
1244 """PurePath subclass that can make system calls.
1245
1246 Path represents a filesystem path but unlike PurePath, also offers
1247 methods to do system calls on path objects. Depending on your system,
1248 instantiating a Path will return either a PosixPath or a WindowsPath
1249 object. You can also instantiate a PosixPath or WindowsPath directly,
1250 but cannot instantiate a WindowsPath on a POSIX system or vice versa.
1251 """
1252 __slots__ = (
1253 '_accessor',
1254 '_closed',
1255 )
1256
1257 def __new__(cls, *args, **kwargs):
1258 if cls is Path:
1259 cls = WindowsPath if os.name == 'nt' else PosixPath
1260 self = cls._from_parts(args, init=False)
1261 if not self._flavour.is_supported:
1262 raise NotImplementedError("cannot instantiate %r on your system"
1263 % (cls.__name__,))
1264 self._init()
1265 return self
1266
1267 def _init(self,
1268 # Private non-constructor arguments
1269 template=None,
1270 ):
1271 self._closed = False
1272 if template is not None:
1273 self._accessor = template._accessor
1274 else:
1275 self._accessor = _normal_accessor
1276
1277 def _make_child_relpath(self, part):
1278 # This is an optimization used for dir walking. `part` must be
1279 # a single part relative to this path.
1280 parts = self._parts + [part]
1281 return self._from_parsed_parts(self._drv, self._root, parts)
1282
1283 def __enter__(self):
1284 if self._closed:
1285 self._raise_closed()
1286 return self
1287
1288 def __exit__(self, t, v, tb):
1289 self._closed = True
1290
1291 def _raise_closed(self):
1292 raise ValueError("I/O operation on closed path")
1293
1294 def _opener(self, name, flags, mode=0o666):
1295 # A stub for the opener argument to built-in open()
1296 return self._accessor.open(self, flags, mode)
1297
1298 def _raw_open(self, flags, mode=0o777):
1299 """
1300 Open the file pointed by this path and return a file descriptor,
1301 as os.open() does.
1302 """
1303 if self._closed:
1304 self._raise_closed()
1305 return self._accessor.open(self, flags, mode)
1306
1307 # Public API
1308
1309 @classmethod
1310 def cwd(cls):
1311 """Return a new path pointing to the current working directory
1312 (as returned by os.getcwd()).
1313 """
1314 return cls(os.getcwd())
1315
1316 @classmethod
1317 def home(cls):
1318 """Return a new path pointing to the user's home directory (as
1319 returned by os.path.expanduser('~')).
1320 """
1321 return cls(cls()._flavour.gethomedir(None))
1322
1323 def samefile(self, other_path):
1324 """Return whether other_path is the same or not as this file
1325 (as returned by os.path.samefile()).
1326 """
1327 if hasattr(os.path, "samestat"):
1328 st = self.stat()
1329 try:
1330 other_st = other_path.stat()
1331 except AttributeError:
1332 other_st = os.stat(other_path)
1333 return os.path.samestat(st, other_st)
1334 else:
1335 filename1 = six.text_type(self)
1336 filename2 = six.text_type(other_path)
1337 st1 = _win32_get_unique_path_id(filename1)
1338 st2 = _win32_get_unique_path_id(filename2)
1339 return st1 == st2
1340
1341 def iterdir(self):
1342 """Iterate over the files in this directory. Does not yield any
1343 result for the special paths '.' and '..'.
1344 """
1345 if self._closed:
1346 self._raise_closed()
1347 for name in self._accessor.listdir(self):
1348 if name in ('.', '..'):
1349 # Yielding a path object for these makes little sense
1350 continue
1351 yield self._make_child_relpath(name)
1352 if self._closed:
1353 self._raise_closed()
1354
1355 def glob(self, pattern):
1356 """Iterate over this subtree and yield all existing files (of any
1357 kind, including directories) matching the given relative pattern.
1358 """
1359 if not pattern:
1360 raise ValueError("Unacceptable pattern: {0!r}".format(pattern))
1361 pattern = self._flavour.casefold(pattern)
1362 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1363 if drv or root:
1364 raise NotImplementedError("Non-relative patterns are unsupported")
1365 selector = _make_selector(tuple(pattern_parts))
1366 for p in selector.select_from(self):
1367 yield p
1368
1369 def rglob(self, pattern):
1370 """Recursively yield all existing files (of any kind, including
1371 directories) matching the given relative pattern, anywhere in
1372 this subtree.
1373 """
1374 pattern = self._flavour.casefold(pattern)
1375 drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
1376 if drv or root:
1377 raise NotImplementedError("Non-relative patterns are unsupported")
1378 selector = _make_selector(("**",) + tuple(pattern_parts))
1379 for p in selector.select_from(self):
1380 yield p
1381
1382 def absolute(self):
1383 """Return an absolute version of this path. This function works
1384 even if the path doesn't point to anything.
1385
1386 No normalization is done, i.e. all '.' and '..' will be kept along.
1387 Use resolve() to get the canonical path to a file.
1388 """
1389 # XXX untested yet!
1390 if self._closed:
1391 self._raise_closed()
1392 if self.is_absolute():
1393 return self
1394 # FIXME this must defer to the specific flavour (and, under Windows,
1395 # use nt._getfullpathname())
1396 obj = self._from_parts([os.getcwd()] + self._parts, init=False)
1397 obj._init(template=self)
1398 return obj
1399
1400 def resolve(self, strict=False):
1401 """
1402 Make the path absolute, resolving all symlinks on the way and also
1403 normalizing it (for example turning slashes into backslashes under
1404 Windows).
1405 """
1406 if self._closed:
1407 self._raise_closed()
1408 s = self._flavour.resolve(self, strict=strict)
1409 if s is None:
1410 # No symlink resolution => for consistency, raise an error if
1411 # the path is forbidden
1412 # but not raise error if file does not exist (see issue #54).
1413
1414 def _try_func():
1415 self.stat()
1416
1417 def _exc_func(exc):
1418 pass
1419
1420 _try_except_filenotfounderror(_try_func, _exc_func)
1421 s = str(self.absolute())
1422 else:
1423 # ensure s is a string (normpath requires this on older python)
1424 s = str(s)
1425 # Now we have no symlinks in the path, it's safe to normalize it.
1426 normed = self._flavour.pathmod.normpath(s)
1427 obj = self._from_parts((normed,), init=False)
1428 obj._init(template=self)
1429 return obj
1430
1431 def stat(self):
1432 """
1433 Return the result of the stat() system call on this path, like
1434 os.stat() does.
1435 """
1436 return self._accessor.stat(self)
1437
1438 def owner(self):
1439 """
1440 Return the login name of the file owner.
1441 """
1442 import pwd
1443 return pwd.getpwuid(self.stat().st_uid).pw_name
1444
1445 def group(self):
1446 """
1447 Return the group name of the file gid.
1448 """
1449 import grp
1450 return grp.getgrgid(self.stat().st_gid).gr_name
1451
1452 def open(self, mode='r', buffering=-1, encoding=None,
1453 errors=None, newline=None):
1454 """
1455 Open the file pointed by this path and return a file object, as
1456 the built-in open() function does.
1457 """
1458 if self._closed:
1459 self._raise_closed()
1460 if sys.version_info >= (3, 3):
1461 return io.open(
1462 str(self), mode, buffering, encoding, errors, newline,
1463 opener=self._opener)
1464 else:
1465 return io.open(str(self), mode, buffering,
1466 encoding, errors, newline)
1467
1468 def read_bytes(self):
1469 """
1470 Open the file in bytes mode, read it, and close the file.
1471 """
1472 with self.open(mode='rb') as f:
1473 return f.read()
1474
1475 def read_text(self, encoding=None, errors=None):
1476 """
1477 Open the file in text mode, read it, and close the file.
1478 """
1479 with self.open(mode='r', encoding=encoding, errors=errors) as f:
1480 return f.read()
1481
1482 def write_bytes(self, data):
1483 """
1484 Open the file in bytes mode, write to it, and close the file.
1485 """
1486 if not isinstance(data, six.binary_type):
1487 raise TypeError(
1488 'data must be %s, not %s' %
1489 (six.binary_type.__name__, data.__class__.__name__))
1490 with self.open(mode='wb') as f:
1491 return f.write(data)
1492
1493 def write_text(self, data, encoding=None, errors=None):
1494 """
1495 Open the file in text mode, write to it, and close the file.
1496 """
1497 if not isinstance(data, six.text_type):
1498 raise TypeError(
1499 'data must be %s, not %s' %
1500 (six.text_type.__name__, data.__class__.__name__))
1501 with self.open(mode='w', encoding=encoding, errors=errors) as f:
1502 return f.write(data)
1503
1504 def touch(self, mode=0o666, exist_ok=True):
1505 """
1506 Create this file with the given access mode, if it doesn't exist.
1507 """
1508 if self._closed:
1509 self._raise_closed()
1510 if exist_ok:
1511 # First try to bump modification time
1512 # Implementation note: GNU touch uses the UTIME_NOW option of
1513 # the utimensat() / futimens() functions.
1514 try:
1515 self._accessor.utime(self, None)
1516 except OSError:
1517 # Avoid exception chaining
1518 pass
1519 else:
1520 return
1521 flags = os.O_CREAT | os.O_WRONLY
1522 if not exist_ok:
1523 flags |= os.O_EXCL
1524 fd = self._raw_open(flags, mode)
1525 os.close(fd)
1526
1527 def mkdir(self, mode=0o777, parents=False, exist_ok=False):
1528 """
1529 Create a new directory at this given path.
1530 """
1531 if self._closed:
1532 self._raise_closed()
1533
1534 def _try_func():
1535 self._accessor.mkdir(self, mode)
1536
1537 def _exc_func(exc):
1538 if not parents or self.parent == self:
1539 raise exc
1540 self.parent.mkdir(parents=True, exist_ok=True)
1541 self.mkdir(mode, parents=False, exist_ok=exist_ok)
1542
1543 try:
1544 _try_except_filenotfounderror(_try_func, _exc_func)
1545 except OSError:
1546 # Cannot rely on checking for EEXIST, since the operating system
1547 # could give priority to other errors like EACCES or EROFS
1548 if not exist_ok or not self.is_dir():
1549 raise
1550
1551 def chmod(self, mode):
1552 """
1553 Change the permissions of the path, like os.chmod().
1554 """
1555 if self._closed:
1556 self._raise_closed()
1557 self._accessor.chmod(self, mode)
1558
1559 def lchmod(self, mode):
1560 """
1561 Like chmod(), except if the path points to a symlink, the symlink's
1562 permissions are changed, rather than its target's.
1563 """
1564 if self._closed:
1565 self._raise_closed()
1566 self._accessor.lchmod(self, mode)
1567
1568 def unlink(self):
1569 """
1570 Remove this file or link.
1571 If the path is a directory, use rmdir() instead.
1572 """
1573 if self._closed:
1574 self._raise_closed()
1575 self._accessor.unlink(self)
1576
1577 def rmdir(self):
1578 """
1579 Remove this directory. The directory must be empty.
1580 """
1581 if self._closed:
1582 self._raise_closed()
1583 self._accessor.rmdir(self)
1584
1585 def lstat(self):
1586 """
1587 Like stat(), except if the path points to a symlink, the symlink's
1588 status information is returned, rather than its target's.
1589 """
1590 if self._closed:
1591 self._raise_closed()
1592 return self._accessor.lstat(self)
1593
1594 def rename(self, target):
1595 """
1596 Rename this path to the given path.
1597 """
1598 if self._closed:
1599 self._raise_closed()
1600 self._accessor.rename(self, target)
1601
1602 def replace(self, target):
1603 """
1604 Rename this path to the given path, clobbering the existing
1605 destination if it exists.
1606 """
1607 if sys.version_info < (3, 3):
1608 raise NotImplementedError("replace() is only available "
1609 "with Python 3.3 and later")
1610 if self._closed:
1611 self._raise_closed()
1612 self._accessor.replace(self, target)
1613
1614 def symlink_to(self, target, target_is_directory=False):
1615 """
1616 Make this path a symlink pointing to the given path.
1617 Note the order of arguments (self, target) is the reverse of
1618 os.symlink's.
1619 """
1620 if self._closed:
1621 self._raise_closed()
1622 self._accessor.symlink(target, self, target_is_directory)
1623
1624 # Convenience functions for querying the stat results
1625
1626 def exists(self):
1627 """
1628 Whether this path exists.
1629 """
1630 try:
1631 self.stat()
1632 except OSError as e:
1633 if not _ignore_error(e):
1634 raise
1635 return False
1636 except ValueError:
1637 # Non-encodable path
1638 return False
1639 return True
1640
1641 def is_dir(self):
1642 """
1643 Whether this path is a directory.
1644 """
1645 try:
1646 return S_ISDIR(self.stat().st_mode)
1647 except OSError as e:
1648 if not _ignore_error(e):
1649 raise
1650 # Path doesn't exist or is a broken symlink
1651 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1652 return False
1653 except ValueError:
1654 # Non-encodable path
1655 return False
1656
1657 def is_file(self):
1658 """
1659 Whether this path is a regular file (also True for symlinks pointing
1660 to regular files).
1661 """
1662 try:
1663 return S_ISREG(self.stat().st_mode)
1664 except OSError as e:
1665 if not _ignore_error(e):
1666 raise
1667 # Path doesn't exist or is a broken symlink
1668 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1669 return False
1670 except ValueError:
1671 # Non-encodable path
1672 return False
1673
1674 def is_mount(self):
1675 """
1676 Check if this path is a POSIX mount point
1677 """
1678 # Need to exist and be a dir
1679 if not self.exists() or not self.is_dir():
1680 return False
1681
1682 parent = Path(self.parent)
1683 try:
1684 parent_dev = parent.stat().st_dev
1685 except OSError:
1686 return False
1687
1688 dev = self.stat().st_dev
1689 if dev != parent_dev:
1690 return True
1691 ino = self.stat().st_ino
1692 parent_ino = parent.stat().st_ino
1693 return ino == parent_ino
1694
1695 def is_symlink(self):
1696 """
1697 Whether this path is a symbolic link.
1698 """
1699 try:
1700 return S_ISLNK(self.lstat().st_mode)
1701 except OSError as e:
1702 if not _ignore_error(e):
1703 raise
1704 # Path doesn't exist
1705 return False
1706 except ValueError:
1707 # Non-encodable path
1708 return False
1709
1710 def is_block_device(self):
1711 """
1712 Whether this path is a block device.
1713 """
1714 try:
1715 return S_ISBLK(self.stat().st_mode)
1716 except OSError as e:
1717 if not _ignore_error(e):
1718 raise
1719 # Path doesn't exist or is a broken symlink
1720 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1721 return False
1722 except ValueError:
1723 # Non-encodable path
1724 return False
1725
1726 def is_char_device(self):
1727 """
1728 Whether this path is a character device.
1729 """
1730 try:
1731 return S_ISCHR(self.stat().st_mode)
1732 except OSError as e:
1733 if not _ignore_error(e):
1734 raise
1735 # Path doesn't exist or is a broken symlink
1736 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1737 return False
1738 except ValueError:
1739 # Non-encodable path
1740 return False
1741
1742 def is_fifo(self):
1743 """
1744 Whether this path is a FIFO.
1745 """
1746 try:
1747 return S_ISFIFO(self.stat().st_mode)
1748 except OSError as e:
1749 if not _ignore_error(e):
1750 raise
1751 # Path doesn't exist or is a broken symlink
1752 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1753 return False
1754 except ValueError:
1755 # Non-encodable path
1756 return False
1757
1758 def is_socket(self):
1759 """
1760 Whether this path is a socket.
1761 """
1762 try:
1763 return S_ISSOCK(self.stat().st_mode)
1764 except OSError as e:
1765 if not _ignore_error(e):
1766 raise
1767 # Path doesn't exist or is a broken symlink
1768 # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
1769 return False
1770 except ValueError:
1771 # Non-encodable path
1772 return False
1773
1774 def expanduser(self):
1775 """ Return a new path with expanded ~ and ~user constructs
1776 (as returned by os.path.expanduser)
1777 """
1778 if (not (self._drv or self._root)
1779 and self._parts and self._parts[0][:1] == '~'):
1780 homedir = self._flavour.gethomedir(self._parts[0][1:])
1781 return self._from_parts([homedir] + self._parts[1:])
1782
1783 return self
1784
1785
1786 class PosixPath(Path, PurePosixPath):
1787 """Path subclass for non-Windows systems.
1788
1789 On a POSIX system, instantiating a Path should return this object.
1790 """
1791 __slots__ = ()
1792
1793
1794 class WindowsPath(Path, PureWindowsPath):
1795 """Path subclass for Windows systems.
1796
1797 On a Windows system, instantiating a Path should return this object.
1798 """
1799 __slots__ = ()
1800
1801 def owner(self):
1802 raise NotImplementedError("Path.owner() is unsupported on this system")
1803
1804 def group(self):
1805 raise NotImplementedError("Path.group() is unsupported on this system")
1806
1807 def is_mount(self):
1808 raise NotImplementedError(
1809 "Path.is_mount() is unsupported on this system")