1import io 2import ntpath 3import operator 4import os 5import posixpath 6import sys 7import warnings 8from glob import _StringGlobber 9from itertools import chain 10from _collections_abc import Sequence 11 12try: 13 import pwd 14except ImportError: 15 pwd = None 16try: 17 import grp 18except ImportError: 19 grp = None 20 21from ._abc import UnsupportedOperation, PurePathBase, PathBase 22 23 24__all__ = [ 25 "PurePath", "PurePosixPath", "PureWindowsPath", 26 "Path", "PosixPath", "WindowsPath", 27 ] 28 29 30class _PathParents(Sequence): 31 """This object provides sequence-like access to the logical ancestors 32 of a path. Don't try to construct it yourself.""" 33 __slots__ = ('_path', '_drv', '_root', '_tail') 34 35 def __init__(self, path): 36 self._path = path 37 self._drv = path.drive 38 self._root = path.root 39 self._tail = path._tail 40 41 def __len__(self): 42 return len(self._tail) 43 44 def __getitem__(self, idx): 45 if isinstance(idx, slice): 46 return tuple(self[i] for i in range(*idx.indices(len(self)))) 47 48 if idx >= len(self) or idx < -len(self): 49 raise IndexError(idx) 50 if idx < 0: 51 idx += len(self) 52 return self._path._from_parsed_parts(self._drv, self._root, 53 self._tail[:-idx - 1]) 54 55 def __repr__(self): 56 return "<{}.parents>".format(type(self._path).__name__) 57 58 59class PurePath(PurePathBase): 60 """Base class for manipulating paths without I/O. 61 62 PurePath represents a filesystem path and offers operations which 63 don't imply any actual filesystem I/O. Depending on your system, 64 instantiating a PurePath will return either a PurePosixPath or a 65 PureWindowsPath object. You can also instantiate either of these classes 66 directly, regardless of your system. 67 """ 68 69 __slots__ = ( 70 # The `_raw_paths` slot stores unnormalized string paths. This is set 71 # in the `__init__()` method. 72 '_raw_paths', 73 74 # The `_drv`, `_root` and `_tail_cached` slots store parsed and 75 # normalized parts of the path. They are set when any of the `drive`, 76 # `root` or `_tail` properties are accessed for the first time. The 77 # three-part division corresponds to the result of 78 # `os.path.splitroot()`, except that the tail is further split on path 79 # separators (i.e. it is a list of strings), and that the root and 80 # tail are normalized. 81 '_drv', '_root', '_tail_cached', 82 83 # The `_str` slot stores the string representation of the path, 84 # computed from the drive, root and tail when `__str__()` is called 85 # for the first time. It's used to implement `_str_normcase` 86 '_str', 87 88 # The `_str_normcase_cached` slot stores the string path with 89 # normalized case. It is set when the `_str_normcase` property is 90 # accessed for the first time. It's used to implement `__eq__()` 91 # `__hash__()`, and `_parts_normcase` 92 '_str_normcase_cached', 93 94 # The `_parts_normcase_cached` slot stores the case-normalized 95 # string path after splitting on path separators. It's set when the 96 # `_parts_normcase` property is accessed for the first time. It's used 97 # to implement comparison methods like `__lt__()`. 98 '_parts_normcase_cached', 99 100 # The `_hash` slot stores the hash of the case-normalized string 101 # path. It's set when `__hash__()` is called for the first time. 102 '_hash', 103 ) 104 parser = os.path 105 _globber = _StringGlobber 106 107 def __new__(cls, *args, **kwargs): 108 """Construct a PurePath from one or several strings and or existing 109 PurePath objects. The strings and path objects are combined so as 110 to yield a canonicalized path, which is incorporated into the 111 new PurePath object. 112 """ 113 if cls is PurePath: 114 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath 115 return object.__new__(cls) 116 117 def __init__(self, *args): 118 paths = [] 119 for arg in args: 120 if isinstance(arg, PurePath): 121 if arg.parser is not self.parser: 122 # GH-103631: Convert separators for backwards compatibility. 123 paths.append(arg.as_posix()) 124 else: 125 paths.extend(arg._raw_paths) 126 else: 127 try: 128 path = os.fspath(arg) 129 except TypeError: 130 path = arg 131 if not isinstance(path, str): 132 raise TypeError( 133 "argument should be a str or an os.PathLike " 134 "object where __fspath__ returns a str, " 135 f"not {type(path).__name__!r}") 136 paths.append(path) 137 # Avoid calling super().__init__, as an optimisation 138 self._raw_paths = paths 139 140 def joinpath(self, *pathsegments): 141 """Combine this path with one or several arguments, and return a 142 new path representing either a subpath (if all arguments are relative 143 paths) or a totally different path (if one of the arguments is 144 anchored). 145 """ 146 return self.with_segments(self, *pathsegments) 147 148 def __truediv__(self, key): 149 try: 150 return self.with_segments(self, key) 151 except TypeError: 152 return NotImplemented 153 154 def __rtruediv__(self, key): 155 try: 156 return self.with_segments(key, self) 157 except TypeError: 158 return NotImplemented 159 160 def __reduce__(self): 161 return self.__class__, tuple(self._raw_paths) 162 163 def __repr__(self): 164 return "{}({!r})".format(self.__class__.__name__, self.as_posix()) 165 166 def __fspath__(self): 167 return str(self) 168 169 def __bytes__(self): 170 """Return the bytes representation of the path. This is only 171 recommended to use under Unix.""" 172 return os.fsencode(self) 173 174 @property 175 def _str_normcase(self): 176 # String with normalized case, for hashing and equality checks 177 try: 178 return self._str_normcase_cached 179 except AttributeError: 180 if self.parser is posixpath: 181 self._str_normcase_cached = str(self) 182 else: 183 self._str_normcase_cached = str(self).lower() 184 return self._str_normcase_cached 185 186 def __hash__(self): 187 try: 188 return self._hash 189 except AttributeError: 190 self._hash = hash(self._str_normcase) 191 return self._hash 192 193 def __eq__(self, other): 194 if not isinstance(other, PurePath): 195 return NotImplemented 196 return self._str_normcase == other._str_normcase and self.parser is other.parser 197 198 @property 199 def _parts_normcase(self): 200 # Cached parts with normalized case, for comparisons. 201 try: 202 return self._parts_normcase_cached 203 except AttributeError: 204 self._parts_normcase_cached = self._str_normcase.split(self.parser.sep) 205 return self._parts_normcase_cached 206 207 def __lt__(self, other): 208 if not isinstance(other, PurePath) or self.parser is not other.parser: 209 return NotImplemented 210 return self._parts_normcase < other._parts_normcase 211 212 def __le__(self, other): 213 if not isinstance(other, PurePath) or self.parser is not other.parser: 214 return NotImplemented 215 return self._parts_normcase <= other._parts_normcase 216 217 def __gt__(self, other): 218 if not isinstance(other, PurePath) or self.parser is not other.parser: 219 return NotImplemented 220 return self._parts_normcase > other._parts_normcase 221 222 def __ge__(self, other): 223 if not isinstance(other, PurePath) or self.parser is not other.parser: 224 return NotImplemented 225 return self._parts_normcase >= other._parts_normcase 226 227 def __str__(self): 228 """Return the string representation of the path, suitable for 229 passing to system calls.""" 230 try: 231 return self._str 232 except AttributeError: 233 self._str = self._format_parsed_parts(self.drive, self.root, 234 self._tail) or '.' 235 return self._str 236 237 @classmethod 238 def _format_parsed_parts(cls, drv, root, tail): 239 if drv or root: 240 return drv + root + cls.parser.sep.join(tail) 241 elif tail and cls.parser.splitdrive(tail[0])[0]: 242 tail = ['.'] + tail 243 return cls.parser.sep.join(tail) 244 245 def _from_parsed_parts(self, drv, root, tail): 246 path = self._from_parsed_string(self._format_parsed_parts(drv, root, tail)) 247 path._drv = drv 248 path._root = root 249 path._tail_cached = tail 250 return path 251 252 def _from_parsed_string(self, path_str): 253 path = self.with_segments(path_str) 254 path._str = path_str or '.' 255 return path 256 257 @classmethod 258 def _parse_path(cls, path): 259 if not path: 260 return '', '', [] 261 sep = cls.parser.sep 262 altsep = cls.parser.altsep 263 if altsep: 264 path = path.replace(altsep, sep) 265 drv, root, rel = cls.parser.splitroot(path) 266 if not root and drv.startswith(sep) and not drv.endswith(sep): 267 drv_parts = drv.split(sep) 268 if len(drv_parts) == 4 and drv_parts[2] not in '?.': 269 # e.g. //server/share 270 root = sep 271 elif len(drv_parts) == 6: 272 # e.g. //?/unc/server/share 273 root = sep 274 parsed = [sys.intern(str(x)) for x in rel.split(sep) if x and x != '.'] 275 return drv, root, parsed 276 277 @property 278 def _raw_path(self): 279 """The joined but unnormalized path.""" 280 paths = self._raw_paths 281 if len(paths) == 0: 282 path = '' 283 elif len(paths) == 1: 284 path = paths[0] 285 else: 286 path = self.parser.join(*paths) 287 return path 288 289 @property 290 def drive(self): 291 """The drive prefix (letter or UNC path), if any.""" 292 try: 293 return self._drv 294 except AttributeError: 295 self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path) 296 return self._drv 297 298 @property 299 def root(self): 300 """The root of the path, if any.""" 301 try: 302 return self._root 303 except AttributeError: 304 self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path) 305 return self._root 306 307 @property 308 def _tail(self): 309 try: 310 return self._tail_cached 311 except AttributeError: 312 self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path) 313 return self._tail_cached 314 315 @property 316 def anchor(self): 317 """The concatenation of the drive and root, or ''.""" 318 return self.drive + self.root 319 320 @property 321 def parts(self): 322 """An object providing sequence-like access to the 323 components in the filesystem path.""" 324 if self.drive or self.root: 325 return (self.drive + self.root,) + tuple(self._tail) 326 else: 327 return tuple(self._tail) 328 329 @property 330 def parent(self): 331 """The logical parent of the path.""" 332 drv = self.drive 333 root = self.root 334 tail = self._tail 335 if not tail: 336 return self 337 return self._from_parsed_parts(drv, root, tail[:-1]) 338 339 @property 340 def parents(self): 341 """A sequence of this path's logical parents.""" 342 # The value of this property should not be cached on the path object, 343 # as doing so would introduce a reference cycle. 344 return _PathParents(self) 345 346 @property 347 def name(self): 348 """The final path component, if any.""" 349 tail = self._tail 350 if not tail: 351 return '' 352 return tail[-1] 353 354 def with_name(self, name): 355 """Return a new path with the file name changed.""" 356 p = self.parser 357 if not name or p.sep in name or (p.altsep and p.altsep in name) or name == '.': 358 raise ValueError(f"Invalid name {name!r}") 359 tail = self._tail.copy() 360 if not tail: 361 raise ValueError(f"{self!r} has an empty name") 362 tail[-1] = name 363 return self._from_parsed_parts(self.drive, self.root, tail) 364 365 def relative_to(self, other, /, *_deprecated, walk_up=False): 366 """Return the relative path to another path identified by the passed 367 arguments. If the operation is not possible (because this is not 368 related to the other path), raise ValueError. 369 370 The *walk_up* parameter controls whether `..` may be used to resolve 371 the path. 372 """ 373 if _deprecated: 374 msg = ("support for supplying more than one positional argument " 375 "to pathlib.PurePath.relative_to() is deprecated and " 376 "scheduled for removal in Python 3.14") 377 warnings.warn(msg, DeprecationWarning, stacklevel=2) 378 other = self.with_segments(other, *_deprecated) 379 elif not isinstance(other, PurePath): 380 other = self.with_segments(other) 381 for step, path in enumerate(chain([other], other.parents)): 382 if path == self or path in self.parents: 383 break 384 elif not walk_up: 385 raise ValueError(f"{str(self)!r} is not in the subpath of {str(other)!r}") 386 elif path.name == '..': 387 raise ValueError(f"'..' segment in {str(other)!r} cannot be walked") 388 else: 389 raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors") 390 parts = ['..'] * step + self._tail[len(path._tail):] 391 return self._from_parsed_parts('', '', parts) 392 393 def is_relative_to(self, other, /, *_deprecated): 394 """Return True if the path is relative to another path or False. 395 """ 396 if _deprecated: 397 msg = ("support for supplying more than one argument to " 398 "pathlib.PurePath.is_relative_to() is deprecated and " 399 "scheduled for removal in Python 3.14") 400 warnings.warn(msg, DeprecationWarning, stacklevel=2) 401 other = self.with_segments(other, *_deprecated) 402 elif not isinstance(other, PurePath): 403 other = self.with_segments(other) 404 return other == self or other in self.parents 405 406 def is_absolute(self): 407 """True if the path is absolute (has both a root and, if applicable, 408 a drive).""" 409 if self.parser is posixpath: 410 # Optimization: work with raw paths on POSIX. 411 for path in self._raw_paths: 412 if path.startswith('/'): 413 return True 414 return False 415 return self.parser.isabs(self) 416 417 def is_reserved(self): 418 """Return True if the path contains one of the special names reserved 419 by the system, if any.""" 420 msg = ("pathlib.PurePath.is_reserved() is deprecated and scheduled " 421 "for removal in Python 3.15. Use os.path.isreserved() to " 422 "detect reserved paths on Windows.") 423 warnings.warn(msg, DeprecationWarning, stacklevel=2) 424 if self.parser is ntpath: 425 return self.parser.isreserved(self) 426 return False 427 428 def as_uri(self): 429 """Return the path as a URI.""" 430 if not self.is_absolute(): 431 raise ValueError("relative path can't be expressed as a file URI") 432 433 drive = self.drive 434 if len(drive) == 2 and drive[1] == ':': 435 # It's a path on a local drive => 'file:///c:/a/b' 436 prefix = 'file:///' + drive 437 path = self.as_posix()[2:] 438 elif drive: 439 # It's a path on a network drive => 'file://host/share/a/b' 440 prefix = 'file:' 441 path = self.as_posix() 442 else: 443 # It's a posix path => 'file:///etc/hosts' 444 prefix = 'file://' 445 path = str(self) 446 from urllib.parse import quote_from_bytes 447 return prefix + quote_from_bytes(os.fsencode(path)) 448 449 @property 450 def _pattern_str(self): 451 """The path expressed as a string, for use in pattern-matching.""" 452 # The string representation of an empty path is a single dot ('.'). Empty 453 # paths shouldn't match wildcards, so we change it to the empty string. 454 path_str = str(self) 455 return '' if path_str == '.' else path_str 456 457# Subclassing os.PathLike makes isinstance() checks slower, 458# which in turn makes Path construction slower. Register instead! 459os.PathLike.register(PurePath) 460 461 462class PurePosixPath(PurePath): 463 """PurePath subclass for non-Windows systems. 464 465 On a POSIX system, instantiating a PurePath should return this object. 466 However, you can also instantiate it directly on any system. 467 """ 468 parser = posixpath 469 __slots__ = () 470 471 472class PureWindowsPath(PurePath): 473 """PurePath subclass for Windows systems. 474 475 On a Windows system, instantiating a PurePath should return this object. 476 However, you can also instantiate it directly on any system. 477 """ 478 parser = ntpath 479 __slots__ = () 480 481 482class Path(PathBase, PurePath): 483 """PurePath subclass that can make system calls. 484 485 Path represents a filesystem path but unlike PurePath, also offers 486 methods to do system calls on path objects. Depending on your system, 487 instantiating a Path will return either a PosixPath or a WindowsPath 488 object. You can also instantiate a PosixPath or WindowsPath directly, 489 but cannot instantiate a WindowsPath on a POSIX system or vice versa. 490 """ 491 __slots__ = () 492 as_uri = PurePath.as_uri 493 494 @classmethod 495 def _unsupported_msg(cls, attribute): 496 return f"{cls.__name__}.{attribute} is unsupported on this system" 497 498 def __init__(self, *args, **kwargs): 499 if kwargs: 500 msg = ("support for supplying keyword arguments to pathlib.PurePath " 501 "is deprecated and scheduled for removal in Python {remove}") 502 warnings._deprecated("pathlib.PurePath(**kwargs)", msg, remove=(3, 14)) 503 super().__init__(*args) 504 505 def __new__(cls, *args, **kwargs): 506 if cls is Path: 507 cls = WindowsPath if os.name == 'nt' else PosixPath 508 return object.__new__(cls) 509 510 def stat(self, *, follow_symlinks=True): 511 """ 512 Return the result of the stat() system call on this path, like 513 os.stat() does. 514 """ 515 return os.stat(self, follow_symlinks=follow_symlinks) 516 517 def is_mount(self): 518 """ 519 Check if this path is a mount point 520 """ 521 return os.path.ismount(self) 522 523 def is_junction(self): 524 """ 525 Whether this path is a junction. 526 """ 527 return os.path.isjunction(self) 528 529 def open(self, mode='r', buffering=-1, encoding=None, 530 errors=None, newline=None): 531 """ 532 Open the file pointed to by this path and return a file object, as 533 the built-in open() function does. 534 """ 535 if "b" not in mode: 536 encoding = io.text_encoding(encoding) 537 return io.open(self, mode, buffering, encoding, errors, newline) 538 539 def read_text(self, encoding=None, errors=None, newline=None): 540 """ 541 Open the file in text mode, read it, and close the file. 542 """ 543 # Call io.text_encoding() here to ensure any warning is raised at an 544 # appropriate stack level. 545 encoding = io.text_encoding(encoding) 546 return PathBase.read_text(self, encoding, errors, newline) 547 548 def write_text(self, data, encoding=None, errors=None, newline=None): 549 """ 550 Open the file in text mode, write to it, and close the file. 551 """ 552 # Call io.text_encoding() here to ensure any warning is raised at an 553 # appropriate stack level. 554 encoding = io.text_encoding(encoding) 555 return PathBase.write_text(self, data, encoding, errors, newline) 556 557 _remove_leading_dot = operator.itemgetter(slice(2, None)) 558 _remove_trailing_slash = operator.itemgetter(slice(-1)) 559 560 def _filter_trailing_slash(self, paths): 561 sep = self.parser.sep 562 anchor_len = len(self.anchor) 563 for path_str in paths: 564 if len(path_str) > anchor_len and path_str[-1] == sep: 565 path_str = path_str[:-1] 566 yield path_str 567 568 def iterdir(self): 569 """Yield path objects of the directory contents. 570 571 The children are yielded in arbitrary order, and the 572 special entries '.' and '..' are not included. 573 """ 574 root_dir = str(self) 575 with os.scandir(root_dir) as scandir_it: 576 paths = [entry.path for entry in scandir_it] 577 if root_dir == '.': 578 paths = map(self._remove_leading_dot, paths) 579 return map(self._from_parsed_string, paths) 580 581 def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=False): 582 """Iterate over this subtree and yield all existing files (of any 583 kind, including directories) matching the given relative pattern. 584 """ 585 sys.audit("pathlib.Path.glob", self, pattern) 586 if not isinstance(pattern, PurePath): 587 pattern = self.with_segments(pattern) 588 if pattern.anchor: 589 raise NotImplementedError("Non-relative patterns are unsupported") 590 parts = pattern._tail.copy() 591 if not parts: 592 raise ValueError("Unacceptable pattern: {!r}".format(pattern)) 593 raw = pattern._raw_path 594 if raw[-1] in (self.parser.sep, self.parser.altsep): 595 # GH-65238: pathlib doesn't preserve trailing slash. Add it back. 596 parts.append('') 597 select = self._glob_selector(parts[::-1], case_sensitive, recurse_symlinks) 598 root = str(self) 599 paths = select(root) 600 601 # Normalize results 602 if root == '.': 603 paths = map(self._remove_leading_dot, paths) 604 if parts[-1] == '': 605 paths = map(self._remove_trailing_slash, paths) 606 elif parts[-1] == '**': 607 paths = self._filter_trailing_slash(paths) 608 paths = map(self._from_parsed_string, paths) 609 return paths 610 611 def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=False): 612 """Recursively yield all existing files (of any kind, including 613 directories) matching the given relative pattern, anywhere in 614 this subtree. 615 """ 616 sys.audit("pathlib.Path.rglob", self, pattern) 617 if not isinstance(pattern, PurePath): 618 pattern = self.with_segments(pattern) 619 pattern = '**' / pattern 620 return self.glob(pattern, case_sensitive=case_sensitive, recurse_symlinks=recurse_symlinks) 621 622 def walk(self, top_down=True, on_error=None, follow_symlinks=False): 623 """Walk the directory tree from this directory, similar to os.walk().""" 624 sys.audit("pathlib.Path.walk", self, on_error, follow_symlinks) 625 root_dir = str(self) 626 if not follow_symlinks: 627 follow_symlinks = os._walk_symlinks_as_files 628 results = os.walk(root_dir, top_down, on_error, follow_symlinks) 629 for path_str, dirnames, filenames in results: 630 if root_dir == '.': 631 path_str = path_str[2:] 632 yield self._from_parsed_string(path_str), dirnames, filenames 633 634 def absolute(self): 635 """Return an absolute version of this path 636 No normalization or symlink resolution is performed. 637 638 Use resolve() to resolve symlinks and remove '..' segments. 639 """ 640 if self.is_absolute(): 641 return self 642 if self.root: 643 drive = os.path.splitroot(os.getcwd())[0] 644 return self._from_parsed_parts(drive, self.root, self._tail) 645 if self.drive: 646 # There is a CWD on each drive-letter drive. 647 cwd = os.path.abspath(self.drive) 648 else: 649 cwd = os.getcwd() 650 if not self._tail: 651 # Fast path for "empty" paths, e.g. Path("."), Path("") or Path(). 652 # We pass only one argument to with_segments() to avoid the cost 653 # of joining, and we exploit the fact that getcwd() returns a 654 # fully-normalized string by storing it in _str. This is used to 655 # implement Path.cwd(). 656 return self._from_parsed_string(cwd) 657 drive, root, rel = os.path.splitroot(cwd) 658 if not rel: 659 return self._from_parsed_parts(drive, root, self._tail) 660 tail = rel.split(self.parser.sep) 661 tail.extend(self._tail) 662 return self._from_parsed_parts(drive, root, tail) 663 664 def resolve(self, strict=False): 665 """ 666 Make the path absolute, resolving all symlinks on the way and also 667 normalizing it. 668 """ 669 670 return self.with_segments(os.path.realpath(self, strict=strict)) 671 672 if pwd: 673 def owner(self, *, follow_symlinks=True): 674 """ 675 Return the login name of the file owner. 676 """ 677 uid = self.stat(follow_symlinks=follow_symlinks).st_uid 678 return pwd.getpwuid(uid).pw_name 679 680 if grp: 681 def group(self, *, follow_symlinks=True): 682 """ 683 Return the group name of the file gid. 684 """ 685 gid = self.stat(follow_symlinks=follow_symlinks).st_gid 686 return grp.getgrgid(gid).gr_name 687 688 if hasattr(os, "readlink"): 689 def readlink(self): 690 """ 691 Return the path to which the symbolic link points. 692 """ 693 return self.with_segments(os.readlink(self)) 694 695 def touch(self, mode=0o666, exist_ok=True): 696 """ 697 Create this file with the given access mode, if it doesn't exist. 698 """ 699 700 if exist_ok: 701 # First try to bump modification time 702 # Implementation note: GNU touch uses the UTIME_NOW option of 703 # the utimensat() / futimens() functions. 704 try: 705 os.utime(self, None) 706 except OSError: 707 # Avoid exception chaining 708 pass 709 else: 710 return 711 flags = os.O_CREAT | os.O_WRONLY 712 if not exist_ok: 713 flags |= os.O_EXCL 714 fd = os.open(self, flags, mode) 715 os.close(fd) 716 717 def mkdir(self, mode=0o777, parents=False, exist_ok=False): 718 """ 719 Create a new directory at this given path. 720 """ 721 try: 722 os.mkdir(self, mode) 723 except FileNotFoundError: 724 if not parents or self.parent == self: 725 raise 726 self.parent.mkdir(parents=True, exist_ok=True) 727 self.mkdir(mode, parents=False, exist_ok=exist_ok) 728 except OSError: 729 # Cannot rely on checking for EEXIST, since the operating system 730 # could give priority to other errors like EACCES or EROFS 731 if not exist_ok or not self.is_dir(): 732 raise 733 734 def chmod(self, mode, *, follow_symlinks=True): 735 """ 736 Change the permissions of the path, like os.chmod(). 737 """ 738 os.chmod(self, mode, follow_symlinks=follow_symlinks) 739 740 def unlink(self, missing_ok=False): 741 """ 742 Remove this file or link. 743 If the path is a directory, use rmdir() instead. 744 """ 745 try: 746 os.unlink(self) 747 except FileNotFoundError: 748 if not missing_ok: 749 raise 750 751 def rmdir(self): 752 """ 753 Remove this directory. The directory must be empty. 754 """ 755 os.rmdir(self) 756 757 def rename(self, target): 758 """ 759 Rename this path to the target path. 760 761 The target path may be absolute or relative. Relative paths are 762 interpreted relative to the current working directory, *not* the 763 directory of the Path object. 764 765 Returns the new Path instance pointing to the target path. 766 """ 767 os.rename(self, target) 768 return self.with_segments(target) 769 770 def replace(self, target): 771 """ 772 Rename this path to the target path, overwriting if that path exists. 773 774 The target path may be absolute or relative. Relative paths are 775 interpreted relative to the current working directory, *not* the 776 directory of the Path object. 777 778 Returns the new Path instance pointing to the target path. 779 """ 780 os.replace(self, target) 781 return self.with_segments(target) 782 783 if hasattr(os, "symlink"): 784 def symlink_to(self, target, target_is_directory=False): 785 """ 786 Make this path a symlink pointing to the target path. 787 Note the order of arguments (link, target) is the reverse of os.symlink. 788 """ 789 os.symlink(target, self, target_is_directory) 790 791 if hasattr(os, "link"): 792 def hardlink_to(self, target): 793 """ 794 Make this path a hard link pointing to the same file as *target*. 795 796 Note the order of arguments (self, target) is the reverse of os.link's. 797 """ 798 os.link(target, self) 799 800 def expanduser(self): 801 """ Return a new path with expanded ~ and ~user constructs 802 (as returned by os.path.expanduser) 803 """ 804 if (not (self.drive or self.root) and 805 self._tail and self._tail[0][:1] == '~'): 806 homedir = os.path.expanduser(self._tail[0]) 807 if homedir[:1] == "~": 808 raise RuntimeError("Could not determine home directory.") 809 drv, root, tail = self._parse_path(homedir) 810 return self._from_parsed_parts(drv, root, tail + self._tail[1:]) 811 812 return self 813 814 @classmethod 815 def from_uri(cls, uri): 816 """Return a new path from the given 'file' URI.""" 817 if not uri.startswith('file:'): 818 raise ValueError(f"URI does not start with 'file:': {uri!r}") 819 path = uri[5:] 820 if path[:3] == '///': 821 # Remove empty authority 822 path = path[2:] 823 elif path[:12] == '//localhost/': 824 # Remove 'localhost' authority 825 path = path[11:] 826 if path[:3] == '///' or (path[:1] == '/' and path[2:3] in ':|'): 827 # Remove slash before DOS device/UNC path 828 path = path[1:] 829 if path[1:2] == '|': 830 # Replace bar with colon in DOS drive 831 path = path[:1] + ':' + path[2:] 832 from urllib.parse import unquote_to_bytes 833 path = cls(os.fsdecode(unquote_to_bytes(path))) 834 if not path.is_absolute(): 835 raise ValueError(f"URI is not absolute: {uri!r}") 836 return path 837 838 839class PosixPath(Path, PurePosixPath): 840 """Path subclass for non-Windows systems. 841 842 On a POSIX system, instantiating a Path should return this object. 843 """ 844 __slots__ = () 845 846 if os.name == 'nt': 847 def __new__(cls, *args, **kwargs): 848 raise UnsupportedOperation( 849 f"cannot instantiate {cls.__name__!r} on your system") 850 851class WindowsPath(Path, PureWindowsPath): 852 """Path subclass for Windows systems. 853 854 On a Windows system, instantiating a Path should return this object. 855 """ 856 __slots__ = () 857 858 if os.name != 'nt': 859 def __new__(cls, *args, **kwargs): 860 raise UnsupportedOperation( 861 f"cannot instantiate {cls.__name__!r} on your system") 862