gpath
86class GPath(Hashable, Sized, Iterable, render.Renderable): 87 """ 88 An immutable generalised abstract file path that has no dependency on any real filesystem. 89 90 The path can be manipulated on a system that is different from where it originated, notably including systems with a different operating system, and it can represent file paths on a system other than local. Examples where this is useful include remote management of servers and when cross-compiling source code for a different platform. 91 92 Since GPath objects are immutable, all operations return a new instance. The path is always stored in a normalised state, and is always treated as case sensitive. 93 94 The path can be rendered as a string using <code>str(<var>g</var>)</code>, which will use `/` as the path separator if possible to maximise cross-platform compatibility. 95 """ 96 97 __slots__ = ( 98 '_parts', 99 '_root', 100 '_drive', 101 '_parent_level', 102 '_platform', 103 '_encoding', 104 ) 105 106 107 def __init__(self, 108 path: Union[str, bytes, os.PathLike, GPath, None]="", 109 platform: Optional[Union[str, Platform]]=None, 110 encoding: Optional[str]=None, 111 ): 112 """ 113 Initialise a normalised and generalised abstract file path, possibly by copying an existing GPath object. 114 115 Parameters 116 ---------- 117 `path` 118 : path-like object representing a (possibly unnormalised) file path, or a GPath object to be copied 119 120 `platform` 121 : interpret `path` as originating from a specific platform. This is usually not required for normal file paths on Windows, Linux or macOS, and is needed only for edge cases (see [compatibility](https://github.com/yushiyangk/gpath#compatibility) in the readme). If `path` is a GPath, this argument has no effect. The platform name should be one of the keys in `gpath.platform.platform_names`. If specified, the platform will propagate to new GPaths returned by operations on this GPath; for binary operations of two GPaths, the platform specified by the left operand will be propagated. See also the `from_*()` static methods. 122 123 `encoding` 124 : the text encoding that should be used to decode paths given as bytes-like objects; if not specified, `'utf_8'` will be used by default. The encoding name should be one of the standard Python text encodings, as listed in the `codecs` module of the standard library. If specified, the encoding will propagate to new GPaths returned by operations on this GPath; for binary operations of two GPaths, the encoding specified by the left operand will be propagated. 125 126 Raises 127 ------ 128 `ValueError` if `other` is an invalid GPath 129 130 Examples 131 -------- 132 ```python 133 GPath("/") 134 GPath("/usr/bin") 135 GPath("C:/Program Files") 136 ``` 137 """ 138 139 self._parts: tuple[str, ...] = tuple() # root- or parent- relative path 140 self._root: bool = False 141 self._drive: str = "" 142 self._parent_level: int = 0 143 144 self._platform: Optional[Platform] = Platform.from_str(platform) if isinstance(platform, str) else platform 145 self._encoding: Optional[str] = encoding 146 147 if isinstance(path, GPath): 148 path._validate() 149 self._parts = path._parts 150 self._root = path._root 151 self._drive = path._drive 152 self._parent_level = path._parent_level 153 154 self._platform = path._platform if self._platform is None else self._platform 155 self._encoding = path._encoding if self._encoding is None else self._encoding 156 return 157 158 if path is None or path == "": 159 return 160 161 path = os.fspath(path) 162 163 if isinstance(path, bytes): 164 if self._encoding is None: 165 path = path.decode(DEFAULT_ENCODING) 166 else: 167 path = path.decode(self._encoding) 168 169 # path is a str 170 171 if self._platform is None: 172 platform = Platform.GENERIC 173 else: 174 platform = self._platform 175 176 if platform == Platform.POSIX: 177 for root in _rules.posix_rules.roots: 178 if path.startswith(root): 179 self._root = True 180 break 181 182 if self._root: 183 rootless_path = path[1:] 184 else: 185 rootless_path = path 186 187 parts = _split_relative(rootless_path, delimiters=_rules.posix_rules.separators) 188 189 elif platform == Platform.WINDOWS: 190 if len(path) >= 2 and path[1] in _rules.windows_rules.drive_postfixes: 191 self._drive = path[0] 192 driveless_path = path[2:] 193 else: 194 driveless_path = path 195 196 for root in _rules.windows_rules.roots: 197 if driveless_path.startswith(root): 198 self._root = True 199 break 200 201 if self._root: 202 rootless_path = driveless_path[1:] 203 else: 204 rootless_path = driveless_path 205 206 parts = _split_relative(rootless_path, delimiters=_rules.windows_rules.separators) 207 208 else: 209 if len(path) >= 2 and path[1] in _rules.generic_rules.drive_postfixes: 210 self._drive = path[0] 211 driveless_path = path[2:] 212 else: 213 driveless_path = path 214 215 for root in _rules.generic_rules.roots: 216 if driveless_path.startswith(root): 217 self._root = True 218 break 219 220 if self._root: 221 rootless_path = driveless_path[1:] 222 else: 223 rootless_path = driveless_path 224 225 parts = _split_relative(rootless_path, delimiters=_rules.generic_rules.separators) 226 227 228 parts = _normalise_relative(parts) 229 parent_level = 0 230 while parent_level < len(parts) and parts[parent_level] in _rules.generic_rules.parent_indicators: 231 parent_level += 1 232 self._parts = tuple(parts[parent_level:]) 233 if self._root == False: 234 self._parent_level = parent_level 235 236 237 @property 238 def named_parts(self) -> list[str]: 239 """ 240 Read-only named components of the path, not including the filesystem root, drive name, or any parent directories 241 242 Examples 243 -------- 244 ```python 245 GPath("usr/local/bin").named_parts # ["usr", "local", "bin"] 246 GPath("../../Documents").named_parts # ["Documents"] 247 GPath("/usr/bin").named_parts # ["usr", "bin"] 248 GPath("C:/Program Files").named_parts # ["Program Files"] 249 ``` 250 """ 251 return list(self._parts) 252 253 @property 254 def relative_parts(self) -> list[str]: 255 """ 256 Read-only relative components of the path, not including the filesystem root or drive name, including one item for each level of parent directory 257 258 Examples 259 -------- 260 ```python 261 GPath("usr/local/bin").relative_parts # ["usr", "local", "bin"] 262 GPath("../../Documents").relative_parts # ["..", "..", "Documents"] 263 GPath("/usr/bin").relative_parts # ["usr", "bin"] 264 GPath("C:/Program Files").relative_parts # ["Program Files"] 265 ``` 266 """ 267 return self.parent_parts + list(self._parts) 268 269 @property 270 def absolute(self) -> bool: 271 """ 272 Read-only flag for whether the path is an absolute path 273 274 Examples 275 -------- 276 ```python 277 GPath("/").absolute # True 278 GPath("C:/Windows").absolute # True 279 GPath("local/bin").absolute # False 280 GPath("../../Documents").absolute # False 281 ``` 282 """ 283 return self._root 284 285 @property 286 def root(self) -> bool: 287 """ 288 Read-only flag for whether the path is exactly the root of the filesystem 289 290 Examples 291 -------- 292 ```python 293 GPath("/").root # True 294 GPath("C:/").root # True 295 GPath("/usr/bin").root # False 296 GPath("C:/Windows").root # False 297 GPath("../../Documents").root # False 298 ``` 299 """ 300 return self._root and len(self._parts) == 0 301 302 @property 303 def drive(self) -> str: 304 """ 305 Read-only drive name 306 307 Examples 308 -------- 309 ```python 310 GPath("C:/Windows").drive # "C:" 311 GPath("/usr/bin").drive # "" 312 GPath("../../Documents").drive # "" 313 ``` 314 """ 315 return self._drive 316 317 @property 318 def parent_level(self) -> int: 319 """ 320 Read-only number of levels of parent directories that the path is relative to, which may be 0 321 322 Examples 323 -------- 324 ```python 325 GPath("../../Documents").parent_level # 2 326 GPath("usr/local/bin").parent_level # 0 327 ``` 328 """ 329 return self._parent_level 330 331 @property 332 def parent_parts(self) -> list[str]: 333 """ 334 Read-only path components representing a parent directory that it is relative to, if any, with one item for each level of parent directory 335 336 Examples 337 -------- 338 ```python 339 GPath("../../Documents").parent_parts # ["..", ".."] 340 GPath("usr/local/bin").parent_parts # [] 341 ``` 342 """ 343 return [_rules.generic_rules.parent_indicators[0] for i in range(self._parent_level)] 344 345 @property 346 def encoding(self) -> Union[str, None]: 347 """ 348 Read-only encoding used to decode other paths that are given as bytes-like objects, or None if the default should be used 349 """ 350 return self._encoding 351 352 @property 353 def platform(self) -> Union[str, None]: 354 """ 355 Read-only platform that other non-GPath operands should be interepreted as, or None if the default should be used 356 """ 357 return str(self._platform) if self._platform is not None else None 358 359 360 @staticmethod 361 def from_posix(path: Union[str, bytes, os.PathLike, GPath, None]="", encoding: Optional[str]=None) -> GPath: 362 """ 363 Initialise a GPath that originates from a POSIX-like operating system, or copy a GPath such that any future non-GPath operands would be interpreted as originating from a POSIX-like operating system. 364 365 See `__init__()` for details. 366 367 Equivalent to `GPath(path, platform='posix')` 368 ``` 369 """ 370 return GPath(path, platform=Platform.POSIX, encoding=encoding) 371 372 @staticmethod 373 def from_linux(path: Union[str, bytes, os.PathLike, GPath, None]="", encoding: Optional[str]=None) -> GPath: 374 """ 375 Alias of `from_posix()` 376 """ 377 return GPath.from_posix(path, encoding=encoding) 378 379 @staticmethod 380 def from_macos(path: Union[str, bytes, os.PathLike, GPath, None]="", encoding: Optional[str]=None) -> GPath: 381 """ 382 Alias of `from_posix()` 383 """ 384 return GPath.from_posix(path, encoding=encoding) 385 386 387 @staticmethod 388 def from_windows(path: Union[str, bytes, os.PathLike, GPath, None]="", encoding: Optional[str]=None) -> GPath: 389 """ 390 Initialise a GPath that originates from a Windows operating system, or copy a GPath such that any future non-GPath operands would be interpreted as originating from a Windows operating system. 391 392 See `__init__()` for details. 393 394 Equivalent to `GPath(path, platform='windows')` 395 ``` 396 """ 397 return GPath(path, platform=Platform.WINDOWS, encoding=encoding) 398 399 400 #@overload 401 #@staticmethod 402 #def partition(paths: Iterable[GPathLike], /, *, allow_current, allow_parents, platform, encoding) -> dict[GPath, list[GPath]]: 403 # ... 404 #@overload 405 #@staticmethod 406 #def partition(*paths: GPathLike, allow_current, allow_parents, platform, encoding) -> dict[GPath, list[GPath]]: 407 # ... 408 @staticmethod 409 def partition( 410 *paths, 411 allow_current: bool=True, 412 allow_parents: bool=True, 413 platform: Optional[Union[str, Platform]]=None, 414 encoding: Optional[str]=None, 415 ) -> dict[GPath, list[GPath]]: 416 """ 417 Partition a collection of paths based on shared common base paths such that each path belongs to one partition. 418 419 For each partition, return a list of relative paths from the base path of that partition to each corresponding input path within that partition, unless `allow_parents` is True (see below). If the input collection is ordered, the output order is preserved within each partition. If the input collection contains duplicates, the corresponding output lists will as well. 420 421 The number of partitions is minimised by merging partitions as much as possible, so that each partition represents the highest possible level base path. Two partitions can no longer be merged when there is no common base path between them, as determined by `common_with()`. This method takes the same optional arguments as `common_with()`, with the same default values. 422 423 Parameters 424 ---------- 425 `paths: Iterable[GPath | str | bytes | os.PathLike]` or `*paths: GPath | str | bytes | os.PathLike` 426 : the paths to be partitioned, which can be given as either a list-like object or as variadic arguments 427 428 `allow_current` 429 : whether non-parent relative paths with no shared components should be considered to have a common base path (see `common_with()`) 430 431 `allow_parents` 432 : whether paths that are relative to different levels of parent directories should be considered to have a common base path (see `common_with()`). **Warning**: when set to True, the output lists for each partition are invalidated, and explicitly set to empty. This is because it is not possible in general to obtain a relative path from the base path to its members if the base path is a parent directory of a higher level than the member (see `relpath_from()`). This option should be True if and only if the list of members in each partition are not of interest; in most cases False is more appropriate. 433 434 `platform` 435 : the originating platform that should be assumed when interpreting non-GPath objects in `paths`, if any (see `__init__()`). 436 437 `encoding` 438 : the text encoding that should be used to decode bytes-like objects in `paths`, if any (see `__init__()`). 439 440 Returns 441 ------- 442 a dictionary that maps the common base path of each partition to a list of relative paths 443 444 Raises 445 ------ 446 `ValueError` 447 if any of the GPaths are invalid 448 449 Examples 450 -------- 451 ```python 452 GPath.partition("/usr/bin", "/usr/local/bin", "../../doc", "C:/Windows", "C:/Program Files") 453 454 assert partitions == { 455 GPath("/usr") : [GPath("bin"), GPath("local")], 456 GPath("../../doc") : [GPath("")], 457 GPath("C:/") : [GPath("Windows"), GPath("Program Files")], 458 } 459 ``` 460 """ 461 flattened_paths: list[GPathLike] = [] 462 for path_or_list in paths: 463 if _is_gpathlike(path_or_list): 464 flattened_paths.append(path_or_list) 465 else: 466 flattened_paths.extend(path_or_list) 467 gpaths = [path if isinstance(path, GPath) else GPath(path, encoding=encoding, platform=platform) for path in flattened_paths] 468 469 partition_map = {} 470 if len(gpaths) > 0: 471 if allow_parents == True: 472 partition_map[gpaths[0]] = [] 473 else: 474 partition_map[gpaths[0]] = [gpaths[0]] 475 476 for path in gpaths[1:]: 477 partition_found = False 478 for partition in partition_map: 479 candidate_common = partition.common_with(path, allow_current=allow_current, allow_parents=allow_parents) 480 if candidate_common is not None: 481 partition_found = True 482 if candidate_common != partition: 483 partition_map[candidate_common] = partition_map[partition] 484 del partition_map[partition] 485 if allow_parents == False: 486 partition_map[candidate_common].append(path) 487 break 488 if not partition_found: 489 if allow_parents == True: 490 partition_map[path] = [] 491 else: 492 partition_map[path] = [path] 493 494 for partition, path_list in partition_map.items(): 495 partition_map[partition] = [path.subpath_from(partition) for path in path_list] 496 497 return partition_map 498 499 500 #@overload 501 #@staticmethod 502 #def join(paths: Iterable[GPathLike], /, *, platform, encoding) -> GPath: 503 # ... 504 #@overload 505 #@staticmethod 506 #def join(*paths: GPathLike, platform, encoding) -> GPath: 507 # ... 508 @staticmethod 509 def join(*paths, platform: Optional[Union[str, Platform]]=None, encoding: Optional[str]=None) -> GPath: 510 """ 511 Join a sequence of paths into a single path. Apart from the first item in the sequence, all subsequent paths should be relative paths and any absolute paths will be ignored. 512 513 Parameters 514 ---------- 515 `paths`: `Sequence[GPath | str | bytes | os.PathLike]` or `*paths: GPath | str | bytes | os.PathLike` 516 : the paths to be combined, which can be given as either a list-like object or as variadic arguments 517 518 `platform` 519 : the originating platform that should be assumed when interpreting non-GPath objects in `paths`, if any (see `__init__()`). 520 521 `encoding` 522 : the text encoding that should be used to decode bytes-like objects in `paths`, if any (see `__init__()`). 523 524 Returns 525 ------- 526 the combined path 527 528 Raises 529 ------ 530 `ValueError` if any of the GPaths are invalid 531 532 Examples 533 -------- 534 ```python 535 GPath.join("usr", "local", "bin") # GPath("usr/local/bin") 536 GPath.join("/usr/local/bin", "../../bin") # GPath("/usr/bin") 537 GPath.join("C:/", "Windows") # GPath("C:/Windows") 538 ``` 539 """ 540 flattened_paths: list[GPathLike] = [] 541 for path_or_list in paths: 542 if _is_gpathlike(path_or_list): 543 flattened_paths.append(path_or_list) 544 else: 545 flattened_paths.extend(path_or_list) 546 547 if len(flattened_paths) == 0: 548 return GPath(encoding=encoding, platform=platform) 549 550 combined_path = flattened_paths[0] 551 if not isinstance(combined_path, GPath): 552 combined_path = GPath(combined_path, encoding=encoding, platform=platform) 553 for path in flattened_paths[1:]: 554 combined_path = combined_path + path 555 556 return combined_path 557 558 559 def as_relative(self, parent_level: Optional[int]=None) -> GPath: 560 """ 561 Convert the path to a relative path and return a new copy. 562 563 Parameters 564 ---------- 565 `parent_level` 566 : the number of levels of parent directories that the returned path should be relative to, which may be 0. If set to None, the returned path will have the same parent level as the current path if it is currently a relative path, or have no parent level (i.e. 0) otherwise. 567 568 Raises 569 ------ 570 `TypeError` if `parent_level` is not a valid type 571 572 Examples 573 -------- 574 ```python 575 GPath("/usr/bin").as_relative() # GPath("usr/bin") 576 GPath("C:/Windows").as_relative() # GPath("C:Windows") 577 GPath("../Documents").as_relative() # GPath("../Documents") 578 ``` 579 """ 580 581 new_path = GPath(self) 582 new_path._root = False 583 if parent_level is None: 584 pass 585 elif isinstance(parent_level, int): 586 new_path._parent_level = parent_level 587 else: 588 raise TypeError(f"parent_level must be an int: {parent_level} ({type(parent_level)})") 589 590 return new_path 591 592 593 def as_absolute(self) -> GPath: 594 """ 595 Convert the path to an absolute path and return a new copy. 596 597 Any parent directory that the path is relative to will be removed. If the path is already absolute, an identical copy is returned. 598 599 Examples 600 -------- 601 ```python 602 GPath("usr/bin").as_absolute() # GPath("/usr/bin") 603 GPath("../Documents").as_absolute() # GPath("/Documents") 604 GPath("C:Windows").as_absolute() # GPath("C:/Windows") 605 ``` 606 """ 607 new_path = GPath(self) 608 new_path._root = True 609 new_path._parent_level = 0 610 return new_path 611 612 613 def with_drive(self, drive: Union[str, bytes, None]=None) -> GPath: 614 """ 615 Return a new copy of the path with the drive set to `drive`. 616 617 If `drive` is `""` or None, this would be equivalent to `without_drive()`. 618 619 Parameters 620 ---------- 621 `drive` 622 : the drive for the returned path, or either `""` or None if the returned path should have no drive 623 624 Returns 625 ------- 626 `GPath` 627 : a new path with the given drive 628 629 Raises 630 ------ 631 - `TypeError` if `drive` is not a valid type 632 - `ValueError` if `drive` has more than one character 633 634 Examples 635 -------- 636 ```python 637 GPath("C:/Windows").with_drive() # GPath("/Windows") 638 GPath("C:/Windows").with_drive("D") # GPath("D:/Windows") 639 GPath("/Windows").with_drive("C") # GPath("C:/Windows") 640 ``` 641 """ 642 if drive is None: 643 drive = "" 644 elif isinstance(drive, bytes): 645 if self._encoding is None: 646 drive = drive.decode(DEFAULT_ENCODING) 647 else: 648 drive = drive.decode(self._encoding) 649 elif isinstance(drive, str): 650 pass 651 else: 652 raise TypeError(f"drive must be a str or bytes object: {drive} ({type(drive)})") 653 654 if len(drive) > 1: 655 raise ValueError(f"drive can only be a single character, an empty string or None: {drive}") 656 657 new_path = GPath(self) 658 new_path._drive = drive 659 return new_path 660 661 662 def without_drive(self) -> GPath: 663 """ 664 Return a new copy of the path without a drive. 665 666 Equivalent to `with_drive("")` or `with_drive(None)`. 667 668 Returns 669 ------- 670 `GPath` 671 : a new path without a drive 672 673 Examples 674 -------- 675 ```python 676 GPath("C:/Windows").without_drive() # GPath("/Windows") 677 ``` 678 """ 679 return self.with_drive(None) 680 681 682 def common_with(self, other: GPathLike, allow_current: bool=True, allow_parents: bool=False) -> Optional[GPath]: 683 """ 684 Find the longest common base path shared between `self` and `other`, or return None if no such path exists. 685 686 A common base path might not exist if one path is an absolute path while the other is a relative path, or if the two paths are in different filesystems (with different drive names), or in other cases as controlled by the `allow_current` and `allow_parents` options. 687 688 If using the default options of `allow_current=True` and `allow_parent=False`, the binary operator for bitwise-and can be used: `__and__()` (usage: <code><var>g1</var> & <var>g2</var></code>). 689 690 Parameters 691 ---------- 692 `other` 693 : the path to compare with 694 695 `allow_current` 696 : whether two non-parent relative paths that do not share any components should be considered to have a common base path, namely the imaginary current working directory. For instance, `GPath("some/rel/path").find_common("another/rel/path")` will return `GPath("")` if set to True, or return None if set to False. 697 698 `allow_parents` 699 : whether two relative paths that are relative to different levels of parent directories should be considered to have a common base path, which is the highest level of parent directory between the two paths. For instance, `GPath("../rel/to/parent").find_common("../../rel/to/grandparent")` will return `GPath("../..")` if set to True, or return None if set to False. **Warning**: when set to True, given a higher level of parent directory as output, it may not be possible to find the relative path to one of the inputs (see `relpath_from()`); in most cases False is more appropriate. 700 701 Returns 702 ------- 703 `GPath` 704 : the longest common base path, which may be empty, if it exists 705 706 `None` 707 : otherwise 708 709 Raises 710 ------ 711 `ValueError` if either `self` or `other` is an invalid GPath 712 713 Examples 714 -------- 715 ```python 716 GPath("/usr/bin").find_common("/usr/local/bin") # GPath("/usr") 717 GPath("C:/Windows/System32").find_common("C:/Program Files") # GPath("C:/") 718 GPath("../Documents").find_common("../Pictures") # GPath("..") 719 ``` 720 """ 721 self._validate() 722 if isinstance(other, GPath): 723 other._validate() 724 else: 725 other = GPath(other, encoding=self._encoding) 726 727 if self._drive != other._drive: 728 return None 729 if self._root != other._root: 730 return None 731 732 if allow_parents: 733 allow_current = True 734 735 parts = [] 736 if self._root: 737 common_path = GPath(self) 738 for part1, part2 in zip(self._parts, other._parts): 739 if part1 == part2: 740 parts.append(part1) 741 else: 742 if self._parent_level != other._parent_level: 743 if not allow_parents: 744 return None 745 746 common_path = GPath(self) 747 common_path._parent_level = max(self._parent_level, other._parent_level) 748 else: 749 common_path = GPath(self) 750 for part1, part2 in zip(self._parts, other._parts): 751 if part1 == part2: 752 parts.append(part1) 753 754 common_path._parts = tuple(parts) 755 756 if not allow_current and not bool(common_path): 757 if common_path != self or common_path != other: 758 return None 759 return common_path 760 761 762 def subpath_from(self, base: GPathLike) -> Optional[GPath]: 763 """ 764 Find the relative subpath from `base` to `self` if possible and if `base` contains `self`, or return None otherwise. 765 766 None will also be returned if there are unknown components in the subpath from `base` to `self`. For instance, if `self` is relative to the parent directory while `base` is relative to the grandparent directory, the path from the grandparent directory `../..` to the parent directory `..` cannot be known. 767 768 Similar to `relpath_from()`, but `self` must be a descendent of `base`. 769 770 Parameters 771 ---------- 772 `base` 773 : the base path that the relative subpath should start from 774 775 Returns 776 ------- 777 `GPath` 778 : relative subpath from `base` to `self`, which may be empty, if it exists 779 780 `None` 781 : otherwise 782 783 Raises 784 ------ 785 `ValueError` if either `self` or `base` is an invalid GPath 786 787 Examples 788 -------- 789 ```python 790 GPath("/usr/local/bin").subpath_from("/usr") # GPath("local/bin") 791 GPath("/usr/bin").subpath_from("/usr/local/bin") # None 792 GPath("/usr/bin").subpath_from("../Documents") # None 793 ``` 794 """ 795 if not isinstance(base, GPath): 796 base = GPath(base, encoding=self._encoding) 797 798 if self.common_with(base, allow_current=True, allow_parents=False) is not None and self in base: 799 # If self._parent_level > base._parent_level, self is not in base, whereas if self._parent_level < base._parent_level, path from base to self's parent cannot be known 800 base_length = len(base._parts) 801 new_path = GPath(self) 802 new_path._parts = self._parts[base_length:] # () when self == base 803 new_path._drive = "" 804 new_path._root = False 805 new_path._parent_level = 0 806 return new_path 807 else: 808 return None 809 810 811 def relpath_from(self, origin: GPathLike) -> Optional[GPath]: 812 """ 813 Find the relative path from `origin` to `self` if possible, or return None otherwise. 814 815 None will also be returned if there are unknown components in the relative path from `origin` to `self`. For instance, if `self` is relative to the parent directory while `base` base is relative to the grandparent directory, the path from the grandparent directory `../..` to the parent directory `..` cannot be known. 816 817 Similar to `subpath_from()`, but `self` does not need to be a descendent of `origin`. 818 819 Parameters 820 ---------- 821 `origin` 822 : the origin that the relative path should start from 823 824 Returns 825 ------- 826 `GPath` 827 : relative path from `origin` to `self`, which may be empty, if it exists 828 829 `None` 830 : otherwise 831 832 Raises 833 ------ 834 `ValueError` if either `self` or `origin` is an invalid GPath 835 836 Examples 837 -------- 838 ```python 839 GPath("/usr/local/bin").subpath_from("/usr") # GPath("local/bin") 840 GPath("/usr/bin").subpath_from("/usr/local/bin") # GPath("../../bin") 841 GPath("/usr/bin").subpath_from("../Documents") # None 842 ``` 843 """ 844 self._validate() 845 if not isinstance(origin, GPath): 846 origin = GPath(origin, encoding=self._encoding) 847 848 if origin._root: 849 common = self.common_with(origin) 850 if common is None: 851 return None 852 853 new_path = GPath(self) 854 new_path._parent_level = len(origin) - len(common) 855 new_path._parts = self._parts[len(common):] 856 new_path._drive = "" 857 new_path._root = False 858 return new_path 859 860 else: 861 common = self.common_with(origin, allow_current=True, allow_parents=True) 862 if common is None: 863 return None 864 if common._parent_level > self._parent_level: 865 return None # Path from common to self's parent cannot be known 866 867 # common._dotdot == self._dotdot 868 # origin._dotdot <= self._dotdot 869 870 new_path = GPath(self) 871 new_path._drive = "" 872 new_path._root = False 873 if len(common) == 0: 874 if origin._parent_level == self._parent_level: 875 new_path._parent_level = len(origin) 876 else: 877 new_path._parent_level = (common._parent_level - origin._parent_level) + len(origin) 878 new_path._parts = self._parts 879 else: 880 new_path._parent_level = len(origin) - len(common) 881 new_path._parts = self._parts[len(common):] 882 883 return new_path 884 885 886 def render(self, platform: Union[str, Platform, None]) -> render.RenderedPath: 887 """ 888 Convert the path to a RenderedPath for printing in a specific target operating system. 889 890 This will convert, and coerce if necessary, the generalised abstract GPath into a platform-specific path which can then be converted to a printable string using <code>str(<var>renderred_path</var>)</code>. The resulting string will be in the format preferred by the target platform. 891 892 If the GPath contains features that the target platform does not support (such as drive name when the target platform is POSIX), and if there are no analogous features in the target platform, they will be dropped in the rendered path. 893 894 The rendered path also implements total ordering with binary comparisons, e.g. <code><var>r1</var> < <var>r2</var></code>, making it useful for sorting and collation. This ordering is done with platform-specific semantics, unlike GPath which does not have meaningful order. 895 896 Parameters 897 ---------- 898 `platform` 899 : the target platform where the path is to be used 900 901 Returns 902 ------- 903 `RenderedPath` 904 : platform-specific path ready for sorting or output 905 906 Examples 907 -------- 908 ```python 909 # Print examples 910 print(GPath("/usr/bin").render('linux')) # /usr/bin 911 print(GPath("/usr/bin").render('windows')) # \\usr\\bin 912 print(GPath("C:/Windows").render('linux')) # /Windows 913 print(GPath("C:/Windows").render('windows')) # C:\\Windows 914 915 # Ordering examples 916 GPath("").render('linux') < GPath("abc").render('linux') # True 917 GPath("abc").render('linux') < GPath("..").render('linux') # True 918 GPath("..").render('linux') < GPath("../..").render('linux') # True 919 GPath("../..").render('linux') < GPath("/").render('linux') # True 920 GPath("/").render('linux') < GPath("C:/").render('linux') # False 921 GPath("/").render('linux') <= GPath("C:/").render('linux') # True 922 GPath("/").render('windows') < GPath("C:/").render('windows') # True 923 ``` 924 """ 925 if platform is None: 926 platform = DEFAULT_PLATFORM 927 elif isinstance(platform, str): 928 platform = Platform.from_str(platform) 929 return render.get_type(platform)(self) 930 931 932 def __hash__(self) -> int: 933 """ 934 Calculate hash of the GPath object. 935 936 Usage: <code>hash(<var>g</var>)</code> 937 """ 938 return hash(self._tuple) 939 940 941 def __eq__(self, other: GPathLike) -> bool: 942 """ 943 Check if two GPaths are completely identical. 944 945 Always return False if `other` is not a GPath object, even if it is a GPath-like object. 946 947 Usage: <code><var>g1</var> == <var>g2</var></code> 948 949 Examples 950 -------- 951 ```python 952 GPath("/usr/bin") == GPath("/usr/bin") # True 953 GPath("/usr/bin") == GPath("usr/bin") # False 954 GPath("C:/") == GPath("D:/") # False 955 ``` 956 """ 957 if not isinstance(other, GPath): 958 other = GPath(other, encoding=self._encoding) 959 return self._tuple == other._tuple 960 961 962 def __bool__(self) -> bool: 963 """ 964 False if `self` is a relative path without any relative components and without a drive, and True otherwise. 965 966 Usage: <code>bool(<var>g</var>)</code>, <code>not <var>g</var></code>, or <code>if <var>g</var>:</code> 967 968 Examples 969 -------- 970 ```python 971 bool(GPath("/")) # True 972 bool(GPath("..")) # True 973 bool(GPath("doc")) # True 974 bool(GPath("")) # False 975 ``` 976 """ 977 return self._root or self._drive != "" or self._parent_level != 0 or len(self._parts) > 0 978 979 980 def __str__(self) -> str: 981 """ 982 Return a platform-independent string representation of the path. 983 984 Usage: <code>str(<var>g</var>)</code> 985 """ 986 return str(self.render(Platform.GENERIC)) 987 988 989 def __repr__(self) -> str: 990 """ 991 Return a string that, when printed, gives the Python code associated with instantiating the GPath object. 992 993 Usage: <code>repr(<var>g</var>)</code> 994 """ 995 if self._platform is None: 996 platform_repr = "" 997 else: 998 platform_repr = f", platform={repr(self._platform)}" 999 1000 if self._encoding is None: 1001 encoding_repr = "" 1002 else: 1003 encoding_repr = f", encoding={repr(self._encoding)}" 1004 1005 if bool(self): 1006 return f"GPath({repr(str(self))}{platform_repr}{encoding_repr})" 1007 else: 1008 return f"GPath({repr('')}{platform_repr}{encoding_repr})" 1009 1010 1011 def __len__(self) -> int: 1012 """ 1013 Get the number of named path components, excluding any drive name or parent directories. 1014 1015 Usage: <code>len(<var>g</var>)</code> 1016 1017 Examples 1018 -------- 1019 ```python 1020 len(GPath("/usr/bin")) # 2 1021 len(GPath("/")) # 0 1022 len(GPath("C:/Windows")) # 0 1023 len(GPath("C:/")) # 0 1024 ``` 1025 """ 1026 return len(self._parts) 1027 1028 1029 def __getitem__(self, index: Union[int, slice]) -> Union[str, list[str]]: 1030 """ 1031 Get a 0-indexed named path component, or a slice of path components, excluding any drive name or parent directories. 1032 1033 Usage: <code><var>g</var>[<var>n</var>]</code>, <code><var>g</var>[<var>start</var>:<var>end</var>]</code>, <code><var>g</var>[<var>start</var>:<var>end</var>:<var>step</var>]</code>, etc. 1034 1035 Examples 1036 -------- 1037 ```python 1038 GPath("/usr/local/bin")[1] # "local" 1039 GPath("/usr/local/bin")[-1] # "bin" 1040 GPath("/usr/local/bin")[1:] # ["local", "bin"] 1041 GPath("/usr/local/bin")[::2] # ["usr", "bin"] 1042 ``` 1043 """ 1044 if isinstance(index, int): 1045 return self._parts[index] 1046 elif isinstance(index, slice): 1047 return list(self._parts[index]) 1048 1049 1050 def __iter__(self) -> Iterator[str]: 1051 """ 1052 Get an iterator through the named path components, excluding any drive name or parent directories. 1053 1054 Usage: <code>iter(<var>g</var>)</code> or <code>for <var>p</var> in <var>g</var>:</code> 1055 """ 1056 return iter(self._parts) 1057 1058 1059 def __contains__(self, other: GPathLike) -> bool: 1060 """ 1061 Check if the path represented by `self` contains the path represented by `other`; i.e. check if `self` is a parent directory of `other`. 1062 1063 Usage: <code><var>other</var> in <var>self</var></code> 1064 1065 Raises `ValueError` if either GPath is invalid 1066 1067 Examples 1068 -------- 1069 ```python 1070 GPath("/usr/local/bin") in GPath("/usr") # True 1071 GPath("/usr/local/bin") in GPath("/bin") # False 1072 GPath("..") in GPath("../..") # True 1073 GPath("..") in GPath("C:/") # False 1074 ``` 1075 """ 1076 if not isinstance(other, GPath): 1077 other = GPath(other, encoding=self._encoding) 1078 1079 common_path = self.common_with(other, allow_current=True, allow_parents=True) 1080 return common_path is not None and common_path == self 1081 1082 1083 def __add__(self, other: GPathLike) -> GPath: 1084 """ 1085 Add (concatenate) `other` to the end of `self`, and return a new copy. 1086 1087 If `other` is an absolute path, the returned path will be an absolute path that matches `other`, apart from the drive name. 1088 1089 If `other` has a drive, the returned path will have the same drive as `other`. Otherwise, the returned path will have the same drive as `self`. If neither has a drive, the returned path will not have a drive as well. 1090 1091 Alias: `__truediv__()` 1092 1093 Usage: <code><var>self</var> + <var>other</var></code> or <code><var>self</var> / <var>other</var></code> 1094 1095 Raises `ValueError` if either GPath is invalid 1096 1097 Examples 1098 -------- 1099 ```python 1100 GPath("/usr") + GPath("local/bin") # GPath("/usr/local/bin") 1101 GPath("C:/Windows/System32") + GPath("../SysWOW64") # GPath("C:/Windows/SysWOW64") 1102 GPath("C:/Windows/System32") + GPath("/usr/bin") # GPath("C:/usr/bin") 1103 GPath("..") + GPath("../..") # GPath("../../..") 1104 GPath("..") / GPath("../..") # GPath("../../..") 1105 ``` 1106 """ 1107 if isinstance(other, GPath): 1108 other._validate 1109 else: 1110 other = GPath(other, encoding=self._encoding) 1111 1112 new_path = GPath(self) 1113 if other._root: 1114 new_path._parts = other._parts 1115 new_path._root = other._root 1116 new_path._parent_level = other._parent_level 1117 else: 1118 new_parts = [part for part in self._parts] 1119 for i in range(other._parent_level): 1120 if len(new_parts) > 0: 1121 new_parts.pop() 1122 elif not new_path._root: 1123 new_path._parent_level += 1 1124 else: 1125 pass # parent of directory of root is still root 1126 1127 new_parts.extend(other._parts) 1128 new_path._parts = tuple(new_parts) 1129 1130 if other._drive != "": 1131 new_path._drive = other._drive 1132 1133 return new_path 1134 1135 1136 def __sub__(self, n: int) -> GPath: 1137 """ 1138 Remove `n` components from the end of the path and return a new copy. 1139 1140 Usage: <code><var>self</var> - <var>n</var></code> 1141 1142 Raises `ValueError` if `self` is an invalid GPath or if `n` is negative 1143 1144 Examples 1145 -------- 1146 ```python 1147 GPath("C:/Windows/System32") - 1 # GPath("C:/Windows") 1148 GPath("/usr/bin") - 2 # GPath("/") 1149 GPath("Documents") - 3 # GPath("..") 1150 GPath("/") - 1 # GPath("/") 1151 ``` 1152 """ 1153 if n < 0: 1154 raise ValueError("cannot subtract a negative number of components from the path: {n}; use __add__() instead") 1155 1156 new_path = GPath(self) 1157 new_parts = [part for part in self._parts] 1158 for i in range(n): 1159 if len(new_parts) > 0: 1160 new_parts.pop() 1161 elif not new_path._root: 1162 new_path._parent_level += 1 1163 else: 1164 pass # removing components from root should still give root 1165 new_path._parts = tuple(new_parts) 1166 return new_path 1167 1168 1169 def __mul__(self, n: int) -> GPath: 1170 """ 1171 Duplicate the named components of `self` `n` times and return a new path with the duplicated components. 1172 1173 Named components will be duplicated separately from the components representing a parent directory. If `self` is an absolute path, only the relative components will be duplicated. 1174 1175 If `n` is 0, the result is an empty path (either relative or absolute). 1176 1177 Usage: <code><var>self</var> * <var>n</var></code> 1178 1179 Raises `ValueError` if `self` is an invalid GPath or if `n` is negative. 1180 1181 Examples 1182 -------- 1183 ```python 1184 GPath("/usr/bin") * 2 # GPath("/usr/bin/usr/bin") 1185 GPath("../docs") * 2 # GPath("../../docs/docs") 1186 GPath("C:/Windows") * 0 # GPath("C:/") 1187 ``` 1188 """ 1189 if n < 0: 1190 raise ValueError("cannot multiply path by a negative integer: {n}") 1191 new_path = GPath(self) 1192 new_path._parent_level = self._parent_level * n 1193 new_path._parts = self._parts * n 1194 return new_path 1195 1196 1197 def __truediv__(self, other: GPathLike) -> GPath: 1198 """ 1199 Alias of `__add__()`. 1200 1201 Usage: <code><var>self</var> + <var>other</var></code> or <code><var>self</var> / <var>other</var></code> 1202 """ 1203 return self.__add__(other) 1204 1205 1206 def __and__(self, other: GPathLike) -> Union[GPath, None]: 1207 """ 1208 Equivalent to `self.common_with(other)`, using the default options of `common_with()`. 1209 1210 Usage: <code><var>g1</var> & <var>g2</var></code> 1211 """ 1212 return self.common_with(other) 1213 1214 1215 def __lshift__(self, n: int) -> GPath: 1216 """ 1217 Move the imaginary current working directory `n` steps up the filesystem tree. 1218 1219 If `self` is a relative path, remove up to `n` levels of parent directories from the start of the path and return a copy. If it is an absolute path, return a copy of `self` unchanged. 1220 1221 If `n` is negative, this is equivalent to `__rshift__(-n)`. 1222 1223 Usage: <code><var>self</var> << <var>n</var></code> 1224 1225 Raises `ValueError` if `self` is an invalid GPath. 1226 1227 Examples 1228 -------- 1229 ```python 1230 GPath("../SysWOW64/drivers") << 1 # GPath("SysWOW64/drivers") 1231 GPath("../doc") << 2 # GPath("doc") 1232 GPath("/usr/bin") << 2 # GPath("/usr/bin") 1233 ``` 1234 """ 1235 if n < 0: 1236 return self.__rshift__(-1 * n) 1237 new_path = GPath(self) 1238 if not new_path._root: 1239 new_path._parent_level = max(new_path._parent_level - n, 0) 1240 return new_path 1241 1242 1243 def __rshift__(self, n: int) -> GPath: 1244 """ 1245 Move the imaginary current working directory `n` steps down the filesystem tree. 1246 1247 If `self` is a relative path, add `n` levels of parent directories to the start of the path and return a copy. If it is an absolute path, return a copy of `self` unchanged. 1248 1249 If `n` is negative, this is equivalent to `__lshift__(-n)`. 1250 1251 Usage: <code><var>self</var> >> <var>n</var></code> 1252 1253 Raises `ValueError` if `self` is an invalid GPath 1254 1255 Examples 1256 -------- 1257 ```python 1258 GPath("../SysWOW64/drivers") >> 1 # GPath("../../SysWOW64/drivers") 1259 GPath("/usr/bin") >> 2 # GPath("/usr/bin") 1260 ``` 1261 """ 1262 if n < 0: 1263 return self.__lshift__(-1 * n) 1264 new_path = GPath(self) 1265 if not new_path._root: 1266 new_path._parent_level += n 1267 return new_path 1268 1269 1270 @property 1271 def _tuple(self) -> tuple: 1272 # Get a tuple of all fields 1273 return ( 1274 self._root, 1275 self._drive, 1276 self._parent_level, 1277 self._parts, 1278 self._platform, 1279 self._encoding, 1280 ) 1281 1282 1283 def _validate(self) -> bool: 1284 # Check if self is in a valid state 1285 if self._parent_level < 0: 1286 raise ValueError(f"invalid GPath, _parent cannot be negative: {repr(self)}") 1287 if self._root: 1288 if self._parent_level != 0: 1289 raise ValueError(f"invalid GPath, _parent must be 0 when root is True: {repr(self)}") 1290 return True
An immutable generalised abstract file path that has no dependency on any real filesystem.
The path can be manipulated on a system that is different from where it originated, notably including systems with a different operating system, and it can represent file paths on a system other than local. Examples where this is useful include remote management of servers and when cross-compiling source code for a different platform.
Since GPath objects are immutable, all operations return a new instance. The path is always stored in a normalised state, and is always treated as case sensitive.
The path can be rendered as a string using str(g)
, which will use /
as the path separator if possible to maximise cross-platform compatibility.
107 def __init__(self, 108 path: Union[str, bytes, os.PathLike, GPath, None]="", 109 platform: Optional[Union[str, Platform]]=None, 110 encoding: Optional[str]=None, 111 ): 112 """ 113 Initialise a normalised and generalised abstract file path, possibly by copying an existing GPath object. 114 115 Parameters 116 ---------- 117 `path` 118 : path-like object representing a (possibly unnormalised) file path, or a GPath object to be copied 119 120 `platform` 121 : interpret `path` as originating from a specific platform. This is usually not required for normal file paths on Windows, Linux or macOS, and is needed only for edge cases (see [compatibility](https://github.com/yushiyangk/gpath#compatibility) in the readme). If `path` is a GPath, this argument has no effect. The platform name should be one of the keys in `gpath.platform.platform_names`. If specified, the platform will propagate to new GPaths returned by operations on this GPath; for binary operations of two GPaths, the platform specified by the left operand will be propagated. See also the `from_*()` static methods. 122 123 `encoding` 124 : the text encoding that should be used to decode paths given as bytes-like objects; if not specified, `'utf_8'` will be used by default. The encoding name should be one of the standard Python text encodings, as listed in the `codecs` module of the standard library. If specified, the encoding will propagate to new GPaths returned by operations on this GPath; for binary operations of two GPaths, the encoding specified by the left operand will be propagated. 125 126 Raises 127 ------ 128 `ValueError` if `other` is an invalid GPath 129 130 Examples 131 -------- 132 ```python 133 GPath("/") 134 GPath("/usr/bin") 135 GPath("C:/Program Files") 136 ``` 137 """ 138 139 self._parts: tuple[str, ...] = tuple() # root- or parent- relative path 140 self._root: bool = False 141 self._drive: str = "" 142 self._parent_level: int = 0 143 144 self._platform: Optional[Platform] = Platform.from_str(platform) if isinstance(platform, str) else platform 145 self._encoding: Optional[str] = encoding 146 147 if isinstance(path, GPath): 148 path._validate() 149 self._parts = path._parts 150 self._root = path._root 151 self._drive = path._drive 152 self._parent_level = path._parent_level 153 154 self._platform = path._platform if self._platform is None else self._platform 155 self._encoding = path._encoding if self._encoding is None else self._encoding 156 return 157 158 if path is None or path == "": 159 return 160 161 path = os.fspath(path) 162 163 if isinstance(path, bytes): 164 if self._encoding is None: 165 path = path.decode(DEFAULT_ENCODING) 166 else: 167 path = path.decode(self._encoding) 168 169 # path is a str 170 171 if self._platform is None: 172 platform = Platform.GENERIC 173 else: 174 platform = self._platform 175 176 if platform == Platform.POSIX: 177 for root in _rules.posix_rules.roots: 178 if path.startswith(root): 179 self._root = True 180 break 181 182 if self._root: 183 rootless_path = path[1:] 184 else: 185 rootless_path = path 186 187 parts = _split_relative(rootless_path, delimiters=_rules.posix_rules.separators) 188 189 elif platform == Platform.WINDOWS: 190 if len(path) >= 2 and path[1] in _rules.windows_rules.drive_postfixes: 191 self._drive = path[0] 192 driveless_path = path[2:] 193 else: 194 driveless_path = path 195 196 for root in _rules.windows_rules.roots: 197 if driveless_path.startswith(root): 198 self._root = True 199 break 200 201 if self._root: 202 rootless_path = driveless_path[1:] 203 else: 204 rootless_path = driveless_path 205 206 parts = _split_relative(rootless_path, delimiters=_rules.windows_rules.separators) 207 208 else: 209 if len(path) >= 2 and path[1] in _rules.generic_rules.drive_postfixes: 210 self._drive = path[0] 211 driveless_path = path[2:] 212 else: 213 driveless_path = path 214 215 for root in _rules.generic_rules.roots: 216 if driveless_path.startswith(root): 217 self._root = True 218 break 219 220 if self._root: 221 rootless_path = driveless_path[1:] 222 else: 223 rootless_path = driveless_path 224 225 parts = _split_relative(rootless_path, delimiters=_rules.generic_rules.separators) 226 227 228 parts = _normalise_relative(parts) 229 parent_level = 0 230 while parent_level < len(parts) and parts[parent_level] in _rules.generic_rules.parent_indicators: 231 parent_level += 1 232 self._parts = tuple(parts[parent_level:]) 233 if self._root == False: 234 self._parent_level = parent_level
Initialise a normalised and generalised abstract file path, possibly by copying an existing GPath object.
Parameters
path
: path-like object representing a (possibly unnormalised) file path, or a GPath object to be copied
platform
: interpret path
as originating from a specific platform. This is usually not required for normal file paths on Windows, Linux or macOS, and is needed only for edge cases (see compatibility in the readme). If path
is a GPath, this argument has no effect. The platform name should be one of the keys in gpath.platform.platform_names
. If specified, the platform will propagate to new GPaths returned by operations on this GPath; for binary operations of two GPaths, the platform specified by the left operand will be propagated. See also the from_*()
static methods.
encoding
: the text encoding that should be used to decode paths given as bytes-like objects; if not specified, 'utf_8'
will be used by default. The encoding name should be one of the standard Python text encodings, as listed in the codecs
module of the standard library. If specified, the encoding will propagate to new GPaths returned by operations on this GPath; for binary operations of two GPaths, the encoding specified by the left operand will be propagated.
Raises
ValueError
if other
is an invalid GPath
Examples
GPath("/")
GPath("/usr/bin")
GPath("C:/Program Files")
Read-only named components of the path, not including the filesystem root, drive name, or any parent directories
Examples
GPath("usr/local/bin").named_parts # ["usr", "local", "bin"]
GPath("../../Documents").named_parts # ["Documents"]
GPath("/usr/bin").named_parts # ["usr", "bin"]
GPath("C:/Program Files").named_parts # ["Program Files"]
Read-only relative components of the path, not including the filesystem root or drive name, including one item for each level of parent directory
Examples
GPath("usr/local/bin").relative_parts # ["usr", "local", "bin"]
GPath("../../Documents").relative_parts # ["..", "..", "Documents"]
GPath("/usr/bin").relative_parts # ["usr", "bin"]
GPath("C:/Program Files").relative_parts # ["Program Files"]
Read-only flag for whether the path is an absolute path
Examples
GPath("/").absolute # True
GPath("C:/Windows").absolute # True
GPath("local/bin").absolute # False
GPath("../../Documents").absolute # False
Read-only flag for whether the path is exactly the root of the filesystem
Examples
GPath("/").root # True
GPath("C:/").root # True
GPath("/usr/bin").root # False
GPath("C:/Windows").root # False
GPath("../../Documents").root # False
Read-only drive name
Examples
GPath("C:/Windows").drive # "C:"
GPath("/usr/bin").drive # ""
GPath("../../Documents").drive # ""
Read-only number of levels of parent directories that the path is relative to, which may be 0
Examples
GPath("../../Documents").parent_level # 2
GPath("usr/local/bin").parent_level # 0
Read-only path components representing a parent directory that it is relative to, if any, with one item for each level of parent directory
Examples
GPath("../../Documents").parent_parts # ["..", ".."]
GPath("usr/local/bin").parent_parts # []
Read-only encoding used to decode other paths that are given as bytes-like objects, or None if the default should be used
Read-only platform that other non-GPath operands should be interepreted as, or None if the default should be used
360 @staticmethod 361 def from_posix(path: Union[str, bytes, os.PathLike, GPath, None]="", encoding: Optional[str]=None) -> GPath: 362 """ 363 Initialise a GPath that originates from a POSIX-like operating system, or copy a GPath such that any future non-GPath operands would be interpreted as originating from a POSIX-like operating system. 364 365 See `__init__()` for details. 366 367 Equivalent to `GPath(path, platform='posix')` 368 ``` 369 """ 370 return GPath(path, platform=Platform.POSIX, encoding=encoding)
Initialise a GPath that originates from a POSIX-like operating system, or copy a GPath such that any future non-GPath operands would be interpreted as originating from a POSIX-like operating system.
See __init__()
for details.
Equivalent to GPath(path, platform='posix')
```
372 @staticmethod 373 def from_linux(path: Union[str, bytes, os.PathLike, GPath, None]="", encoding: Optional[str]=None) -> GPath: 374 """ 375 Alias of `from_posix()` 376 """ 377 return GPath.from_posix(path, encoding=encoding)
Alias of from_posix()
379 @staticmethod 380 def from_macos(path: Union[str, bytes, os.PathLike, GPath, None]="", encoding: Optional[str]=None) -> GPath: 381 """ 382 Alias of `from_posix()` 383 """ 384 return GPath.from_posix(path, encoding=encoding)
Alias of from_posix()
387 @staticmethod 388 def from_windows(path: Union[str, bytes, os.PathLike, GPath, None]="", encoding: Optional[str]=None) -> GPath: 389 """ 390 Initialise a GPath that originates from a Windows operating system, or copy a GPath such that any future non-GPath operands would be interpreted as originating from a Windows operating system. 391 392 See `__init__()` for details. 393 394 Equivalent to `GPath(path, platform='windows')` 395 ``` 396 """ 397 return GPath(path, platform=Platform.WINDOWS, encoding=encoding)
Initialise a GPath that originates from a Windows operating system, or copy a GPath such that any future non-GPath operands would be interpreted as originating from a Windows operating system.
See __init__()
for details.
Equivalent to GPath(path, platform='windows')
```
408 @staticmethod 409 def partition( 410 *paths, 411 allow_current: bool=True, 412 allow_parents: bool=True, 413 platform: Optional[Union[str, Platform]]=None, 414 encoding: Optional[str]=None, 415 ) -> dict[GPath, list[GPath]]: 416 """ 417 Partition a collection of paths based on shared common base paths such that each path belongs to one partition. 418 419 For each partition, return a list of relative paths from the base path of that partition to each corresponding input path within that partition, unless `allow_parents` is True (see below). If the input collection is ordered, the output order is preserved within each partition. If the input collection contains duplicates, the corresponding output lists will as well. 420 421 The number of partitions is minimised by merging partitions as much as possible, so that each partition represents the highest possible level base path. Two partitions can no longer be merged when there is no common base path between them, as determined by `common_with()`. This method takes the same optional arguments as `common_with()`, with the same default values. 422 423 Parameters 424 ---------- 425 `paths: Iterable[GPath | str | bytes | os.PathLike]` or `*paths: GPath | str | bytes | os.PathLike` 426 : the paths to be partitioned, which can be given as either a list-like object or as variadic arguments 427 428 `allow_current` 429 : whether non-parent relative paths with no shared components should be considered to have a common base path (see `common_with()`) 430 431 `allow_parents` 432 : whether paths that are relative to different levels of parent directories should be considered to have a common base path (see `common_with()`). **Warning**: when set to True, the output lists for each partition are invalidated, and explicitly set to empty. This is because it is not possible in general to obtain a relative path from the base path to its members if the base path is a parent directory of a higher level than the member (see `relpath_from()`). This option should be True if and only if the list of members in each partition are not of interest; in most cases False is more appropriate. 433 434 `platform` 435 : the originating platform that should be assumed when interpreting non-GPath objects in `paths`, if any (see `__init__()`). 436 437 `encoding` 438 : the text encoding that should be used to decode bytes-like objects in `paths`, if any (see `__init__()`). 439 440 Returns 441 ------- 442 a dictionary that maps the common base path of each partition to a list of relative paths 443 444 Raises 445 ------ 446 `ValueError` 447 if any of the GPaths are invalid 448 449 Examples 450 -------- 451 ```python 452 GPath.partition("/usr/bin", "/usr/local/bin", "../../doc", "C:/Windows", "C:/Program Files") 453 454 assert partitions == { 455 GPath("/usr") : [GPath("bin"), GPath("local")], 456 GPath("../../doc") : [GPath("")], 457 GPath("C:/") : [GPath("Windows"), GPath("Program Files")], 458 } 459 ``` 460 """ 461 flattened_paths: list[GPathLike] = [] 462 for path_or_list in paths: 463 if _is_gpathlike(path_or_list): 464 flattened_paths.append(path_or_list) 465 else: 466 flattened_paths.extend(path_or_list) 467 gpaths = [path if isinstance(path, GPath) else GPath(path, encoding=encoding, platform=platform) for path in flattened_paths] 468 469 partition_map = {} 470 if len(gpaths) > 0: 471 if allow_parents == True: 472 partition_map[gpaths[0]] = [] 473 else: 474 partition_map[gpaths[0]] = [gpaths[0]] 475 476 for path in gpaths[1:]: 477 partition_found = False 478 for partition in partition_map: 479 candidate_common = partition.common_with(path, allow_current=allow_current, allow_parents=allow_parents) 480 if candidate_common is not None: 481 partition_found = True 482 if candidate_common != partition: 483 partition_map[candidate_common] = partition_map[partition] 484 del partition_map[partition] 485 if allow_parents == False: 486 partition_map[candidate_common].append(path) 487 break 488 if not partition_found: 489 if allow_parents == True: 490 partition_map[path] = [] 491 else: 492 partition_map[path] = [path] 493 494 for partition, path_list in partition_map.items(): 495 partition_map[partition] = [path.subpath_from(partition) for path in path_list] 496 497 return partition_map
Partition a collection of paths based on shared common base paths such that each path belongs to one partition.
For each partition, return a list of relative paths from the base path of that partition to each corresponding input path within that partition, unless allow_parents
is True (see below). If the input collection is ordered, the output order is preserved within each partition. If the input collection contains duplicates, the corresponding output lists will as well.
The number of partitions is minimised by merging partitions as much as possible, so that each partition represents the highest possible level base path. Two partitions can no longer be merged when there is no common base path between them, as determined by common_with()
. This method takes the same optional arguments as common_with()
, with the same default values.
Parameters
paths: Iterable[GPath | str | bytes | os.PathLike]
or *paths: GPath | str | bytes | os.PathLike
: the paths to be partitioned, which can be given as either a list-like object or as variadic arguments
allow_current
: whether non-parent relative paths with no shared components should be considered to have a common base path (see common_with()
)
allow_parents
: whether paths that are relative to different levels of parent directories should be considered to have a common base path (see common_with()
). Warning: when set to True, the output lists for each partition are invalidated, and explicitly set to empty. This is because it is not possible in general to obtain a relative path from the base path to its members if the base path is a parent directory of a higher level than the member (see relpath_from()
). This option should be True if and only if the list of members in each partition are not of interest; in most cases False is more appropriate.
platform
: the originating platform that should be assumed when interpreting non-GPath objects in paths
, if any (see __init__()
).
encoding
: the text encoding that should be used to decode bytes-like objects in paths
, if any (see __init__()
).
Returns
a dictionary that maps the common base path of each partition to a list of relative paths
Raises
ValueError
if any of the GPaths are invalid
Examples
GPath.partition("/usr/bin", "/usr/local/bin", "../../doc", "C:/Windows", "C:/Program Files")
assert partitions == {
GPath("/usr") : [GPath("bin"), GPath("local")],
GPath("../../doc") : [GPath("")],
GPath("C:/") : [GPath("Windows"), GPath("Program Files")],
}
508 @staticmethod 509 def join(*paths, platform: Optional[Union[str, Platform]]=None, encoding: Optional[str]=None) -> GPath: 510 """ 511 Join a sequence of paths into a single path. Apart from the first item in the sequence, all subsequent paths should be relative paths and any absolute paths will be ignored. 512 513 Parameters 514 ---------- 515 `paths`: `Sequence[GPath | str | bytes | os.PathLike]` or `*paths: GPath | str | bytes | os.PathLike` 516 : the paths to be combined, which can be given as either a list-like object or as variadic arguments 517 518 `platform` 519 : the originating platform that should be assumed when interpreting non-GPath objects in `paths`, if any (see `__init__()`). 520 521 `encoding` 522 : the text encoding that should be used to decode bytes-like objects in `paths`, if any (see `__init__()`). 523 524 Returns 525 ------- 526 the combined path 527 528 Raises 529 ------ 530 `ValueError` if any of the GPaths are invalid 531 532 Examples 533 -------- 534 ```python 535 GPath.join("usr", "local", "bin") # GPath("usr/local/bin") 536 GPath.join("/usr/local/bin", "../../bin") # GPath("/usr/bin") 537 GPath.join("C:/", "Windows") # GPath("C:/Windows") 538 ``` 539 """ 540 flattened_paths: list[GPathLike] = [] 541 for path_or_list in paths: 542 if _is_gpathlike(path_or_list): 543 flattened_paths.append(path_or_list) 544 else: 545 flattened_paths.extend(path_or_list) 546 547 if len(flattened_paths) == 0: 548 return GPath(encoding=encoding, platform=platform) 549 550 combined_path = flattened_paths[0] 551 if not isinstance(combined_path, GPath): 552 combined_path = GPath(combined_path, encoding=encoding, platform=platform) 553 for path in flattened_paths[1:]: 554 combined_path = combined_path + path 555 556 return combined_path
Join a sequence of paths into a single path. Apart from the first item in the sequence, all subsequent paths should be relative paths and any absolute paths will be ignored.
Parameters
paths
: Sequence[GPath | str | bytes | os.PathLike]
or *paths: GPath | str | bytes | os.PathLike
: the paths to be combined, which can be given as either a list-like object or as variadic arguments
platform
: the originating platform that should be assumed when interpreting non-GPath objects in paths
, if any (see __init__()
).
encoding
: the text encoding that should be used to decode bytes-like objects in paths
, if any (see __init__()
).
Returns
the combined path
Raises
ValueError
if any of the GPaths are invalid
Examples
GPath.join("usr", "local", "bin") # GPath("usr/local/bin")
GPath.join("/usr/local/bin", "../../bin") # GPath("/usr/bin")
GPath.join("C:/", "Windows") # GPath("C:/Windows")
559 def as_relative(self, parent_level: Optional[int]=None) -> GPath: 560 """ 561 Convert the path to a relative path and return a new copy. 562 563 Parameters 564 ---------- 565 `parent_level` 566 : the number of levels of parent directories that the returned path should be relative to, which may be 0. If set to None, the returned path will have the same parent level as the current path if it is currently a relative path, or have no parent level (i.e. 0) otherwise. 567 568 Raises 569 ------ 570 `TypeError` if `parent_level` is not a valid type 571 572 Examples 573 -------- 574 ```python 575 GPath("/usr/bin").as_relative() # GPath("usr/bin") 576 GPath("C:/Windows").as_relative() # GPath("C:Windows") 577 GPath("../Documents").as_relative() # GPath("../Documents") 578 ``` 579 """ 580 581 new_path = GPath(self) 582 new_path._root = False 583 if parent_level is None: 584 pass 585 elif isinstance(parent_level, int): 586 new_path._parent_level = parent_level 587 else: 588 raise TypeError(f"parent_level must be an int: {parent_level} ({type(parent_level)})") 589 590 return new_path
Convert the path to a relative path and return a new copy.
Parameters
parent_level
: the number of levels of parent directories that the returned path should be relative to, which may be 0. If set to None, the returned path will have the same parent level as the current path if it is currently a relative path, or have no parent level (i.e. 0) otherwise.
Raises
TypeError
if parent_level
is not a valid type
Examples
GPath("/usr/bin").as_relative() # GPath("usr/bin")
GPath("C:/Windows").as_relative() # GPath("C:Windows")
GPath("../Documents").as_relative() # GPath("../Documents")
593 def as_absolute(self) -> GPath: 594 """ 595 Convert the path to an absolute path and return a new copy. 596 597 Any parent directory that the path is relative to will be removed. If the path is already absolute, an identical copy is returned. 598 599 Examples 600 -------- 601 ```python 602 GPath("usr/bin").as_absolute() # GPath("/usr/bin") 603 GPath("../Documents").as_absolute() # GPath("/Documents") 604 GPath("C:Windows").as_absolute() # GPath("C:/Windows") 605 ``` 606 """ 607 new_path = GPath(self) 608 new_path._root = True 609 new_path._parent_level = 0 610 return new_path
Convert the path to an absolute path and return a new copy.
Any parent directory that the path is relative to will be removed. If the path is already absolute, an identical copy is returned.
Examples
GPath("usr/bin").as_absolute() # GPath("/usr/bin")
GPath("../Documents").as_absolute() # GPath("/Documents")
GPath("C:Windows").as_absolute() # GPath("C:/Windows")
613 def with_drive(self, drive: Union[str, bytes, None]=None) -> GPath: 614 """ 615 Return a new copy of the path with the drive set to `drive`. 616 617 If `drive` is `""` or None, this would be equivalent to `without_drive()`. 618 619 Parameters 620 ---------- 621 `drive` 622 : the drive for the returned path, or either `""` or None if the returned path should have no drive 623 624 Returns 625 ------- 626 `GPath` 627 : a new path with the given drive 628 629 Raises 630 ------ 631 - `TypeError` if `drive` is not a valid type 632 - `ValueError` if `drive` has more than one character 633 634 Examples 635 -------- 636 ```python 637 GPath("C:/Windows").with_drive() # GPath("/Windows") 638 GPath("C:/Windows").with_drive("D") # GPath("D:/Windows") 639 GPath("/Windows").with_drive("C") # GPath("C:/Windows") 640 ``` 641 """ 642 if drive is None: 643 drive = "" 644 elif isinstance(drive, bytes): 645 if self._encoding is None: 646 drive = drive.decode(DEFAULT_ENCODING) 647 else: 648 drive = drive.decode(self._encoding) 649 elif isinstance(drive, str): 650 pass 651 else: 652 raise TypeError(f"drive must be a str or bytes object: {drive} ({type(drive)})") 653 654 if len(drive) > 1: 655 raise ValueError(f"drive can only be a single character, an empty string or None: {drive}") 656 657 new_path = GPath(self) 658 new_path._drive = drive 659 return new_path
Return a new copy of the path with the drive set to drive
.
If drive
is ""
or None, this would be equivalent to without_drive()
.
Parameters
drive
: the drive for the returned path, or either ""
or None if the returned path should have no drive
Returns
GPath
: a new path with the given drive
Raises
TypeError
ifdrive
is not a valid typeValueError
ifdrive
has more than one character
Examples
GPath("C:/Windows").with_drive() # GPath("/Windows")
GPath("C:/Windows").with_drive("D") # GPath("D:/Windows")
GPath("/Windows").with_drive("C") # GPath("C:/Windows")
662 def without_drive(self) -> GPath: 663 """ 664 Return a new copy of the path without a drive. 665 666 Equivalent to `with_drive("")` or `with_drive(None)`. 667 668 Returns 669 ------- 670 `GPath` 671 : a new path without a drive 672 673 Examples 674 -------- 675 ```python 676 GPath("C:/Windows").without_drive() # GPath("/Windows") 677 ``` 678 """ 679 return self.with_drive(None)
Return a new copy of the path without a drive.
Equivalent to with_drive("")
or with_drive(None)
.
Returns
GPath
: a new path without a drive
Examples
GPath("C:/Windows").without_drive() # GPath("/Windows")
682 def common_with(self, other: GPathLike, allow_current: bool=True, allow_parents: bool=False) -> Optional[GPath]: 683 """ 684 Find the longest common base path shared between `self` and `other`, or return None if no such path exists. 685 686 A common base path might not exist if one path is an absolute path while the other is a relative path, or if the two paths are in different filesystems (with different drive names), or in other cases as controlled by the `allow_current` and `allow_parents` options. 687 688 If using the default options of `allow_current=True` and `allow_parent=False`, the binary operator for bitwise-and can be used: `__and__()` (usage: <code><var>g1</var> & <var>g2</var></code>). 689 690 Parameters 691 ---------- 692 `other` 693 : the path to compare with 694 695 `allow_current` 696 : whether two non-parent relative paths that do not share any components should be considered to have a common base path, namely the imaginary current working directory. For instance, `GPath("some/rel/path").find_common("another/rel/path")` will return `GPath("")` if set to True, or return None if set to False. 697 698 `allow_parents` 699 : whether two relative paths that are relative to different levels of parent directories should be considered to have a common base path, which is the highest level of parent directory between the two paths. For instance, `GPath("../rel/to/parent").find_common("../../rel/to/grandparent")` will return `GPath("../..")` if set to True, or return None if set to False. **Warning**: when set to True, given a higher level of parent directory as output, it may not be possible to find the relative path to one of the inputs (see `relpath_from()`); in most cases False is more appropriate. 700 701 Returns 702 ------- 703 `GPath` 704 : the longest common base path, which may be empty, if it exists 705 706 `None` 707 : otherwise 708 709 Raises 710 ------ 711 `ValueError` if either `self` or `other` is an invalid GPath 712 713 Examples 714 -------- 715 ```python 716 GPath("/usr/bin").find_common("/usr/local/bin") # GPath("/usr") 717 GPath("C:/Windows/System32").find_common("C:/Program Files") # GPath("C:/") 718 GPath("../Documents").find_common("../Pictures") # GPath("..") 719 ``` 720 """ 721 self._validate() 722 if isinstance(other, GPath): 723 other._validate() 724 else: 725 other = GPath(other, encoding=self._encoding) 726 727 if self._drive != other._drive: 728 return None 729 if self._root != other._root: 730 return None 731 732 if allow_parents: 733 allow_current = True 734 735 parts = [] 736 if self._root: 737 common_path = GPath(self) 738 for part1, part2 in zip(self._parts, other._parts): 739 if part1 == part2: 740 parts.append(part1) 741 else: 742 if self._parent_level != other._parent_level: 743 if not allow_parents: 744 return None 745 746 common_path = GPath(self) 747 common_path._parent_level = max(self._parent_level, other._parent_level) 748 else: 749 common_path = GPath(self) 750 for part1, part2 in zip(self._parts, other._parts): 751 if part1 == part2: 752 parts.append(part1) 753 754 common_path._parts = tuple(parts) 755 756 if not allow_current and not bool(common_path): 757 if common_path != self or common_path != other: 758 return None 759 return common_path
Find the longest common base path shared between self
and other
, or return None if no such path exists.
A common base path might not exist if one path is an absolute path while the other is a relative path, or if the two paths are in different filesystems (with different drive names), or in other cases as controlled by the allow_current
and allow_parents
options.
If using the default options of allow_current=True
and allow_parent=False
, the binary operator for bitwise-and can be used: __and__()
(usage: g1 & g2
).
Parameters
other
: the path to compare with
allow_current
: whether two non-parent relative paths that do not share any components should be considered to have a common base path, namely the imaginary current working directory. For instance, GPath("some/rel/path").find_common("another/rel/path")
will return GPath("")
if set to True, or return None if set to False.
allow_parents
: whether two relative paths that are relative to different levels of parent directories should be considered to have a common base path, which is the highest level of parent directory between the two paths. For instance, GPath("../rel/to/parent").find_common("../../rel/to/grandparent")
will return GPath("../..")
if set to True, or return None if set to False. Warning: when set to True, given a higher level of parent directory as output, it may not be possible to find the relative path to one of the inputs (see relpath_from()
); in most cases False is more appropriate.
Returns
GPath
: the longest common base path, which may be empty, if it exists
None
: otherwise
Raises
ValueError
if either self
or other
is an invalid GPath
Examples
GPath("/usr/bin").find_common("/usr/local/bin") # GPath("/usr")
GPath("C:/Windows/System32").find_common("C:/Program Files") # GPath("C:/")
GPath("../Documents").find_common("../Pictures") # GPath("..")
762 def subpath_from(self, base: GPathLike) -> Optional[GPath]: 763 """ 764 Find the relative subpath from `base` to `self` if possible and if `base` contains `self`, or return None otherwise. 765 766 None will also be returned if there are unknown components in the subpath from `base` to `self`. For instance, if `self` is relative to the parent directory while `base` is relative to the grandparent directory, the path from the grandparent directory `../..` to the parent directory `..` cannot be known. 767 768 Similar to `relpath_from()`, but `self` must be a descendent of `base`. 769 770 Parameters 771 ---------- 772 `base` 773 : the base path that the relative subpath should start from 774 775 Returns 776 ------- 777 `GPath` 778 : relative subpath from `base` to `self`, which may be empty, if it exists 779 780 `None` 781 : otherwise 782 783 Raises 784 ------ 785 `ValueError` if either `self` or `base` is an invalid GPath 786 787 Examples 788 -------- 789 ```python 790 GPath("/usr/local/bin").subpath_from("/usr") # GPath("local/bin") 791 GPath("/usr/bin").subpath_from("/usr/local/bin") # None 792 GPath("/usr/bin").subpath_from("../Documents") # None 793 ``` 794 """ 795 if not isinstance(base, GPath): 796 base = GPath(base, encoding=self._encoding) 797 798 if self.common_with(base, allow_current=True, allow_parents=False) is not None and self in base: 799 # If self._parent_level > base._parent_level, self is not in base, whereas if self._parent_level < base._parent_level, path from base to self's parent cannot be known 800 base_length = len(base._parts) 801 new_path = GPath(self) 802 new_path._parts = self._parts[base_length:] # () when self == base 803 new_path._drive = "" 804 new_path._root = False 805 new_path._parent_level = 0 806 return new_path 807 else: 808 return None
Find the relative subpath from base
to self
if possible and if base
contains self
, or return None otherwise.
None will also be returned if there are unknown components in the subpath from base
to self
. For instance, if self
is relative to the parent directory while base
is relative to the grandparent directory, the path from the grandparent directory ../..
to the parent directory ..
cannot be known.
Similar to relpath_from()
, but self
must be a descendent of base
.
Parameters
base
: the base path that the relative subpath should start from
Returns
GPath
: relative subpath from base
to self
, which may be empty, if it exists
None
: otherwise
Raises
ValueError
if either self
or base
is an invalid GPath
Examples
GPath("/usr/local/bin").subpath_from("/usr") # GPath("local/bin")
GPath("/usr/bin").subpath_from("/usr/local/bin") # None
GPath("/usr/bin").subpath_from("../Documents") # None
811 def relpath_from(self, origin: GPathLike) -> Optional[GPath]: 812 """ 813 Find the relative path from `origin` to `self` if possible, or return None otherwise. 814 815 None will also be returned if there are unknown components in the relative path from `origin` to `self`. For instance, if `self` is relative to the parent directory while `base` base is relative to the grandparent directory, the path from the grandparent directory `../..` to the parent directory `..` cannot be known. 816 817 Similar to `subpath_from()`, but `self` does not need to be a descendent of `origin`. 818 819 Parameters 820 ---------- 821 `origin` 822 : the origin that the relative path should start from 823 824 Returns 825 ------- 826 `GPath` 827 : relative path from `origin` to `self`, which may be empty, if it exists 828 829 `None` 830 : otherwise 831 832 Raises 833 ------ 834 `ValueError` if either `self` or `origin` is an invalid GPath 835 836 Examples 837 -------- 838 ```python 839 GPath("/usr/local/bin").subpath_from("/usr") # GPath("local/bin") 840 GPath("/usr/bin").subpath_from("/usr/local/bin") # GPath("../../bin") 841 GPath("/usr/bin").subpath_from("../Documents") # None 842 ``` 843 """ 844 self._validate() 845 if not isinstance(origin, GPath): 846 origin = GPath(origin, encoding=self._encoding) 847 848 if origin._root: 849 common = self.common_with(origin) 850 if common is None: 851 return None 852 853 new_path = GPath(self) 854 new_path._parent_level = len(origin) - len(common) 855 new_path._parts = self._parts[len(common):] 856 new_path._drive = "" 857 new_path._root = False 858 return new_path 859 860 else: 861 common = self.common_with(origin, allow_current=True, allow_parents=True) 862 if common is None: 863 return None 864 if common._parent_level > self._parent_level: 865 return None # Path from common to self's parent cannot be known 866 867 # common._dotdot == self._dotdot 868 # origin._dotdot <= self._dotdot 869 870 new_path = GPath(self) 871 new_path._drive = "" 872 new_path._root = False 873 if len(common) == 0: 874 if origin._parent_level == self._parent_level: 875 new_path._parent_level = len(origin) 876 else: 877 new_path._parent_level = (common._parent_level - origin._parent_level) + len(origin) 878 new_path._parts = self._parts 879 else: 880 new_path._parent_level = len(origin) - len(common) 881 new_path._parts = self._parts[len(common):] 882 883 return new_path
Find the relative path from origin
to self
if possible, or return None otherwise.
None will also be returned if there are unknown components in the relative path from origin
to self
. For instance, if self
is relative to the parent directory while base
base is relative to the grandparent directory, the path from the grandparent directory ../..
to the parent directory ..
cannot be known.
Similar to subpath_from()
, but self
does not need to be a descendent of origin
.
Parameters
origin
: the origin that the relative path should start from
Returns
GPath
: relative path from origin
to self
, which may be empty, if it exists
None
: otherwise
Raises
ValueError
if either self
or origin
is an invalid GPath
Examples
GPath("/usr/local/bin").subpath_from("/usr") # GPath("local/bin")
GPath("/usr/bin").subpath_from("/usr/local/bin") # GPath("../../bin")
GPath("/usr/bin").subpath_from("../Documents") # None
886 def render(self, platform: Union[str, Platform, None]) -> render.RenderedPath: 887 """ 888 Convert the path to a RenderedPath for printing in a specific target operating system. 889 890 This will convert, and coerce if necessary, the generalised abstract GPath into a platform-specific path which can then be converted to a printable string using <code>str(<var>renderred_path</var>)</code>. The resulting string will be in the format preferred by the target platform. 891 892 If the GPath contains features that the target platform does not support (such as drive name when the target platform is POSIX), and if there are no analogous features in the target platform, they will be dropped in the rendered path. 893 894 The rendered path also implements total ordering with binary comparisons, e.g. <code><var>r1</var> < <var>r2</var></code>, making it useful for sorting and collation. This ordering is done with platform-specific semantics, unlike GPath which does not have meaningful order. 895 896 Parameters 897 ---------- 898 `platform` 899 : the target platform where the path is to be used 900 901 Returns 902 ------- 903 `RenderedPath` 904 : platform-specific path ready for sorting or output 905 906 Examples 907 -------- 908 ```python 909 # Print examples 910 print(GPath("/usr/bin").render('linux')) # /usr/bin 911 print(GPath("/usr/bin").render('windows')) # \\usr\\bin 912 print(GPath("C:/Windows").render('linux')) # /Windows 913 print(GPath("C:/Windows").render('windows')) # C:\\Windows 914 915 # Ordering examples 916 GPath("").render('linux') < GPath("abc").render('linux') # True 917 GPath("abc").render('linux') < GPath("..").render('linux') # True 918 GPath("..").render('linux') < GPath("../..").render('linux') # True 919 GPath("../..").render('linux') < GPath("/").render('linux') # True 920 GPath("/").render('linux') < GPath("C:/").render('linux') # False 921 GPath("/").render('linux') <= GPath("C:/").render('linux') # True 922 GPath("/").render('windows') < GPath("C:/").render('windows') # True 923 ``` 924 """ 925 if platform is None: 926 platform = DEFAULT_PLATFORM 927 elif isinstance(platform, str): 928 platform = Platform.from_str(platform) 929 return render.get_type(platform)(self)
Convert the path to a RenderedPath for printing in a specific target operating system.
This will convert, and coerce if necessary, the generalised abstract GPath into a platform-specific path which can then be converted to a printable string using str(renderred_path)
. The resulting string will be in the format preferred by the target platform.
If the GPath contains features that the target platform does not support (such as drive name when the target platform is POSIX), and if there are no analogous features in the target platform, they will be dropped in the rendered path.
The rendered path also implements total ordering with binary comparisons, e.g. r1 < r2
, making it useful for sorting and collation. This ordering is done with platform-specific semantics, unlike GPath which does not have meaningful order.
Parameters
platform
: the target platform where the path is to be used
Returns
RenderedPath
: platform-specific path ready for sorting or output
Examples
# Print examples
print(GPath("/usr/bin").render('linux')) # /usr/bin
print(GPath("/usr/bin").render('windows')) # \usr\bin
print(GPath("C:/Windows").render('linux')) # /Windows
print(GPath("C:/Windows").render('windows')) # C:\Windows
# Ordering examples
GPath("").render('linux') < GPath("abc").render('linux') # True
GPath("abc").render('linux') < GPath("..").render('linux') # True
GPath("..").render('linux') < GPath("../..").render('linux') # True
GPath("../..").render('linux') < GPath("/").render('linux') # True
GPath("/").render('linux') < GPath("C:/").render('linux') # False
GPath("/").render('linux') <= GPath("C:/").render('linux') # True
GPath("/").render('windows') < GPath("C:/").render('windows') # True
932 def __hash__(self) -> int: 933 """ 934 Calculate hash of the GPath object. 935 936 Usage: <code>hash(<var>g</var>)</code> 937 """ 938 return hash(self._tuple)
Calculate hash of the GPath object.
Usage: hash(g)
941 def __eq__(self, other: GPathLike) -> bool: 942 """ 943 Check if two GPaths are completely identical. 944 945 Always return False if `other` is not a GPath object, even if it is a GPath-like object. 946 947 Usage: <code><var>g1</var> == <var>g2</var></code> 948 949 Examples 950 -------- 951 ```python 952 GPath("/usr/bin") == GPath("/usr/bin") # True 953 GPath("/usr/bin") == GPath("usr/bin") # False 954 GPath("C:/") == GPath("D:/") # False 955 ``` 956 """ 957 if not isinstance(other, GPath): 958 other = GPath(other, encoding=self._encoding) 959 return self._tuple == other._tuple
Check if two GPaths are completely identical.
Always return False if other
is not a GPath object, even if it is a GPath-like object.
Usage: g1 == g2
Examples
GPath("/usr/bin") == GPath("/usr/bin") # True
GPath("/usr/bin") == GPath("usr/bin") # False
GPath("C:/") == GPath("D:/") # False
962 def __bool__(self) -> bool: 963 """ 964 False if `self` is a relative path without any relative components and without a drive, and True otherwise. 965 966 Usage: <code>bool(<var>g</var>)</code>, <code>not <var>g</var></code>, or <code>if <var>g</var>:</code> 967 968 Examples 969 -------- 970 ```python 971 bool(GPath("/")) # True 972 bool(GPath("..")) # True 973 bool(GPath("doc")) # True 974 bool(GPath("")) # False 975 ``` 976 """ 977 return self._root or self._drive != "" or self._parent_level != 0 or len(self._parts) > 0
False if self
is a relative path without any relative components and without a drive, and True otherwise.
Usage: bool(g)
, not g
, or if g:
Examples
bool(GPath("/")) # True
bool(GPath("..")) # True
bool(GPath("doc")) # True
bool(GPath("")) # False
980 def __str__(self) -> str: 981 """ 982 Return a platform-independent string representation of the path. 983 984 Usage: <code>str(<var>g</var>)</code> 985 """ 986 return str(self.render(Platform.GENERIC))
Return a platform-independent string representation of the path.
Usage: str(g)
989 def __repr__(self) -> str: 990 """ 991 Return a string that, when printed, gives the Python code associated with instantiating the GPath object. 992 993 Usage: <code>repr(<var>g</var>)</code> 994 """ 995 if self._platform is None: 996 platform_repr = "" 997 else: 998 platform_repr = f", platform={repr(self._platform)}" 999 1000 if self._encoding is None: 1001 encoding_repr = "" 1002 else: 1003 encoding_repr = f", encoding={repr(self._encoding)}" 1004 1005 if bool(self): 1006 return f"GPath({repr(str(self))}{platform_repr}{encoding_repr})" 1007 else: 1008 return f"GPath({repr('')}{platform_repr}{encoding_repr})"
Return a string that, when printed, gives the Python code associated with instantiating the GPath object.
Usage: repr(g)
1011 def __len__(self) -> int: 1012 """ 1013 Get the number of named path components, excluding any drive name or parent directories. 1014 1015 Usage: <code>len(<var>g</var>)</code> 1016 1017 Examples 1018 -------- 1019 ```python 1020 len(GPath("/usr/bin")) # 2 1021 len(GPath("/")) # 0 1022 len(GPath("C:/Windows")) # 0 1023 len(GPath("C:/")) # 0 1024 ``` 1025 """ 1026 return len(self._parts)
Get the number of named path components, excluding any drive name or parent directories.
Usage: len(g)
Examples
len(GPath("/usr/bin")) # 2
len(GPath("/")) # 0
len(GPath("C:/Windows")) # 0
len(GPath("C:/")) # 0
1029 def __getitem__(self, index: Union[int, slice]) -> Union[str, list[str]]: 1030 """ 1031 Get a 0-indexed named path component, or a slice of path components, excluding any drive name or parent directories. 1032 1033 Usage: <code><var>g</var>[<var>n</var>]</code>, <code><var>g</var>[<var>start</var>:<var>end</var>]</code>, <code><var>g</var>[<var>start</var>:<var>end</var>:<var>step</var>]</code>, etc. 1034 1035 Examples 1036 -------- 1037 ```python 1038 GPath("/usr/local/bin")[1] # "local" 1039 GPath("/usr/local/bin")[-1] # "bin" 1040 GPath("/usr/local/bin")[1:] # ["local", "bin"] 1041 GPath("/usr/local/bin")[::2] # ["usr", "bin"] 1042 ``` 1043 """ 1044 if isinstance(index, int): 1045 return self._parts[index] 1046 elif isinstance(index, slice): 1047 return list(self._parts[index])
Get a 0-indexed named path component, or a slice of path components, excluding any drive name or parent directories.
Usage: g[n]
, g[start:end]
, g[start:end:step]
, etc.
Examples
GPath("/usr/local/bin")[1] # "local"
GPath("/usr/local/bin")[-1] # "bin"
GPath("/usr/local/bin")[1:] # ["local", "bin"]
GPath("/usr/local/bin")[::2] # ["usr", "bin"]
1050 def __iter__(self) -> Iterator[str]: 1051 """ 1052 Get an iterator through the named path components, excluding any drive name or parent directories. 1053 1054 Usage: <code>iter(<var>g</var>)</code> or <code>for <var>p</var> in <var>g</var>:</code> 1055 """ 1056 return iter(self._parts)
Get an iterator through the named path components, excluding any drive name or parent directories.
Usage: iter(g)
or for p in g:
1059 def __contains__(self, other: GPathLike) -> bool: 1060 """ 1061 Check if the path represented by `self` contains the path represented by `other`; i.e. check if `self` is a parent directory of `other`. 1062 1063 Usage: <code><var>other</var> in <var>self</var></code> 1064 1065 Raises `ValueError` if either GPath is invalid 1066 1067 Examples 1068 -------- 1069 ```python 1070 GPath("/usr/local/bin") in GPath("/usr") # True 1071 GPath("/usr/local/bin") in GPath("/bin") # False 1072 GPath("..") in GPath("../..") # True 1073 GPath("..") in GPath("C:/") # False 1074 ``` 1075 """ 1076 if not isinstance(other, GPath): 1077 other = GPath(other, encoding=self._encoding) 1078 1079 common_path = self.common_with(other, allow_current=True, allow_parents=True) 1080 return common_path is not None and common_path == self
Check if the path represented by self
contains the path represented by other
; i.e. check if self
is a parent directory of other
.
Usage: other in self
Raises ValueError
if either GPath is invalid
Examples
GPath("/usr/local/bin") in GPath("/usr") # True
GPath("/usr/local/bin") in GPath("/bin") # False
GPath("..") in GPath("../..") # True
GPath("..") in GPath("C:/") # False
1083 def __add__(self, other: GPathLike) -> GPath: 1084 """ 1085 Add (concatenate) `other` to the end of `self`, and return a new copy. 1086 1087 If `other` is an absolute path, the returned path will be an absolute path that matches `other`, apart from the drive name. 1088 1089 If `other` has a drive, the returned path will have the same drive as `other`. Otherwise, the returned path will have the same drive as `self`. If neither has a drive, the returned path will not have a drive as well. 1090 1091 Alias: `__truediv__()` 1092 1093 Usage: <code><var>self</var> + <var>other</var></code> or <code><var>self</var> / <var>other</var></code> 1094 1095 Raises `ValueError` if either GPath is invalid 1096 1097 Examples 1098 -------- 1099 ```python 1100 GPath("/usr") + GPath("local/bin") # GPath("/usr/local/bin") 1101 GPath("C:/Windows/System32") + GPath("../SysWOW64") # GPath("C:/Windows/SysWOW64") 1102 GPath("C:/Windows/System32") + GPath("/usr/bin") # GPath("C:/usr/bin") 1103 GPath("..") + GPath("../..") # GPath("../../..") 1104 GPath("..") / GPath("../..") # GPath("../../..") 1105 ``` 1106 """ 1107 if isinstance(other, GPath): 1108 other._validate 1109 else: 1110 other = GPath(other, encoding=self._encoding) 1111 1112 new_path = GPath(self) 1113 if other._root: 1114 new_path._parts = other._parts 1115 new_path._root = other._root 1116 new_path._parent_level = other._parent_level 1117 else: 1118 new_parts = [part for part in self._parts] 1119 for i in range(other._parent_level): 1120 if len(new_parts) > 0: 1121 new_parts.pop() 1122 elif not new_path._root: 1123 new_path._parent_level += 1 1124 else: 1125 pass # parent of directory of root is still root 1126 1127 new_parts.extend(other._parts) 1128 new_path._parts = tuple(new_parts) 1129 1130 if other._drive != "": 1131 new_path._drive = other._drive 1132 1133 return new_path
Add (concatenate) other
to the end of self
, and return a new copy.
If other
is an absolute path, the returned path will be an absolute path that matches other
, apart from the drive name.
If other
has a drive, the returned path will have the same drive as other
. Otherwise, the returned path will have the same drive as self
. If neither has a drive, the returned path will not have a drive as well.
Alias: __truediv__()
Usage: self + other
or self / other
Raises ValueError
if either GPath is invalid
Examples
GPath("/usr") + GPath("local/bin") # GPath("/usr/local/bin")
GPath("C:/Windows/System32") + GPath("../SysWOW64") # GPath("C:/Windows/SysWOW64")
GPath("C:/Windows/System32") + GPath("/usr/bin") # GPath("C:/usr/bin")
GPath("..") + GPath("../..") # GPath("../../..")
GPath("..") / GPath("../..") # GPath("../../..")
1136 def __sub__(self, n: int) -> GPath: 1137 """ 1138 Remove `n` components from the end of the path and return a new copy. 1139 1140 Usage: <code><var>self</var> - <var>n</var></code> 1141 1142 Raises `ValueError` if `self` is an invalid GPath or if `n` is negative 1143 1144 Examples 1145 -------- 1146 ```python 1147 GPath("C:/Windows/System32") - 1 # GPath("C:/Windows") 1148 GPath("/usr/bin") - 2 # GPath("/") 1149 GPath("Documents") - 3 # GPath("..") 1150 GPath("/") - 1 # GPath("/") 1151 ``` 1152 """ 1153 if n < 0: 1154 raise ValueError("cannot subtract a negative number of components from the path: {n}; use __add__() instead") 1155 1156 new_path = GPath(self) 1157 new_parts = [part for part in self._parts] 1158 for i in range(n): 1159 if len(new_parts) > 0: 1160 new_parts.pop() 1161 elif not new_path._root: 1162 new_path._parent_level += 1 1163 else: 1164 pass # removing components from root should still give root 1165 new_path._parts = tuple(new_parts) 1166 return new_path
Remove n
components from the end of the path and return a new copy.
Usage: self - n
Raises ValueError
if self
is an invalid GPath or if n
is negative
Examples
GPath("C:/Windows/System32") - 1 # GPath("C:/Windows")
GPath("/usr/bin") - 2 # GPath("/")
GPath("Documents") - 3 # GPath("..")
GPath("/") - 1 # GPath("/")
1169 def __mul__(self, n: int) -> GPath: 1170 """ 1171 Duplicate the named components of `self` `n` times and return a new path with the duplicated components. 1172 1173 Named components will be duplicated separately from the components representing a parent directory. If `self` is an absolute path, only the relative components will be duplicated. 1174 1175 If `n` is 0, the result is an empty path (either relative or absolute). 1176 1177 Usage: <code><var>self</var> * <var>n</var></code> 1178 1179 Raises `ValueError` if `self` is an invalid GPath or if `n` is negative. 1180 1181 Examples 1182 -------- 1183 ```python 1184 GPath("/usr/bin") * 2 # GPath("/usr/bin/usr/bin") 1185 GPath("../docs") * 2 # GPath("../../docs/docs") 1186 GPath("C:/Windows") * 0 # GPath("C:/") 1187 ``` 1188 """ 1189 if n < 0: 1190 raise ValueError("cannot multiply path by a negative integer: {n}") 1191 new_path = GPath(self) 1192 new_path._parent_level = self._parent_level * n 1193 new_path._parts = self._parts * n 1194 return new_path
Duplicate the named components of self
n
times and return a new path with the duplicated components.
Named components will be duplicated separately from the components representing a parent directory. If self
is an absolute path, only the relative components will be duplicated.
If n
is 0, the result is an empty path (either relative or absolute).
Usage: self * n
Raises ValueError
if self
is an invalid GPath or if n
is negative.
Examples
GPath("/usr/bin") * 2 # GPath("/usr/bin/usr/bin")
GPath("../docs") * 2 # GPath("../../docs/docs")
GPath("C:/Windows") * 0 # GPath("C:/")
1197 def __truediv__(self, other: GPathLike) -> GPath: 1198 """ 1199 Alias of `__add__()`. 1200 1201 Usage: <code><var>self</var> + <var>other</var></code> or <code><var>self</var> / <var>other</var></code> 1202 """ 1203 return self.__add__(other)
Alias of __add__()
.
Usage: self + other
or self / other
1206 def __and__(self, other: GPathLike) -> Union[GPath, None]: 1207 """ 1208 Equivalent to `self.common_with(other)`, using the default options of `common_with()`. 1209 1210 Usage: <code><var>g1</var> & <var>g2</var></code> 1211 """ 1212 return self.common_with(other)
Equivalent to self.common_with(other)
, using the default options of common_with()
.
Usage: g1 & g2
1215 def __lshift__(self, n: int) -> GPath: 1216 """ 1217 Move the imaginary current working directory `n` steps up the filesystem tree. 1218 1219 If `self` is a relative path, remove up to `n` levels of parent directories from the start of the path and return a copy. If it is an absolute path, return a copy of `self` unchanged. 1220 1221 If `n` is negative, this is equivalent to `__rshift__(-n)`. 1222 1223 Usage: <code><var>self</var> << <var>n</var></code> 1224 1225 Raises `ValueError` if `self` is an invalid GPath. 1226 1227 Examples 1228 -------- 1229 ```python 1230 GPath("../SysWOW64/drivers") << 1 # GPath("SysWOW64/drivers") 1231 GPath("../doc") << 2 # GPath("doc") 1232 GPath("/usr/bin") << 2 # GPath("/usr/bin") 1233 ``` 1234 """ 1235 if n < 0: 1236 return self.__rshift__(-1 * n) 1237 new_path = GPath(self) 1238 if not new_path._root: 1239 new_path._parent_level = max(new_path._parent_level - n, 0) 1240 return new_path
Move the imaginary current working directory n
steps up the filesystem tree.
If self
is a relative path, remove up to n
levels of parent directories from the start of the path and return a copy. If it is an absolute path, return a copy of self
unchanged.
If n
is negative, this is equivalent to __rshift__(-n)
.
Usage: self << n
Raises ValueError
if self
is an invalid GPath.
Examples
GPath("../SysWOW64/drivers") << 1 # GPath("SysWOW64/drivers")
GPath("../doc") << 2 # GPath("doc")
GPath("/usr/bin") << 2 # GPath("/usr/bin")
1243 def __rshift__(self, n: int) -> GPath: 1244 """ 1245 Move the imaginary current working directory `n` steps down the filesystem tree. 1246 1247 If `self` is a relative path, add `n` levels of parent directories to the start of the path and return a copy. If it is an absolute path, return a copy of `self` unchanged. 1248 1249 If `n` is negative, this is equivalent to `__lshift__(-n)`. 1250 1251 Usage: <code><var>self</var> >> <var>n</var></code> 1252 1253 Raises `ValueError` if `self` is an invalid GPath 1254 1255 Examples 1256 -------- 1257 ```python 1258 GPath("../SysWOW64/drivers") >> 1 # GPath("../../SysWOW64/drivers") 1259 GPath("/usr/bin") >> 2 # GPath("/usr/bin") 1260 ``` 1261 """ 1262 if n < 0: 1263 return self.__lshift__(-1 * n) 1264 new_path = GPath(self) 1265 if not new_path._root: 1266 new_path._parent_level += n 1267 return new_path
Move the imaginary current working directory n
steps down the filesystem tree.
If self
is a relative path, add n
levels of parent directories to the start of the path and return a copy. If it is an absolute path, return a copy of self
unchanged.
If n
is negative, this is equivalent to __lshift__(-n)
.
Usage: self >> n
Raises ValueError
if self
is an invalid GPath
Examples
GPath("../SysWOW64/drivers") >> 1 # GPath("../../SysWOW64/drivers")
GPath("/usr/bin") >> 2 # GPath("/usr/bin")
Inherited Members
- collections.abc.Hashable
- __subclasshook__
- collections.abc.Iterable
- __class_getitem__
- builtins.object
- __new__
- __getattribute__
- __setattr__
- __delattr__
- __lt__
- __le__
- __ne__
- __gt__
- __ge__
- __reduce_ex__
- __reduce__
- __getstate__
- __init_subclass__
- __format__
- __sizeof__
- __dir__