Coverage for /Users/coordt/Documents/code/bump-my-version/bumpversion/utils.py: 70%

39 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-06-12 09:26 -0500

1"""General utilities.""" 

2 

3import string 

4from typing import Any, List, Tuple 

5 

6 

7def extract_regex_flags(regex_pattern: str) -> Tuple[str, str]: 

8 """ 

9 Extract the regex flags from the regex pattern. 

10 

11 Args: 

12 regex_pattern: The pattern that might start with regex flags 

13 

14 Returns: 

15 A tuple of the regex pattern without the flag string and regex flag string 

16 """ 

17 import re 

18 

19 flag_pattern = r"^(\(\?[aiLmsux]+\))" 

20 bits = re.split(flag_pattern, regex_pattern) 

21 return (regex_pattern, "") if len(bits) == 1 else (bits[2], bits[1]) 

22 

23 

24def recursive_sort_dict(input_value: Any) -> Any: 

25 """Sort a dictionary recursively.""" 

26 if not isinstance(input_value, dict): 

27 return input_value 

28 

29 return {key: recursive_sort_dict(input_value[key]) for key in sorted(input_value.keys())} 

30 

31 

32def key_val_string(d: dict) -> str: 

33 """Render the dictionary as a comma-delimited key=value string.""" 

34 return ", ".join(f"{k}={v}" for k, v in sorted(d.items())) 

35 

36 

37def labels_for_format(serialize_format: str) -> List[str]: 

38 """Return a list of labels for the given serialize_format.""" 

39 return [item[1] for item in string.Formatter().parse(serialize_format) if item[1]] 

40 

41 

42def get_overrides(**kwargs) -> dict: 

43 """Return a dictionary containing only the overridden key-values.""" 

44 return {key: val for key, val in kwargs.items() if val is not None} 

45 

46 

47def get_nested_value(d: dict, path: str) -> Any: 

48 """ 

49 Retrieves the value of a nested key in a dictionary based on the given path. 

50 

51 Args: 

52 d: The dictionary to search. 

53 path: A string representing the path to the nested key, separated by periods. 

54 

55 Returns: 

56 The value of the nested key. 

57 

58 Raises: 

59 KeyError: If a key in the path does not exist. 

60 ValueError: If an element in the path is not a dictionary. 

61 """ 

62 keys = path.split(".") 

63 current_element = d 

64 

65 for key in keys: 

66 if not isinstance(current_element, dict): 66 ↛ 67line 66 didn't jump to line 67, because the condition on line 66 was never true

67 raise ValueError(f"Element at '{'.'.join(keys[:keys.index(key)])}' is not a dictionary") 

68 

69 if key not in current_element: 69 ↛ 70line 69 didn't jump to line 70, because the condition on line 69 was never true

70 raise KeyError(f"Key '{key}' not found at '{'.'.join(keys[:keys.index(key)])}'") 

71 

72 current_element = current_element[key] 

73 

74 return current_element 

75 

76 

77def set_nested_value(d: dict, value: Any, path: str) -> None: 

78 """ 

79 Sets the value of a nested key in a dictionary based on the given path. 

80 

81 Args: 

82 d: The dictionary to search. 

83 value: The value to set. 

84 path: A string representing the path to the nested key, separated by periods. 

85 

86 Raises: 

87 ValueError: If an element in the path is not a dictionary. 

88 """ 

89 keys = path.split(".") 

90 last_element = keys[-1] 

91 current_element = d 

92 

93 for i, key in enumerate(keys): 

94 if key == last_element: 

95 current_element[key] = value 

96 elif key not in current_element: 96 ↛ 97line 96 didn't jump to line 97, because the condition on line 96 was never true

97 raise KeyError(f"Key '{key}' not found at '{'.'.join(keys[:keys.index(key)])}'") 

98 elif not isinstance(current_element[key], dict): 98 ↛ 99line 98 didn't jump to line 99, because the condition on line 98 was never true

99 raise ValueError(f"Path '{'.'.join(keys[:i+1])}' does not lead to a dictionary.") 

100 else: 

101 current_element = current_element[key]