Coverage for /pythoncovmergedfiles/medio/medio/usr/lib/python3.9/pathlib.py: 33%

910 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-20 07:00 +0000

1import fnmatch 

2import functools 

3import io 

4import ntpath 

5import os 

6import posixpath 

7import re 

8import sys 

9from _collections_abc import Sequence 

10from errno import EINVAL, ENOENT, ENOTDIR, EBADF, ELOOP 

11from operator import attrgetter 

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

13from urllib.parse import quote_from_bytes as urlquote_from_bytes 

14 

15 

16supports_symlinks = True 

17if os.name == 'nt': 

18 import nt 

19 if sys.getwindowsversion()[:2] >= (6, 0): 

20 from nt import _getfinalpathname 

21 else: 

22 supports_symlinks = False 

23 _getfinalpathname = None 

24else: 

25 nt = None 

26 

27 

28__all__ = [ 

29 "PurePath", "PurePosixPath", "PureWindowsPath", 

30 "Path", "PosixPath", "WindowsPath", 

31 ] 

32 

33# 

34# Internals 

35# 

36 

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

38_IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF, ELOOP) 

39 

40_IGNORED_WINERRORS = ( 

41 21, # ERROR_NOT_READY - drive exists but is not accessible 

42 123, # ERROR_INVALID_NAME - fix for bpo-35306 

43 1921, # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself 

44) 

45 

46def _ignore_error(exception): 

47 return (getattr(exception, 'errno', None) in _IGNORED_ERROS or 

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

49 

50 

51def _is_wildcard_pattern(pat): 

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

53 # be looked up directly as a file. 

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

55 

56 

57class _Flavour(object): 

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

59 semantics.""" 

60 

61 def __init__(self): 

62 self.join = self.sep.join 

63 

64 def parse_parts(self, parts): 

65 parsed = [] 

66 sep = self.sep 

67 altsep = self.altsep 

68 drv = root = '' 

69 it = reversed(parts) 

70 for part in it: 

71 if not part: 

72 continue 

73 if altsep: 

74 part = part.replace(altsep, sep) 

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

76 if sep in rel: 

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

78 if x and x != '.': 

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

80 else: 

81 if rel and rel != '.': 

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

83 if drv or root: 

84 if not drv: 

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

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

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

88 for part in it: 

89 if not part: 

90 continue 

91 if altsep: 

92 part = part.replace(altsep, sep) 

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

94 if drv: 

95 break 

96 break 

97 if drv or root: 

98 parsed.append(drv + root) 

99 parsed.reverse() 

100 return drv, root, parsed 

101 

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

103 """ 

104 Join the two paths represented by the respective 

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

106 """ 

107 if root2: 

108 if not drv2 and drv: 

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

110 elif drv2: 

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

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

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

114 else: 

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

116 return drv, root, parts + parts2 

117 return drv2, root2, parts2 

118 

119 

120class _WindowsFlavour(_Flavour): 

121 # Reference for Windows paths can be found at 

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

123 

124 sep = '\\' 

125 altsep = '/' 

126 has_drv = True 

127 pathmod = ntpath 

128 

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

130 

131 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') 

132 ext_namespace_prefix = '\\\\?\\' 

133 

134 reserved_names = ( 

135 {'CON', 'PRN', 'AUX', 'NUL'} | 

136 {'COM%d' % i for i in range(1, 10)} | 

137 {'LPT%d' % i for i in range(1, 10)} 

138 ) 

139 

140 # Interesting findings about extended paths: 

141 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported 

142 # but '\\?\c:/a' is not 

143 # - extended paths are always absolute; "relative" extended paths will 

144 # fail. 

145 

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

147 first = part[0:1] 

148 second = part[1:2] 

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

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

151 # components (according to MSDN docs). 

152 prefix, part = self._split_extended_path(part) 

153 first = part[0:1] 

154 second = part[1:2] 

155 else: 

156 prefix = '' 

157 third = part[2:3] 

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

159 # is a UNC path: 

160 # vvvvvvvvvvvvvvvvvvvvv root 

161 # \\machine\mountpoint\directory\etc\... 

162 # directory ^^^^^^^^^^^^^^ 

163 index = part.find(sep, 2) 

164 if index != -1: 

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

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

167 # (after the initial two) 

168 if index2 != index + 1: 

169 if index2 == -1: 

170 index2 = len(part) 

171 if prefix: 

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

173 else: 

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

175 drv = root = '' 

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

177 drv = part[:2] 

178 part = part[2:] 

179 first = third 

180 if first == sep: 

181 root = first 

182 part = part.lstrip(sep) 

183 return prefix + drv, root, part 

184 

185 def casefold(self, s): 

186 return s.lower() 

187 

188 def casefold_parts(self, parts): 

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

190 

191 def compile_pattern(self, pattern): 

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

193 

194 def resolve(self, path, strict=False): 

195 s = str(path) 

196 if not s: 

197 return os.getcwd() 

198 previous_s = None 

199 if _getfinalpathname is not None: 

200 if strict: 

201 return self._ext_to_normal(_getfinalpathname(s)) 

202 else: 

203 tail_parts = [] # End of the path after the first one not found 

204 while True: 

205 try: 

206 s = self._ext_to_normal(_getfinalpathname(s)) 

207 except FileNotFoundError: 

208 previous_s = s 

209 s, tail = os.path.split(s) 

210 tail_parts.append(tail) 

211 if previous_s == s: 

212 return path 

213 else: 

214 return os.path.join(s, *reversed(tail_parts)) 

215 # Means fallback on absolute 

216 return None 

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 _ext_to_normal(self, s): 

229 # Turn back an extended path into a normal DOS-like path 

230 return self._split_extended_path(s)[1] 

231 

232 def is_reserved(self, parts): 

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

234 # (e.g. r"..\NUL" is reserved but not r"foo\NUL"). 

235 # We err on the side of caution and return True for paths which are 

236 # not considered reserved by Windows. 

237 if not parts: 

238 return False 

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

240 # UNC paths are never reserved 

241 return False 

242 return parts[-1].partition('.')[0].upper() in self.reserved_names 

243 

244 def make_uri(self, path): 

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

246 drive = path.drive 

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

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

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

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

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

252 else: 

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

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

255 

256 def gethomedir(self, username): 

257 if 'USERPROFILE' in os.environ: 

258 userhome = os.environ['USERPROFILE'] 

259 elif 'HOMEPATH' in os.environ: 

260 try: 

261 drv = os.environ['HOMEDRIVE'] 

262 except KeyError: 

263 drv = '' 

264 userhome = drv + os.environ['HOMEPATH'] 

265 else: 

266 raise RuntimeError("Can't determine home directory") 

267 

268 if username: 

269 # Try to guess user home directory. By default all users 

270 # directories are located in the same place and are named by 

271 # corresponding usernames. If current user home directory points 

272 # to nonstandard place, this guess is likely wrong. 

273 if os.environ['USERNAME'] != username: 

274 drv, root, parts = self.parse_parts((userhome,)) 

275 if parts[-1] != os.environ['USERNAME']: 

276 raise RuntimeError("Can't determine home directory " 

277 "for %r" % username) 

278 parts[-1] = username 

279 if drv or root: 

280 userhome = drv + root + self.join(parts[1:]) 

281 else: 

282 userhome = self.join(parts) 

283 return userhome 

284 

285class _PosixFlavour(_Flavour): 

286 sep = '/' 

287 altsep = '' 

288 has_drv = False 

289 pathmod = posixpath 

290 

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

292 

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

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

295 stripped_part = part.lstrip(sep) 

296 # According to POSIX path resolution: 

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

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

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

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

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

302 return '', sep * 2, stripped_part 

303 else: 

304 return '', sep, stripped_part 

305 else: 

306 return '', '', part 

307 

308 def casefold(self, s): 

309 return s 

310 

311 def casefold_parts(self, parts): 

312 return parts 

313 

314 def compile_pattern(self, pattern): 

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

316 

317 def resolve(self, path, strict=False): 

318 sep = self.sep 

319 accessor = path._accessor 

320 seen = {} 

321 def _resolve(path, rest): 

322 if rest.startswith(sep): 

323 path = '' 

324 

325 for name in rest.split(sep): 

326 if not name or name == '.': 

327 # current dir 

328 continue 

329 if name == '..': 

330 # parent dir 

331 path, _, _ = path.rpartition(sep) 

332 continue 

333 if path.endswith(sep): 

334 newpath = path + name 

335 else: 

336 newpath = path + sep + name 

337 if newpath in seen: 

338 # Already seen this path 

339 path = seen[newpath] 

340 if path is not None: 

341 # use cached value 

342 continue 

343 # The symlink is not resolved, so we must have a symlink loop. 

344 raise RuntimeError("Symlink loop from %r" % newpath) 

345 # Resolve the symbolic link 

346 try: 

347 target = accessor.readlink(newpath) 

348 except OSError as e: 

349 if e.errno != EINVAL and strict: 

350 raise 

351 # Not a symlink, or non-strict mode. We just leave the path 

352 # untouched. 

353 path = newpath 

354 else: 

355 seen[newpath] = None # not resolved symlink 

356 path = _resolve(path, target) 

357 seen[newpath] = path # resolved symlink 

358 

359 return path 

360 # NOTE: according to POSIX, getcwd() cannot contain path components 

361 # which are symlinks. 

362 base = '' if path.is_absolute() else os.getcwd() 

363 return _resolve(base, str(path)) or sep 

364 

365 def is_reserved(self, parts): 

366 return False 

367 

368 def make_uri(self, path): 

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

370 # for portability to other applications. 

371 bpath = bytes(path) 

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

373 

374 def gethomedir(self, username): 

375 if not username: 

376 try: 

377 return os.environ['HOME'] 

378 except KeyError: 

379 import pwd 

380 return pwd.getpwuid(os.getuid()).pw_dir 

381 else: 

382 import pwd 

383 try: 

384 return pwd.getpwnam(username).pw_dir 

385 except KeyError: 

386 raise RuntimeError("Can't determine home directory " 

387 "for %r" % username) 

388 

389 

390_windows_flavour = _WindowsFlavour() 

391_posix_flavour = _PosixFlavour() 

392 

393 

394class _Accessor: 

395 """An accessor implements a particular (system-specific or not) way of 

396 accessing paths on the filesystem.""" 

397 

398 

399class _NormalAccessor(_Accessor): 

400 

401 stat = os.stat 

402 

403 lstat = os.lstat 

404 

405 open = os.open 

406 

407 listdir = os.listdir 

408 

409 scandir = os.scandir 

410 

411 chmod = os.chmod 

412 

413 if hasattr(os, "lchmod"): 

414 lchmod = os.lchmod 

415 else: 

416 def lchmod(self, pathobj, mode): 

417 raise NotImplementedError("lchmod() not available on this system") 

418 

419 mkdir = os.mkdir 

420 

421 unlink = os.unlink 

422 

423 if hasattr(os, "link"): 

424 link_to = os.link 

425 else: 

426 @staticmethod 

427 def link_to(self, target): 

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

429 

430 rmdir = os.rmdir 

431 

432 rename = os.rename 

433 

434 replace = os.replace 

435 

436 if nt: 

437 if supports_symlinks: 

438 symlink = os.symlink 

439 else: 

440 def symlink(a, b, target_is_directory): 

441 raise NotImplementedError("symlink() not available on this system") 

442 else: 

443 # Under POSIX, os.symlink() takes two args 

444 @staticmethod 

445 def symlink(a, b, target_is_directory): 

446 return os.symlink(a, b) 

447 

448 utime = os.utime 

449 

450 # Helper for resolve() 

451 def readlink(self, path): 

452 return os.readlink(path) 

453 

454 def owner(self, path): 

455 try: 

456 import pwd 

457 return pwd.getpwuid(self.stat(path).st_uid).pw_name 

458 except ImportError: 

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

460 

461 def group(self, path): 

462 try: 

463 import grp 

464 return grp.getgrgid(self.stat(path).st_gid).gr_name 

465 except ImportError: 

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

467 

468 

469_normal_accessor = _NormalAccessor() 

470 

471 

472# 

473# Globbing helpers 

474# 

475 

476def _make_selector(pattern_parts, flavour): 

477 pat = pattern_parts[0] 

478 child_parts = pattern_parts[1:] 

479 if pat == '**': 

480 cls = _RecursiveWildcardSelector 

481 elif '**' in pat: 

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

483 elif _is_wildcard_pattern(pat): 

484 cls = _WildcardSelector 

485 else: 

486 cls = _PreciseSelector 

487 return cls(pat, child_parts, flavour) 

488 

489if hasattr(functools, "lru_cache"): 

490 _make_selector = functools.lru_cache()(_make_selector) 

491 

492 

493class _Selector: 

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

495 of a given path.""" 

496 

497 def __init__(self, child_parts, flavour): 

498 self.child_parts = child_parts 

499 if child_parts: 

500 self.successor = _make_selector(child_parts, flavour) 

501 self.dironly = True 

502 else: 

503 self.successor = _TerminatingSelector() 

504 self.dironly = False 

505 

506 def select_from(self, parent_path): 

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

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

509 path_cls = type(parent_path) 

510 is_dir = path_cls.is_dir 

511 exists = path_cls.exists 

512 scandir = parent_path._accessor.scandir 

513 if not is_dir(parent_path): 

514 return iter([]) 

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

516 

517 

518class _TerminatingSelector: 

519 

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

521 yield parent_path 

522 

523 

524class _PreciseSelector(_Selector): 

525 

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

527 self.name = name 

528 _Selector.__init__(self, child_parts, flavour) 

529 

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

531 try: 

532 path = parent_path._make_child_relpath(self.name) 

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

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

535 yield p 

536 except PermissionError: 

537 return 

538 

539 

540class _WildcardSelector(_Selector): 

541 

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

543 self.match = flavour.compile_pattern(pat) 

544 _Selector.__init__(self, child_parts, flavour) 

545 

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

547 try: 

548 with scandir(parent_path) as scandir_it: 

549 entries = list(scandir_it) 

550 for entry in entries: 

551 if self.dironly: 

552 try: 

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

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

555 # among the errors ignored by _ignore_error() 

556 if not entry.is_dir(): 

557 continue 

558 except OSError as e: 

559 if not _ignore_error(e): 

560 raise 

561 continue 

562 name = entry.name 

563 if self.match(name): 

564 path = parent_path._make_child_relpath(name) 

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

566 yield p 

567 except PermissionError: 

568 return 

569 

570 

571class _RecursiveWildcardSelector(_Selector): 

572 

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

574 _Selector.__init__(self, child_parts, flavour) 

575 

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

577 yield parent_path 

578 try: 

579 with scandir(parent_path) as scandir_it: 

580 entries = list(scandir_it) 

581 for entry in entries: 

582 entry_is_dir = False 

583 try: 

584 entry_is_dir = entry.is_dir() 

585 except OSError as e: 

586 if not _ignore_error(e): 

587 raise 

588 if entry_is_dir and not entry.is_symlink(): 

589 path = parent_path._make_child_relpath(entry.name) 

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

591 yield p 

592 except PermissionError: 

593 return 

594 

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

596 try: 

597 yielded = set() 

598 try: 

599 successor_select = self.successor._select_from 

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

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

602 if p not in yielded: 

603 yield p 

604 yielded.add(p) 

605 finally: 

606 yielded.clear() 

607 except PermissionError: 

608 return 

609 

610 

611# 

612# Public API 

613# 

614 

615class _PathParents(Sequence): 

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

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

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

619 

620 def __init__(self, path): 

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

622 self._pathcls = type(path) 

623 self._drv = path._drv 

624 self._root = path._root 

625 self._parts = path._parts 

626 

627 def __len__(self): 

628 if self._drv or self._root: 

629 return len(self._parts) - 1 

630 else: 

631 return len(self._parts) 

632 

633 def __getitem__(self, idx): 

634 if idx < 0 or idx >= len(self): 

635 raise IndexError(idx) 

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

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

638 

639 def __repr__(self): 

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

641 

642 

643class PurePath(object): 

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

645 

646 PurePath represents a filesystem path and offers operations which 

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

648 instantiating a PurePath will return either a PurePosixPath or a 

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

650 directly, regardless of your system. 

651 """ 

652 __slots__ = ( 

653 '_drv', '_root', '_parts', 

654 '_str', '_hash', '_pparts', '_cached_cparts', 

655 ) 

656 

657 def __new__(cls, *args): 

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

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

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

661 new PurePath object. 

662 """ 

663 if cls is PurePath: 

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

665 return cls._from_parts(args) 

666 

667 def __reduce__(self): 

668 # Using the parts tuple helps share interned path parts 

669 # when pickling related paths. 

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

671 

672 @classmethod 

673 def _parse_args(cls, args): 

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

675 # canonicalize some constructor arguments. 

676 parts = [] 

677 for a in args: 

678 if isinstance(a, PurePath): 

679 parts += a._parts 

680 else: 

681 a = os.fspath(a) 

682 if isinstance(a, str): 

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

684 parts.append(str(a)) 

685 else: 

686 raise TypeError( 

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

688 "object returning str, not %r" 

689 % type(a)) 

690 return cls._flavour.parse_parts(parts) 

691 

692 @classmethod 

693 def _from_parts(cls, args, init=True): 

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

695 # right flavour. 

696 self = object.__new__(cls) 

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

698 self._drv = drv 

699 self._root = root 

700 self._parts = parts 

701 if init: 

702 self._init() 

703 return self 

704 

705 @classmethod 

706 def _from_parsed_parts(cls, drv, root, parts, init=True): 

707 self = object.__new__(cls) 

708 self._drv = drv 

709 self._root = root 

710 self._parts = parts 

711 if init: 

712 self._init() 

713 return self 

714 

715 @classmethod 

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

717 if drv or root: 

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

719 else: 

720 return cls._flavour.join(parts) 

721 

722 def _init(self): 

723 # Overridden in concrete Path 

724 pass 

725 

726 def _make_child(self, args): 

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

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

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

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

731 

732 def __str__(self): 

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

734 passing to system calls.""" 

735 try: 

736 return self._str 

737 except AttributeError: 

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

739 self._parts) or '.' 

740 return self._str 

741 

742 def __fspath__(self): 

743 return str(self) 

744 

745 def as_posix(self): 

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

747 slashes.""" 

748 f = self._flavour 

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

750 

751 def __bytes__(self): 

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

753 recommended to use under Unix.""" 

754 return os.fsencode(self) 

755 

756 def __repr__(self): 

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

758 

759 def as_uri(self): 

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

761 if not self.is_absolute(): 

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

763 return self._flavour.make_uri(self) 

764 

765 @property 

766 def _cparts(self): 

767 # Cached casefolded parts, for hashing and comparison 

768 try: 

769 return self._cached_cparts 

770 except AttributeError: 

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

772 return self._cached_cparts 

773 

774 def __eq__(self, other): 

775 if not isinstance(other, PurePath): 

776 return NotImplemented 

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

778 

779 def __hash__(self): 

780 try: 

781 return self._hash 

782 except AttributeError: 

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

784 return self._hash 

785 

786 def __lt__(self, other): 

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

788 return NotImplemented 

789 return self._cparts < other._cparts 

790 

791 def __le__(self, other): 

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

793 return NotImplemented 

794 return self._cparts <= other._cparts 

795 

796 def __gt__(self, other): 

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

798 return NotImplemented 

799 return self._cparts > other._cparts 

800 

801 def __ge__(self, other): 

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

803 return NotImplemented 

804 return self._cparts >= other._cparts 

805 

806 def __class_getitem__(cls, type): 

807 return cls 

808 

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

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

811 

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

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

814 

815 @property 

816 def anchor(self): 

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

818 anchor = self._drv + self._root 

819 return anchor 

820 

821 @property 

822 def name(self): 

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

824 parts = self._parts 

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

826 return '' 

827 return parts[-1] 

828 

829 @property 

830 def suffix(self): 

831 """ 

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

833 

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

835 """ 

836 name = self.name 

837 i = name.rfind('.') 

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

839 return name[i:] 

840 else: 

841 return '' 

842 

843 @property 

844 def suffixes(self): 

845 """ 

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

847 

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

849 """ 

850 name = self.name 

851 if name.endswith('.'): 

852 return [] 

853 name = name.lstrip('.') 

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

855 

856 @property 

857 def stem(self): 

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

859 name = self.name 

860 i = name.rfind('.') 

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

862 return name[:i] 

863 else: 

864 return name 

865 

866 def with_name(self, name): 

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

868 if not self.name: 

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

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

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

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

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

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

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

876 

877 def with_stem(self, stem): 

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

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

880 

881 def with_suffix(self, suffix): 

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

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

884 string, remove the suffix from the path. 

885 """ 

886 f = self._flavour 

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

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

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

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

891 name = self.name 

892 if not name: 

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

894 old_suffix = self.suffix 

895 if not old_suffix: 

896 name = name + suffix 

897 else: 

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

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

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

901 

902 def relative_to(self, *other): 

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

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

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

906 """ 

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

908 # separate parts, i.e.: 

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

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

911 if not other: 

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

913 parts = self._parts 

914 drv = self._drv 

915 root = self._root 

916 if root: 

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

918 else: 

919 abs_parts = parts 

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

921 if to_root: 

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

923 else: 

924 to_abs_parts = to_parts 

925 n = len(to_abs_parts) 

926 cf = self._flavour.casefold_parts 

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

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

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

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

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

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

933 abs_parts[n:]) 

934 

935 def is_relative_to(self, *other): 

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

937 """ 

938 try: 

939 self.relative_to(*other) 

940 return True 

941 except ValueError: 

942 return False 

943 

944 @property 

945 def parts(self): 

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

947 components in the filesystem path.""" 

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

949 # is accessed. XXX is this necessary? 

950 try: 

951 return self._pparts 

952 except AttributeError: 

953 self._pparts = tuple(self._parts) 

954 return self._pparts 

955 

956 def joinpath(self, *args): 

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

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

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

960 anchored). 

961 """ 

962 return self._make_child(args) 

963 

964 def __truediv__(self, key): 

965 try: 

966 return self._make_child((key,)) 

967 except TypeError: 

968 return NotImplemented 

969 

970 def __rtruediv__(self, key): 

971 try: 

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

973 except TypeError: 

974 return NotImplemented 

975 

976 @property 

977 def parent(self): 

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

979 drv = self._drv 

980 root = self._root 

981 parts = self._parts 

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

983 return self 

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

985 

986 @property 

987 def parents(self): 

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

989 return _PathParents(self) 

990 

991 def is_absolute(self): 

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

993 a drive).""" 

994 if not self._root: 

995 return False 

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

997 

998 def is_reserved(self): 

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

1000 by the system, if any.""" 

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

1002 

1003 def match(self, path_pattern): 

1004 """ 

1005 Return True if this path matches the given pattern. 

1006 """ 

1007 cf = self._flavour.casefold 

1008 path_pattern = cf(path_pattern) 

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

1010 if not pat_parts: 

1011 raise ValueError("empty pattern") 

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

1013 return False 

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

1015 return False 

1016 parts = self._cparts 

1017 if drv or root: 

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

1019 return False 

1020 pat_parts = pat_parts[1:] 

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

1022 return False 

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

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

1025 return False 

1026 return True 

1027 

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

1029# optimizations in PurePath._parse_args(). 

1030os.PathLike.register(PurePath) 

1031 

1032 

1033class PurePosixPath(PurePath): 

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

1035 

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

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

1038 """ 

1039 _flavour = _posix_flavour 

1040 __slots__ = () 

1041 

1042 

1043class PureWindowsPath(PurePath): 

1044 """PurePath subclass for Windows systems. 

1045 

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

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

1048 """ 

1049 _flavour = _windows_flavour 

1050 __slots__ = () 

1051 

1052 

1053# Filesystem-accessing classes 

1054 

1055 

1056class Path(PurePath): 

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

1058 

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

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

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

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

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

1064 """ 

1065 __slots__ = ( 

1066 '_accessor', 

1067 ) 

1068 

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

1070 if cls is Path: 

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

1072 self = cls._from_parts(args, init=False) 

1073 if not self._flavour.is_supported: 

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

1075 % (cls.__name__,)) 

1076 self._init() 

1077 return self 

1078 

1079 def _init(self, 

1080 # Private non-constructor arguments 

1081 template=None, 

1082 ): 

1083 if template is not None: 

1084 self._accessor = template._accessor 

1085 else: 

1086 self._accessor = _normal_accessor 

1087 

1088 def _make_child_relpath(self, part): 

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

1090 # a single part relative to this path. 

1091 parts = self._parts + [part] 

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

1093 

1094 def __enter__(self): 

1095 return self 

1096 

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

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

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

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

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

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

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

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

1105 # removed in the future. 

1106 pass 

1107 

1108 def _opener(self, name, flags, mode=0o666): 

1109 # A stub for the opener argument to built-in open() 

1110 return self._accessor.open(self, flags, mode) 

1111 

1112 def _raw_open(self, flags, mode=0o777): 

1113 """ 

1114 Open the file pointed by this path and return a file descriptor, 

1115 as os.open() does. 

1116 """ 

1117 return self._accessor.open(self, flags, mode) 

1118 

1119 # Public API 

1120 

1121 @classmethod 

1122 def cwd(cls): 

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

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

1125 """ 

1126 return cls(os.getcwd()) 

1127 

1128 @classmethod 

1129 def home(cls): 

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

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

1132 """ 

1133 return cls(cls()._flavour.gethomedir(None)) 

1134 

1135 def samefile(self, other_path): 

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

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

1138 """ 

1139 st = self.stat() 

1140 try: 

1141 other_st = other_path.stat() 

1142 except AttributeError: 

1143 other_st = self._accessor.stat(other_path) 

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

1145 

1146 def iterdir(self): 

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

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

1149 """ 

1150 for name in self._accessor.listdir(self): 

1151 if name in {'.', '..'}: 

1152 # Yielding a path object for these makes little sense 

1153 continue 

1154 yield self._make_child_relpath(name) 

1155 

1156 def glob(self, pattern): 

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

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

1159 """ 

1160 sys.audit("pathlib.Path.glob", self, pattern) 

1161 if not pattern: 

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

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

1164 if drv or root: 

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

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

1167 for p in selector.select_from(self): 

1168 yield p 

1169 

1170 def rglob(self, pattern): 

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

1172 directories) matching the given relative pattern, anywhere in 

1173 this subtree. 

1174 """ 

1175 sys.audit("pathlib.Path.rglob", self, pattern) 

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

1177 if drv or root: 

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

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

1180 for p in selector.select_from(self): 

1181 yield p 

1182 

1183 def absolute(self): 

1184 """Return an absolute version of this path. This function works 

1185 even if the path doesn't point to anything. 

1186 

1187 No normalization is done, i.e. all '.' and '..' will be kept along. 

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

1189 """ 

1190 # XXX untested yet! 

1191 if self.is_absolute(): 

1192 return self 

1193 # FIXME this must defer to the specific flavour (and, under Windows, 

1194 # use nt._getfullpathname()) 

1195 obj = self._from_parts([os.getcwd()] + self._parts, init=False) 

1196 obj._init(template=self) 

1197 return obj 

1198 

1199 def resolve(self, strict=False): 

1200 """ 

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

1202 normalizing it (for example turning slashes into backslashes under 

1203 Windows). 

1204 """ 

1205 s = self._flavour.resolve(self, strict=strict) 

1206 if s is None: 

1207 # No symlink resolution => for consistency, raise an error if 

1208 # the path doesn't exist or is forbidden 

1209 self.stat() 

1210 s = str(self.absolute()) 

1211 # Now we have no symlinks in the path, it's safe to normalize it. 

1212 normed = self._flavour.pathmod.normpath(s) 

1213 obj = self._from_parts((normed,), init=False) 

1214 obj._init(template=self) 

1215 return obj 

1216 

1217 def stat(self): 

1218 """ 

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

1220 os.stat() does. 

1221 """ 

1222 return self._accessor.stat(self) 

1223 

1224 def owner(self): 

1225 """ 

1226 Return the login name of the file owner. 

1227 """ 

1228 return self._accessor.owner(self) 

1229 

1230 def group(self): 

1231 """ 

1232 Return the group name of the file gid. 

1233 """ 

1234 return self._accessor.group(self) 

1235 

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

1237 errors=None, newline=None): 

1238 """ 

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

1240 the built-in open() function does. 

1241 """ 

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

1243 opener=self._opener) 

1244 

1245 def read_bytes(self): 

1246 """ 

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

1248 """ 

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

1250 return f.read() 

1251 

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

1253 """ 

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

1255 """ 

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

1257 return f.read() 

1258 

1259 def write_bytes(self, data): 

1260 """ 

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

1262 """ 

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

1264 view = memoryview(data) 

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

1266 return f.write(view) 

1267 

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

1269 """ 

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

1271 """ 

1272 if not isinstance(data, str): 

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

1274 data.__class__.__name__) 

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

1276 return f.write(data) 

1277 

1278 def readlink(self): 

1279 """ 

1280 Return the path to which the symbolic link points. 

1281 """ 

1282 path = self._accessor.readlink(self) 

1283 obj = self._from_parts((path,), init=False) 

1284 obj._init(template=self) 

1285 return obj 

1286 

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

1288 """ 

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

1290 """ 

1291 if exist_ok: 

1292 # First try to bump modification time 

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

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

1295 try: 

1296 self._accessor.utime(self, None) 

1297 except OSError: 

1298 # Avoid exception chaining 

1299 pass 

1300 else: 

1301 return 

1302 flags = os.O_CREAT | os.O_WRONLY 

1303 if not exist_ok: 

1304 flags |= os.O_EXCL 

1305 fd = self._raw_open(flags, mode) 

1306 os.close(fd) 

1307 

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

1309 """ 

1310 Create a new directory at this given path. 

1311 """ 

1312 try: 

1313 self._accessor.mkdir(self, mode) 

1314 except FileNotFoundError: 

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

1316 raise 

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

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

1319 except OSError: 

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

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

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

1323 raise 

1324 

1325 def chmod(self, mode): 

1326 """ 

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

1328 """ 

1329 self._accessor.chmod(self, mode) 

1330 

1331 def lchmod(self, mode): 

1332 """ 

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

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

1335 """ 

1336 self._accessor.lchmod(self, mode) 

1337 

1338 def unlink(self, missing_ok=False): 

1339 """ 

1340 Remove this file or link. 

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

1342 """ 

1343 try: 

1344 self._accessor.unlink(self) 

1345 except FileNotFoundError: 

1346 if not missing_ok: 

1347 raise 

1348 

1349 def rmdir(self): 

1350 """ 

1351 Remove this directory. The directory must be empty. 

1352 """ 

1353 self._accessor.rmdir(self) 

1354 

1355 def lstat(self): 

1356 """ 

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

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

1359 """ 

1360 return self._accessor.lstat(self) 

1361 

1362 def rename(self, target): 

1363 """ 

1364 Rename this path to the target path. 

1365 

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

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

1368 directory of the Path object. 

1369 

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

1371 """ 

1372 self._accessor.rename(self, target) 

1373 return self.__class__(target) 

1374 

1375 def replace(self, target): 

1376 """ 

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

1378 

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

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

1381 directory of the Path object. 

1382 

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

1384 """ 

1385 self._accessor.replace(self, target) 

1386 return self.__class__(target) 

1387 

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

1389 """ 

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

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

1392 """ 

1393 self._accessor.symlink(target, self, target_is_directory) 

1394 

1395 def link_to(self, target): 

1396 """ 

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

1398 

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

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

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

1402 matches that of os.link. 

1403 

1404 """ 

1405 self._accessor.link_to(self, target) 

1406 

1407 # Convenience functions for querying the stat results 

1408 

1409 def exists(self): 

1410 """ 

1411 Whether this path exists. 

1412 """ 

1413 try: 

1414 self.stat() 

1415 except OSError as e: 

1416 if not _ignore_error(e): 

1417 raise 

1418 return False 

1419 except ValueError: 

1420 # Non-encodable path 

1421 return False 

1422 return True 

1423 

1424 def is_dir(self): 

1425 """ 

1426 Whether this path is a directory. 

1427 """ 

1428 try: 

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

1430 except OSError as e: 

1431 if not _ignore_error(e): 

1432 raise 

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

1434 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) 

1435 return False 

1436 except ValueError: 

1437 # Non-encodable path 

1438 return False 

1439 

1440 def is_file(self): 

1441 """ 

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

1443 to regular files). 

1444 """ 

1445 try: 

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

1447 except OSError as e: 

1448 if not _ignore_error(e): 

1449 raise 

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

1451 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) 

1452 return False 

1453 except ValueError: 

1454 # Non-encodable path 

1455 return False 

1456 

1457 def is_mount(self): 

1458 """ 

1459 Check if this path is a POSIX mount point 

1460 """ 

1461 # Need to exist and be a dir 

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

1463 return False 

1464 

1465 try: 

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

1467 except OSError: 

1468 return False 

1469 

1470 dev = self.stat().st_dev 

1471 if dev != parent_dev: 

1472 return True 

1473 ino = self.stat().st_ino 

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

1475 return ino == parent_ino 

1476 

1477 def is_symlink(self): 

1478 """ 

1479 Whether this path is a symbolic link. 

1480 """ 

1481 try: 

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

1483 except OSError as e: 

1484 if not _ignore_error(e): 

1485 raise 

1486 # Path doesn't exist 

1487 return False 

1488 except ValueError: 

1489 # Non-encodable path 

1490 return False 

1491 

1492 def is_block_device(self): 

1493 """ 

1494 Whether this path is a block device. 

1495 """ 

1496 try: 

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

1498 except OSError as e: 

1499 if not _ignore_error(e): 

1500 raise 

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

1502 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) 

1503 return False 

1504 except ValueError: 

1505 # Non-encodable path 

1506 return False 

1507 

1508 def is_char_device(self): 

1509 """ 

1510 Whether this path is a character device. 

1511 """ 

1512 try: 

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

1514 except OSError as e: 

1515 if not _ignore_error(e): 

1516 raise 

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

1518 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) 

1519 return False 

1520 except ValueError: 

1521 # Non-encodable path 

1522 return False 

1523 

1524 def is_fifo(self): 

1525 """ 

1526 Whether this path is a FIFO. 

1527 """ 

1528 try: 

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

1530 except OSError as e: 

1531 if not _ignore_error(e): 

1532 raise 

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

1534 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) 

1535 return False 

1536 except ValueError: 

1537 # Non-encodable path 

1538 return False 

1539 

1540 def is_socket(self): 

1541 """ 

1542 Whether this path is a socket. 

1543 """ 

1544 try: 

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

1546 except OSError as e: 

1547 if not _ignore_error(e): 

1548 raise 

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

1550 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) 

1551 return False 

1552 except ValueError: 

1553 # Non-encodable path 

1554 return False 

1555 

1556 def expanduser(self): 

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

1558 (as returned by os.path.expanduser) 

1559 """ 

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

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

1562 homedir = self._flavour.gethomedir(self._parts[0][1:]) 

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

1564 

1565 return self 

1566 

1567 

1568class PosixPath(Path, PurePosixPath): 

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

1570 

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

1572 """ 

1573 __slots__ = () 

1574 

1575class WindowsPath(Path, PureWindowsPath): 

1576 """Path subclass for Windows systems. 

1577 

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

1579 """ 

1580 __slots__ = () 

1581 

1582 def is_mount(self): 

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