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

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

2

3 import dataclasses

4 from io import StringIO

5 from pprint import pprint

6 from typing import Any, Optional

7

8 from bumpversion.bump import get_next_version

9 from bumpversion.config import Config

10 from bumpversion.exceptions import BadInputError

11 from bumpversion.ui import print_error, print_info

12 from bumpversion.utils import get_context, recursive_sort_dict

13

14

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

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

17 if len(value) == 1:

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

19 else:

20 buffer = StringIO()

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

22 print_info(buffer.getvalue())

23

24

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

26 """Output the value as yaml."""

27 from bumpversion.yaml_dump import dump

28

29 print_info(dump(recursive_sort_dict(value)))

30

31

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

33 """Output the value as json."""

34 import json

35

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

37 if dataclasses.is_dataclass(obj):

38 return str(obj)

39 elif isinstance(obj, type):

40 return obj.__name__

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

42

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

44

45

46 OUTPUTTERS = {

47 "yaml": output_yaml,

48 "json": output_json,

49 "default": output_default,

50}

51

52

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

54 """

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

56

57 Copied and modified from Django Template variable resolutions

58

59 Resolution methods:

60

61 - Mapping key lookup

62 - Attribute lookup

63 - Sequence index

64

65 Args:

66 obj: The object to access

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

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

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

70

71 Returns:

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

73

74 Raises:

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

76

77 # noqa: DAR401

78 """

79 lookups = name.split(".")

80 current = obj

81 try: # catch-all for unexpected failures

82 for bit in lookups:

83 try: # dictionary lookup

84 current = current[bit]

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

86 # numpy < 1.9 and 1.9+ respectively

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

88 try: # attribute lookup

89 current = getattr(current, bit)

90 except (TypeError, AttributeError):

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

92 if bit in dir(current):

93 raise

94 try: # list-index lookup

95 current = current[int(bit)]

96 except (

97 IndexError, # list index out of range

98 ValueError, # invalid literal for int()

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

100 TypeError,

101 ): # un-subscript-able object

102 return default

103 return current

104 except Exception as e: # noqa: BLE001 # pragma: no cover

105 if err_on_missing:

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

107 else:

108 return default

109

110

111 def log_list(config: Config, version_part: Optional[str], new_version: Optional[str]) -> None:

112 """Output configuration with new version."""

113 ctx = get_context(config)

114 if version_part:

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

116 next_version = get_next_version(version, config, version_part, new_version)

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

118

119 print_info(f"new_version={next_version_str}")

120

121 config_dict = recursive_sort_dict(config.model_dump(exclude={"scm_info", "parts"}))

122 for key, value in config_dict.items():

123 print_info(f"{key}={value}")

124

125

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

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

128 config_dict = config.model_dump()

129 ctx = get_context(config)

130

131 if increment:

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

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

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

135 config_dict["new_version"] = next_version_str

136

137 try:

138 if "all" in args or not args:

139 show_items = config_dict

140 else:

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

142

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

144 except BadInputError as e:

145 print_error(e.message)