Coverage for texgit / repository / fix_path.py: 100%

25 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-22 02:50 +0000

1"""A tool for fixing all occurrences of a Path.""" 

2 

3from contextlib import suppress 

4from itertools import chain 

5from os import sep 

6from re import MULTILINE, Match, escape, sub 

7from typing import Final 

8 

9from pycommons.io.path import Path 

10 

11#: the replacement string for base paths 

12BASE_PATH_REPLACEMENT: Final[str] = "{...}" 

13 

14#: the literal string start and ends. 

15__SE: Final[tuple[tuple[str, str], ...]] = tuple(sorted(chain( 

16 ((escape(strs), escape(stre)) for strs, stre in ( 

17 (" ", " "), ("'", "'"), ("(", ")"), ("{", "}"), ("[", "]"), 

18 ("<", ">"), ("`", "`"), (",", " "), (",", ","), ('"', '"'), 

19 (";", " "), (";", ";"), (" ", ". "))), 

20 (("^", "$"), ("^", " "), (" ", "$"), ("^", ","), (",", "$"), 

21 ("^", ";"), (";", "$"), ("^", escape(". ")), 

22 ("^", escape(".") + "$"))), 

23)) 

24 

25 

26def replace_base_path(orig: str, base_path: str) -> str: 

27 r""" 

28 Replace all occurrences of the `base_path` in the original string. 

29 

30 Any reasonably delimited occurrence of `base_path` as well as any sub-path 

31 under `base_path` that points to an existing file or directory are 

32 replaced with relativizations starting with `{...}`. 

33 

34 :param orig: the original string 

35 :param base_path: the base path 

36 :return: the fixed string 

37 

38 >>> from pycommons.io.temp import temp_dir 

39 >>> with temp_dir() as td: 

40 ... td.resolve_inside("x").ensure_dir_exists() 

41 ... td.resolve_inside("x/y").write_all_str("5") 

42 ... a = replace_base_path(f"blablabla {td}/x ", td) 

43 ... b = replace_base_path(f"{td}/x/y", td) 

44 ... c = replace_base_path(f"{td}/x.", td) 

45 ... d = replace_base_path("\n".join(("blaa", f"{td}/x.x", "y")), td) 

46 ... e = replace_base_path("\n".join(("blaa", f"{td}/x.", "y")), td) 

47 ... f = replace_base_path(f"xc'{td}/x/y'yy", td) 

48 ... g = replace_base_path(td, td) 

49 ... h = replace_base_path(td + "/", td) 

50 >>> a 

51 'blablabla {...}/x ' 

52 

53 >>> b 

54 '{...}/x/y' 

55 

56 >>> c 

57 '{...}/x.' 

58 

59 >>> d[-6:] 

60 '/x.x\ny' 

61 

62 >>> e 

63 'blaa\n{...}/x.\ny' 

64 

65 >>> f 

66 "xc'{...}/x/y'yy" 

67 

68 >>> g 

69 '{...}' 

70 

71 >>> h 

72 '{...}/' 

73 """ 

74 if str.__len__(orig) <= 0: 

75 return "" 

76 path: Final[Path] = Path(base_path) 

77 path_re: Final[str] = escape(path) 

78 

79 def __replacer(data: Match, __bp: Path = path) -> str: 

80 prefix: Final[str] = data.group(1) 

81 subpath: Final[str] = data.group(2) 

82 suffix: Final[str] = data.group(3) 

83 with suppress(ValueError): 

84 usesubpath: Final[str] = subpath[str.__len__(sep):] \ 

85 if subpath.startswith(sep) else subpath 

86 if (str.__len__(usesubpath) <= 0) \ 

87 or __bp.resolve_inside(usesubpath).exists(): 

88 return f"{prefix}{{...}}{subpath}{suffix}" 

89 return data.group(0) 

90 

91 for start, end in __SE: 

92 orig = sub(f"({start}){path_re}(.*?)({end})", __replacer, orig, 

93 flags=MULTILINE) 

94 return orig