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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

797 statements  

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

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

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

4# 

5# Distributed under the terms of the MIT License. 

6 

7import fnmatch 

8import functools 

9import io 

10import ntpath 

11import os 

12import posixpath 

13import re 

14import sys 

15import warnings 

16from _collections_abc import Sequence 

17from errno import ENOENT, ENOTDIR, EBADF, ELOOP 

18from operator import attrgetter 

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

20from urllib.parse import quote_from_bytes as urlquote_from_bytes 

21 

22 

23__all__ = [ 

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

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

26 ] 

27 

28# 

29# Internals 

30# 

31 

32_WINERROR_NOT_READY = 21 # drive exists but is not accessible 

33_WINERROR_INVALID_NAME = 123 # fix for bpo-35306 

34_WINERROR_CANT_RESOLVE_FILENAME = 1921 # broken symlink pointing to itself 

35 

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

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

38 

39_IGNORED_WINERRORS = ( 

40 _WINERROR_NOT_READY, 

41 _WINERROR_INVALID_NAME, 

42 _WINERROR_CANT_RESOLVE_FILENAME) 

43 

44def _ignore_error(exception): 

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

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

47 

48 

49def _is_wildcard_pattern(pat): 

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

51 # be looked up directly as a file. 

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

53 

54 

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

56 io_text_encoding = io.text_encoding 

57else: 

58 def io_text_encoding(encoding, stacklevel=2): 

59 return encoding 

60 

61 

62class _Flavour(object): 

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

64 semantics.""" 

65 

66 sep: str 

67 altsep: str 

68 

69 def __init__(self): 

70 self.join = self.sep.join 

71 

72 def parse_parts(self, parts): 

73 parsed = [] 

74 sep = self.sep 

75 altsep = self.altsep 

76 drv = root = '' 

77 it = reversed(parts) 

78 for part in it: 

79 if not part: 

80 continue 

81 if altsep: 

82 part = part.replace(altsep, sep) 

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

84 if sep in rel: 

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

86 if x and x != '.': 

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

88 else: 

89 if rel and rel != '.': 

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

91 if drv or root: 

92 if not drv: 

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

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

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

96 for part in it: 

97 if not part: 

98 continue 

99 if altsep: 

100 part = part.replace(altsep, sep) 

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

102 if drv: 

103 break 

104 break 

105 if drv or root: 

106 parsed.append(drv + root) 

107 parsed.reverse() 

108 return drv, root, parsed 

109 

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

111 """ 

112 Join the two paths represented by the respective 

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

114 """ 

115 if root2: 

116 if not drv2 and drv: 

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

118 elif drv2: 

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

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

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

122 else: 

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

124 return drv, root, parts + parts2 

125 return drv2, root2, parts2 

126 

127 

128class _WindowsFlavour(_Flavour): 

129 # Reference for Windows paths can be found at 

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

131 

132 sep = '\\' 

133 altsep = '/' 

134 has_drv = True 

135 pathmod = ntpath 

136 

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

138 

139 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') 

140 ext_namespace_prefix = '\\\\?\\' 

141 

142 reserved_names = ( 

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

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

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

146 ) 

147 

148 # Interesting findings about extended paths: 

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

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

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

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

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

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

155 # regular name character in the object namespace. 

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

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

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

159 # thus limited to MAX_PATH. 

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

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

162 

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

164 first = part[0:1] 

165 second = part[1:2] 

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

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

168 # components (according to MSDN docs). 

169 prefix, part = self._split_extended_path(part) 

170 first = part[0:1] 

171 second = part[1:2] 

172 else: 

173 prefix = '' 

174 third = part[2:3] 

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

176 # is a UNC path: 

177 # vvvvvvvvvvvvvvvvvvvvv root 

178 # \\machine\mountpoint\directory\etc\... 

179 # directory ^^^^^^^^^^^^^^ 

180 index = part.find(sep, 2) 

181 if index != -1: 

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

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

184 # (after the initial two) 

185 if index2 != index + 1: 

186 if index2 == -1: 

187 index2 = len(part) 

188 if prefix: 

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

190 else: 

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

192 drv = root = '' 

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

194 drv = part[:2] 

195 part = part[2:] 

196 first = third 

197 if first == sep: 

198 root = first 

199 part = part.lstrip(sep) 

200 return prefix + drv, root, part 

201 

202 def casefold(self, s): 

203 return s.lower() 

204 

205 def casefold_parts(self, parts): 

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

207 

208 def compile_pattern(self, pattern): 

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

210 

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

212 prefix = '' 

213 if s.startswith(ext_prefix): 

214 prefix = s[:4] 

215 s = s[4:] 

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

217 prefix += s[:3] 

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

219 return prefix, s 

220 

221 def is_reserved(self, parts): 

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

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

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

225 # which are not considered reserved by Windows. 

226 if not parts: 

227 return False 

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

229 # UNC paths are never reserved 

230 return False 

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

232 return name.upper() in self.reserved_names 

233 

234 def make_uri(self, path): 

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

236 drive = path.drive 

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

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

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

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

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

242 else: 

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

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

245 

246 

247class _PosixFlavour(_Flavour): 

248 sep = '/' 

249 altsep = '' 

250 has_drv = False 

251 pathmod = posixpath 

252 

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

254 

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

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

257 stripped_part = part.lstrip(sep) 

258 # According to POSIX path resolution: 

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

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

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

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

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

264 return '', sep * 2, stripped_part 

265 else: 

266 return '', sep, stripped_part 

267 else: 

268 return '', '', part 

269 

270 def casefold(self, s): 

271 return s 

272 

273 def casefold_parts(self, parts): 

274 return parts 

275 

276 def compile_pattern(self, pattern): 

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

278 

279 def is_reserved(self, parts): 

280 return False 

281 

282 def make_uri(self, path): 

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

284 # for portability to other applications. 

285 bpath = bytes(path) 

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

287 

288 

289_windows_flavour = _WindowsFlavour() 

290_posix_flavour = _PosixFlavour() 

291 

292 

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

294 from os.path import realpath as os_path_realpath 

295elif os.name == "posix": 

296 from pathlib2._posixpath import realpath as os_path_realpath 

297else: 

298 from pathlib2._ntpath import realpath as os_path_realpath 

299 

300 

301# 

302# Globbing helpers 

303# 

304 

305def _make_selector(pattern_parts, flavour): 

306 pat = pattern_parts[0] 

307 child_parts = pattern_parts[1:] 

308 if pat == '**': 

309 cls = _RecursiveWildcardSelector 

310 elif '**' in pat: 

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

312 elif _is_wildcard_pattern(pat): 

313 cls = _WildcardSelector 

314 else: 

315 cls = _PreciseSelector 

316 return cls(pat, child_parts, flavour) 

317 

318if hasattr(functools, "lru_cache"): 

319 _make_selector = functools.lru_cache()(_make_selector) 

320 

321 

322class _Selector: 

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

324 of a given path.""" 

325 

326 def __init__(self, child_parts, flavour): 

327 self.child_parts = child_parts 

328 if child_parts: 

329 self.successor = _make_selector(child_parts, flavour) 

330 self.dironly = True 

331 else: 

332 self.successor = _TerminatingSelector() 

333 self.dironly = False 

334 

335 def select_from(self, parent_path): 

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

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

338 path_cls = type(parent_path) 

339 is_dir = path_cls.is_dir 

340 exists = path_cls.exists 

341 scandir = path_cls._scandir 

342 if not is_dir(parent_path): 

343 return iter([]) 

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

345 

346 

347class _TerminatingSelector: 

348 

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

350 yield parent_path 

351 

352 

353class _PreciseSelector(_Selector): 

354 

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

356 self.name = name 

357 _Selector.__init__(self, child_parts, flavour) 

358 

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

360 try: 

361 path = parent_path._make_child_relpath(self.name) 

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

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

364 yield p 

365 except PermissionError: 

366 return 

367 

368 

369class _WildcardSelector(_Selector): 

370 

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

372 self.match = flavour.compile_pattern(pat) 

373 _Selector.__init__(self, child_parts, flavour) 

374 

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

376 try: 

377 with scandir(parent_path) as scandir_it: 

378 entries = list(scandir_it) 

379 for entry in entries: 

380 if self.dironly: 

381 try: 

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

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

384 # among the errors ignored by _ignore_error() 

385 if not entry.is_dir(): 

386 continue 

387 except OSError as e: 

388 if not _ignore_error(e): 

389 raise 

390 continue 

391 name = entry.name 

392 if self.match(name): 

393 path = parent_path._make_child_relpath(name) 

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

395 yield p 

396 except PermissionError: 

397 return 

398 

399 

400class _RecursiveWildcardSelector(_Selector): 

401 

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

403 _Selector.__init__(self, child_parts, flavour) 

404 

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

406 yield parent_path 

407 try: 

408 with scandir(parent_path) as scandir_it: 

409 entries = list(scandir_it) 

410 for entry in entries: 

411 entry_is_dir = False 

412 try: 

413 entry_is_dir = entry.is_dir() 

414 except OSError as e: 

415 if not _ignore_error(e): 

416 raise 

417 if entry_is_dir and not entry.is_symlink(): 

418 path = parent_path._make_child_relpath(entry.name) 

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

420 yield p 

421 except PermissionError: 

422 return 

423 

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

425 try: 

426 yielded = set() 

427 try: 

428 successor_select = self.successor._select_from 

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

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

431 if p not in yielded: 

432 yield p 

433 yielded.add(p) 

434 finally: 

435 yielded.clear() 

436 except PermissionError: 

437 return 

438 

439 

440# 

441# Public API 

442# 

443 

444class _PathParents(Sequence): 

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

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

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

448 

449 def __init__(self, path): 

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

451 self._pathcls = type(path) 

452 self._drv = path._drv 

453 self._root = path._root 

454 self._parts = path._parts 

455 

456 def __len__(self): 

457 if self._drv or self._root: 

458 return len(self._parts) - 1 

459 else: 

460 return len(self._parts) 

461 

462 def __getitem__(self, idx): 

463 if isinstance(idx, slice): 

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

465 

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

467 raise IndexError(idx) 

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

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

470 

471 def __repr__(self): 

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

473 

474 

475class PurePath(object): 

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

477 

478 PurePath represents a filesystem path and offers operations which 

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

480 instantiating a PurePath will return either a PurePosixPath or a 

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

482 directly, regardless of your system. 

483 """ 

484 __slots__ = ( 

485 '_drv', '_root', '_parts', 

486 '_str', '_hash', '_pparts', '_cached_cparts', 

487 ) 

488 

489 def __new__(cls, *args): 

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

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

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

493 new PurePath object. 

494 """ 

495 if cls is PurePath: 

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

497 return cls._from_parts(args) 

498 

499 def __reduce__(self): 

500 # Using the parts tuple helps share interned path parts 

501 # when pickling related paths. 

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

503 

504 @classmethod 

505 def _parse_args(cls, args): 

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

507 # canonicalize some constructor arguments. 

508 parts = [] 

509 for a in args: 

510 if isinstance(a, PurePath): 

511 parts += a._parts 

512 else: 

513 a = os.fspath(a) 

514 if isinstance(a, str): 

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

516 parts.append(str(a)) 

517 else: 

518 raise TypeError( 

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

520 "object returning str, not %r" 

521 % type(a)) 

522 return cls._flavour.parse_parts(parts) 

523 

524 @classmethod 

525 def _from_parts(cls, args): 

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

527 # right flavour. 

528 self = object.__new__(cls) 

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

530 self._drv = drv 

531 self._root = root 

532 self._parts = parts 

533 return self 

534 

535 @classmethod 

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

537 self = object.__new__(cls) 

538 self._drv = drv 

539 self._root = root 

540 self._parts = parts 

541 return self 

542 

543 @classmethod 

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

545 if drv or root: 

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

547 else: 

548 return cls._flavour.join(parts) 

549 

550 def _make_child(self, args): 

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

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

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

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

555 

556 def __str__(self): 

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

558 passing to system calls.""" 

559 try: 

560 return self._str 

561 except AttributeError: 

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

563 self._parts) or '.' 

564 return self._str 

565 

566 def __fspath__(self): 

567 return str(self) 

568 

569 def as_posix(self): 

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

571 slashes.""" 

572 f = self._flavour 

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

574 

575 def __bytes__(self): 

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

577 recommended to use under Unix.""" 

578 return os.fsencode(self) 

579 

580 def __repr__(self): 

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

582 

583 def as_uri(self): 

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

585 if not self.is_absolute(): 

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

587 return self._flavour.make_uri(self) 

588 

589 @property 

590 def _cparts(self): 

591 # Cached casefolded parts, for hashing and comparison 

592 try: 

593 return self._cached_cparts 

594 except AttributeError: 

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

596 return self._cached_cparts 

597 

598 def __eq__(self, other): 

599 if not isinstance(other, PurePath): 

600 return NotImplemented 

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

602 

603 def __hash__(self): 

604 try: 

605 return self._hash 

606 except AttributeError: 

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

608 return self._hash 

609 

610 def __lt__(self, other): 

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

612 return NotImplemented 

613 return self._cparts < other._cparts 

614 

615 def __le__(self, other): 

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

617 return NotImplemented 

618 return self._cparts <= other._cparts 

619 

620 def __gt__(self, other): 

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

622 return NotImplemented 

623 return self._cparts > other._cparts 

624 

625 def __ge__(self, other): 

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

627 return NotImplemented 

628 return self._cparts >= other._cparts 

629 

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

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

632 

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

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

635 

636 @property 

637 def anchor(self): 

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

639 anchor = self._drv + self._root 

640 return anchor 

641 

642 @property 

643 def name(self): 

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

645 parts = self._parts 

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

647 return '' 

648 return parts[-1] 

649 

650 @property 

651 def suffix(self): 

652 """ 

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

654 

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

656 """ 

657 name = self.name 

658 i = name.rfind('.') 

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

660 return name[i:] 

661 else: 

662 return '' 

663 

664 @property 

665 def suffixes(self): 

666 """ 

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

668 

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

670 """ 

671 name = self.name 

672 if name.endswith('.'): 

673 return [] 

674 name = name.lstrip('.') 

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

676 

677 @property 

678 def stem(self): 

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

680 name = self.name 

681 i = name.rfind('.') 

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

683 return name[:i] 

684 else: 

685 return name 

686 

687 def with_name(self, name): 

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

689 if not self.name: 

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

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

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

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

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

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

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

697 

698 def with_stem(self, stem): 

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

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

701 

702 def with_suffix(self, suffix): 

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

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

705 string, remove the suffix from the path. 

706 """ 

707 f = self._flavour 

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

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

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

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

712 name = self.name 

713 if not name: 

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

715 old_suffix = self.suffix 

716 if not old_suffix: 

717 name = name + suffix 

718 else: 

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

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

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

722 

723 def relative_to(self, *other): 

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

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

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

727 """ 

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

729 # separate parts, i.e.: 

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

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

732 if not other: 

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

734 parts = self._parts 

735 drv = self._drv 

736 root = self._root 

737 if root: 

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

739 else: 

740 abs_parts = parts 

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

742 if to_root: 

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

744 else: 

745 to_abs_parts = to_parts 

746 n = len(to_abs_parts) 

747 cf = self._flavour.casefold_parts 

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

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

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

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

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

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

754 abs_parts[n:]) 

755 

756 def is_relative_to(self, *other): 

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

758 """ 

759 try: 

760 self.relative_to(*other) 

761 return True 

762 except ValueError: 

763 return False 

764 

765 @property 

766 def parts(self): 

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

768 components in the filesystem path.""" 

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

770 # is accessed. XXX is this necessary? 

771 try: 

772 return self._pparts 

773 except AttributeError: 

774 self._pparts = tuple(self._parts) 

775 return self._pparts 

776 

777 def joinpath(self, *args): 

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

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

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

781 anchored). 

782 """ 

783 return self._make_child(args) 

784 

785 def __truediv__(self, key): 

786 try: 

787 return self._make_child((key,)) 

788 except TypeError: 

789 return NotImplemented 

790 

791 def __rtruediv__(self, key): 

792 try: 

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

794 except TypeError: 

795 return NotImplemented 

796 

797 @property 

798 def parent(self): 

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

800 drv = self._drv 

801 root = self._root 

802 parts = self._parts 

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

804 return self 

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

806 

807 @property 

808 def parents(self): 

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

810 return _PathParents(self) 

811 

812 def is_absolute(self): 

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

814 a drive).""" 

815 if not self._root: 

816 return False 

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

818 

819 def is_reserved(self): 

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

821 by the system, if any.""" 

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

823 

824 def match(self, path_pattern): 

825 """ 

826 Return True if this path matches the given pattern. 

827 """ 

828 cf = self._flavour.casefold 

829 path_pattern = cf(path_pattern) 

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

831 if not pat_parts: 

832 raise ValueError("empty pattern") 

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

834 return False 

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

836 return False 

837 parts = self._cparts 

838 if drv or root: 

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

840 return False 

841 pat_parts = pat_parts[1:] 

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

843 return False 

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

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

846 return False 

847 return True 

848 

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

850# optimizations in PurePath._parse_args(). 

851os.PathLike.register(PurePath) 

852 

853 

854class PurePosixPath(PurePath): 

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

856 

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

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

859 """ 

860 _flavour = _posix_flavour 

861 __slots__ = () 

862 

863 

864class PureWindowsPath(PurePath): 

865 """PurePath subclass for Windows systems. 

866 

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

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

869 """ 

870 _flavour = _windows_flavour 

871 __slots__ = () 

872 

873 

874# Filesystem-accessing classes 

875 

876 

877class Path(PurePath): 

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

879 

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

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

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

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

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

885 """ 

886 __slots__ = () 

887 

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

889 if cls is Path: 

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

891 self = cls._from_parts(args) 

892 if not self._flavour.is_supported: 

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

894 % (cls.__name__,)) 

895 return self 

896 

897 def _make_child_relpath(self, part): 

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

899 # a single part relative to this path. 

900 parts = self._parts + [part] 

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

902 

903 def __enter__(self): 

904 return self 

905 

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

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

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

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

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

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

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

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

914 # removed in the future. 

915 pass 

916 

917 # Public API 

918 

919 @classmethod 

920 def cwd(cls): 

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

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

923 """ 

924 return cls(os.getcwd()) 

925 

926 @classmethod 

927 def home(cls): 

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

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

930 """ 

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

932 

933 def samefile(self, other_path): 

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

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

936 """ 

937 st = self.stat() 

938 try: 

939 other_st = other_path.stat() 

940 except AttributeError: 

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

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

943 

944 def iterdir(self): 

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

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

947 """ 

948 for name in os.listdir(self): 

949 yield self._make_child_relpath(name) 

950 

951 def _scandir(self): 

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

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

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

955 return os.scandir(self) 

956 

957 def glob(self, pattern): 

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

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

960 """ 

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

962 if not pattern: 

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

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

965 if drv or root: 

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

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

968 for p in selector.select_from(self): 

969 yield p 

970 

971 def rglob(self, pattern): 

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

973 directories) matching the given relative pattern, anywhere in 

974 this subtree. 

975 """ 

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

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

978 if drv or root: 

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

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

981 for p in selector.select_from(self): 

982 yield p 

983 

984 def absolute(self): 

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

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

987 

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

989 """ 

990 if self.is_absolute(): 

991 return self 

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

993 

994 def resolve(self, strict=False): 

995 """ 

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

997 normalizing it. 

998 """ 

999 

1000 def check_eloop(e): 

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

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

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

1004 

1005 try: 

1006 s = os_path_realpath(self, strict=strict) 

1007 except OSError as e: 

1008 check_eloop(e) 

1009 raise 

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

1011 

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

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

1014 if not strict: 

1015 try: 

1016 p.stat() 

1017 except OSError as e: 

1018 check_eloop(e) 

1019 return p 

1020 

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

1022 """ 

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

1024 os.stat() does. 

1025 """ 

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

1027 

1028 def owner(self): 

1029 """ 

1030 Return the login name of the file owner. 

1031 """ 

1032 try: 

1033 import pwd 

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

1035 except ImportError: 

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

1037 

1038 def group(self): 

1039 """ 

1040 Return the group name of the file gid. 

1041 """ 

1042 

1043 try: 

1044 import grp 

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

1046 except ImportError: 

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

1048 

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

1050 errors=None, newline=None): 

1051 """ 

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

1053 the built-in open() function does. 

1054 """ 

1055 if "b" not in mode: 

1056 encoding = io_text_encoding(encoding) 

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

1058 

1059 def read_bytes(self): 

1060 """ 

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

1062 """ 

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

1064 return f.read() 

1065 

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

1067 """ 

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

1069 """ 

1070 encoding = io_text_encoding(encoding) 

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

1072 return f.read() 

1073 

1074 def write_bytes(self, data): 

1075 """ 

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

1077 """ 

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

1079 view = memoryview(data) 

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

1081 return f.write(view) 

1082 

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

1084 """ 

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

1086 """ 

1087 if not isinstance(data, str): 

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

1089 data.__class__.__name__) 

1090 encoding = io_text_encoding(encoding) 

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

1092 return f.write(data) 

1093 

1094 def readlink(self): 

1095 """ 

1096 Return the path to which the symbolic link points. 

1097 """ 

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

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

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

1101 

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

1103 """ 

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

1105 """ 

1106 

1107 if exist_ok: 

1108 # First try to bump modification time 

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

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

1111 try: 

1112 os.utime(self, None) 

1113 except OSError: 

1114 # Avoid exception chaining 

1115 pass 

1116 else: 

1117 return 

1118 flags = os.O_CREAT | os.O_WRONLY 

1119 if not exist_ok: 

1120 flags |= os.O_EXCL 

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

1122 os.close(fd) 

1123 

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

1125 """ 

1126 Create a new directory at this given path. 

1127 """ 

1128 try: 

1129 os.mkdir(self, mode) 

1130 except FileNotFoundError: 

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

1132 raise 

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

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

1135 except OSError: 

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

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

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

1139 raise 

1140 

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

1142 """ 

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

1144 """ 

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

1146 

1147 def lchmod(self, mode): 

1148 """ 

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

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

1151 """ 

1152 self.chmod(mode, follow_symlinks=False) 

1153 

1154 def unlink(self, missing_ok=False): 

1155 """ 

1156 Remove this file or link. 

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

1158 """ 

1159 try: 

1160 os.unlink(self) 

1161 except FileNotFoundError: 

1162 if not missing_ok: 

1163 raise 

1164 

1165 def rmdir(self): 

1166 """ 

1167 Remove this directory. The directory must be empty. 

1168 """ 

1169 os.rmdir(self) 

1170 

1171 def lstat(self): 

1172 """ 

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

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

1175 """ 

1176 return self.stat(follow_symlinks=False) 

1177 

1178 def rename(self, target): 

1179 """ 

1180 Rename this path to the target path. 

1181 

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

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

1184 directory of the Path object. 

1185 

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

1187 """ 

1188 os.rename(self, target) 

1189 return self.__class__(target) 

1190 

1191 def replace(self, target): 

1192 """ 

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

1194 

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

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

1197 directory of the Path object. 

1198 

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

1200 """ 

1201 os.replace(self, target) 

1202 return self.__class__(target) 

1203 

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

1205 """ 

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

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

1208 """ 

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

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

1211 os.symlink(target, self, target_is_directory) 

1212 

1213 def hardlink_to(self, target): 

1214 """ 

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

1216 

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

1218 """ 

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

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

1221 os.link(target, self) 

1222 

1223 def link_to(self, target): 

1224 """ 

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

1226 

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

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

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

1230 matches that of os.link. 

1231 

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

1233 Use `hardlink_to()` instead. 

1234 """ 

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

1236 "for removal in Python 3.12. " 

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

1238 DeprecationWarning, stacklevel=2) 

1239 self.__class__(target).hardlink_to(self) 

1240 

1241 # Convenience functions for querying the stat results 

1242 

1243 def exists(self): 

1244 """ 

1245 Whether this path exists. 

1246 """ 

1247 try: 

1248 self.stat() 

1249 except OSError as e: 

1250 if not _ignore_error(e): 

1251 raise 

1252 return False 

1253 except ValueError: 

1254 # Non-encodable path 

1255 return False 

1256 return True 

1257 

1258 def is_dir(self): 

1259 """ 

1260 Whether this path is a directory. 

1261 """ 

1262 try: 

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

1264 except OSError as e: 

1265 if not _ignore_error(e): 

1266 raise 

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

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

1269 return False 

1270 except ValueError: 

1271 # Non-encodable path 

1272 return False 

1273 

1274 def is_file(self): 

1275 """ 

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

1277 to regular files). 

1278 """ 

1279 try: 

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

1281 except OSError as e: 

1282 if not _ignore_error(e): 

1283 raise 

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

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

1286 return False 

1287 except ValueError: 

1288 # Non-encodable path 

1289 return False 

1290 

1291 def is_mount(self): 

1292 """ 

1293 Check if this path is a POSIX mount point 

1294 """ 

1295 # Need to exist and be a dir 

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

1297 return False 

1298 

1299 try: 

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

1301 except OSError: 

1302 return False 

1303 

1304 dev = self.stat().st_dev 

1305 if dev != parent_dev: 

1306 return True 

1307 ino = self.stat().st_ino 

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

1309 return ino == parent_ino 

1310 

1311 def is_symlink(self): 

1312 """ 

1313 Whether this path is a symbolic link. 

1314 """ 

1315 try: 

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

1317 except OSError as e: 

1318 if not _ignore_error(e): 

1319 raise 

1320 # Path doesn't exist 

1321 return False 

1322 except ValueError: 

1323 # Non-encodable path 

1324 return False 

1325 

1326 def is_block_device(self): 

1327 """ 

1328 Whether this path is a block device. 

1329 """ 

1330 try: 

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

1332 except OSError as e: 

1333 if not _ignore_error(e): 

1334 raise 

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

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

1337 return False 

1338 except ValueError: 

1339 # Non-encodable path 

1340 return False 

1341 

1342 def is_char_device(self): 

1343 """ 

1344 Whether this path is a character device. 

1345 """ 

1346 try: 

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

1348 except OSError as e: 

1349 if not _ignore_error(e): 

1350 raise 

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

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

1353 return False 

1354 except ValueError: 

1355 # Non-encodable path 

1356 return False 

1357 

1358 def is_fifo(self): 

1359 """ 

1360 Whether this path is a FIFO. 

1361 """ 

1362 try: 

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

1364 except OSError as e: 

1365 if not _ignore_error(e): 

1366 raise 

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

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

1369 return False 

1370 except ValueError: 

1371 # Non-encodable path 

1372 return False 

1373 

1374 def is_socket(self): 

1375 """ 

1376 Whether this path is a socket. 

1377 """ 

1378 try: 

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

1380 except OSError as e: 

1381 if not _ignore_error(e): 

1382 raise 

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

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

1385 return False 

1386 except ValueError: 

1387 # Non-encodable path 

1388 return False 

1389 

1390 def expanduser(self): 

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

1392 (as returned by os.path.expanduser) 

1393 """ 

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

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

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

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

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

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

1400 

1401 return self 

1402 

1403 

1404class PosixPath(Path, PurePosixPath): 

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

1406 

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

1408 """ 

1409 __slots__ = () 

1410 

1411class WindowsPath(Path, PureWindowsPath): 

1412 """Path subclass for Windows systems. 

1413 

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

1415 """ 

1416 __slots__ = () 

1417 

1418 def is_mount(self): 

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