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
« 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."""
3import datetime
4from collections import UserDict
5from io import StringIO
6from textwrap import indent
7from typing import Any, Callable, Union
9DumperFunc = Callable[[Any], str]
12class YAMLDumpers(UserDict):
13 """Registry of YAML dumpers."""
15 def add_dumper(self, data_type: type, dumper: DumperFunc) -> None:
16 """Add a YAML dumper."""
17 self.data[data_type] = dumper
20YAML_DUMPERS = YAMLDumpers()
22INDENT = " "
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)
32 return YAML_DUMPERS[str](str(data))
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}"'
41YAML_DUMPERS.add_dumper(str, format_str)
44def format_int(val: int) -> str:
45 """Return a YAML representation of an int."""
46 return str(val)
49YAML_DUMPERS.add_dumper(int, format_int)
52def format_float(data: float) -> str:
53 """Return a YAML representation of a float."""
54 inf_value = 1e300
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)
76YAML_DUMPERS.add_dumper(float, format_float)
79def format_bool(val: bool) -> str:
80 """Return a YAML representation of a bool."""
81 return "true" if val else "false"
84YAML_DUMPERS.add_dumper(bool, format_bool)
87def format_dict(val: dict) -> str:
88 """Return a YAML representation of a dict."""
89 buffer = StringIO()
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")
99 return buffer.getvalue()
102YAML_DUMPERS.add_dumper(dict, format_dict)
105def format_sequence(val: Union[list, tuple]) -> str:
106 """Return a string representation of a value."""
107 buffer = StringIO()
109 for item in val:
110 rendered_value = dump(item).strip()
111 if isinstance(item, dict):
112 rendered_value = indent(rendered_value, INDENT).strip()
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")
120 return buffer.getvalue()
123YAML_DUMPERS.add_dumper(list, format_sequence)
124YAML_DUMPERS.add_dumper(tuple, format_sequence)
127def format_none(_: None) -> str:
128 """Return a YAML representation of None."""
129 return "null"
132YAML_DUMPERS.add_dumper(type(None), format_none)
135def format_date(val: datetime.date) -> str:
136 """Return a YAML representation of a date."""
137 return val.isoformat()
140YAML_DUMPERS.add_dumper(datetime.date, format_date)
143def format_datetime(val: datetime.datetime) -> str:
144 """Return a string representation of a value."""
145 return val.isoformat(" ")
148YAML_DUMPERS.add_dumper(datetime.datetime, format_datetime)