gpath
85class GPath(Hashable, Sized, Iterable): 86 """ 87 An immutable generalised abstract file path that has no dependency on any real filesystem. 88 89 The path can be manipulated on a system that is different from where it originated, particularly with a different operating environment, 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. 90 91 The path is always stored in a normalised state, and is always treated as case sensitive. 92 93 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. 94 """ 95 96 __slots__ = ( 97 '_parts', 98 '_root', 99 '_drive', 100 '_parent_level', 101 '_encoding', 102 ) 103 104 105 def __init__(self, path: Union[str, bytes, os.PathLike, GPath, None]="", encoding: Optional[str]=None): 106 """ 107 Initialise a normalised and generalised abstract file path, possibly by copying an existing GPath object. 108 109 Parameters 110 ---------- 111 `path` 112 : path-like object representing a (possibly unnormalised) file path, or a GPath object to be copied 113 114 `encoding` 115 : 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 name should be one of the standard Python text encodings, as listed in the `codecs` module of the standard library. The specified encoding will propagate to new GPaths that result from operations on this GPath. If a binary operation involves two GPaths, the encoding specified by the left operand will be propagated to the result. 116 117 Raises 118 ------ 119 `ValueError` if `other` is an invalid GPath 120 121 Examples 122 -------- 123 ```python 124 GPath("/") 125 GPath("/usr/bin") 126 GPath("C:/Program Files") 127 ``` 128 """ 129 130 self._parts: tuple[str, ...] = tuple() # root- or parent- relative path 131 self._root: bool = False 132 self._drive: str = "" 133 self._parent_level: int = 0 134 135 self._encoding: Optional[str] = encoding 136 137 if path is None or path == "": 138 return 139 140 if isinstance(path, GPath): 141 path._validate() 142 self._parts = path._parts 143 self._root = path._root 144 self._drive = path._drive 145 self._parent_level = path._parent_level 146 147 self._encoding = path._encoding if encoding is None else encoding 148 return 149 150 path = os.fspath(path) 151 152 if isinstance(path, bytes): 153 if self._encoding is None: 154 path = path.decode(DEFAULT_ENCODING) 155 else: 156 path = path.decode(self._encoding) 157 158 # path is a str 159 160 if len(path) >= 2 and path[1] in _rules.generic_rules.drive_postfixes: 161 self._drive = path[0] 162 driveless_path = path[2:] 163 else: 164 driveless_path = path 165 166 for root in _rules.generic_rules.roots: 167 if driveless_path.startswith(root): 168 self._root = True 169 break 170 171 if self._root: 172 rootless_path = driveless_path[1:] 173 else: 174 rootless_path = driveless_path 175 176 177 parts = _split_relative(rootless_path, delimiters=(set(_rules.generic_rules.separators) | set(_rules.generic_rules.separators))) 178 parts = _normalise_relative(parts) 179 parent_level = 0 180 while parent_level < len(parts) and parts[parent_level] in _rules.generic_rules.parent_indicators: 181 parent_level += 1 182 self._parts = tuple(parts[parent_level:]) 183 if self._root == False: 184 self._parent_level = parent_level 185 186 187 @property 188 def named_parts(self) -> list[str]: 189 """ 190 Read-only named components of the path, not including the filesystem root, drive name, or any parent directories 191 192 Examples 193 -------- 194 ```python 195 GPath("usr/local/bin").named_parts # ["usr", "local", "bin"] 196 GPath("../../Documents").named_parts # ["Documents"] 197 GPath("/usr/bin").named_parts # ["usr", "bin"] 198 GPath("C:/Program Files").named_parts # ["Program Files"] 199 ``` 200 """ 201 return list(self._parts) 202 203 @property 204 def parent_level(self) -> int: 205 """ 206 Read-only number of levels of parent directories that the path is relative to, which may be 0 207 208 Examples 209 -------- 210 ```python 211 GPath("../../Documents").parent_level # 2 212 GPath("usr/local/bin").parent_level # 0 213 ``` 214 """ 215 return self._parent_level 216 217 @property 218 def parent_parts(self) -> list[str]: 219 """ 220 Read-only path components representing a parent directory that it is relative to, if any, with a copy of `parent_indicator` for each level of parent directory 221 222 Examples 223 -------- 224 ```python 225 GPath("../../Documents").parent_parts # ["..", ".."] 226 GPath("usr/local/bin").parent_parts # [] 227 ``` 228 """ 229 return [_rules.generic_rules.parent_indicators[0] for i in range(self._parent_level)] 230 231 @property 232 def relative_parts(self) -> list[str]: 233 """ 234 Read-only relative components of the path, not including the filesystem root or drive name, with a copy of `parent_indicator` for each level of parent directory 235 236 Examples 237 -------- 238 ```python 239 GPath("usr/local/bin").relative_parts # ["usr", "local", "bin"] 240 GPath("../../Documents").relative_parts # ["..", "..", "Documents"] 241 GPath("/usr/bin").relative_parts # ["usr", "bin"] 242 GPath("C:/Program Files").relative_parts # ["Program Files"] 243 ``` 244 """ 245 return self.parent_parts + list(self._parts) 246 247 @property 248 def drive(self) -> str: 249 """ 250 Read-only drive name 251 252 Examples 253 -------- 254 ```python 255 GPath("C:/Windows").drive # "C:" 256 GPath("/usr/bin").drive # "" 257 GPath("../../Documents").drive # "" 258 ``` 259 """ 260 return self._drive 261 262 @property 263 def absolute(self) -> bool: 264 """ 265 Read-only flag for whether the path is an absolute path 266 267 Examples 268 -------- 269 ```python 270 GPath("/").absolute # True 271 GPath("C:/Windows").absolute # True 272 GPath("local/bin").absolute # False 273 GPath("../../Documents").absolute # False 274 ``` 275 """ 276 return self._root 277 278 @property 279 def root(self) -> bool: 280 """ 281 Read-only flag for whether the path is exactly the root of the filesystem 282 283 Examples 284 -------- 285 ```python 286 GPath("/").root # True 287 GPath("C:/").root # True 288 GPath("/usr/bin").root # False 289 GPath("C:/Windows").root # False 290 GPath("../../Documents").root # False 291 ``` 292 """ 293 return self._root and len(self._parts) == 0 294 295 @property 296 def encoding(self) -> Union[str, None]: 297 """ 298 Read-only encoding used to decode other paths that are given as bytes-like objects 299 """ 300 return self._encoding 301 302 303 @overload 304 def partition(paths: Iterable[GPathLike], **kwargs) -> dict[GPath, list[GPath]]: 305 ... 306 @overload 307 def partition(*paths: GPathLike, **kwargs) -> dict[GPath, list[GPath]]: 308 ... 309 @staticmethod 310 def partition(*paths, allow_current: bool=True, allow_parents: bool=True, encoding: Optional[str]=None) -> dict[GPath, list[GPath]]: 311 """ 312 Partition a collection of paths based on shared common base paths such that each path belongs to one partition. 313 314 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. 315 316 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. 317 318 Parameters 319 ---------- 320 `paths: Iterable[GPath | str | bytes | os.PathLike]` or `*paths: GPath | str | bytes | os.PathLike` 321 : the paths to be partitioned, which can be given as either a list-like object or as variadic arguments 322 323 `allow_current` 324 : whether non-parent relative paths with no shared components should be considered to have a common base path (see `common_with()`) 325 326 `allow_parents` 327 : 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. 328 329 `encoding` 330 : the text encoding that should be used to decode bytes-like objects in `paths`, if any (see `__init__()`). 331 332 Returns 333 ------- 334 a dictionary that maps the common base path of each partition to a list of relative paths 335 336 Raises 337 ------ 338 `ValueError` 339 if any of the GPaths are invalid 340 341 Examples 342 -------- 343 ```python 344 GPath.partition("/usr/bin", "/usr/local/bin", "../../doc", "C:/Windows", "C:/Program Files") 345 346 assert partitions == { 347 GPath("/usr") : [GPath("bin"), GPath("local")], 348 GPath("../../doc") : [GPath("")], 349 GPath("C:/") : [GPath("Windows"), GPath("Program Files")], 350 } 351 ``` 352 """ 353 flattened_paths: list[GPathLike] = [] 354 for path_or_list in paths: 355 if _is_gpathlike(path_or_list): 356 flattened_paths.append(path_or_list) 357 else: 358 flattened_paths.extend(path_or_list) 359 gpaths = [path if isinstance(path, GPath) else GPath(path, encoding=encoding) for path in flattened_paths] 360 361 partition_map = {} 362 if len(gpaths) > 0: 363 if allow_parents == True: 364 partition_map[gpaths[0]] = [] 365 else: 366 partition_map[gpaths[0]] = [gpaths[0]] 367 368 for path in gpaths[1:]: 369 partition_found = False 370 for partition in partition_map: 371 candidate_common = partition.common_with(path, allow_current=allow_current, allow_parents=allow_parents) 372 if candidate_common is not None: 373 partition_found = True 374 if candidate_common != partition: 375 partition_map[candidate_common] = partition_map[partition] 376 del partition_map[partition] 377 if allow_parents == False: 378 partition_map[candidate_common].append(path) 379 break 380 if not partition_found: 381 if allow_parents == True: 382 partition_map[path] = [] 383 else: 384 partition_map[path] = [path] 385 386 for partition, path_list in partition_map.items(): 387 partition_map[partition] = [path.subpath_from(partition) for path in path_list] 388 389 return partition_map 390 391 392 @overload 393 def join(paths: Iterable[GPathLike], **kwargs) -> GPath: 394 ... 395 @overload 396 def join(*paths: GPathLike, **kwargs) -> GPath: 397 ... 398 @staticmethod 399 def join(*paths, encoding: Optional[str]=None) -> GPath: 400 """ 401 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. 402 403 Parameters 404 ---------- 405 `paths`: `Sequence[GPath | str | bytes | os.PathLike]` or `*paths: GPath | str | bytes | os.PathLike` 406 : the paths to be combined, which can be given as either a list-like object or as variadic arguments 407 408 `encoding` 409 : the text encoding that should be used to decode bytes-like objects in `paths`, if any (see `__init__()`). 410 411 Returns 412 ------- 413 the combined path 414 415 Raises 416 ------ 417 `ValueError` if any of the GPaths are invalid 418 419 Examples 420 -------- 421 ```python 422 GPath.join("usr", "local", "bin") # GPath("usr/local/bin") 423 GPath.join("/usr/local/bin", "../../bin") # GPath("/usr/bin") 424 GPath.join("C:/", "Windows") # GPath("C:/Windows") 425 ``` 426 """ 427 flattened_paths: list[GPathLike] = [] 428 for path_or_list in paths: 429 if _is_gpathlike(path_or_list): 430 flattened_paths.append(path_or_list) 431 else: 432 flattened_paths.extend(path_or_list) 433 434 if len(flattened_paths) == 0: 435 return GPath(encoding=encoding) 436 437 combined_path = flattened_paths[0] 438 if not isinstance(combined_path, GPath): 439 combined_path = GPath(combined_path, encoding=encoding) 440 for path in flattened_paths[1:]: 441 combined_path = combined_path + path 442 443 return combined_path 444 445 446 def as_relative(self, parent_level: Optional[int]=None) -> GPath: 447 """ 448 Convert the path to a relative path and return a new copy. 449 450 Parameters 451 ---------- 452 `parent_level` 453 : 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. 454 455 Raises 456 ------ 457 `TypeError` if `parent_level` is not a valid type 458 459 Examples 460 -------- 461 ```python 462 GPath("/usr/bin").as_relative() # GPath("usr/bin") 463 GPath("C:/Windows").as_relative() # GPath("C:Windows") 464 GPath("../Documents").as_relative() # GPath("../Documents") 465 ``` 466 """ 467 468 new_path = GPath(self) 469 new_path._root = False 470 if parent_level is None: 471 pass 472 elif isinstance(parent_level, int): 473 new_path._parent_level = parent_level 474 else: 475 raise TypeError(f"parent_level must be an int: {parent_level} ({type(parent_level)})") 476 477 return new_path 478 479 480 def as_absolute(self) -> GPath: 481 """ 482 Convert the path to an absolute path and return a new copy. 483 484 Any parent directory that the path is relative to will be removed. If the path is already absolute, an identical copy is returned. 485 486 Examples 487 -------- 488 ```python 489 GPath("usr/bin").as_absolute() # GPath("/usr/bin") 490 GPath("../Documents").as_absolute() # GPath("/Documents") 491 GPath("C:Windows").as_absolute() # GPath("C:/Windows") 492 ``` 493 """ 494 new_path = GPath(self) 495 new_path._root = True 496 new_path._parent_level = 0 497 return new_path 498 499 500 def with_drive(self, drive: Union[str, bytes, None]=None) -> GPath: 501 """ 502 Return a new copy of the path with the drive set to `drive`. 503 504 If `drive` is `""` or None, this would be equivalent to `without_drive()`. 505 506 Parameters 507 ---------- 508 `drive` 509 : the drive for the returned path, or either `""` or None if the returned path should have no drive 510 511 Returns 512 ------- 513 `GPath` 514 : a new path with the given drive 515 516 Raises 517 ------ 518 - `TypeError` if `drive` is not a valid type 519 - `ValueError` if `drive` has more than one character 520 521 Examples 522 -------- 523 ```python 524 GPath("C:/Windows").with_drive() # GPath("/Windows") 525 GPath("C:/Windows").with_drive("D") # GPath("D:/Windows") 526 GPath("/Windows").with_drive("C") # GPath("C:/Windows") 527 ``` 528 """ 529 if drive is None: 530 drive = "" 531 elif isinstance(drive, bytes): 532 if self._encoding is None: 533 drive = drive.decode(DEFAULT_ENCODING) 534 else: 535 drive = drive.decode(self._encoding) 536 elif isinstance(drive, str): 537 pass 538 else: 539 raise TypeError(f"drive must be a str or bytes object: {drive} ({type(drive)})") 540 541 if len(drive) > 1: 542 raise ValueError(f"drive can only be a single character, an empty string or None: {drive}") 543 544 new_path = GPath(self) 545 new_path._drive = drive 546 return new_path 547 548 549 def without_drive(self) -> GPath: 550 """ 551 Return a new copy of the path without a drive. 552 553 Equivalent to `with_drive("")` or `with_drive(None)`. 554 555 Returns 556 ------- 557 `GPath` 558 : a new path without a drive 559 560 Examples 561 -------- 562 ```python 563 GPath("C:/Windows").without_drive() # GPath("/Windows") 564 ``` 565 """ 566 return self.with_drive(None) 567 568 569 def common_with(self, other: GPathLike, allow_current: bool=True, allow_parents: bool=False) -> Optional[GPath]: 570 """ 571 Find the longest common base path shared between `self` and `other`, or return None if no such path exists. 572 573 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. 574 575 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>). 576 577 Parameters 578 ---------- 579 `other` 580 : the path to compare with 581 582 `allow_current` 583 : 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. 584 585 `allow_parents` 586 : 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. 587 588 Returns 589 ------- 590 `GPath` 591 : the longest common base path, which may be empty, if it exists 592 593 `None` 594 : otherwise 595 596 Raises 597 ------ 598 `ValueError` if either `self` or `other` is an invalid GPath 599 600 Examples 601 -------- 602 ```python 603 GPath("/usr/bin").find_common("/usr/local/bin") # GPath("/usr") 604 GPath("C:/Windows/System32").find_common("C:/Program Files") # GPath("C:/") 605 GPath("../Documents").find_common("../Pictures") # GPath("..") 606 ``` 607 """ 608 self._validate() 609 if isinstance(other, GPath): 610 other._validate() 611 else: 612 other = GPath(other, encoding=self._encoding) 613 614 if self._drive != other._drive: 615 return None 616 if self._root != other._root: 617 return None 618 619 if allow_parents: 620 allow_current = True 621 622 parts = [] 623 if self._root: 624 common_path = GPath(self) 625 for part1, part2 in zip(self._parts, other._parts): 626 if part1 == part2: 627 parts.append(part1) 628 else: 629 if self._parent_level != other._parent_level: 630 if not allow_parents: 631 return None 632 633 common_path = GPath(self) 634 common_path._parent_level = max(self._parent_level, other._parent_level) 635 else: 636 common_path = GPath(self) 637 for part1, part2 in zip(self._parts, other._parts): 638 if part1 == part2: 639 parts.append(part1) 640 641 common_path._parts = tuple(parts) 642 643 if not allow_current and not bool(common_path): 644 if common_path != self or common_path != other: 645 return None 646 return common_path 647 648 649 def subpath_from(self, base: GPathLike) -> Optional[GPath]: 650 """ 651 Find the relative subpath from `base` to `self` if possible and if `base` contains `self`, or return None otherwise. 652 653 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. 654 655 Similar to `relpath_from()`, but `self` must be a descendent of `base`. 656 657 Parameters 658 ---------- 659 `base` 660 : the base path that the relative subpath should start from 661 662 Returns 663 ------- 664 `GPath` 665 : relative subpath from `base` to `self`, which may be empty, if it exists 666 667 `None` 668 : otherwise 669 670 Raises 671 ------ 672 `ValueError` if either `self` or `base` is an invalid GPath 673 674 Examples 675 -------- 676 ```python 677 GPath("/usr/local/bin").subpath_from("/usr") # GPath("local/bin") 678 GPath("/usr/bin").subpath_from("/usr/local/bin") # None 679 GPath("/usr/bin").subpath_from("../Documents") # None 680 ``` 681 """ 682 if not isinstance(base, GPath): 683 base = GPath(base, encoding=self._encoding) 684 685 if self.common_with(base, allow_current=True, allow_parents=False) is not None and self in base: 686 # 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 687 base_length = len(base._parts) 688 new_path = GPath(self) 689 new_path._parts = self._parts[base_length:] # () when self == base 690 new_path._drive = "" 691 new_path._root = False 692 new_path._parent_level = 0 693 return new_path 694 else: 695 return None 696 697 698 def relpath_from(self, origin: GPathLike) -> Optional[GPath]: 699 """ 700 Find the relative path from `origin` to `self` if possible, or return None otherwise. 701 702 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. 703 704 Similar to `subpath_from()`, but `self` does not need to be a descendent of `origin`. 705 706 Parameters 707 ---------- 708 `origin` 709 : the origin that the relative path should start from 710 711 Returns 712 ------- 713 `GPath` 714 : relative path from `origin` to `self`, which may be empty, if it exists 715 716 `None` 717 : otherwise 718 719 Raises 720 ------ 721 `ValueError` if either `self` or `origin` is an invalid GPath 722 723 Examples 724 -------- 725 ```python 726 GPath("/usr/local/bin").subpath_from("/usr") # GPath("local/bin") 727 GPath("/usr/bin").subpath_from("/usr/local/bin") # GPath("../../bin") 728 GPath("/usr/bin").subpath_from("../Documents") # None 729 ``` 730 """ 731 self._validate() 732 if not isinstance(origin, GPath): 733 origin = GPath(origin, encoding=self._encoding) 734 735 if origin._root: 736 common = self.common_with(origin) 737 if common is None: 738 return None 739 740 new_path = GPath(self) 741 new_path._parent_level = len(origin) - len(common) 742 new_path._parts = self._parts[len(common):] 743 new_path._drive = "" 744 new_path._root = False 745 return new_path 746 747 else: 748 common = self.common_with(origin, allow_current=True, allow_parents=True) 749 if common is None: 750 return None 751 if common._parent_level > self._parent_level: 752 return None # Path from common to self's parent cannot be known 753 754 # common._dotdot == self._dotdot 755 # origin._dotdot <= self._dotdot 756 757 new_path = GPath(self) 758 new_path._drive = "" 759 new_path._root = False 760 if len(common) == 0: 761 if origin._parent_level == self._parent_level: 762 new_path._parent_level = len(origin) 763 else: 764 new_path._parent_level = (common._parent_level - origin._parent_level) + len(origin) 765 new_path._parts = self._parts 766 else: 767 new_path._parent_level = len(origin) - len(common) 768 new_path._parts = self._parts[len(common):] 769 770 return new_path 771 772 773 def __hash__(self) -> int: 774 """ 775 Calculate hash of the GPath object. 776 777 Usage: <code>hash(<var>g</var>)</code> 778 """ 779 return hash(self._tuple) 780 781 782 def __eq__(self, other: GPathLike) -> bool: 783 """ 784 Check if two GPaths are completely identical. 785 786 Always return False if `other` is not a GPath object, even if it is a GPath-like object. 787 788 Usage: <code><var>g1</var> == <var>g2</var></code> 789 790 Examples 791 -------- 792 ```python 793 GPath("/usr/bin") == GPath("/usr/bin") # True 794 GPath("/usr/bin") == GPath("usr/bin") # False 795 GPath("C:/") == GPath("D:/") # False 796 ``` 797 """ 798 if not isinstance(other, GPath): 799 other = GPath(other, encoding=self._encoding) 800 return self._tuple == other._tuple 801 802 803 def __bool__(self) -> bool: 804 """ 805 Truthy if `self` is an absolute path, if `self` is relative to a parent directory, or if `self` has at least one named component. 806 807 Usage: <code>bool(<var>g</var>)</code>, <code>not <var>g</var></code>, or <code>if <var>g</var>:</code> 808 809 Examples 810 -------- 811 ```python 812 bool(GPath("/")) # True 813 bool(GPath("..")) # True 814 bool(GPath("doc")) # True 815 bool(GPath("")) # False 816 ``` 817 """ 818 return self._root or self._drive != "" or self._parent_level != 0 or len(self._parts) > 0 819 820 821 def __str__(self) -> str: 822 """ 823 Return a string representation of the path. 824 825 Usage: <code>str(<var>g</var>)</code> 826 """ 827 if bool(self): 828 if self.root and self._drive == "": 829 return _rules.generic_rules.roots[0] 830 else: 831 return (self._drive + _rules.generic_rules.drive_postfixes[0] if self._drive != "" else "") + (_rules.generic_rules.roots[0] if self._root else "") + _rules.generic_rules.separators[0].join(self.relative_parts) 832 else: 833 return _rules.generic_rules.current_indicators[0] 834 835 836 def __repr__(self) -> str: 837 """ 838 Return a string that, when printed, gives the Python code associated with instantiating the GPath object. 839 840 Usage: <code>repr(<var>g</var>)</code> 841 """ 842 if self._encoding is None: 843 encoding_repr = "" 844 else: 845 encoding_repr = f", encoding={repr(self._encoding)}" 846 847 if bool(self): 848 return f"GPath({repr(str(self))}{encoding_repr})" 849 else: 850 return f"GPath({repr('')}{encoding_repr})" 851 852 853 def __len__(self) -> int: 854 """ 855 Get the number of named path components, excluding any drive name or parent directories. 856 857 Usage: <code>len(<var>g</var>)</code> 858 859 Examples 860 -------- 861 ```python 862 len(GPath("/usr/bin")) # 2 863 len(GPath("/")) # 0 864 len(GPath("C:/Windows")) # 0 865 len(GPath("C:/")) # 0 866 ``` 867 """ 868 return len(self._parts) 869 870 871 def __getitem__(self, index: Union[int, slice]) -> Union[str, list[str]]: 872 """ 873 Get a 0-indexed named path component, or a slice of path components, excluding any drive name or parent directories. 874 875 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. 876 877 Examples 878 -------- 879 ```python 880 GPath("/usr/local/bin")[1] # "local" 881 GPath("/usr/local/bin")[-1] # "bin" 882 GPath("/usr/local/bin")[1:] # ["local", "bin"] 883 GPath("/usr/local/bin")[::2] # ["usr", "bin"] 884 ``` 885 """ 886 if isinstance(index, int): 887 return self._parts[index] 888 elif isinstance(index, slice): 889 return list(self._parts[index]) 890 891 892 def __iter__(self) -> Iterator[str]: 893 """ 894 Get an iterator through the named path components, excluding any drive name or parent directories. 895 896 Usage: <code>iter(<var>g</var>)</code> or <code>for <var>p</var> in <var>g</var>:</code> 897 """ 898 return iter(self._parts) 899 900 901 def __contains__(self, other: GPathLike) -> bool: 902 """ 903 Check if the path represented by `self` contains the path represented by `other`; i.e. check if `self` is a parent directory of `other`. 904 905 Usage: <code><var>other</var> in <var>self</var></code> 906 907 Raises `ValueError` if either GPath is invalid 908 909 Examples 910 -------- 911 ```python 912 GPath("/usr/local/bin") in GPath("/usr") # True 913 GPath("/usr/local/bin") in GPath("/bin") # False 914 GPath("..") in GPath("../..") # True 915 GPath("..") in GPath("C:/") # False 916 ``` 917 """ 918 if not isinstance(other, GPath): 919 other = GPath(other, encoding=self._encoding) 920 921 common_path = self.common_with(other, allow_current=True, allow_parents=True) 922 return common_path is not None and common_path == self 923 924 925 def __add__(self, other: GPathLike) -> GPath: 926 """ 927 Add (concatenate) `other` to the end of `self`, and return a new copy. 928 929 If `other` is an absolute path, the returned path will be an absolute path that matches `other`, apart from the drive name. 930 931 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. 932 933 Alias: `__truediv__()` 934 935 Usage: <code><var>self</var> + <var>other</var></code> or <code><var>self</var> / <var>other</var></code> 936 937 Raises `ValueError` if either GPath is invalid 938 939 Examples 940 -------- 941 ```python 942 GPath("/usr") + GPath("local/bin") # GPath("/usr/local/bin") 943 GPath("C:/Windows/System32") + GPath("../SysWOW64") # GPath("C:/Windows/SysWOW64") 944 GPath("C:/Windows/System32") + GPath("/usr/bin") # GPath("C:/usr/bin") 945 GPath("..") + GPath("../..") # GPath("../../..") 946 GPath("..") / GPath("../..") # GPath("../../..") 947 ``` 948 """ 949 if isinstance(other, GPath): 950 other._validate 951 else: 952 other = GPath(other, encoding=self._encoding) 953 954 new_path = GPath(self) 955 if other._root: 956 new_path._parts = other._parts 957 new_path._root = other._root 958 new_path._parent_level = other._parent_level 959 else: 960 new_parts = [part for part in self._parts] 961 for i in range(other._parent_level): 962 if len(new_parts) > 0: 963 new_parts.pop() 964 elif not new_path._root: 965 new_path._parent_level += 1 966 else: 967 pass # parent of directory of root is still root 968 969 new_parts.extend(other._parts) 970 new_path._parts = tuple(new_parts) 971 972 if other._drive != "": 973 new_path._drive = other._drive 974 975 return new_path 976 977 978 def __sub__(self, n: int) -> GPath: 979 """ 980 Remove `n` components from the end of the path and return a new copy. 981 982 Usage: <code><var>self</var> - <var>n</var></code> 983 984 Raises `ValueError` if `self` is an invalid GPath or if `n` is negative 985 986 Examples 987 -------- 988 ```python 989 GPath("C:/Windows/System32") - 1 # GPath("C:/Windows") 990 GPath("/usr/bin") - 2 # GPath("/") 991 GPath("Documents") - 3 # GPath("..") 992 GPath("/") - 1 # GPath("/") 993 ``` 994 """ 995 if n < 0: 996 raise ValueError("cannot subtract a negative number of components from the path: {n}; use __add__() instead") 997 998 new_path = GPath(self) 999 new_parts = [part for part in self._parts] 1000 for i in range(n): 1001 if len(new_parts) > 0: 1002 new_parts.pop() 1003 elif not new_path._root: 1004 new_path._parent_level += 1 1005 else: 1006 pass # removing components from root should still give root 1007 new_path._parts = tuple(new_parts) 1008 return new_path 1009 1010 1011 def __mul__(self, n: int) -> GPath: 1012 """ 1013 Duplicate the named components of `self` `n` times and return a new path with the duplicated components. 1014 1015 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. 1016 1017 If `n` is 0, the result is an empty path (either relative or absolute). 1018 1019 Usage: <code><var>self</var> * <var>n</var></code> 1020 1021 Raises `ValueError` if `self` is an invalid GPath or if `n` is negative. 1022 1023 Examples 1024 -------- 1025 ```python 1026 GPath("/usr/bin") * 2 # GPath("/usr/bin/usr/bin") 1027 GPath("../docs") * 2 # GPath("../../docs/docs") 1028 GPath("C:/Windows") * 0 # GPath("C:/") 1029 ``` 1030 """ 1031 if n < 0: 1032 raise ValueError("cannot multiply path by a negative integer: {n}") 1033 new_path = GPath(self) 1034 new_path._parent_level = self._parent_level * n 1035 new_path._parts = self._parts * n 1036 return new_path 1037 1038 1039 def __truediv__(self, other: GPathLike) -> GPath: 1040 """ 1041 Alias of `__add__()`. 1042 1043 Usage: <code><var>self</var> + <var>other</var></code> or <code><var>self</var> / <var>other</var></code> 1044 """ 1045 return self.__add__(other) 1046 1047 1048 def __and__(self, other: GPathLike) -> Union[GPath, None]: 1049 """ 1050 Equivalent to `self.common_with(other)`, using the default options of `common_with()`. 1051 1052 Usage: <code><var>g1</var> & <var>g2</var></code> 1053 """ 1054 return self.common_with(other) 1055 1056 1057 def __lshift__(self, n: int) -> GPath: 1058 """ 1059 Move the imaginary current working directory `n` steps up the filesystem tree. 1060 1061 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. 1062 1063 If `n` is negative, this is equivalent to `__rshift__(-n)`. 1064 1065 Usage: <code><var>self</var> << <var>n</var></code> 1066 1067 Raises `ValueError` if `self` is an invalid GPath. 1068 1069 Examples 1070 -------- 1071 ```python 1072 GPath("../SysWOW64/drivers") << 1 # GPath("SysWOW64/drivers") 1073 GPath("../doc") << 2 # GPath("doc") 1074 GPath("/usr/bin") << 2 # GPath("/usr/bin") 1075 ``` 1076 """ 1077 if n < 0: 1078 return self.__rshift__(-1 * n) 1079 new_path = GPath(self) 1080 if not new_path._root: 1081 new_path._parent_level = max(new_path._parent_level - n, 0) 1082 return new_path 1083 1084 1085 def __rshift__(self, n: int) -> GPath: 1086 """ 1087 Move the imaginary current working directory `n` steps down the filesystem tree. 1088 1089 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. 1090 1091 If `n` is negative, this is equivalent to `__lshift__(-n)`. 1092 1093 Usage: <code><var>self</var> >> <var>n</var></code> 1094 1095 Raises `ValueError` if `self` is an invalid GPath 1096 1097 Examples 1098 -------- 1099 ```python 1100 GPath("../SysWOW64/drivers") >> 1 # GPath("../../SysWOW64/drivers") 1101 GPath("/usr/bin") >> 2 # GPath("/usr/bin") 1102 ``` 1103 """ 1104 if n < 0: 1105 return self.__lshift__(-1 * n) 1106 new_path = GPath(self) 1107 if not new_path._root: 1108 new_path._parent_level += n 1109 return new_path 1110 1111 1112 @property 1113 def _tuple(self) -> tuple: 1114 # Get a tuple of all fields 1115 return ( 1116 self._root, 1117 self._drive, 1118 self._parent_level, 1119 self._parts, 1120 self._encoding, 1121 ) 1122 1123 1124 def _validate(self) -> bool: 1125 # Check if self is in a valid state 1126 if self._parent_level < 0: 1127 raise ValueError(f"invalid GPath, _parent cannot be negative: {repr(self)}") 1128 if self._root: 1129 if self._parent_level != 0: 1130 raise ValueError(f"invalid GPath, _parent must be 0 when root is True: {repr(self)}") 1131 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, particularly with a different operating environment, 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.
Constructor summary
- GPath( path: Union[str, bytes, os.PathLike, GPath, NoneType] = '', encoding: Optional[str] = None)
-
Initialise a normalised and generalised abstract file path, possibly by copying an existing GPath object.
Instance variables summary
- named_parts: list[str]
-
Read-only named components of the path, not including the filesystem root, drive name, or any parent directories
- parent_level: int
-
Read-only number of levels of parent directories that the path is relative to, which may be 0
- parent_parts: list[str]
-
Read-only path components representing a parent directory that it is relative to, if any, with a copy of
parent_indicator
for each level of parent directory - relative_parts: list[str]
-
Read-only relative components of the path, not including the filesystem root or drive name, with a copy of
parent_indicator
for each level of parent directory - drive: str
-
Read-only drive name
- absolute: bool
-
Read-only flag for whether the path is an absolute path
- root: bool
-
Read-only flag for whether the path is exactly the root of the filesystem
- encoding: Optional[str]
-
Read-only encoding used to decode other paths that are given as bytes-like objects
Static methods summary
- partition( *paths, allow_current: bool = True, allow_parents: bool = True, encoding: Optional[str] = None) -> dict[GPath, list[GPath]]:
-
Partition a collection of paths based on shared common base paths such that each path belongs to one partition.
- join(*paths, encoding: Optional[str] = None) -> GPath:
-
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.
Instance methods summary
- as_relative(self, parent_level: Optional[int] = None) -> GPath:
-
Convert the path to a relative path and return a new copy.
- as_absolute(self) -> GPath:
-
Convert the path to an absolute path and return a new copy.
- with_drive(self, drive: Union[str, bytes, NoneType] = None) -> GPath:
-
Return a new copy of the path with the drive set to
drive
. - without_drive(self) -> GPath:
-
Return a new copy of the path without a drive.
- common_with( self, other: Union[GPath, str, bytes, os.PathLike], allow_current: bool = True, allow_parents: bool = False) -> Optional[GPath]:
-
Find the longest common base path shared between
self
andother
, or return None if no such path exists. - subpath_from( self, base: Union[GPath, str, bytes, os.PathLike]) -> Optional[GPath]:
-
Find the relative subpath from
base
toself
if possible and ifbase
containsself
, or return None otherwise. - relpath_from( self, origin: Union[GPath, str, bytes, os.PathLike]) -> Optional[GPath]:
-
Find the relative path from
origin
toself
if possible, or return None otherwise. - __hash__(self) -> int:
-
Calculate hash of the GPath object.
- __eq__(self, other: Union[GPath, str, bytes, os.PathLike]) -> bool:
-
Check if two GPaths are completely identical.
- __bool__(self) -> bool:
-
Truthy if
self
is an absolute path, ifself
is relative to a parent directory, or ifself
has at least one named component. - __str__(self) -> str:
-
Return a string representation of the path.
- __repr__(self) -> str:
-
Return a string that, when printed, gives the Python code associated with instantiating the GPath object.
- __len__(self) -> int:
-
Get the number of named path components, excluding any drive name or parent directories.
- __getitem__(self, index: Union[int, slice]) -> Union[str, list[str]]:
-
Get a 0-indexed named path component, or a slice of path components, excluding any drive name or parent directories.
- __iter__(self) -> Iterator[str]:
-
Get an iterator through the named path components, excluding any drive name or parent directories.
- __contains__(self, other: Union[GPath, str, bytes, os.PathLike]) -> bool:
-
Check if the path represented by
self
contains the path represented byother
; i.e. check ifself
is a parent directory ofother
. - __add__( self, other: Union[GPath, str, bytes, os.PathLike]) -> GPath:
-
Add (concatenate)
other
to the end ofself
, and return a new copy. - __sub__(self, n: int) -> GPath:
-
Remove
n
components from the end of the path and return a new copy. - __mul__(self, n: int) -> GPath:
-
Duplicate the named components of
self
n
times and return a new path with the duplicated components. - __truediv__( self, other: Union[GPath, str, bytes, os.PathLike]) -> GPath:
-
Alias of
__add__()
. - __and__( self, other: Union[GPath, str, bytes, os.PathLike]) -> Optional[GPath]:
-
Equivalent to
self.common_with(other)
, using the default options ofcommon_with()
. - __lshift__(self, n: int) -> GPath:
-
Move the imaginary current working directory
n
steps up the filesystem tree. - __rshift__(self, n: int) -> GPath:
-
Move the imaginary current working directory
n
steps down the filesystem tree.
105 def __init__(self, path: Union[str, bytes, os.PathLike, GPath, None]="", encoding: Optional[str]=None): 106 """ 107 Initialise a normalised and generalised abstract file path, possibly by copying an existing GPath object. 108 109 Parameters 110 ---------- 111 `path` 112 : path-like object representing a (possibly unnormalised) file path, or a GPath object to be copied 113 114 `encoding` 115 : 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 name should be one of the standard Python text encodings, as listed in the `codecs` module of the standard library. The specified encoding will propagate to new GPaths that result from operations on this GPath. If a binary operation involves two GPaths, the encoding specified by the left operand will be propagated to the result. 116 117 Raises 118 ------ 119 `ValueError` if `other` is an invalid GPath 120 121 Examples 122 -------- 123 ```python 124 GPath("/") 125 GPath("/usr/bin") 126 GPath("C:/Program Files") 127 ``` 128 """ 129 130 self._parts: tuple[str, ...] = tuple() # root- or parent- relative path 131 self._root: bool = False 132 self._drive: str = "" 133 self._parent_level: int = 0 134 135 self._encoding: Optional[str] = encoding 136 137 if path is None or path == "": 138 return 139 140 if isinstance(path, GPath): 141 path._validate() 142 self._parts = path._parts 143 self._root = path._root 144 self._drive = path._drive 145 self._parent_level = path._parent_level 146 147 self._encoding = path._encoding if encoding is None else encoding 148 return 149 150 path = os.fspath(path) 151 152 if isinstance(path, bytes): 153 if self._encoding is None: 154 path = path.decode(DEFAULT_ENCODING) 155 else: 156 path = path.decode(self._encoding) 157 158 # path is a str 159 160 if len(path) >= 2 and path[1] in _rules.generic_rules.drive_postfixes: 161 self._drive = path[0] 162 driveless_path = path[2:] 163 else: 164 driveless_path = path 165 166 for root in _rules.generic_rules.roots: 167 if driveless_path.startswith(root): 168 self._root = True 169 break 170 171 if self._root: 172 rootless_path = driveless_path[1:] 173 else: 174 rootless_path = driveless_path 175 176 177 parts = _split_relative(rootless_path, delimiters=(set(_rules.generic_rules.separators) | set(_rules.generic_rules.separators))) 178 parts = _normalise_relative(parts) 179 parent_level = 0 180 while parent_level < len(parts) and parts[parent_level] in _rules.generic_rules.parent_indicators: 181 parent_level += 1 182 self._parts = tuple(parts[parent_level:]) 183 if self._root == False: 184 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
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 name should be one of the standard Python text encodings, as listed in the codecs
module of the standard library. The specified encoding will propagate to new GPaths that result from operations on this GPath. If a binary operation involves two GPaths, the encoding specified by the left operand will be propagated to the result.
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 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 a copy of parent_indicator
for each level of parent directory
Examples
GPath("../../Documents").parent_parts # ["..", ".."]
GPath("usr/local/bin").parent_parts # []
Read-only relative components of the path, not including the filesystem root or drive name, with a copy of parent_indicator
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 drive name
Examples
GPath("C:/Windows").drive # "C:"
GPath("/usr/bin").drive # ""
GPath("../../Documents").drive # ""
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 encoding used to decode other paths that are given as bytes-like objects
309 @staticmethod 310 def partition(*paths, allow_current: bool=True, allow_parents: bool=True, encoding: Optional[str]=None) -> dict[GPath, list[GPath]]: 311 """ 312 Partition a collection of paths based on shared common base paths such that each path belongs to one partition. 313 314 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. 315 316 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. 317 318 Parameters 319 ---------- 320 `paths: Iterable[GPath | str | bytes | os.PathLike]` or `*paths: GPath | str | bytes | os.PathLike` 321 : the paths to be partitioned, which can be given as either a list-like object or as variadic arguments 322 323 `allow_current` 324 : whether non-parent relative paths with no shared components should be considered to have a common base path (see `common_with()`) 325 326 `allow_parents` 327 : 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. 328 329 `encoding` 330 : the text encoding that should be used to decode bytes-like objects in `paths`, if any (see `__init__()`). 331 332 Returns 333 ------- 334 a dictionary that maps the common base path of each partition to a list of relative paths 335 336 Raises 337 ------ 338 `ValueError` 339 if any of the GPaths are invalid 340 341 Examples 342 -------- 343 ```python 344 GPath.partition("/usr/bin", "/usr/local/bin", "../../doc", "C:/Windows", "C:/Program Files") 345 346 assert partitions == { 347 GPath("/usr") : [GPath("bin"), GPath("local")], 348 GPath("../../doc") : [GPath("")], 349 GPath("C:/") : [GPath("Windows"), GPath("Program Files")], 350 } 351 ``` 352 """ 353 flattened_paths: list[GPathLike] = [] 354 for path_or_list in paths: 355 if _is_gpathlike(path_or_list): 356 flattened_paths.append(path_or_list) 357 else: 358 flattened_paths.extend(path_or_list) 359 gpaths = [path if isinstance(path, GPath) else GPath(path, encoding=encoding) for path in flattened_paths] 360 361 partition_map = {} 362 if len(gpaths) > 0: 363 if allow_parents == True: 364 partition_map[gpaths[0]] = [] 365 else: 366 partition_map[gpaths[0]] = [gpaths[0]] 367 368 for path in gpaths[1:]: 369 partition_found = False 370 for partition in partition_map: 371 candidate_common = partition.common_with(path, allow_current=allow_current, allow_parents=allow_parents) 372 if candidate_common is not None: 373 partition_found = True 374 if candidate_common != partition: 375 partition_map[candidate_common] = partition_map[partition] 376 del partition_map[partition] 377 if allow_parents == False: 378 partition_map[candidate_common].append(path) 379 break 380 if not partition_found: 381 if allow_parents == True: 382 partition_map[path] = [] 383 else: 384 partition_map[path] = [path] 385 386 for partition, path_list in partition_map.items(): 387 partition_map[partition] = [path.subpath_from(partition) for path in path_list] 388 389 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.
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")],
}
398 @staticmethod 399 def join(*paths, encoding: Optional[str]=None) -> GPath: 400 """ 401 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. 402 403 Parameters 404 ---------- 405 `paths`: `Sequence[GPath | str | bytes | os.PathLike]` or `*paths: GPath | str | bytes | os.PathLike` 406 : the paths to be combined, which can be given as either a list-like object or as variadic arguments 407 408 `encoding` 409 : the text encoding that should be used to decode bytes-like objects in `paths`, if any (see `__init__()`). 410 411 Returns 412 ------- 413 the combined path 414 415 Raises 416 ------ 417 `ValueError` if any of the GPaths are invalid 418 419 Examples 420 -------- 421 ```python 422 GPath.join("usr", "local", "bin") # GPath("usr/local/bin") 423 GPath.join("/usr/local/bin", "../../bin") # GPath("/usr/bin") 424 GPath.join("C:/", "Windows") # GPath("C:/Windows") 425 ``` 426 """ 427 flattened_paths: list[GPathLike] = [] 428 for path_or_list in paths: 429 if _is_gpathlike(path_or_list): 430 flattened_paths.append(path_or_list) 431 else: 432 flattened_paths.extend(path_or_list) 433 434 if len(flattened_paths) == 0: 435 return GPath(encoding=encoding) 436 437 combined_path = flattened_paths[0] 438 if not isinstance(combined_path, GPath): 439 combined_path = GPath(combined_path, encoding=encoding) 440 for path in flattened_paths[1:]: 441 combined_path = combined_path + path 442 443 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
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")
446 def as_relative(self, parent_level: Optional[int]=None) -> GPath: 447 """ 448 Convert the path to a relative path and return a new copy. 449 450 Parameters 451 ---------- 452 `parent_level` 453 : 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. 454 455 Raises 456 ------ 457 `TypeError` if `parent_level` is not a valid type 458 459 Examples 460 -------- 461 ```python 462 GPath("/usr/bin").as_relative() # GPath("usr/bin") 463 GPath("C:/Windows").as_relative() # GPath("C:Windows") 464 GPath("../Documents").as_relative() # GPath("../Documents") 465 ``` 466 """ 467 468 new_path = GPath(self) 469 new_path._root = False 470 if parent_level is None: 471 pass 472 elif isinstance(parent_level, int): 473 new_path._parent_level = parent_level 474 else: 475 raise TypeError(f"parent_level must be an int: {parent_level} ({type(parent_level)})") 476 477 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")
480 def as_absolute(self) -> GPath: 481 """ 482 Convert the path to an absolute path and return a new copy. 483 484 Any parent directory that the path is relative to will be removed. If the path is already absolute, an identical copy is returned. 485 486 Examples 487 -------- 488 ```python 489 GPath("usr/bin").as_absolute() # GPath("/usr/bin") 490 GPath("../Documents").as_absolute() # GPath("/Documents") 491 GPath("C:Windows").as_absolute() # GPath("C:/Windows") 492 ``` 493 """ 494 new_path = GPath(self) 495 new_path._root = True 496 new_path._parent_level = 0 497 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")
500 def with_drive(self, drive: Union[str, bytes, None]=None) -> GPath: 501 """ 502 Return a new copy of the path with the drive set to `drive`. 503 504 If `drive` is `""` or None, this would be equivalent to `without_drive()`. 505 506 Parameters 507 ---------- 508 `drive` 509 : the drive for the returned path, or either `""` or None if the returned path should have no drive 510 511 Returns 512 ------- 513 `GPath` 514 : a new path with the given drive 515 516 Raises 517 ------ 518 - `TypeError` if `drive` is not a valid type 519 - `ValueError` if `drive` has more than one character 520 521 Examples 522 -------- 523 ```python 524 GPath("C:/Windows").with_drive() # GPath("/Windows") 525 GPath("C:/Windows").with_drive("D") # GPath("D:/Windows") 526 GPath("/Windows").with_drive("C") # GPath("C:/Windows") 527 ``` 528 """ 529 if drive is None: 530 drive = "" 531 elif isinstance(drive, bytes): 532 if self._encoding is None: 533 drive = drive.decode(DEFAULT_ENCODING) 534 else: 535 drive = drive.decode(self._encoding) 536 elif isinstance(drive, str): 537 pass 538 else: 539 raise TypeError(f"drive must be a str or bytes object: {drive} ({type(drive)})") 540 541 if len(drive) > 1: 542 raise ValueError(f"drive can only be a single character, an empty string or None: {drive}") 543 544 new_path = GPath(self) 545 new_path._drive = drive 546 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")
549 def without_drive(self) -> GPath: 550 """ 551 Return a new copy of the path without a drive. 552 553 Equivalent to `with_drive("")` or `with_drive(None)`. 554 555 Returns 556 ------- 557 `GPath` 558 : a new path without a drive 559 560 Examples 561 -------- 562 ```python 563 GPath("C:/Windows").without_drive() # GPath("/Windows") 564 ``` 565 """ 566 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")
569 def common_with(self, other: GPathLike, allow_current: bool=True, allow_parents: bool=False) -> Optional[GPath]: 570 """ 571 Find the longest common base path shared between `self` and `other`, or return None if no such path exists. 572 573 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. 574 575 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>). 576 577 Parameters 578 ---------- 579 `other` 580 : the path to compare with 581 582 `allow_current` 583 : 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. 584 585 `allow_parents` 586 : 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. 587 588 Returns 589 ------- 590 `GPath` 591 : the longest common base path, which may be empty, if it exists 592 593 `None` 594 : otherwise 595 596 Raises 597 ------ 598 `ValueError` if either `self` or `other` is an invalid GPath 599 600 Examples 601 -------- 602 ```python 603 GPath("/usr/bin").find_common("/usr/local/bin") # GPath("/usr") 604 GPath("C:/Windows/System32").find_common("C:/Program Files") # GPath("C:/") 605 GPath("../Documents").find_common("../Pictures") # GPath("..") 606 ``` 607 """ 608 self._validate() 609 if isinstance(other, GPath): 610 other._validate() 611 else: 612 other = GPath(other, encoding=self._encoding) 613 614 if self._drive != other._drive: 615 return None 616 if self._root != other._root: 617 return None 618 619 if allow_parents: 620 allow_current = True 621 622 parts = [] 623 if self._root: 624 common_path = GPath(self) 625 for part1, part2 in zip(self._parts, other._parts): 626 if part1 == part2: 627 parts.append(part1) 628 else: 629 if self._parent_level != other._parent_level: 630 if not allow_parents: 631 return None 632 633 common_path = GPath(self) 634 common_path._parent_level = max(self._parent_level, other._parent_level) 635 else: 636 common_path = GPath(self) 637 for part1, part2 in zip(self._parts, other._parts): 638 if part1 == part2: 639 parts.append(part1) 640 641 common_path._parts = tuple(parts) 642 643 if not allow_current and not bool(common_path): 644 if common_path != self or common_path != other: 645 return None 646 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("..")
649 def subpath_from(self, base: GPathLike) -> Optional[GPath]: 650 """ 651 Find the relative subpath from `base` to `self` if possible and if `base` contains `self`, or return None otherwise. 652 653 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. 654 655 Similar to `relpath_from()`, but `self` must be a descendent of `base`. 656 657 Parameters 658 ---------- 659 `base` 660 : the base path that the relative subpath should start from 661 662 Returns 663 ------- 664 `GPath` 665 : relative subpath from `base` to `self`, which may be empty, if it exists 666 667 `None` 668 : otherwise 669 670 Raises 671 ------ 672 `ValueError` if either `self` or `base` is an invalid GPath 673 674 Examples 675 -------- 676 ```python 677 GPath("/usr/local/bin").subpath_from("/usr") # GPath("local/bin") 678 GPath("/usr/bin").subpath_from("/usr/local/bin") # None 679 GPath("/usr/bin").subpath_from("../Documents") # None 680 ``` 681 """ 682 if not isinstance(base, GPath): 683 base = GPath(base, encoding=self._encoding) 684 685 if self.common_with(base, allow_current=True, allow_parents=False) is not None and self in base: 686 # 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 687 base_length = len(base._parts) 688 new_path = GPath(self) 689 new_path._parts = self._parts[base_length:] # () when self == base 690 new_path._drive = "" 691 new_path._root = False 692 new_path._parent_level = 0 693 return new_path 694 else: 695 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
698 def relpath_from(self, origin: GPathLike) -> Optional[GPath]: 699 """ 700 Find the relative path from `origin` to `self` if possible, or return None otherwise. 701 702 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. 703 704 Similar to `subpath_from()`, but `self` does not need to be a descendent of `origin`. 705 706 Parameters 707 ---------- 708 `origin` 709 : the origin that the relative path should start from 710 711 Returns 712 ------- 713 `GPath` 714 : relative path from `origin` to `self`, which may be empty, if it exists 715 716 `None` 717 : otherwise 718 719 Raises 720 ------ 721 `ValueError` if either `self` or `origin` is an invalid GPath 722 723 Examples 724 -------- 725 ```python 726 GPath("/usr/local/bin").subpath_from("/usr") # GPath("local/bin") 727 GPath("/usr/bin").subpath_from("/usr/local/bin") # GPath("../../bin") 728 GPath("/usr/bin").subpath_from("../Documents") # None 729 ``` 730 """ 731 self._validate() 732 if not isinstance(origin, GPath): 733 origin = GPath(origin, encoding=self._encoding) 734 735 if origin._root: 736 common = self.common_with(origin) 737 if common is None: 738 return None 739 740 new_path = GPath(self) 741 new_path._parent_level = len(origin) - len(common) 742 new_path._parts = self._parts[len(common):] 743 new_path._drive = "" 744 new_path._root = False 745 return new_path 746 747 else: 748 common = self.common_with(origin, allow_current=True, allow_parents=True) 749 if common is None: 750 return None 751 if common._parent_level > self._parent_level: 752 return None # Path from common to self's parent cannot be known 753 754 # common._dotdot == self._dotdot 755 # origin._dotdot <= self._dotdot 756 757 new_path = GPath(self) 758 new_path._drive = "" 759 new_path._root = False 760 if len(common) == 0: 761 if origin._parent_level == self._parent_level: 762 new_path._parent_level = len(origin) 763 else: 764 new_path._parent_level = (common._parent_level - origin._parent_level) + len(origin) 765 new_path._parts = self._parts 766 else: 767 new_path._parent_level = len(origin) - len(common) 768 new_path._parts = self._parts[len(common):] 769 770 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
773 def __hash__(self) -> int: 774 """ 775 Calculate hash of the GPath object. 776 777 Usage: <code>hash(<var>g</var>)</code> 778 """ 779 return hash(self._tuple)
Calculate hash of the GPath object.
Usage: hash(g)
782 def __eq__(self, other: GPathLike) -> bool: 783 """ 784 Check if two GPaths are completely identical. 785 786 Always return False if `other` is not a GPath object, even if it is a GPath-like object. 787 788 Usage: <code><var>g1</var> == <var>g2</var></code> 789 790 Examples 791 -------- 792 ```python 793 GPath("/usr/bin") == GPath("/usr/bin") # True 794 GPath("/usr/bin") == GPath("usr/bin") # False 795 GPath("C:/") == GPath("D:/") # False 796 ``` 797 """ 798 if not isinstance(other, GPath): 799 other = GPath(other, encoding=self._encoding) 800 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
803 def __bool__(self) -> bool: 804 """ 805 Truthy if `self` is an absolute path, if `self` is relative to a parent directory, or if `self` has at least one named component. 806 807 Usage: <code>bool(<var>g</var>)</code>, <code>not <var>g</var></code>, or <code>if <var>g</var>:</code> 808 809 Examples 810 -------- 811 ```python 812 bool(GPath("/")) # True 813 bool(GPath("..")) # True 814 bool(GPath("doc")) # True 815 bool(GPath("")) # False 816 ``` 817 """ 818 return self._root or self._drive != "" or self._parent_level != 0 or len(self._parts) > 0
Truthy if self
is an absolute path, if self
is relative to a parent directory, or if self
has at least one named component.
Usage: bool(g)
, not g
, or if g:
Examples
bool(GPath("/")) # True
bool(GPath("..")) # True
bool(GPath("doc")) # True
bool(GPath("")) # False
821 def __str__(self) -> str: 822 """ 823 Return a string representation of the path. 824 825 Usage: <code>str(<var>g</var>)</code> 826 """ 827 if bool(self): 828 if self.root and self._drive == "": 829 return _rules.generic_rules.roots[0] 830 else: 831 return (self._drive + _rules.generic_rules.drive_postfixes[0] if self._drive != "" else "") + (_rules.generic_rules.roots[0] if self._root else "") + _rules.generic_rules.separators[0].join(self.relative_parts) 832 else: 833 return _rules.generic_rules.current_indicators[0]
Return a string representation of the path.
Usage: str(g)
836 def __repr__(self) -> str: 837 """ 838 Return a string that, when printed, gives the Python code associated with instantiating the GPath object. 839 840 Usage: <code>repr(<var>g</var>)</code> 841 """ 842 if self._encoding is None: 843 encoding_repr = "" 844 else: 845 encoding_repr = f", encoding={repr(self._encoding)}" 846 847 if bool(self): 848 return f"GPath({repr(str(self))}{encoding_repr})" 849 else: 850 return f"GPath({repr('')}{encoding_repr})"
Return a string that, when printed, gives the Python code associated with instantiating the GPath object.
Usage: repr(g)
853 def __len__(self) -> int: 854 """ 855 Get the number of named path components, excluding any drive name or parent directories. 856 857 Usage: <code>len(<var>g</var>)</code> 858 859 Examples 860 -------- 861 ```python 862 len(GPath("/usr/bin")) # 2 863 len(GPath("/")) # 0 864 len(GPath("C:/Windows")) # 0 865 len(GPath("C:/")) # 0 866 ``` 867 """ 868 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
871 def __getitem__(self, index: Union[int, slice]) -> Union[str, list[str]]: 872 """ 873 Get a 0-indexed named path component, or a slice of path components, excluding any drive name or parent directories. 874 875 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. 876 877 Examples 878 -------- 879 ```python 880 GPath("/usr/local/bin")[1] # "local" 881 GPath("/usr/local/bin")[-1] # "bin" 882 GPath("/usr/local/bin")[1:] # ["local", "bin"] 883 GPath("/usr/local/bin")[::2] # ["usr", "bin"] 884 ``` 885 """ 886 if isinstance(index, int): 887 return self._parts[index] 888 elif isinstance(index, slice): 889 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"]
892 def __iter__(self) -> Iterator[str]: 893 """ 894 Get an iterator through the named path components, excluding any drive name or parent directories. 895 896 Usage: <code>iter(<var>g</var>)</code> or <code>for <var>p</var> in <var>g</var>:</code> 897 """ 898 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:
901 def __contains__(self, other: GPathLike) -> bool: 902 """ 903 Check if the path represented by `self` contains the path represented by `other`; i.e. check if `self` is a parent directory of `other`. 904 905 Usage: <code><var>other</var> in <var>self</var></code> 906 907 Raises `ValueError` if either GPath is invalid 908 909 Examples 910 -------- 911 ```python 912 GPath("/usr/local/bin") in GPath("/usr") # True 913 GPath("/usr/local/bin") in GPath("/bin") # False 914 GPath("..") in GPath("../..") # True 915 GPath("..") in GPath("C:/") # False 916 ``` 917 """ 918 if not isinstance(other, GPath): 919 other = GPath(other, encoding=self._encoding) 920 921 common_path = self.common_with(other, allow_current=True, allow_parents=True) 922 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
925 def __add__(self, other: GPathLike) -> GPath: 926 """ 927 Add (concatenate) `other` to the end of `self`, and return a new copy. 928 929 If `other` is an absolute path, the returned path will be an absolute path that matches `other`, apart from the drive name. 930 931 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. 932 933 Alias: `__truediv__()` 934 935 Usage: <code><var>self</var> + <var>other</var></code> or <code><var>self</var> / <var>other</var></code> 936 937 Raises `ValueError` if either GPath is invalid 938 939 Examples 940 -------- 941 ```python 942 GPath("/usr") + GPath("local/bin") # GPath("/usr/local/bin") 943 GPath("C:/Windows/System32") + GPath("../SysWOW64") # GPath("C:/Windows/SysWOW64") 944 GPath("C:/Windows/System32") + GPath("/usr/bin") # GPath("C:/usr/bin") 945 GPath("..") + GPath("../..") # GPath("../../..") 946 GPath("..") / GPath("../..") # GPath("../../..") 947 ``` 948 """ 949 if isinstance(other, GPath): 950 other._validate 951 else: 952 other = GPath(other, encoding=self._encoding) 953 954 new_path = GPath(self) 955 if other._root: 956 new_path._parts = other._parts 957 new_path._root = other._root 958 new_path._parent_level = other._parent_level 959 else: 960 new_parts = [part for part in self._parts] 961 for i in range(other._parent_level): 962 if len(new_parts) > 0: 963 new_parts.pop() 964 elif not new_path._root: 965 new_path._parent_level += 1 966 else: 967 pass # parent of directory of root is still root 968 969 new_parts.extend(other._parts) 970 new_path._parts = tuple(new_parts) 971 972 if other._drive != "": 973 new_path._drive = other._drive 974 975 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("../../..")
978 def __sub__(self, n: int) -> GPath: 979 """ 980 Remove `n` components from the end of the path and return a new copy. 981 982 Usage: <code><var>self</var> - <var>n</var></code> 983 984 Raises `ValueError` if `self` is an invalid GPath or if `n` is negative 985 986 Examples 987 -------- 988 ```python 989 GPath("C:/Windows/System32") - 1 # GPath("C:/Windows") 990 GPath("/usr/bin") - 2 # GPath("/") 991 GPath("Documents") - 3 # GPath("..") 992 GPath("/") - 1 # GPath("/") 993 ``` 994 """ 995 if n < 0: 996 raise ValueError("cannot subtract a negative number of components from the path: {n}; use __add__() instead") 997 998 new_path = GPath(self) 999 new_parts = [part for part in self._parts] 1000 for i in range(n): 1001 if len(new_parts) > 0: 1002 new_parts.pop() 1003 elif not new_path._root: 1004 new_path._parent_level += 1 1005 else: 1006 pass # removing components from root should still give root 1007 new_path._parts = tuple(new_parts) 1008 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("/")
1011 def __mul__(self, n: int) -> GPath: 1012 """ 1013 Duplicate the named components of `self` `n` times and return a new path with the duplicated components. 1014 1015 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. 1016 1017 If `n` is 0, the result is an empty path (either relative or absolute). 1018 1019 Usage: <code><var>self</var> * <var>n</var></code> 1020 1021 Raises `ValueError` if `self` is an invalid GPath or if `n` is negative. 1022 1023 Examples 1024 -------- 1025 ```python 1026 GPath("/usr/bin") * 2 # GPath("/usr/bin/usr/bin") 1027 GPath("../docs") * 2 # GPath("../../docs/docs") 1028 GPath("C:/Windows") * 0 # GPath("C:/") 1029 ``` 1030 """ 1031 if n < 0: 1032 raise ValueError("cannot multiply path by a negative integer: {n}") 1033 new_path = GPath(self) 1034 new_path._parent_level = self._parent_level * n 1035 new_path._parts = self._parts * n 1036 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:/")
1039 def __truediv__(self, other: GPathLike) -> GPath: 1040 """ 1041 Alias of `__add__()`. 1042 1043 Usage: <code><var>self</var> + <var>other</var></code> or <code><var>self</var> / <var>other</var></code> 1044 """ 1045 return self.__add__(other)
Alias of __add__()
.
Usage: self + other
or self / other
1048 def __and__(self, other: GPathLike) -> Union[GPath, None]: 1049 """ 1050 Equivalent to `self.common_with(other)`, using the default options of `common_with()`. 1051 1052 Usage: <code><var>g1</var> & <var>g2</var></code> 1053 """ 1054 return self.common_with(other)
Equivalent to self.common_with(other)
, using the default options of common_with()
.
Usage: g1 & g2
1057 def __lshift__(self, n: int) -> GPath: 1058 """ 1059 Move the imaginary current working directory `n` steps up the filesystem tree. 1060 1061 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. 1062 1063 If `n` is negative, this is equivalent to `__rshift__(-n)`. 1064 1065 Usage: <code><var>self</var> << <var>n</var></code> 1066 1067 Raises `ValueError` if `self` is an invalid GPath. 1068 1069 Examples 1070 -------- 1071 ```python 1072 GPath("../SysWOW64/drivers") << 1 # GPath("SysWOW64/drivers") 1073 GPath("../doc") << 2 # GPath("doc") 1074 GPath("/usr/bin") << 2 # GPath("/usr/bin") 1075 ``` 1076 """ 1077 if n < 0: 1078 return self.__rshift__(-1 * n) 1079 new_path = GPath(self) 1080 if not new_path._root: 1081 new_path._parent_level = max(new_path._parent_level - n, 0) 1082 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")
1085 def __rshift__(self, n: int) -> GPath: 1086 """ 1087 Move the imaginary current working directory `n` steps down the filesystem tree. 1088 1089 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. 1090 1091 If `n` is negative, this is equivalent to `__lshift__(-n)`. 1092 1093 Usage: <code><var>self</var> >> <var>n</var></code> 1094 1095 Raises `ValueError` if `self` is an invalid GPath 1096 1097 Examples 1098 -------- 1099 ```python 1100 GPath("../SysWOW64/drivers") >> 1 # GPath("../../SysWOW64/drivers") 1101 GPath("/usr/bin") >> 2 # GPath("/usr/bin") 1102 ``` 1103 """ 1104 if n < 0: 1105 return self.__lshift__(-1 * n) 1106 new_path = GPath(self) 1107 if not new_path._root: 1108 new_path._parent_level += n 1109 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__