Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pathlib2/__init__.py: 35%

801 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:25 +0000

1# Copyright (c) 2001-2022 Python Software Foundation; All Rights Reserved 

2# Copyright (c) 2014-2022 Matthias C. M. Troffaes and contributors 

3# Copyright (c) 2012-2014 Antoine Pitrou and contributors 

4# 

5# Distributed under the terms of the MIT License. 

6 

7import fnmatch 

8import functools 

9import io 

10import ntpath 

11import os 

12import posixpath 

13import re 

14import sys 

15import warnings 

16from _collections_abc import Sequence 

17from errno import ENOENT, ENOTDIR, EBADF, ELOOP 

18from operator import attrgetter 

19from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO 

20from urllib.parse import quote_from_bytes as urlquote_from_bytes 

21 

22 

23__all__ = [ 

24 "PurePath", "PurePosixPath", "PureWindowsPath", 

25 "Path", "PosixPath", "WindowsPath", 

26 ] 

27 

28# 

29# Internals 

30# 

31 

32_WINERROR_NOT_READY = 21 # drive exists but is not accessible 

33_WINERROR_INVALID_NAME = 123 # fix for bpo-35306 

34_WINERROR_CANT_RESOLVE_FILENAME = 1921 # broken symlink pointing to itself 

35 

36# EBADF - guard against macOS `stat` throwing EBADF 

37_IGNORED_ERRNOS = (ENOENT, ENOTDIR, EBADF, ELOOP) 

38 

39_IGNORED_WINERRORS = ( 

40 _WINERROR_NOT_READY, 

41 _WINERROR_INVALID_NAME, 

42 _WINERROR_CANT_RESOLVE_FILENAME) 

43 

44def _ignore_error(exception): 

45 return (getattr(exception, 'errno', None) in _IGNORED_ERRNOS or 

46 getattr(exception, 'winerror', None) in _IGNORED_WINERRORS) 

47 

48 

49def _is_wildcard_pattern(pat): 

50 # Whether this pattern needs actual matching using fnmatch, or can 

51 # be looked up directly as a file. 

52 return "*" in pat or "?" in pat or "[" in pat 

53 

54 

55if sys.version_info >= (3, 10): 

56 io_text_encoding = io.text_encoding 

57else: 

58 def io_text_encoding(encoding, stacklevel=2): 

59 return encoding 

60 

61 

62if sys.version_info >= (3, 8): 

63 sys_audit = sys.audit 

64else: 

65 def sys_audit(*args): 

66 return 

67 

68 

69class _Flavour(object): 

70 """A flavour implements a particular (platform-specific) set of path 

71 semantics.""" 

72 

73 sep: str 

74 altsep: str 

75 

76 def __init__(self): 

77 self.join = self.sep.join 

78 

79 def parse_parts(self, parts): 

80 parsed = [] 

81 sep = self.sep 

82 altsep = self.altsep 

83 drv = root = '' 

84 it = reversed(parts) 

85 for part in it: 

86 if not part: 

87 continue 

88 if altsep: 

89 part = part.replace(altsep, sep) 

90 drv, root, rel = self.splitroot(part) 

91 if sep in rel: 

92 for x in reversed(rel.split(sep)): 

93 if x and x != '.': 

94 parsed.append(sys.intern(x)) 

95 else: 

96 if rel and rel != '.': 

97 parsed.append(sys.intern(rel)) 

98 if drv or root: 

99 if not drv: 

100 # If no drive is present, try to find one in the previous 

101 # parts. This makes the result of parsing e.g. 

102 # ("C:", "/", "a") reasonably intuitive. 

103 for part in it: 

104 if not part: 

105 continue 

106 if altsep: 

107 part = part.replace(altsep, sep) 

108 drv = self.splitroot(part)[0] 

109 if drv: 

110 break 

111 break 

112 if drv or root: 

113 parsed.append(drv + root) 

114 parsed.reverse() 

115 return drv, root, parsed 

116 

117 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2): 

118 """ 

119 Join the two paths represented by the respective 

120 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple. 

121 """ 

122 if root2: 

123 if not drv2 and drv: 

124 return drv, root2, [drv + root2] + parts2[1:] 

125 elif drv2: 

126 if drv2 == drv or self.casefold(drv2) == self.casefold(drv): 

127 # Same drive => second path is relative to the first 

128 return drv, root, parts + parts2[1:] 

129 else: 

130 # Second path is non-anchored (common case) 

131 return drv, root, parts + parts2 

132 return drv2, root2, parts2 

133 

134 

135class _WindowsFlavour(_Flavour): 

136 # Reference for Windows paths can be found at 

137 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx 

138 

139 sep = '\\' 

140 altsep = '/' 

141 has_drv = True 

142 pathmod = ntpath 

143 

144 is_supported = (os.name == 'nt') 

145 

146 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') 

147 ext_namespace_prefix = '\\\\?\\' 

148 

149 reserved_names = ( 

150 {'CON', 'PRN', 'AUX', 'NUL', 'CONIN$', 'CONOUT$'} | 

151 {'COM%s' % c for c in '123456789\xb9\xb2\xb3'} | 

152 {'LPT%s' % c for c in '123456789\xb9\xb2\xb3'} 

153 ) 

154 

155 # Interesting findings about extended paths: 

156 # * '\\?\c:\a' is an extended path, which bypasses normal Windows API 

157 # path processing. Thus relative paths are not resolved and slash is not 

158 # translated to backslash. It has the native NT path limit of 32767 

159 # characters, but a bit less after resolving device symbolic links, 

160 # such as '\??\C:' => '\Device\HarddiskVolume2'. 

161 # * '\\?\c:/a' looks for a device named 'C:/a' because slash is a 

162 # regular name character in the object namespace. 

163 # * '\\?\c:\foo/bar' is invalid because '/' is illegal in NT filesystems. 

164 # The only path separator at the filesystem level is backslash. 

165 # * '//?/c:\a' and '//?/c:/a' are effectively equivalent to '\\.\c:\a' and 

166 # thus limited to MAX_PATH. 

167 # * Prior to Windows 8, ANSI API bytes paths are limited to MAX_PATH, 

168 # even with the '\\?\' prefix. 

169 

170 def splitroot(self, part, sep=sep): 

171 first = part[0:1] 

172 second = part[1:2] 

173 if (second == sep and first == sep): 

174 # XXX extended paths should also disable the collapsing of "." 

175 # components (according to MSDN docs). 

176 prefix, part = self._split_extended_path(part) 

177 first = part[0:1] 

178 second = part[1:2] 

179 else: 

180 prefix = '' 

181 third = part[2:3] 

182 if (second == sep and first == sep and third != sep): 

183 # is a UNC path: 

184 # vvvvvvvvvvvvvvvvvvvvv root 

185 # \\machine\mountpoint\directory\etc\... 

186 # directory ^^^^^^^^^^^^^^ 

187 index = part.find(sep, 2) 

188 if index != -1: 

189 index2 = part.find(sep, index + 1) 

190 # a UNC path can't have two slashes in a row 

191 # (after the initial two) 

192 if index2 != index + 1: 

193 if index2 == -1: 

194 index2 = len(part) 

195 if prefix: 

196 return prefix + part[1:index2], sep, part[index2+1:] 

197 else: 

198 return part[:index2], sep, part[index2+1:] 

199 drv = root = '' 

200 if second == ':' and first in self.drive_letters: 

201 drv = part[:2] 

202 part = part[2:] 

203 first = third 

204 if first == sep: 

205 root = first 

206 part = part.lstrip(sep) 

207 return prefix + drv, root, part 

208 

209 def casefold(self, s): 

210 return s.lower() 

211 

212 def casefold_parts(self, parts): 

213 return [p.lower() for p in parts] 

214 

215 def compile_pattern(self, pattern): 

216 return re.compile(fnmatch.translate(pattern), re.IGNORECASE).fullmatch 

217 

218 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix): 

219 prefix = '' 

220 if s.startswith(ext_prefix): 

221 prefix = s[:4] 

222 s = s[4:] 

223 if s.startswith('UNC\\'): 

224 prefix += s[:3] 

225 s = '\\' + s[3:] 

226 return prefix, s 

227 

228 def is_reserved(self, parts): 

229 # NOTE: the rules for reserved names seem somewhat complicated 

230 # (e.g. r"..\NUL" is reserved but not r"foo\NUL" if "foo" does not 

231 # exist). We err on the side of caution and return True for paths 

232 # which are not considered reserved by Windows. 

233 if not parts: 

234 return False 

235 if parts[0].startswith('\\\\'): 

236 # UNC paths are never reserved 

237 return False 

238 name = parts[-1].partition('.')[0].partition(':')[0].rstrip(' ') 

239 return name.upper() in self.reserved_names 

240 

241 def make_uri(self, path): 

242 # Under Windows, file URIs use the UTF-8 encoding. 

243 drive = path.drive 

244 if len(drive) == 2 and drive[1] == ':': 

245 # It's a path on a local drive => 'file:///c:/a/b' 

246 rest = path.as_posix()[2:].lstrip('/') 

247 return 'file:///%s/%s' % ( 

248 drive, urlquote_from_bytes(rest.encode('utf-8'))) 

249 else: 

250 # It's a path on a network drive => 'file://host/share/a/b' 

251 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8')) 

252 

253 

254class _PosixFlavour(_Flavour): 

255 sep = '/' 

256 altsep = '' 

257 has_drv = False 

258 pathmod = posixpath 

259 

260 is_supported = (os.name != 'nt') 

261 

262 def splitroot(self, part, sep=sep): 

263 if part and part[0] == sep: 

264 stripped_part = part.lstrip(sep) 

265 # According to POSIX path resolution: 

266 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11 

267 # "A pathname that begins with two successive slashes may be 

268 # interpreted in an implementation-defined manner, although more 

269 # than two leading slashes shall be treated as a single slash". 

270 if len(part) - len(stripped_part) == 2: 

271 return '', sep * 2, stripped_part 

272 else: 

273 return '', sep, stripped_part 

274 else: 

275 return '', '', part 

276 

277 def casefold(self, s): 

278 return s 

279 

280 def casefold_parts(self, parts): 

281 return parts 

282 

283 def compile_pattern(self, pattern): 

284 return re.compile(fnmatch.translate(pattern)).fullmatch 

285 

286 def is_reserved(self, parts): 

287 return False 

288 

289 def make_uri(self, path): 

290 # We represent the path using the local filesystem encoding, 

291 # for portability to other applications. 

292 bpath = bytes(path) 

293 return 'file://' + urlquote_from_bytes(bpath) 

294 

295 

296_windows_flavour = _WindowsFlavour() 

297_posix_flavour = _PosixFlavour() 

298 

299 

300if sys.version_info >= (3, 10): 

301 from os.path import realpath as os_path_realpath 

302elif os.name == "posix": 

303 from pathlib2._posixpath import realpath as os_path_realpath 

304else: 

305 from pathlib2._ntpath import realpath as os_path_realpath 

306 

307 

308# 

309# Globbing helpers 

310# 

311 

312def _make_selector(pattern_parts, flavour): 

313 pat = pattern_parts[0] 

314 child_parts = pattern_parts[1:] 

315 if pat == '**': 

316 cls = _RecursiveWildcardSelector 

317 elif '**' in pat: 

318 raise ValueError("Invalid pattern: '**' can only be an entire path component") 

319 elif _is_wildcard_pattern(pat): 

320 cls = _WildcardSelector 

321 else: 

322 cls = _PreciseSelector 

323 return cls(pat, child_parts, flavour) 

324 

325if hasattr(functools, "lru_cache"): 

326 _make_selector = functools.lru_cache()(_make_selector) 

327 

328 

329class _Selector: 

330 """A selector matches a specific glob pattern part against the children 

331 of a given path.""" 

332 

333 def __init__(self, child_parts, flavour): 

334 self.child_parts = child_parts 

335 if child_parts: 

336 self.successor = _make_selector(child_parts, flavour) 

337 self.dironly = True 

338 else: 

339 self.successor = _TerminatingSelector() 

340 self.dironly = False 

341 

342 def select_from(self, parent_path): 

343 """Iterate over all child paths of `parent_path` matched by this 

344 selector. This can contain parent_path itself.""" 

345 path_cls = type(parent_path) 

346 is_dir = path_cls.is_dir 

347 exists = path_cls.exists 

348 scandir = path_cls._scandir 

349 if not is_dir(parent_path): 

350 return iter([]) 

351 return self._select_from(parent_path, is_dir, exists, scandir) 

352 

353 

354class _TerminatingSelector: 

355 

356 def _select_from(self, parent_path, is_dir, exists, scandir): 

357 yield parent_path 

358 

359 

360class _PreciseSelector(_Selector): 

361 

362 def __init__(self, name, child_parts, flavour): 

363 self.name = name 

364 _Selector.__init__(self, child_parts, flavour) 

365 

366 def _select_from(self, parent_path, is_dir, exists, scandir): 

367 try: 

368 path = parent_path._make_child_relpath(self.name) 

369 if (is_dir if self.dironly else exists)(path): 

370 for p in self.successor._select_from(path, is_dir, exists, scandir): 

371 yield p 

372 except PermissionError: 

373 return 

374 

375 

376class _WildcardSelector(_Selector): 

377 

378 def __init__(self, pat, child_parts, flavour): 

379 self.match = flavour.compile_pattern(pat) 

380 _Selector.__init__(self, child_parts, flavour) 

381 

382 def _select_from(self, parent_path, is_dir, exists, scandir): 

383 try: 

384 with scandir(parent_path) as scandir_it: 

385 entries = list(scandir_it) 

386 for entry in entries: 

387 if self.dironly: 

388 try: 

389 # "entry.is_dir()" can raise PermissionError 

390 # in some cases (see bpo-38894), which is not 

391 # among the errors ignored by _ignore_error() 

392 if not entry.is_dir(): 

393 continue 

394 except OSError as e: 

395 if not _ignore_error(e): 

396 raise 

397 continue 

398 name = entry.name 

399 if self.match(name): 

400 path = parent_path._make_child_relpath(name) 

401 for p in self.successor._select_from(path, is_dir, exists, scandir): 

402 yield p 

403 except PermissionError: 

404 return 

405 

406 

407class _RecursiveWildcardSelector(_Selector): 

408 

409 def __init__(self, pat, child_parts, flavour): 

410 _Selector.__init__(self, child_parts, flavour) 

411 

412 def _iterate_directories(self, parent_path, is_dir, scandir): 

413 yield parent_path 

414 try: 

415 with scandir(parent_path) as scandir_it: 

416 entries = list(scandir_it) 

417 for entry in entries: 

418 entry_is_dir = False 

419 try: 

420 entry_is_dir = entry.is_dir() 

421 except OSError as e: 

422 if not _ignore_error(e): 

423 raise 

424 if entry_is_dir and not entry.is_symlink(): 

425 path = parent_path._make_child_relpath(entry.name) 

426 for p in self._iterate_directories(path, is_dir, scandir): 

427 yield p 

428 except PermissionError: 

429 return 

430 

431 def _select_from(self, parent_path, is_dir, exists, scandir): 

432 try: 

433 yielded = set() 

434 try: 

435 successor_select = self.successor._select_from 

436 for starting_point in self._iterate_directories(parent_path, is_dir, scandir): 

437 for p in successor_select(starting_point, is_dir, exists, scandir): 

438 if p not in yielded: 

439 yield p 

440 yielded.add(p) 

441 finally: 

442 yielded.clear() 

443 except PermissionError: 

444 return 

445 

446 

447# 

448# Public API 

449# 

450 

451class _PathParents(Sequence): 

452 """This object provides sequence-like access to the logical ancestors 

453 of a path. Don't try to construct it yourself.""" 

454 __slots__ = ('_pathcls', '_drv', '_root', '_parts') 

455 

456 def __init__(self, path): 

457 # We don't store the instance to avoid reference cycles 

458 self._pathcls = type(path) 

459 self._drv = path._drv 

460 self._root = path._root 

461 self._parts = path._parts 

462 

463 def __len__(self): 

464 if self._drv or self._root: 

465 return len(self._parts) - 1 

466 else: 

467 return len(self._parts) 

468 

469 def __getitem__(self, idx): 

470 if isinstance(idx, slice): 

471 return tuple(self[i] for i in range(*idx.indices(len(self)))) 

472 

473 if idx >= len(self) or idx < -len(self): 

474 raise IndexError(idx) 

475 return self._pathcls._from_parsed_parts(self._drv, self._root, 

476 self._parts[:-idx - 1]) 

477 

478 def __repr__(self): 

479 return "<{}.parents>".format(self._pathcls.__name__) 

480 

481 

482class PurePath(object): 

483 """Base class for manipulating paths without I/O. 

484 

485 PurePath represents a filesystem path and offers operations which 

486 don't imply any actual filesystem I/O. Depending on your system, 

487 instantiating a PurePath will return either a PurePosixPath or a 

488 PureWindowsPath object. You can also instantiate either of these classes 

489 directly, regardless of your system. 

490 """ 

491 __slots__ = ( 

492 '_drv', '_root', '_parts', 

493 '_str', '_hash', '_pparts', '_cached_cparts', 

494 ) 

495 

496 def __new__(cls, *args): 

497 """Construct a PurePath from one or several strings and or existing 

498 PurePath objects. The strings and path objects are combined so as 

499 to yield a canonicalized path, which is incorporated into the 

500 new PurePath object. 

501 """ 

502 if cls is PurePath: 

503 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath 

504 return cls._from_parts(args) 

505 

506 def __reduce__(self): 

507 # Using the parts tuple helps share interned path parts 

508 # when pickling related paths. 

509 return (self.__class__, tuple(self._parts)) 

510 

511 @classmethod 

512 def _parse_args(cls, args): 

513 # This is useful when you don't want to create an instance, just 

514 # canonicalize some constructor arguments. 

515 parts = [] 

516 for a in args: 

517 if isinstance(a, PurePath): 

518 parts += a._parts 

519 else: 

520 a = os.fspath(a) 

521 if isinstance(a, str): 

522 # Force-cast str subclasses to str (issue #21127) 

523 parts.append(str(a)) 

524 else: 

525 raise TypeError( 

526 "argument should be a str object or an os.PathLike " 

527 "object returning str, not %r" 

528 % type(a)) 

529 return cls._flavour.parse_parts(parts) 

530 

531 @classmethod 

532 def _from_parts(cls, args): 

533 # We need to call _parse_args on the instance, so as to get the 

534 # right flavour. 

535 self = object.__new__(cls) 

536 drv, root, parts = self._parse_args(args) 

537 self._drv = drv 

538 self._root = root 

539 self._parts = parts 

540 return self 

541 

542 @classmethod 

543 def _from_parsed_parts(cls, drv, root, parts): 

544 self = object.__new__(cls) 

545 self._drv = drv 

546 self._root = root 

547 self._parts = parts 

548 return self 

549 

550 @classmethod 

551 def _format_parsed_parts(cls, drv, root, parts): 

552 if drv or root: 

553 return drv + root + cls._flavour.join(parts[1:]) 

554 else: 

555 return cls._flavour.join(parts) 

556 

557 def _make_child(self, args): 

558 drv, root, parts = self._parse_args(args) 

559 drv, root, parts = self._flavour.join_parsed_parts( 

560 self._drv, self._root, self._parts, drv, root, parts) 

561 return self._from_parsed_parts(drv, root, parts) 

562 

563 def __str__(self): 

564 """Return the string representation of the path, suitable for 

565 passing to system calls.""" 

566 try: 

567 return self._str 

568 except AttributeError: 

569 self._str = self._format_parsed_parts(self._drv, self._root, 

570 self._parts) or '.' 

571 return self._str 

572 

573 def __fspath__(self): 

574 return str(self) 

575 

576 def as_posix(self): 

577 """Return the string representation of the path with forward (/) 

578 slashes.""" 

579 f = self._flavour 

580 return str(self).replace(f.sep, '/') 

581 

582 def __bytes__(self): 

583 """Return the bytes representation of the path. This is only 

584 recommended to use under Unix.""" 

585 return os.fsencode(self) 

586 

587 def __repr__(self): 

588 return "{}({!r})".format(self.__class__.__name__, self.as_posix()) 

589 

590 def as_uri(self): 

591 """Return the path as a 'file' URI.""" 

592 if not self.is_absolute(): 

593 raise ValueError("relative path can't be expressed as a file URI") 

594 return self._flavour.make_uri(self) 

595 

596 @property 

597 def _cparts(self): 

598 # Cached casefolded parts, for hashing and comparison 

599 try: 

600 return self._cached_cparts 

601 except AttributeError: 

602 self._cached_cparts = self._flavour.casefold_parts(self._parts) 

603 return self._cached_cparts 

604 

605 def __eq__(self, other): 

606 if not isinstance(other, PurePath): 

607 return NotImplemented 

608 return self._cparts == other._cparts and self._flavour is other._flavour 

609 

610 def __hash__(self): 

611 try: 

612 return self._hash 

613 except AttributeError: 

614 self._hash = hash(tuple(self._cparts)) 

615 return self._hash 

616 

617 def __lt__(self, other): 

618 if not isinstance(other, PurePath) or self._flavour is not other._flavour: 

619 return NotImplemented 

620 return self._cparts < other._cparts 

621 

622 def __le__(self, other): 

623 if not isinstance(other, PurePath) or self._flavour is not other._flavour: 

624 return NotImplemented 

625 return self._cparts <= other._cparts 

626 

627 def __gt__(self, other): 

628 if not isinstance(other, PurePath) or self._flavour is not other._flavour: 

629 return NotImplemented 

630 return self._cparts > other._cparts 

631 

632 def __ge__(self, other): 

633 if not isinstance(other, PurePath) or self._flavour is not other._flavour: 

634 return NotImplemented 

635 return self._cparts >= other._cparts 

636 

637 drive = property(attrgetter('_drv'), 

638 doc="""The drive prefix (letter or UNC path), if any.""") 

639 

640 root = property(attrgetter('_root'), 

641 doc="""The root of the path, if any.""") 

642 

643 @property 

644 def anchor(self): 

645 """The concatenation of the drive and root, or ''.""" 

646 anchor = self._drv + self._root 

647 return anchor 

648 

649 @property 

650 def name(self): 

651 """The final path component, if any.""" 

652 parts = self._parts 

653 if len(parts) == (1 if (self._drv or self._root) else 0): 

654 return '' 

655 return parts[-1] 

656 

657 @property 

658 def suffix(self): 

659 """ 

660 The final component's last suffix, if any. 

661 

662 This includes the leading period. For example: '.txt' 

663 """ 

664 name = self.name 

665 i = name.rfind('.') 

666 if 0 < i < len(name) - 1: 

667 return name[i:] 

668 else: 

669 return '' 

670 

671 @property 

672 def suffixes(self): 

673 """ 

674 A list of the final component's suffixes, if any. 

675 

676 These include the leading periods. For example: ['.tar', '.gz'] 

677 """ 

678 name = self.name 

679 if name.endswith('.'): 

680 return [] 

681 name = name.lstrip('.') 

682 return ['.' + suffix for suffix in name.split('.')[1:]] 

683 

684 @property 

685 def stem(self): 

686 """The final path component, minus its last suffix.""" 

687 name = self.name 

688 i = name.rfind('.') 

689 if 0 < i < len(name) - 1: 

690 return name[:i] 

691 else: 

692 return name 

693 

694 def with_name(self, name): 

695 """Return a new path with the file name changed.""" 

696 if not self.name: 

697 raise ValueError("%r has an empty name" % (self,)) 

698 drv, root, parts = self._flavour.parse_parts((name,)) 

699 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep] 

700 or drv or root or len(parts) != 1): 

701 raise ValueError("Invalid name %r" % (name)) 

702 return self._from_parsed_parts(self._drv, self._root, 

703 self._parts[:-1] + [name]) 

704 

705 def with_stem(self, stem): 

706 """Return a new path with the stem changed.""" 

707 return self.with_name(stem + self.suffix) 

708 

709 def with_suffix(self, suffix): 

710 """Return a new path with the file suffix changed. If the path 

711 has no suffix, add given suffix. If the given suffix is an empty 

712 string, remove the suffix from the path. 

713 """ 

714 f = self._flavour 

715 if f.sep in suffix or f.altsep and f.altsep in suffix: 

716 raise ValueError("Invalid suffix %r" % (suffix,)) 

717 if suffix and not suffix.startswith('.') or suffix == '.': 

718 raise ValueError("Invalid suffix %r" % (suffix)) 

719 name = self.name 

720 if not name: 

721 raise ValueError("%r has an empty name" % (self,)) 

722 old_suffix = self.suffix 

723 if not old_suffix: 

724 name = name + suffix 

725 else: 

726 name = name[:-len(old_suffix)] + suffix 

727 return self._from_parsed_parts(self._drv, self._root, 

728 self._parts[:-1] + [name]) 

729 

730 def relative_to(self, *other): 

731 """Return the relative path to another path identified by the passed 

732 arguments. If the operation is not possible (because this is not 

733 a subpath of the other path), raise ValueError. 

734 """ 

735 # For the purpose of this method, drive and root are considered 

736 # separate parts, i.e.: 

737 # Path('c:/').relative_to('c:') gives Path('/') 

738 # Path('c:/').relative_to('/') raise ValueError 

739 if not other: 

740 raise TypeError("need at least one argument") 

741 parts = self._parts 

742 drv = self._drv 

743 root = self._root 

744 if root: 

745 abs_parts = [drv, root] + parts[1:] 

746 else: 

747 abs_parts = parts 

748 to_drv, to_root, to_parts = self._parse_args(other) 

749 if to_root: 

750 to_abs_parts = [to_drv, to_root] + to_parts[1:] 

751 else: 

752 to_abs_parts = to_parts 

753 n = len(to_abs_parts) 

754 cf = self._flavour.casefold_parts 

755 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts): 

756 formatted = self._format_parsed_parts(to_drv, to_root, to_parts) 

757 raise ValueError("{!r} is not in the subpath of {!r}" 

758 " OR one path is relative and the other is absolute." 

759 .format(str(self), str(formatted))) 

760 return self._from_parsed_parts('', root if n == 1 else '', 

761 abs_parts[n:]) 

762 

763 def is_relative_to(self, *other): 

764 """Return True if the path is relative to another path or False. 

765 """ 

766 try: 

767 self.relative_to(*other) 

768 return True 

769 except ValueError: 

770 return False 

771 

772 @property 

773 def parts(self): 

774 """An object providing sequence-like access to the 

775 components in the filesystem path.""" 

776 # We cache the tuple to avoid building a new one each time .parts 

777 # is accessed. XXX is this necessary? 

778 try: 

779 return self._pparts 

780 except AttributeError: 

781 self._pparts = tuple(self._parts) 

782 return self._pparts 

783 

784 def joinpath(self, *args): 

785 """Combine this path with one or several arguments, and return a 

786 new path representing either a subpath (if all arguments are relative 

787 paths) or a totally different path (if one of the arguments is 

788 anchored). 

789 """ 

790 return self._make_child(args) 

791 

792 def __truediv__(self, key): 

793 try: 

794 return self._make_child((key,)) 

795 except TypeError: 

796 return NotImplemented 

797 

798 def __rtruediv__(self, key): 

799 try: 

800 return self._from_parts([key] + self._parts) 

801 except TypeError: 

802 return NotImplemented 

803 

804 @property 

805 def parent(self): 

806 """The logical parent of the path.""" 

807 drv = self._drv 

808 root = self._root 

809 parts = self._parts 

810 if len(parts) == 1 and (drv or root): 

811 return self 

812 return self._from_parsed_parts(drv, root, parts[:-1]) 

813 

814 @property 

815 def parents(self): 

816 """A sequence of this path's logical parents.""" 

817 return _PathParents(self) 

818 

819 def is_absolute(self): 

820 """True if the path is absolute (has both a root and, if applicable, 

821 a drive).""" 

822 if not self._root: 

823 return False 

824 return not self._flavour.has_drv or bool(self._drv) 

825 

826 def is_reserved(self): 

827 """Return True if the path contains one of the special names reserved 

828 by the system, if any.""" 

829 return self._flavour.is_reserved(self._parts) 

830 

831 def match(self, path_pattern): 

832 """ 

833 Return True if this path matches the given pattern. 

834 """ 

835 cf = self._flavour.casefold 

836 path_pattern = cf(path_pattern) 

837 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,)) 

838 if not pat_parts: 

839 raise ValueError("empty pattern") 

840 if drv and drv != cf(self._drv): 

841 return False 

842 if root and root != cf(self._root): 

843 return False 

844 parts = self._cparts 

845 if drv or root: 

846 if len(pat_parts) != len(parts): 

847 return False 

848 pat_parts = pat_parts[1:] 

849 elif len(pat_parts) > len(parts): 

850 return False 

851 for part, pat in zip(reversed(parts), reversed(pat_parts)): 

852 if not fnmatch.fnmatchcase(part, pat): 

853 return False 

854 return True 

855 

856# Can't subclass os.PathLike from PurePath and keep the constructor 

857# optimizations in PurePath._parse_args(). 

858os.PathLike.register(PurePath) 

859 

860 

861class PurePosixPath(PurePath): 

862 """PurePath subclass for non-Windows systems. 

863 

864 On a POSIX system, instantiating a PurePath should return this object. 

865 However, you can also instantiate it directly on any system. 

866 """ 

867 _flavour = _posix_flavour 

868 __slots__ = () 

869 

870 

871class PureWindowsPath(PurePath): 

872 """PurePath subclass for Windows systems. 

873 

874 On a Windows system, instantiating a PurePath should return this object. 

875 However, you can also instantiate it directly on any system. 

876 """ 

877 _flavour = _windows_flavour 

878 __slots__ = () 

879 

880 

881# Filesystem-accessing classes 

882 

883 

884class Path(PurePath): 

885 """PurePath subclass that can make system calls. 

886 

887 Path represents a filesystem path but unlike PurePath, also offers 

888 methods to do system calls on path objects. Depending on your system, 

889 instantiating a Path will return either a PosixPath or a WindowsPath 

890 object. You can also instantiate a PosixPath or WindowsPath directly, 

891 but cannot instantiate a WindowsPath on a POSIX system or vice versa. 

892 """ 

893 __slots__ = () 

894 

895 def __new__(cls, *args, **kwargs): 

896 if cls is Path: 

897 cls = WindowsPath if os.name == 'nt' else PosixPath 

898 self = cls._from_parts(args) 

899 if not self._flavour.is_supported: 

900 raise NotImplementedError("cannot instantiate %r on your system" 

901 % (cls.__name__,)) 

902 return self 

903 

904 def _make_child_relpath(self, part): 

905 # This is an optimization used for dir walking. `part` must be 

906 # a single part relative to this path. 

907 parts = self._parts + [part] 

908 return self._from_parsed_parts(self._drv, self._root, parts) 

909 

910 def __enter__(self): 

911 return self 

912 

913 def __exit__(self, t, v, tb): 

914 # https://bugs.python.org/issue39682 

915 # In previous versions of pathlib, this method marked this path as 

916 # closed; subsequent attempts to perform I/O would raise an IOError. 

917 # This functionality was never documented, and had the effect of 

918 # making Path objects mutable, contrary to PEP 428. In Python 3.9 the 

919 # _closed attribute was removed, and this method made a no-op. 

920 # This method and __enter__()/__exit__() should be deprecated and 

921 # removed in the future. 

922 pass 

923 

924 # Public API 

925 

926 @classmethod 

927 def cwd(cls): 

928 """Return a new path pointing to the current working directory 

929 (as returned by os.getcwd()). 

930 """ 

931 return cls(os.getcwd()) 

932 

933 @classmethod 

934 def home(cls): 

935 """Return a new path pointing to the user's home directory (as 

936 returned by os.path.expanduser('~')). 

937 """ 

938 return cls("~").expanduser() 

939 

940 def samefile(self, other_path): 

941 """Return whether other_path is the same or not as this file 

942 (as returned by os.path.samefile()). 

943 """ 

944 st = self.stat() 

945 try: 

946 other_st = other_path.stat() 

947 except AttributeError: 

948 other_st = self.__class__(other_path).stat() 

949 return os.path.samestat(st, other_st) 

950 

951 def iterdir(self): 

952 """Iterate over the files in this directory. Does not yield any 

953 result for the special paths '.' and '..'. 

954 """ 

955 for name in os.listdir(self): 

956 yield self._make_child_relpath(name) 

957 

958 def _scandir(self): 

959 # bpo-24132: a future version of pathlib will support subclassing of 

960 # pathlib.Path to customize how the filesystem is accessed. This 

961 # includes scandir(), which is used to implement glob(). 

962 return os.scandir(self) 

963 

964 def glob(self, pattern): 

965 """Iterate over this subtree and yield all existing files (of any 

966 kind, including directories) matching the given relative pattern. 

967 """ 

968 sys_audit("pathlib.Path.glob", self, pattern) 

969 if not pattern: 

970 raise ValueError("Unacceptable pattern: {!r}".format(pattern)) 

971 drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) 

972 if drv or root: 

973 raise NotImplementedError("Non-relative patterns are unsupported") 

974 selector = _make_selector(tuple(pattern_parts), self._flavour) 

975 for p in selector.select_from(self): 

976 yield p 

977 

978 def rglob(self, pattern): 

979 """Recursively yield all existing files (of any kind, including 

980 directories) matching the given relative pattern, anywhere in 

981 this subtree. 

982 """ 

983 sys_audit("pathlib.Path.rglob", self, pattern) 

984 drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) 

985 if drv or root: 

986 raise NotImplementedError("Non-relative patterns are unsupported") 

987 selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour) 

988 for p in selector.select_from(self): 

989 yield p 

990 

991 def absolute(self): 

992 """Return an absolute version of this path by prepending the current 

993 working directory. No normalization or symlink resolution is performed. 

994 

995 Use resolve() to get the canonical path to a file. 

996 """ 

997 if self.is_absolute(): 

998 return self 

999 return self._from_parts([self.cwd()] + self._parts) 

1000 

1001 def resolve(self, strict=False): 

1002 """ 

1003 Make the path absolute, resolving all symlinks on the way and also 

1004 normalizing it. 

1005 """ 

1006 

1007 def check_eloop(e): 

1008 winerror = getattr(e, 'winerror', 0) 

1009 if e.errno == ELOOP or winerror == _WINERROR_CANT_RESOLVE_FILENAME: 

1010 raise RuntimeError("Symlink loop from %r" % e.filename) 

1011 

1012 try: 

1013 s = os_path_realpath(self, strict=strict) 

1014 except OSError as e: 

1015 check_eloop(e) 

1016 raise 

1017 p = self._from_parts((s,)) 

1018 

1019 # In non-strict mode, realpath() doesn't raise on symlink loops. 

1020 # Ensure we get an exception by calling stat() 

1021 if not strict: 

1022 try: 

1023 p.stat() 

1024 except OSError as e: 

1025 check_eloop(e) 

1026 return p 

1027 

1028 def stat(self, *, follow_symlinks=True): 

1029 """ 

1030 Return the result of the stat() system call on this path, like 

1031 os.stat() does. 

1032 """ 

1033 return os.stat(self, follow_symlinks=follow_symlinks) 

1034 

1035 def owner(self): 

1036 """ 

1037 Return the login name of the file owner. 

1038 """ 

1039 try: 

1040 import pwd 

1041 return pwd.getpwuid(self.stat().st_uid).pw_name 

1042 except ImportError: 

1043 raise NotImplementedError("Path.owner() is unsupported on this system") 

1044 

1045 def group(self): 

1046 """ 

1047 Return the group name of the file gid. 

1048 """ 

1049 

1050 try: 

1051 import grp 

1052 return grp.getgrgid(self.stat().st_gid).gr_name 

1053 except ImportError: 

1054 raise NotImplementedError("Path.group() is unsupported on this system") 

1055 

1056 def open(self, mode='r', buffering=-1, encoding=None, 

1057 errors=None, newline=None): 

1058 """ 

1059 Open the file pointed by this path and return a file object, as 

1060 the built-in open() function does. 

1061 """ 

1062 if "b" not in mode: 

1063 encoding = io_text_encoding(encoding) 

1064 return io.open(self, mode, buffering, encoding, errors, newline) 

1065 

1066 def read_bytes(self): 

1067 """ 

1068 Open the file in bytes mode, read it, and close the file. 

1069 """ 

1070 with self.open(mode='rb') as f: 

1071 return f.read() 

1072 

1073 def read_text(self, encoding=None, errors=None): 

1074 """ 

1075 Open the file in text mode, read it, and close the file. 

1076 """ 

1077 encoding = io_text_encoding(encoding) 

1078 with self.open(mode='r', encoding=encoding, errors=errors) as f: 

1079 return f.read() 

1080 

1081 def write_bytes(self, data): 

1082 """ 

1083 Open the file in bytes mode, write to it, and close the file. 

1084 """ 

1085 # type-check for the buffer interface before truncating the file 

1086 view = memoryview(data) 

1087 with self.open(mode='wb') as f: 

1088 return f.write(view) 

1089 

1090 def write_text(self, data, encoding=None, errors=None, newline=None): 

1091 """ 

1092 Open the file in text mode, write to it, and close the file. 

1093 """ 

1094 if not isinstance(data, str): 

1095 raise TypeError('data must be str, not %s' % 

1096 data.__class__.__name__) 

1097 encoding = io_text_encoding(encoding) 

1098 with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f: 

1099 return f.write(data) 

1100 

1101 def readlink(self): 

1102 """ 

1103 Return the path to which the symbolic link points. 

1104 """ 

1105 if not hasattr(os, "readlink"): 

1106 raise NotImplementedError("os.readlink() not available on this system") 

1107 return self._from_parts((os.readlink(self),)) 

1108 

1109 def touch(self, mode=0o666, exist_ok=True): 

1110 """ 

1111 Create this file with the given access mode, if it doesn't exist. 

1112 """ 

1113 

1114 if exist_ok: 

1115 # First try to bump modification time 

1116 # Implementation note: GNU touch uses the UTIME_NOW option of 

1117 # the utimensat() / futimens() functions. 

1118 try: 

1119 os.utime(self, None) 

1120 except OSError: 

1121 # Avoid exception chaining 

1122 pass 

1123 else: 

1124 return 

1125 flags = os.O_CREAT | os.O_WRONLY 

1126 if not exist_ok: 

1127 flags |= os.O_EXCL 

1128 fd = os.open(self, flags, mode) 

1129 os.close(fd) 

1130 

1131 def mkdir(self, mode=0o777, parents=False, exist_ok=False): 

1132 """ 

1133 Create a new directory at this given path. 

1134 """ 

1135 try: 

1136 os.mkdir(self, mode) 

1137 except FileNotFoundError: 

1138 if not parents or self.parent == self: 

1139 raise 

1140 self.parent.mkdir(parents=True, exist_ok=True) 

1141 self.mkdir(mode, parents=False, exist_ok=exist_ok) 

1142 except OSError: 

1143 # Cannot rely on checking for EEXIST, since the operating system 

1144 # could give priority to other errors like EACCES or EROFS 

1145 if not exist_ok or not self.is_dir(): 

1146 raise 

1147 

1148 def chmod(self, mode, *, follow_symlinks=True): 

1149 """ 

1150 Change the permissions of the path, like os.chmod(). 

1151 """ 

1152 os.chmod(self, mode, follow_symlinks=follow_symlinks) 

1153 

1154 def lchmod(self, mode): 

1155 """ 

1156 Like chmod(), except if the path points to a symlink, the symlink's 

1157 permissions are changed, rather than its target's. 

1158 """ 

1159 self.chmod(mode, follow_symlinks=False) 

1160 

1161 def unlink(self, missing_ok=False): 

1162 """ 

1163 Remove this file or link. 

1164 If the path is a directory, use rmdir() instead. 

1165 """ 

1166 try: 

1167 os.unlink(self) 

1168 except FileNotFoundError: 

1169 if not missing_ok: 

1170 raise 

1171 

1172 def rmdir(self): 

1173 """ 

1174 Remove this directory. The directory must be empty. 

1175 """ 

1176 os.rmdir(self) 

1177 

1178 def lstat(self): 

1179 """ 

1180 Like stat(), except if the path points to a symlink, the symlink's 

1181 status information is returned, rather than its target's. 

1182 """ 

1183 return self.stat(follow_symlinks=False) 

1184 

1185 def rename(self, target): 

1186 """ 

1187 Rename this path to the target path. 

1188 

1189 The target path may be absolute or relative. Relative paths are 

1190 interpreted relative to the current working directory, *not* the 

1191 directory of the Path object. 

1192 

1193 Returns the new Path instance pointing to the target path. 

1194 """ 

1195 os.rename(self, target) 

1196 return self.__class__(target) 

1197 

1198 def replace(self, target): 

1199 """ 

1200 Rename this path to the target path, overwriting if that path exists. 

1201 

1202 The target path may be absolute or relative. Relative paths are 

1203 interpreted relative to the current working directory, *not* the 

1204 directory of the Path object. 

1205 

1206 Returns the new Path instance pointing to the target path. 

1207 """ 

1208 os.replace(self, target) 

1209 return self.__class__(target) 

1210 

1211 def symlink_to(self, target, target_is_directory=False): 

1212 """ 

1213 Make this path a symlink pointing to the target path. 

1214 Note the order of arguments (link, target) is the reverse of os.symlink. 

1215 """ 

1216 if not hasattr(os, "symlink"): 

1217 raise NotImplementedError("os.symlink() not available on this system") 

1218 os.symlink(target, self, target_is_directory) 

1219 

1220 def hardlink_to(self, target): 

1221 """ 

1222 Make this path a hard link pointing to the same file as *target*. 

1223 

1224 Note the order of arguments (self, target) is the reverse of os.link's. 

1225 """ 

1226 if not hasattr(os, "link"): 

1227 raise NotImplementedError("os.link() not available on this system") 

1228 os.link(target, self) 

1229 

1230 def link_to(self, target): 

1231 """ 

1232 Make the target path a hard link pointing to this path. 

1233 

1234 Note this function does not make this path a hard link to *target*, 

1235 despite the implication of the function and argument names. The order 

1236 of arguments (target, link) is the reverse of Path.symlink_to, but 

1237 matches that of os.link. 

1238 

1239 Deprecated since Python 3.10 and scheduled for removal in Python 3.12. 

1240 Use `hardlink_to()` instead. 

1241 """ 

1242 warnings.warn("pathlib.Path.link_to() is deprecated and is scheduled " 

1243 "for removal in Python 3.12. " 

1244 "Use pathlib.Path.hardlink_to() instead.", 

1245 DeprecationWarning, stacklevel=2) 

1246 self.__class__(target).hardlink_to(self) 

1247 

1248 # Convenience functions for querying the stat results 

1249 

1250 def exists(self): 

1251 """ 

1252 Whether this path exists. 

1253 """ 

1254 try: 

1255 self.stat() 

1256 except OSError as e: 

1257 if not _ignore_error(e): 

1258 raise 

1259 return False 

1260 except ValueError: 

1261 # Non-encodable path 

1262 return False 

1263 return True 

1264 

1265 def is_dir(self): 

1266 """ 

1267 Whether this path is a directory. 

1268 """ 

1269 try: 

1270 return S_ISDIR(self.stat().st_mode) 

1271 except OSError as e: 

1272 if not _ignore_error(e): 

1273 raise 

1274 # Path doesn't exist or is a broken symlink 

1275 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ ) 

1276 return False 

1277 except ValueError: 

1278 # Non-encodable path 

1279 return False 

1280 

1281 def is_file(self): 

1282 """ 

1283 Whether this path is a regular file (also True for symlinks pointing 

1284 to regular files). 

1285 """ 

1286 try: 

1287 return S_ISREG(self.stat().st_mode) 

1288 except OSError as e: 

1289 if not _ignore_error(e): 

1290 raise 

1291 # Path doesn't exist or is a broken symlink 

1292 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ ) 

1293 return False 

1294 except ValueError: 

1295 # Non-encodable path 

1296 return False 

1297 

1298 def is_mount(self): 

1299 """ 

1300 Check if this path is a POSIX mount point 

1301 """ 

1302 # Need to exist and be a dir 

1303 if not self.exists() or not self.is_dir(): 

1304 return False 

1305 

1306 try: 

1307 parent_dev = self.parent.stat().st_dev 

1308 except OSError: 

1309 return False 

1310 

1311 dev = self.stat().st_dev 

1312 if dev != parent_dev: 

1313 return True 

1314 ino = self.stat().st_ino 

1315 parent_ino = self.parent.stat().st_ino 

1316 return ino == parent_ino 

1317 

1318 def is_symlink(self): 

1319 """ 

1320 Whether this path is a symbolic link. 

1321 """ 

1322 try: 

1323 return S_ISLNK(self.lstat().st_mode) 

1324 except OSError as e: 

1325 if not _ignore_error(e): 

1326 raise 

1327 # Path doesn't exist 

1328 return False 

1329 except ValueError: 

1330 # Non-encodable path 

1331 return False 

1332 

1333 def is_block_device(self): 

1334 """ 

1335 Whether this path is a block device. 

1336 """ 

1337 try: 

1338 return S_ISBLK(self.stat().st_mode) 

1339 except OSError as e: 

1340 if not _ignore_error(e): 

1341 raise 

1342 # Path doesn't exist or is a broken symlink 

1343 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ ) 

1344 return False 

1345 except ValueError: 

1346 # Non-encodable path 

1347 return False 

1348 

1349 def is_char_device(self): 

1350 """ 

1351 Whether this path is a character device. 

1352 """ 

1353 try: 

1354 return S_ISCHR(self.stat().st_mode) 

1355 except OSError as e: 

1356 if not _ignore_error(e): 

1357 raise 

1358 # Path doesn't exist or is a broken symlink 

1359 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ ) 

1360 return False 

1361 except ValueError: 

1362 # Non-encodable path 

1363 return False 

1364 

1365 def is_fifo(self): 

1366 """ 

1367 Whether this path is a FIFO. 

1368 """ 

1369 try: 

1370 return S_ISFIFO(self.stat().st_mode) 

1371 except OSError as e: 

1372 if not _ignore_error(e): 

1373 raise 

1374 # Path doesn't exist or is a broken symlink 

1375 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ ) 

1376 return False 

1377 except ValueError: 

1378 # Non-encodable path 

1379 return False 

1380 

1381 def is_socket(self): 

1382 """ 

1383 Whether this path is a socket. 

1384 """ 

1385 try: 

1386 return S_ISSOCK(self.stat().st_mode) 

1387 except OSError as e: 

1388 if not _ignore_error(e): 

1389 raise 

1390 # Path doesn't exist or is a broken symlink 

1391 # (see http://web.archive.org/web/20200623061726/https://bitbucket.org/pitrou/pathlib/issues/12/ ) 

1392 return False 

1393 except ValueError: 

1394 # Non-encodable path 

1395 return False 

1396 

1397 def expanduser(self): 

1398 """ Return a new path with expanded ~ and ~user constructs 

1399 (as returned by os.path.expanduser) 

1400 """ 

1401 if (not (self._drv or self._root) and 

1402 self._parts and self._parts[0][:1] == '~'): 

1403 homedir = os.path.expanduser(self._parts[0]) 

1404 if homedir[:1] == "~": 

1405 raise RuntimeError("Could not determine home directory.") 

1406 return self._from_parts([homedir] + self._parts[1:]) 

1407 

1408 return self 

1409 

1410 

1411class PosixPath(Path, PurePosixPath): 

1412 """Path subclass for non-Windows systems. 

1413 

1414 On a POSIX system, instantiating a Path should return this object. 

1415 """ 

1416 __slots__ = () 

1417 

1418class WindowsPath(Path, PureWindowsPath): 

1419 """Path subclass for Windows systems. 

1420 

1421 On a Windows system, instantiating a Path should return this object. 

1422 """ 

1423 __slots__ = () 

1424 

1425 def is_mount(self): 

1426 raise NotImplementedError("Path.is_mount() is unsupported on this system")