Coverage for bioimageio/spec/_internal/utils.py: 67%

76 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-18 12:47 +0000

1from __future__ import annotations 

2 

3import re 

4import sys 

5from functools import wraps 

6from inspect import signature 

7from pathlib import Path 

8from typing import ( 

9 Any, 

10 Callable, 

11 Dict, 

12 Set, 

13 Tuple, 

14 Type, 

15 TypeVar, 

16 Union, 

17) 

18 

19from ruyaml import Optional 

20from typing_extensions import ParamSpec 

21 

22if sys.version_info < (3, 10): # pragma: no cover 

23 SLOTS: Dict[str, bool] = {} 

24else: 

25 SLOTS = {"slots": True} 

26 

27 

28K = TypeVar("K") 

29V = TypeVar("V") 

30NestedDict = Dict[K, "NestedDict[K, V] | V"] 

31 

32if sys.version_info < (3, 9): # pragma: no cover 

33 from functools import lru_cache as cache 

34 

35 def files(package_name: str): 

36 assert package_name == "bioimageio.spec", package_name 

37 return Path(__file__).parent.parent 

38 

39else: 

40 from functools import cache as cache 

41 from importlib.resources import files as files 

42 

43 

44def get_format_version_tuple(format_version: Any) -> Optional[Tuple[int, int, int]]: 

45 if ( 

46 not isinstance(format_version, str) 

47 or format_version.count(".") != 2 

48 or any(not v.isdigit() for v in format_version.split(".")) 

49 ): 

50 return None 

51 

52 parsed = tuple(map(int, format_version.split("."))) 

53 assert len(parsed) == 3 

54 return parsed 

55 

56 

57def nest_dict(flat_dict: Dict[Tuple[K, ...], V]) -> NestedDict[K, V]: 

58 res: NestedDict[K, V] = {} 

59 for k, v in flat_dict.items(): 

60 node: Union[Dict[K, Union[NestedDict[K, V], V]], NestedDict[K, V]] = res 

61 for kk in k[:-1]: 

62 if not isinstance(node, dict): 

63 raise ValueError(f"nesting level collision for flat key {k} at {kk}") 

64 d: NestedDict[K, V] = {} 

65 node = node.setdefault(kk, d) # type: ignore 

66 

67 if not isinstance(node, dict): 

68 raise ValueError(f"nesting level collision for flat key {k}") 

69 

70 node[k[-1]] = v 

71 

72 return res 

73 

74 

75FirstK = TypeVar("FirstK") 

76 

77 

78def nest_dict_with_narrow_first_key( 

79 flat_dict: Dict[Tuple[K, ...], V], first_k: Type[FirstK] 

80) -> Dict[FirstK, "NestedDict[K, V] | V"]: 

81 """convenience function to annotate a special version of a NestedDict. 

82 Root level keys are of a narrower type than the nested keys. If not a ValueError is raisd. 

83 """ 

84 nested = nest_dict(flat_dict) 

85 invalid_first_keys = [k for k in nested if not isinstance(k, first_k)] 

86 if invalid_first_keys: 

87 raise ValueError(f"Invalid root level keys: {invalid_first_keys}") 

88 

89 return nested # type: ignore 

90 

91 

92def unindent(text: str, ignore_first_line: bool = False): 

93 """remove minimum count of spaces at beginning of each line. 

94 

95 Args: 

96 text: indented text 

97 ignore_first_line: allows to correctly unindent doc strings 

98 """ 

99 first = int(ignore_first_line) 

100 lines = text.split("\n") 

101 filled_lines = [line for line in lines[first:] if line] 

102 if len(filled_lines) < 2: 

103 return "\n".join(line.strip() for line in lines) 

104 

105 indent = min(len(line) - len(line.lstrip(" ")) for line in filled_lines) 

106 return "\n".join(lines[:first] + [line[indent:] for line in lines[first:]]) 

107 

108 

109T = TypeVar("T") 

110P = ParamSpec("P") 

111 

112 

113def assert_all_params_set_explicitly(fn: Callable[P, T]) -> Callable[P, T]: 

114 @wraps(fn) 

115 def wrapper(*args: P.args, **kwargs: P.kwargs): 

116 n_args = len(args) 

117 missing: Set[str] = set() 

118 

119 for p in signature(fn).parameters.values(): 

120 if p.kind == p.POSITIONAL_ONLY: 

121 if n_args == 0: 

122 missing.add(p.name) 

123 else: 

124 n_args -= 1 # 'use' positional arg 

125 elif p.kind == p.POSITIONAL_OR_KEYWORD: 

126 if n_args == 0: 

127 if p.name not in kwargs: 

128 missing.add(p.name) 

129 else: 

130 n_args -= 1 # 'use' positional arg 

131 elif p.kind in (p.VAR_POSITIONAL, p.VAR_KEYWORD): 

132 pass 

133 elif p.kind == p.KEYWORD_ONLY: 

134 if p.name not in kwargs: 

135 missing.add(p.name) 

136 

137 assert not missing, f"parameters {missing} of {fn} are not set explicitly" 

138 

139 return fn(*args, **kwargs) 

140 

141 return wrapper 

142 

143 

144def get_os_friendly_file_name(name: str) -> str: 

145 return re.sub(r"\W+|^(?=\d)", "_", name)