|
1 | 1 | #!/usr/bin/env python3 |
2 | 2 | """Filesystem related util functions.""" |
3 | 3 |
|
| 4 | +from collections import namedtuple |
4 | 5 | from typing import Iterable, Callable |
5 | 6 | import itertools |
6 | 7 | import os |
|
18 | 19 | import dulwich.porcelain |
19 | 20 |
|
20 | 21 | HOME = Path.home() |
| 22 | +PosixPathPair = namedtuple("PosixPathPair", ["prefix", "base"]) |
21 | 23 |
|
22 | 24 |
|
23 | 25 | def copy_if_exists(src: str, dst: str | Path = HOME) -> bool: |
@@ -691,3 +693,28 @@ def filter( |
691 | 693 | if sub_pattern: |
692 | 694 | return _filter_sp(path, pattern=pattern, sub_pattern=sub_pattern) |
693 | 695 | return _filter_num(path, pattern=pattern, num_lines=num_lines) |
| 696 | + |
| 697 | + |
| 698 | +def trace_dir_upwards(path: str | Path, name: str) -> PosixPathPair: |
| 699 | + """Find the parent directory with the specified name. |
| 700 | +
|
| 701 | + Args: |
| 702 | + path: A local path contains `/name/`. |
| 703 | + name: The base name (stem) of the parent directory. |
| 704 | +
|
| 705 | + Returns: |
| 706 | + A PosixPathPair which contains the parent directory |
| 707 | + and the relative path to this parent directory. |
| 708 | + """ |
| 709 | + |
| 710 | + def _trace_dir_upwards(path: Path) -> Path: |
| 711 | + while (stem := path.stem) != name: |
| 712 | + if not stem: |
| 713 | + raise ValueError(f"The path {path} does not contain /{name}/!") |
| 714 | + path = path.parent |
| 715 | + return path |
| 716 | + |
| 717 | + if isinstance(path, str): |
| 718 | + path = Path(path) |
| 719 | + prefix = _trace_dir_upwards(path) |
| 720 | + return PosixPathPair(prefix, path.relative_to(prefix)) |
0 commit comments