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

72 statements  

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

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

2 

3import datetime 

4from collections import UserDict 

5from io import StringIO 

6from textwrap import indent 

7from typing import Any, Callable, Union 

8 

9DumperFunc = Callable[[Any], str] 

10 

11 

12class YAMLDumpers(UserDict): 

13 """Registry of YAML dumpers.""" 

14 

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

16 """Add a YAML dumper.""" 

17 self.data[data_type] = dumper 

18 

19 

20YAML_DUMPERS = YAMLDumpers() 

21 

22INDENT = " " 

23 

24 

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

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

27 data_types = type(data).__mro__ 

28 for data_type in data_types: 

29 if data_type in YAML_DUMPERS: 

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

31 

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

33 

34 

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

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

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

38 return f'"{val}"' 

39 

40 

41YAML_DUMPERS.add_dumper(str, format_str) 

42 

43 

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

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

46 return str(val) 

47 

48 

49YAML_DUMPERS.add_dumper(int, format_int) 

50 

51 

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

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

54 inf_value = 1e300 

55 

56 if data != data: 

57 value = ".nan" 

58 elif data == inf_value: 

59 value = ".inf" 

60 elif data == -inf_value: 

61 value = "-.inf" 

62 else: 

63 value = repr(data).lower() 

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

65 # without the decimal parts. For instance: 

66 # >>> repr(1e17) 

67 # '1e17' 

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

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

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

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

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

73 return str(value) 

74 

75 

76YAML_DUMPERS.add_dumper(float, format_float) 

77 

78 

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

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

81 return "true" if val else "false" 

82 

83 

84YAML_DUMPERS.add_dumper(bool, format_bool) 

85 

86 

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

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

89 buffer = StringIO() 

90 

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

92 rendered_value = dump(value).strip() 

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

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

95 else: 

96 rendered_value = f" {rendered_value}" 

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

98 

99 return buffer.getvalue() 

100 

101 

102YAML_DUMPERS.add_dumper(dict, format_dict) 

103 

104 

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

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

107 buffer = StringIO() 

108 

109 for item in val: 

110 rendered_value = dump(item).strip() 

111 if isinstance(item, dict): 

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

113 

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

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

116 else: 

117 rendered_value = f" {rendered_value}" 

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

119 

120 return buffer.getvalue() 

121 

122 

123YAML_DUMPERS.add_dumper(list, format_sequence) 

124YAML_DUMPERS.add_dumper(tuple, format_sequence) 

125 

126 

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

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

129 return "null" 

130 

131 

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

133 

134 

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

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

137 return val.isoformat() 

138 

139 

140YAML_DUMPERS.add_dumper(datetime.date, format_date) 

141 

142 

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

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

145 return val.isoformat(" ") 

146 

147 

148YAML_DUMPERS.add_dumper(datetime.datetime, format_datetime)