Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/pathlib.py: 11%

925 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +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 1921, # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself 

43) 

44 

45def _ignore_error(exception): 

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

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

48 

49 

50def _is_wildcard_pattern(pat): 

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

52 # be looked up directly as a file. 

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

54 

55 

56class _Flavour(object): 

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

58 semantics.""" 

59 

60 def __init__(self): 

61 self.join = self.sep.join 

62 

63 def parse_parts(self, parts): 

64 parsed = [] 

65 sep = self.sep 

66 altsep = self.altsep 

67 drv = root = '' 

68 it = reversed(parts) 

69 for part in it: 

70 if not part: 

71 continue 

72 if altsep: 

73 part = part.replace(altsep, sep) 

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

75 if sep in rel: 

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

77 if x and x != '.': 

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

79 else: 

80 if rel and rel != '.': 

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

82 if drv or root: 

83 if not drv: 

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

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

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

87 for part in it: 

88 if not part: 

89 continue 

90 if altsep: 

91 part = part.replace(altsep, sep) 

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

93 if drv: 

94 break 

95 break 

96 if drv or root: 

97 parsed.append(drv + root) 

98 parsed.reverse() 

99 return drv, root, parsed 

100 

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

102 """ 

103 Join the two paths represented by the respective 

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

105 """ 

106 if root2: 

107 if not drv2 and drv: 

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

109 elif drv2: 

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

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

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

113 else: 

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

115 return drv, root, parts + parts2 

116 return drv2, root2, parts2 

117 

118 

119class _WindowsFlavour(_Flavour): 

120 # Reference for Windows paths can be found at 

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

122 

123 sep = '\\' 

124 altsep = '/' 

125 has_drv = True 

126 pathmod = ntpath 

127 

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

129 

130 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') 

131 ext_namespace_prefix = '\\\\?\\' 

132 

133 reserved_names = ( 

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

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

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

137 ) 

138 

139 # Interesting findings about extended paths: 

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

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

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

143 # fail. 

144 

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

146 first = part[0:1] 

147 second = part[1:2] 

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

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

150 # components (according to MSDN docs). 

151 prefix, part = self._split_extended_path(part) 

152 first = part[0:1] 

153 second = part[1:2] 

154 else: 

155 prefix = '' 

156 third = part[2:3] 

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

158 # is a UNC path: 

159 # vvvvvvvvvvvvvvvvvvvvv root 

160 # \\machine\mountpoint\directory\etc\... 

161 # directory ^^^^^^^^^^^^^^ 

162 index = part.find(sep, 2) 

163 if index != -1: 

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

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

166 # (after the initial two) 

167 if index2 != index + 1: 

168 if index2 == -1: 

169 index2 = len(part) 

170 if prefix: 

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

172 else: 

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

174 drv = root = '' 

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

176 drv = part[:2] 

177 part = part[2:] 

178 first = third 

179 if first == sep: 

180 root = first 

181 part = part.lstrip(sep) 

182 return prefix + drv, root, part 

183 

184 def casefold(self, s): 

185 return s.lower() 

186 

187 def casefold_parts(self, parts): 

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

189 

190 def compile_pattern(self, pattern): 

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

192 

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

194 s = str(path) 

195 if not s: 

196 return os.getcwd() 

197 previous_s = None 

198 if _getfinalpathname is not None: 

199 if strict: 

200 return self._ext_to_normal(_getfinalpathname(s)) 

201 else: 

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

203 while True: 

204 try: 

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

206 except FileNotFoundError: 

207 previous_s = s 

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

209 tail_parts.append(tail) 

210 if previous_s == s: 

211 return path 

212 else: 

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

214 # Means fallback on absolute 

215 return None 

216 

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

218 prefix = '' 

219 if s.startswith(ext_prefix): 

220 prefix = s[:4] 

221 s = s[4:] 

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

223 prefix += s[:3] 

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

225 return prefix, s 

226 

227 def _ext_to_normal(self, s): 

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

229 return self._split_extended_path(s)[1] 

230 

231 def is_reserved(self, parts): 

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

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

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

235 # not considered reserved by Windows. 

236 if not parts: 

237 return False 

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

239 # UNC paths are never reserved 

240 return False 

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

242 

243 def make_uri(self, path): 

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

245 drive = path.drive 

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

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

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

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

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

251 else: 

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

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

254 

255 def gethomedir(self, username): 

256 if 'USERPROFILE' in os.environ: 

257 userhome = os.environ['USERPROFILE'] 

258 elif 'HOMEPATH' in os.environ: 

259 try: 

260 drv = os.environ['HOMEDRIVE'] 

261 except KeyError: 

262 drv = '' 

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

264 else: 

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

266 

267 if username: 

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

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

270 # corresponding usernames. If current user home directory points 

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

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

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

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

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

276 "for %r" % username) 

277 parts[-1] = username 

278 if drv or root: 

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

280 else: 

281 userhome = self.join(parts) 

282 return userhome 

283 

284class _PosixFlavour(_Flavour): 

285 sep = '/' 

286 altsep = '' 

287 has_drv = False 

288 pathmod = posixpath 

289 

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

291 

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

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

294 stripped_part = part.lstrip(sep) 

295 # According to POSIX path resolution: 

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

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

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

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

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

301 return '', sep * 2, stripped_part 

302 else: 

303 return '', sep, stripped_part 

304 else: 

305 return '', '', part 

306 

307 def casefold(self, s): 

308 return s 

309 

310 def casefold_parts(self, parts): 

311 return parts 

312 

313 def compile_pattern(self, pattern): 

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

315 

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

317 sep = self.sep 

318 accessor = path._accessor 

319 seen = {} 

320 def _resolve(path, rest): 

321 if rest.startswith(sep): 

322 path = '' 

323 

324 for name in rest.split(sep): 

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

326 # current dir 

327 continue 

328 if name == '..': 

329 # parent dir 

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

331 continue 

332 newpath = path + sep + name 

333 if newpath in seen: 

334 # Already seen this path 

335 path = seen[newpath] 

336 if path is not None: 

337 # use cached value 

338 continue 

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

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

341 # Resolve the symbolic link 

342 try: 

343 target = accessor.readlink(newpath) 

344 except OSError as e: 

345 if e.errno != EINVAL and strict: 

346 raise 

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

348 # untouched. 

349 path = newpath 

350 else: 

351 seen[newpath] = None # not resolved symlink 

352 path = _resolve(path, target) 

353 seen[newpath] = path # resolved symlink 

354 

355 return path 

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

357 # which are symlinks. 

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

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

360 

361 def is_reserved(self, parts): 

362 return False 

363 

364 def make_uri(self, path): 

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

366 # for portability to other applications. 

367 bpath = bytes(path) 

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

369 

370 def gethomedir(self, username): 

371 if not username: 

372 try: 

373 return os.environ['HOME'] 

374 except KeyError: 

375 import pwd 

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

377 else: 

378 import pwd 

379 try: 

380 return pwd.getpwnam(username).pw_dir 

381 except KeyError: 

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

383 "for %r" % username) 

384 

385 

386_windows_flavour = _WindowsFlavour() 

387_posix_flavour = _PosixFlavour() 

388 

389 

390class _Accessor: 

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

392 accessing paths on the filesystem.""" 

393 

394 

395class _NormalAccessor(_Accessor): 

396 

397 stat = os.stat 

398 

399 lstat = os.lstat 

400 

401 open = os.open 

402 

403 listdir = os.listdir 

404 

405 scandir = os.scandir 

406 

407 chmod = os.chmod 

408 

409 if hasattr(os, "lchmod"): 

410 lchmod = os.lchmod 

411 else: 

412 def lchmod(self, pathobj, mode): 

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

414 

415 mkdir = os.mkdir 

416 

417 unlink = os.unlink 

418 

419 if hasattr(os, "link"): 

420 link_to = os.link 

421 else: 

422 @staticmethod 

423 def link_to(self, target): 

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

425 

426 rmdir = os.rmdir 

427 

428 rename = os.rename 

429 

430 replace = os.replace 

431 

432 if nt: 

433 if supports_symlinks: 

434 symlink = os.symlink 

435 else: 

436 def symlink(a, b, target_is_directory): 

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

438 else: 

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

440 @staticmethod 

441 def symlink(a, b, target_is_directory): 

442 return os.symlink(a, b) 

443 

444 utime = os.utime 

445 

446 # Helper for resolve() 

447 def readlink(self, path): 

448 return os.readlink(path) 

449 

450 

451_normal_accessor = _NormalAccessor() 

452 

453 

454# 

455# Globbing helpers 

456# 

457 

458def _make_selector(pattern_parts, flavour): 

459 pat = pattern_parts[0] 

460 child_parts = pattern_parts[1:] 

461 if pat == '**': 

462 cls = _RecursiveWildcardSelector 

463 elif '**' in pat: 

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

465 elif _is_wildcard_pattern(pat): 

466 cls = _WildcardSelector 

467 else: 

468 cls = _PreciseSelector 

469 return cls(pat, child_parts, flavour) 

470 

471if hasattr(functools, "lru_cache"): 

472 _make_selector = functools.lru_cache()(_make_selector) 

473 

474 

475class _Selector: 

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

477 of a given path.""" 

478 

479 def __init__(self, child_parts, flavour): 

480 self.child_parts = child_parts 

481 if child_parts: 

482 self.successor = _make_selector(child_parts, flavour) 

483 self.dironly = True 

484 else: 

485 self.successor = _TerminatingSelector() 

486 self.dironly = False 

487 

488 def select_from(self, parent_path): 

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

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

491 path_cls = type(parent_path) 

492 is_dir = path_cls.is_dir 

493 exists = path_cls.exists 

494 scandir = parent_path._accessor.scandir 

495 if not is_dir(parent_path): 

496 return iter([]) 

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

498 

499 

500class _TerminatingSelector: 

501 

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

503 yield parent_path 

504 

505 

506class _PreciseSelector(_Selector): 

507 

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

509 self.name = name 

510 _Selector.__init__(self, child_parts, flavour) 

511 

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

513 try: 

514 path = parent_path._make_child_relpath(self.name) 

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

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

517 yield p 

518 except PermissionError: 

519 return 

520 

521 

522class _WildcardSelector(_Selector): 

523 

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

525 self.match = flavour.compile_pattern(pat) 

526 _Selector.__init__(self, child_parts, flavour) 

527 

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

529 try: 

530 with scandir(parent_path) as scandir_it: 

531 entries = list(scandir_it) 

532 for entry in entries: 

533 if self.dironly: 

534 try: 

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

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

537 # among the errors ignored by _ignore_error() 

538 if not entry.is_dir(): 

539 continue 

540 except OSError as e: 

541 if not _ignore_error(e): 

542 raise 

543 continue 

544 name = entry.name 

545 if self.match(name): 

546 path = parent_path._make_child_relpath(name) 

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

548 yield p 

549 except PermissionError: 

550 return 

551 

552 

553class _RecursiveWildcardSelector(_Selector): 

554 

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

556 _Selector.__init__(self, child_parts, flavour) 

557 

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

559 yield parent_path 

560 try: 

561 with scandir(parent_path) as scandir_it: 

562 entries = list(scandir_it) 

563 for entry in entries: 

564 entry_is_dir = False 

565 try: 

566 entry_is_dir = entry.is_dir() 

567 except OSError as e: 

568 if not _ignore_error(e): 

569 raise 

570 if entry_is_dir and not entry.is_symlink(): 

571 path = parent_path._make_child_relpath(entry.name) 

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

573 yield p 

574 except PermissionError: 

575 return 

576 

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

578 try: 

579 yielded = set() 

580 try: 

581 successor_select = self.successor._select_from 

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

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

584 if p not in yielded: 

585 yield p 

586 yielded.add(p) 

587 finally: 

588 yielded.clear() 

589 except PermissionError: 

590 return 

591 

592 

593# 

594# Public API 

595# 

596 

597class _PathParents(Sequence): 

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

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

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

601 

602 def __init__(self, path): 

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

604 self._pathcls = type(path) 

605 self._drv = path._drv 

606 self._root = path._root 

607 self._parts = path._parts 

608 

609 def __len__(self): 

610 if self._drv or self._root: 

611 return len(self._parts) - 1 

612 else: 

613 return len(self._parts) 

614 

615 def __getitem__(self, idx): 

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

617 raise IndexError(idx) 

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

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

620 

621 def __repr__(self): 

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

623 

624 

625class PurePath(object): 

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

627 

628 PurePath represents a filesystem path and offers operations which 

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

630 instantiating a PurePath will return either a PurePosixPath or a 

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

632 directly, regardless of your system. 

633 """ 

634 __slots__ = ( 

635 '_drv', '_root', '_parts', 

636 '_str', '_hash', '_pparts', '_cached_cparts', 

637 ) 

638 

639 def __new__(cls, *args): 

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

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

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

643 new PurePath object. 

644 """ 

645 if cls is PurePath: 

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

647 return cls._from_parts(args) 

648 

649 def __reduce__(self): 

650 # Using the parts tuple helps share interned path parts 

651 # when pickling related paths. 

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

653 

654 @classmethod 

655 def _parse_args(cls, args): 

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

657 # canonicalize some constructor arguments. 

658 parts = [] 

659 for a in args: 

660 if isinstance(a, PurePath): 

661 parts += a._parts 

662 else: 

663 a = os.fspath(a) 

664 if isinstance(a, str): 

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

666 parts.append(str(a)) 

667 else: 

668 raise TypeError( 

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

670 "object returning str, not %r" 

671 % type(a)) 

672 return cls._flavour.parse_parts(parts) 

673 

674 @classmethod 

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

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

677 # right flavour. 

678 self = object.__new__(cls) 

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

680 self._drv = drv 

681 self._root = root 

682 self._parts = parts 

683 if init: 

684 self._init() 

685 return self 

686 

687 @classmethod 

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

689 self = object.__new__(cls) 

690 self._drv = drv 

691 self._root = root 

692 self._parts = parts 

693 if init: 

694 self._init() 

695 return self 

696 

697 @classmethod 

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

699 if drv or root: 

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

701 else: 

702 return cls._flavour.join(parts) 

703 

704 def _init(self): 

705 # Overridden in concrete Path 

706 pass 

707 

708 def _make_child(self, args): 

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

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

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

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

713 

714 def __str__(self): 

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

716 passing to system calls.""" 

717 try: 

718 return self._str 

719 except AttributeError: 

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

721 self._parts) or '.' 

722 return self._str 

723 

724 def __fspath__(self): 

725 return str(self) 

726 

727 def as_posix(self): 

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

729 slashes.""" 

730 f = self._flavour 

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

732 

733 def __bytes__(self): 

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

735 recommended to use under Unix.""" 

736 return os.fsencode(self) 

737 

738 def __repr__(self): 

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

740 

741 def as_uri(self): 

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

743 if not self.is_absolute(): 

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

745 return self._flavour.make_uri(self) 

746 

747 @property 

748 def _cparts(self): 

749 # Cached casefolded parts, for hashing and comparison 

750 try: 

751 return self._cached_cparts 

752 except AttributeError: 

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

754 return self._cached_cparts 

755 

756 def __eq__(self, other): 

757 if not isinstance(other, PurePath): 

758 return NotImplemented 

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

760 

761 def __hash__(self): 

762 try: 

763 return self._hash 

764 except AttributeError: 

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

766 return self._hash 

767 

768 def __lt__(self, other): 

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

770 return NotImplemented 

771 return self._cparts < other._cparts 

772 

773 def __le__(self, other): 

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

775 return NotImplemented 

776 return self._cparts <= other._cparts 

777 

778 def __gt__(self, other): 

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

780 return NotImplemented 

781 return self._cparts > other._cparts 

782 

783 def __ge__(self, other): 

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

785 return NotImplemented 

786 return self._cparts >= other._cparts 

787 

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

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

790 

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

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

793 

794 @property 

795 def anchor(self): 

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

797 anchor = self._drv + self._root 

798 return anchor 

799 

800 @property 

801 def name(self): 

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

803 parts = self._parts 

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

805 return '' 

806 return parts[-1] 

807 

808 @property 

809 def suffix(self): 

810 """ 

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

812 

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

814 """ 

815 name = self.name 

816 i = name.rfind('.') 

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

818 return name[i:] 

819 else: 

820 return '' 

821 

822 @property 

823 def suffixes(self): 

824 """ 

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

826 

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

828 """ 

829 name = self.name 

830 if name.endswith('.'): 

831 return [] 

832 name = name.lstrip('.') 

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

834 

835 @property 

836 def stem(self): 

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

838 name = self.name 

839 i = name.rfind('.') 

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

841 return name[:i] 

842 else: 

843 return name 

844 

845 def with_name(self, name): 

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

847 if not self.name: 

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

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

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

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

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

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

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

855 

856 def with_suffix(self, suffix): 

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

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

859 string, remove the suffix from the path. 

860 """ 

861 f = self._flavour 

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

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

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

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

866 name = self.name 

867 if not name: 

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

869 old_suffix = self.suffix 

870 if not old_suffix: 

871 name = name + suffix 

872 else: 

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

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

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

876 

877 def relative_to(self, *other): 

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

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

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

881 """ 

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

883 # separate parts, i.e.: 

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

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

886 if not other: 

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

888 parts = self._parts 

889 drv = self._drv 

890 root = self._root 

891 if root: 

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

893 else: 

894 abs_parts = parts 

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

896 if to_root: 

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

898 else: 

899 to_abs_parts = to_parts 

900 n = len(to_abs_parts) 

901 cf = self._flavour.casefold_parts 

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

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

904 raise ValueError("{!r} does not start with {!r}" 

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

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

907 abs_parts[n:]) 

908 

909 @property 

910 def parts(self): 

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

912 components in the filesystem path.""" 

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

914 # is accessed. XXX is this necessary? 

915 try: 

916 return self._pparts 

917 except AttributeError: 

918 self._pparts = tuple(self._parts) 

919 return self._pparts 

920 

921 def joinpath(self, *args): 

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

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

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

925 anchored). 

926 """ 

927 return self._make_child(args) 

928 

929 def __truediv__(self, key): 

930 try: 

931 return self._make_child((key,)) 

932 except TypeError: 

933 return NotImplemented 

934 

935 def __rtruediv__(self, key): 

936 try: 

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

938 except TypeError: 

939 return NotImplemented 

940 

941 @property 

942 def parent(self): 

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

944 drv = self._drv 

945 root = self._root 

946 parts = self._parts 

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

948 return self 

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

950 

951 @property 

952 def parents(self): 

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

954 return _PathParents(self) 

955 

956 def is_absolute(self): 

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

958 a drive).""" 

959 if not self._root: 

960 return False 

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

962 

963 def is_reserved(self): 

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

965 by the system, if any.""" 

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

967 

968 def match(self, path_pattern): 

969 """ 

970 Return True if this path matches the given pattern. 

971 """ 

972 cf = self._flavour.casefold 

973 path_pattern = cf(path_pattern) 

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

975 if not pat_parts: 

976 raise ValueError("empty pattern") 

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

978 return False 

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

980 return False 

981 parts = self._cparts 

982 if drv or root: 

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

984 return False 

985 pat_parts = pat_parts[1:] 

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

987 return False 

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

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

990 return False 

991 return True 

992 

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

994# optimizations in PurePath._parse_args(). 

995os.PathLike.register(PurePath) 

996 

997 

998class PurePosixPath(PurePath): 

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

1000 

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

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

1003 """ 

1004 _flavour = _posix_flavour 

1005 __slots__ = () 

1006 

1007 

1008class PureWindowsPath(PurePath): 

1009 """PurePath subclass for Windows systems. 

1010 

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

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

1013 """ 

1014 _flavour = _windows_flavour 

1015 __slots__ = () 

1016 

1017 

1018# Filesystem-accessing classes 

1019 

1020 

1021class Path(PurePath): 

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

1023 

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

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

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

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

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

1029 """ 

1030 __slots__ = ( 

1031 '_accessor', 

1032 '_closed', 

1033 ) 

1034 

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

1036 if cls is Path: 

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

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

1039 if not self._flavour.is_supported: 

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

1041 % (cls.__name__,)) 

1042 self._init() 

1043 return self 

1044 

1045 def _init(self, 

1046 # Private non-constructor arguments 

1047 template=None, 

1048 ): 

1049 self._closed = False 

1050 if template is not None: 

1051 self._accessor = template._accessor 

1052 else: 

1053 self._accessor = _normal_accessor 

1054 

1055 def _make_child_relpath(self, part): 

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

1057 # a single part relative to this path. 

1058 parts = self._parts + [part] 

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

1060 

1061 def __enter__(self): 

1062 if self._closed: 

1063 self._raise_closed() 

1064 return self 

1065 

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

1067 self._closed = True 

1068 

1069 def _raise_closed(self): 

1070 raise ValueError("I/O operation on closed path") 

1071 

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

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

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

1075 

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

1077 """ 

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

1079 as os.open() does. 

1080 """ 

1081 if self._closed: 

1082 self._raise_closed() 

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

1084 

1085 # Public API 

1086 

1087 @classmethod 

1088 def cwd(cls): 

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

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

1091 """ 

1092 return cls(os.getcwd()) 

1093 

1094 @classmethod 

1095 def home(cls): 

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

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

1098 """ 

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

1100 

1101 def samefile(self, other_path): 

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

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

1104 """ 

1105 st = self.stat() 

1106 try: 

1107 other_st = other_path.stat() 

1108 except AttributeError: 

1109 other_st = os.stat(other_path) 

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

1111 

1112 def iterdir(self): 

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

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

1115 """ 

1116 if self._closed: 

1117 self._raise_closed() 

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

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

1120 # Yielding a path object for these makes little sense 

1121 continue 

1122 yield self._make_child_relpath(name) 

1123 if self._closed: 

1124 self._raise_closed() 

1125 

1126 def glob(self, pattern): 

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

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

1129 """ 

1130 if not pattern: 

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

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

1133 if drv or root: 

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

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

1136 for p in selector.select_from(self): 

1137 yield p 

1138 

1139 def rglob(self, pattern): 

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

1141 directories) matching the given relative pattern, anywhere in 

1142 this subtree. 

1143 """ 

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

1145 if drv or root: 

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

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

1148 for p in selector.select_from(self): 

1149 yield p 

1150 

1151 def absolute(self): 

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

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

1154 

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

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

1157 """ 

1158 # XXX untested yet! 

1159 if self._closed: 

1160 self._raise_closed() 

1161 if self.is_absolute(): 

1162 return self 

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

1164 # use nt._getfullpathname()) 

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

1166 obj._init(template=self) 

1167 return obj 

1168 

1169 def resolve(self, strict=False): 

1170 """ 

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

1172 normalizing it (for example turning slashes into backslashes under 

1173 Windows). 

1174 """ 

1175 if self._closed: 

1176 self._raise_closed() 

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

1178 if s is None: 

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

1180 # the path doesn't exist or is forbidden 

1181 self.stat() 

1182 s = str(self.absolute()) 

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

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

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

1186 obj._init(template=self) 

1187 return obj 

1188 

1189 def stat(self): 

1190 """ 

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

1192 os.stat() does. 

1193 """ 

1194 return self._accessor.stat(self) 

1195 

1196 def owner(self): 

1197 """ 

1198 Return the login name of the file owner. 

1199 """ 

1200 import pwd 

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

1202 

1203 def group(self): 

1204 """ 

1205 Return the group name of the file gid. 

1206 """ 

1207 import grp 

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

1209 

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

1211 errors=None, newline=None): 

1212 """ 

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

1214 the built-in open() function does. 

1215 """ 

1216 if self._closed: 

1217 self._raise_closed() 

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

1219 opener=self._opener) 

1220 

1221 def read_bytes(self): 

1222 """ 

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

1224 """ 

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

1226 return f.read() 

1227 

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

1229 """ 

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

1231 """ 

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

1233 return f.read() 

1234 

1235 def write_bytes(self, data): 

1236 """ 

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

1238 """ 

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

1240 view = memoryview(data) 

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

1242 return f.write(view) 

1243 

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

1245 """ 

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

1247 """ 

1248 if not isinstance(data, str): 

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

1250 data.__class__.__name__) 

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

1252 return f.write(data) 

1253 

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

1255 """ 

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

1257 """ 

1258 if self._closed: 

1259 self._raise_closed() 

1260 if exist_ok: 

1261 # First try to bump modification time 

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

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

1264 try: 

1265 self._accessor.utime(self, None) 

1266 except OSError: 

1267 # Avoid exception chaining 

1268 pass 

1269 else: 

1270 return 

1271 flags = os.O_CREAT | os.O_WRONLY 

1272 if not exist_ok: 

1273 flags |= os.O_EXCL 

1274 fd = self._raw_open(flags, mode) 

1275 os.close(fd) 

1276 

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

1278 """ 

1279 Create a new directory at this given path. 

1280 """ 

1281 if self._closed: 

1282 self._raise_closed() 

1283 try: 

1284 self._accessor.mkdir(self, mode) 

1285 except FileNotFoundError: 

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

1287 raise 

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

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

1290 except OSError: 

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

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

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

1294 raise 

1295 

1296 def chmod(self, mode): 

1297 """ 

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

1299 """ 

1300 if self._closed: 

1301 self._raise_closed() 

1302 self._accessor.chmod(self, mode) 

1303 

1304 def lchmod(self, mode): 

1305 """ 

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

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

1308 """ 

1309 if self._closed: 

1310 self._raise_closed() 

1311 self._accessor.lchmod(self, mode) 

1312 

1313 def unlink(self, missing_ok=False): 

1314 """ 

1315 Remove this file or link. 

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

1317 """ 

1318 if self._closed: 

1319 self._raise_closed() 

1320 try: 

1321 self._accessor.unlink(self) 

1322 except FileNotFoundError: 

1323 if not missing_ok: 

1324 raise 

1325 

1326 def rmdir(self): 

1327 """ 

1328 Remove this directory. The directory must be empty. 

1329 """ 

1330 if self._closed: 

1331 self._raise_closed() 

1332 self._accessor.rmdir(self) 

1333 

1334 def lstat(self): 

1335 """ 

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

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

1338 """ 

1339 if self._closed: 

1340 self._raise_closed() 

1341 return self._accessor.lstat(self) 

1342 

1343 def link_to(self, target): 

1344 """ 

1345 Create a hard link pointing to a path named target. 

1346 """ 

1347 if self._closed: 

1348 self._raise_closed() 

1349 self._accessor.link_to(self, target) 

1350 

1351 def rename(self, target): 

1352 """ 

1353 Rename this path to the given path, 

1354 and return a new Path instance pointing to the given path. 

1355 """ 

1356 if self._closed: 

1357 self._raise_closed() 

1358 self._accessor.rename(self, target) 

1359 return self.__class__(target) 

1360 

1361 def replace(self, target): 

1362 """ 

1363 Rename this path to the given path, clobbering the existing 

1364 destination if it exists, and return a new Path instance 

1365 pointing to the given path. 

1366 """ 

1367 if self._closed: 

1368 self._raise_closed() 

1369 self._accessor.replace(self, target) 

1370 return self.__class__(target) 

1371 

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

1373 """ 

1374 Make this path a symlink pointing to the given path. 

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

1376 """ 

1377 if self._closed: 

1378 self._raise_closed() 

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

1380 

1381 # Convenience functions for querying the stat results 

1382 

1383 def exists(self): 

1384 """ 

1385 Whether this path exists. 

1386 """ 

1387 try: 

1388 self.stat() 

1389 except OSError as e: 

1390 if not _ignore_error(e): 

1391 raise 

1392 return False 

1393 except ValueError: 

1394 # Non-encodable path 

1395 return False 

1396 return True 

1397 

1398 def is_dir(self): 

1399 """ 

1400 Whether this path is a directory. 

1401 """ 

1402 try: 

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

1404 except OSError as e: 

1405 if not _ignore_error(e): 

1406 raise 

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

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

1409 return False 

1410 except ValueError: 

1411 # Non-encodable path 

1412 return False 

1413 

1414 def is_file(self): 

1415 """ 

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

1417 to regular files). 

1418 """ 

1419 try: 

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

1421 except OSError as e: 

1422 if not _ignore_error(e): 

1423 raise 

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

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

1426 return False 

1427 except ValueError: 

1428 # Non-encodable path 

1429 return False 

1430 

1431 def is_mount(self): 

1432 """ 

1433 Check if this path is a POSIX mount point 

1434 """ 

1435 # Need to exist and be a dir 

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

1437 return False 

1438 

1439 parent = Path(self.parent) 

1440 try: 

1441 parent_dev = parent.stat().st_dev 

1442 except OSError: 

1443 return False 

1444 

1445 dev = self.stat().st_dev 

1446 if dev != parent_dev: 

1447 return True 

1448 ino = self.stat().st_ino 

1449 parent_ino = parent.stat().st_ino 

1450 return ino == parent_ino 

1451 

1452 def is_symlink(self): 

1453 """ 

1454 Whether this path is a symbolic link. 

1455 """ 

1456 try: 

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

1458 except OSError as e: 

1459 if not _ignore_error(e): 

1460 raise 

1461 # Path doesn't exist 

1462 return False 

1463 except ValueError: 

1464 # Non-encodable path 

1465 return False 

1466 

1467 def is_block_device(self): 

1468 """ 

1469 Whether this path is a block device. 

1470 """ 

1471 try: 

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

1473 except OSError as e: 

1474 if not _ignore_error(e): 

1475 raise 

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

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

1478 return False 

1479 except ValueError: 

1480 # Non-encodable path 

1481 return False 

1482 

1483 def is_char_device(self): 

1484 """ 

1485 Whether this path is a character device. 

1486 """ 

1487 try: 

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

1489 except OSError as e: 

1490 if not _ignore_error(e): 

1491 raise 

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

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

1494 return False 

1495 except ValueError: 

1496 # Non-encodable path 

1497 return False 

1498 

1499 def is_fifo(self): 

1500 """ 

1501 Whether this path is a FIFO. 

1502 """ 

1503 try: 

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

1505 except OSError as e: 

1506 if not _ignore_error(e): 

1507 raise 

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

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

1510 return False 

1511 except ValueError: 

1512 # Non-encodable path 

1513 return False 

1514 

1515 def is_socket(self): 

1516 """ 

1517 Whether this path is a socket. 

1518 """ 

1519 try: 

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

1521 except OSError as e: 

1522 if not _ignore_error(e): 

1523 raise 

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

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

1526 return False 

1527 except ValueError: 

1528 # Non-encodable path 

1529 return False 

1530 

1531 def expanduser(self): 

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

1533 (as returned by os.path.expanduser) 

1534 """ 

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

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

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

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

1539 

1540 return self 

1541 

1542 

1543class PosixPath(Path, PurePosixPath): 

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

1545 

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

1547 """ 

1548 __slots__ = () 

1549 

1550class WindowsPath(Path, PureWindowsPath): 

1551 """Path subclass for Windows systems. 

1552 

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

1554 """ 

1555 __slots__ = () 

1556 

1557 def owner(self): 

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

1559 

1560 def group(self): 

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

1562 

1563 def is_mount(self): 

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