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

62 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-06-11 14:29 -0500

1"""Functions for displaying information about the version.""" 

2 

3import dataclasses 

4from io import StringIO 

5from pprint import pprint 

6from typing import Any, Optional 

7 

8from bumpversion.bump import get_next_version 

9from bumpversion.config import Config 

10from bumpversion.context import get_context 

11from bumpversion.exceptions import BadInputError 

12from bumpversion.ui import print_error, print_info 

13from bumpversion.utils import recursive_sort_dict 

14 

15 

16def output_default(value: dict) -> None: 

17 """Output the value with key=value or just value if there is only one item.""" 

18 if len(value) == 1: 

19 print_info(next(iter(value.values()))) 

20 else: 

21 buffer = StringIO() 

22 pprint(value, stream=buffer, sort_dicts=True) # noqa: T203 

23 print_info(buffer.getvalue()) 

24 

25 

26def output_yaml(value: dict) -> None: 

27 """Output the value as yaml.""" 

28 from bumpversion.yaml_dump import dump 

29 

30 print_info(dump(recursive_sort_dict(value))) 

31 

32 

33def output_json(value: dict) -> None: 

34 """Output the value as json.""" 

35 import json 

36 

37 def default_encoder(obj: Any) -> str: 

38 if dataclasses.is_dataclass(obj): 

39 return str(obj) 

40 elif isinstance(obj, type): 

41 return obj.__name__ 

42 raise TypeError(f"Object of type {type(obj), str(obj)} is not JSON serializable") 

43 

44 print_info(json.dumps(value, sort_keys=True, indent=2, default=default_encoder)) 

45 

46 

47OUTPUTTERS = { 

48 "yaml": output_yaml, 

49 "json": output_json, 

50 "default": output_default, 

51} 

52 

53 

54def resolve_name(obj: Any, name: str, default: Any = None, err_on_missing: bool = False) -> Any: 

55 """ 

56 Get a key or attr ``name`` from obj or default value. 

57 

58 Copied and modified from Django Template variable resolutions 

59 

60 Resolution methods: 

61 

62 - Mapping key lookup 

63 - Attribute lookup 

64 - Sequence index 

65 

66 Args: 

67 obj: The object to access 

68 name: A dotted name to the value, such as ``mykey.0.name`` 

69 default: If the name cannot be resolved from the object, return this value 

70 err_on_missing: Raise a `BadInputError` if the name cannot be resolved 

71 

72 Returns: 

73 The value at the resolved name or the default value. 

74 

75 Raises: 

76 BadInputError: If we cannot resolve the name and `err_on_missing` is `True` 

77 

78 # noqa: DAR401 

79 """ 

80 lookups = name.split(".") 

81 current = obj 

82 try: # catch-all for unexpected failures 

83 for bit in lookups: 

84 try: # dictionary lookup 

85 current = current[bit] 

86 # ValueError/IndexError are for numpy.array lookup on 

87 # numpy < 1.9 and 1.9+ respectively 

88 except (TypeError, AttributeError, KeyError, ValueError, IndexError): 

89 try: # attribute lookup 

90 current = getattr(current, bit) 

91 except (TypeError, AttributeError): 

92 # Reraise if the exception was raised by a @property 

93 if bit in dir(current): 

94 raise 

95 try: # list-index lookup 

96 current = current[int(bit)] 

97 except ( 

98 IndexError, # list index out of range 

99 ValueError, # invalid literal for int() 

100 KeyError, # current is a dict without `int(bit)` key 

101 TypeError, 

102 ): # un-subscript-able object 

103 return default 

104 return current 

105 except Exception as e: # pragma: no cover 

106 if err_on_missing: 

107 raise BadInputError(f"Could not resolve '{name}'") from e 

108 else: 

109 return default 

110 

111 

112def do_show(*args, config: Config, format_: str = "default", increment: Optional[str] = None) -> None: 

113 """Show current version or configuration information.""" 

114 config_dict = config.model_dump() 

115 ctx = get_context(config) 

116 

117 if increment: 

118 version = config.version_config.parse(config.current_version) 

119 next_version = get_next_version(version, config, increment, None) 

120 next_version_str = config.version_config.serialize(next_version, ctx) 

121 config_dict["new_version"] = next_version_str 

122 

123 try: 

124 if "all" in args or not args: 

125 show_items = config_dict 

126 else: 

127 show_items = {key: resolve_name(config_dict, key) for key in args} 

128 

129 OUTPUTTERS.get(format_, OUTPUTTERS["default"])(show_items) 

130 except BadInputError as e: 

131 print_error(e.message)