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

72 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-15 09:15 -0600

1"""A simple YAML dumper to avoid extra dependencies.""" 

2import datetime 

3from collections import UserDict 

4from io import StringIO 

5from textwrap import indent 

6from typing import Any, Callable, Union 

7 

8DumperFunc = Callable[[Any], str] 

9 

10 

11class YAMLDumpers(UserDict): 

12 """Registry of YAML dumpers.""" 

13 

14 def add_dumper(self, data_type: type, dumper: DumperFunc) -> None: 

15 """Add a YAML dumper.""" 

16 self.data[data_type] = dumper 

17 

18 

19YAML_DUMPERS = YAMLDumpers() 

20 

21INDENT = " " 

22 

23 

24def dump(data: Any) -> str: 

25 """Dump a value to a string buffer.""" 

26 data_types = type(data).__mro__ 

27 for data_type in data_types: 

28 if data_type in YAML_DUMPERS: 

29 return YAML_DUMPERS[data_types[0]](data) 

30 

31 return YAML_DUMPERS[str](str(data)) 

32 

33 

34def format_str(val: str) -> str: 

35 """Return a YAML representation of a string.""" 

36 val = val.replace("\\", "\\\\").replace("\n", "\\n") 

37 return f'"{val}"' 

38 

39 

40YAML_DUMPERS.add_dumper(str, format_str) 

41 

42 

43def format_int(val: int) -> str: 

44 """Return a YAML representation of an int.""" 

45 return str(val) 

46 

47 

48YAML_DUMPERS.add_dumper(int, format_int) 

49 

50 

51def format_float(data: float) -> str: 

52 """Return a YAML representation of a float.""" 

53 inf_value = 1e300 

54 

55 if data != data: 

56 value = ".nan" 

57 elif data == inf_value: 

58 value = ".inf" 

59 elif data == -inf_value: 

60 value = "-.inf" 

61 else: 

62 value = repr(data).lower() 

63 # Note that in some cases `repr(data)` represents a float number 

64 # without the decimal parts. For instance: 

65 # >>> repr(1e17) 

66 # '1e17' 

67 # Unfortunately, this is not a valid float representation according 

68 # to the definition of the `!!float` tag. We fix this by adding 

69 # '.0' before the 'e' symbol. 

70 if "." not in value and "e" in value: 

71 value = value.replace("e", ".0e", 1) 

72 return str(value) 

73 

74 

75YAML_DUMPERS.add_dumper(float, format_float) 

76 

77 

78def format_bool(val: bool) -> str: 

79 """Return a YAML representation of a bool.""" 

80 return "true" if val else "false" 

81 

82 

83YAML_DUMPERS.add_dumper(bool, format_bool) 

84 

85 

86def format_dict(val: dict) -> str: 

87 """Return a YAML representation of a dict.""" 

88 buffer = StringIO() 

89 

90 for key, value in sorted(val.items()): 

91 rendered_value = dump(value).strip() 

92 if isinstance(value, (dict, list, tuple)): 

93 rendered_value = f"\n{indent(rendered_value, INDENT)}" 

94 else: 

95 rendered_value = f" {rendered_value}" 

96 buffer.write(f"{key}:{rendered_value}\n") 

97 

98 return buffer.getvalue() 

99 

100 

101YAML_DUMPERS.add_dumper(dict, format_dict) 

102 

103 

104def format_sequence(val: Union[list, tuple]) -> str: 

105 """Return a string representation of a value.""" 

106 buffer = StringIO() 

107 

108 for item in val: 

109 rendered_value = dump(item).strip() 

110 if isinstance(item, dict): 

111 rendered_value = indent(rendered_value, INDENT).strip() 

112 

113 if isinstance(item, (list, tuple)): 

114 rendered_value = f"\n{indent(rendered_value, INDENT)}" 

115 else: 

116 rendered_value = f" {rendered_value}" 

117 buffer.write(f"-{rendered_value}\n") 

118 

119 return buffer.getvalue() 

120 

121 

122YAML_DUMPERS.add_dumper(list, format_sequence) 

123YAML_DUMPERS.add_dumper(tuple, format_sequence) 

124 

125 

126def format_none(_: None) -> str: 

127 """Return a YAML representation of None.""" 

128 return "null" 

129 

130 

131YAML_DUMPERS.add_dumper(type(None), format_none) 

132 

133 

134def format_date(val: datetime.date) -> str: 

135 """Return a YAML representation of a date.""" 

136 return val.isoformat() 

137 

138 

139YAML_DUMPERS.add_dumper(datetime.date, format_date) 

140 

141 

142def format_datetime(val: datetime.datetime) -> str: 

143 """Return a string representation of a value.""" 

144 return val.isoformat(" ") 

145 

146 

147YAML_DUMPERS.add_dumper(datetime.datetime, format_datetime)