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
« 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
8DumperFunc = Callable[[Any], str]
11class YAMLDumpers(UserDict):
12 """Registry of YAML dumpers."""
14 def add_dumper(self, data_type: type, dumper: DumperFunc) -> None:
15 """Add a YAML dumper."""
16 self.data[data_type] = dumper
19YAML_DUMPERS = YAMLDumpers()
21INDENT = " "
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)
31 return YAML_DUMPERS[str](str(data))
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}"'
40YAML_DUMPERS.add_dumper(str, format_str)
43def format_int(val: int) -> str:
44 """Return a YAML representation of an int."""
45 return str(val)
48YAML_DUMPERS.add_dumper(int, format_int)
51def format_float(data: float) -> str:
52 """Return a YAML representation of a float."""
53 inf_value = 1e300
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)
75YAML_DUMPERS.add_dumper(float, format_float)
78def format_bool(val: bool) -> str:
79 """Return a YAML representation of a bool."""
80 return "true" if val else "false"
83YAML_DUMPERS.add_dumper(bool, format_bool)
86def format_dict(val: dict) -> str:
87 """Return a YAML representation of a dict."""
88 buffer = StringIO()
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")
98 return buffer.getvalue()
101YAML_DUMPERS.add_dumper(dict, format_dict)
104def format_sequence(val: Union[list, tuple]) -> str:
105 """Return a string representation of a value."""
106 buffer = StringIO()
108 for item in val:
109 rendered_value = dump(item).strip()
110 if isinstance(item, dict):
111 rendered_value = indent(rendered_value, INDENT).strip()
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")
119 return buffer.getvalue()
122YAML_DUMPERS.add_dumper(list, format_sequence)
123YAML_DUMPERS.add_dumper(tuple, format_sequence)
126def format_none(_: None) -> str:
127 """Return a YAML representation of None."""
128 return "null"
131YAML_DUMPERS.add_dumper(type(None), format_none)
134def format_date(val: datetime.date) -> str:
135 """Return a YAML representation of a date."""
136 return val.isoformat()
139YAML_DUMPERS.add_dumper(datetime.date, format_date)
142def format_datetime(val: datetime.datetime) -> str:
143 """Return a string representation of a value."""
144 return val.isoformat(" ")
147YAML_DUMPERS.add_dumper(datetime.datetime, format_datetime)