Mercurial > repos > shellac > guppy_basecaller
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") |
