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