* Start moving parts of yaml utils to own module Move parts of yaml loader out of the single large file and start to create the structure of the yaml loaders in Ansible [0]. [0]: https://github.com/ansible/ansible/tree/devel/lib/ansible/parsing/yaml * Finish yaml migration, update tests and mocks * Move code around to finish the migration * Update the mocks so that `open` is patched in `homeassistant.util.yaml.loader` instead of `homeassistant.util.yaml`. * Updated mypy ignores * Updated external API of `homeasistant.util.yaml`, see below: Checked what part of the api of `homeassistant.util.yaml` was actually called from outside the tests and added an `__ALL__` that contains only these elements. Updated the tests so that references to internal parts of the API (e.g. the yaml module imported into `homeassistant.util.yaml.loader`) are referenced directly from `homeassistant.util.yaml.loader`. In `tests/test_yaml.py` the import `yaml` refers to `homeassistant.util.yaml` and `yaml_loader` refers to `~.loader`. Future work that remains for the next iteration is to create a custom SafeConstructor and refers to that instead of monkey patching `yaml` with custom loaders. * Update mocks in yaml dumper, check_config
60 lines
2 KiB
Python
60 lines
2 KiB
Python
"""Custom dumper and representers."""
|
|
from collections import OrderedDict
|
|
import yaml
|
|
|
|
from .objects import NodeListClass
|
|
|
|
|
|
def dump(_dict: dict) -> str:
|
|
"""Dump YAML to a string and remove null."""
|
|
return yaml.safe_dump(
|
|
_dict, default_flow_style=False, allow_unicode=True) \
|
|
.replace(': null\n', ':\n')
|
|
|
|
|
|
def save_yaml(path: str, data: dict) -> None:
|
|
"""Save YAML to a file."""
|
|
# Dump before writing to not truncate the file if dumping fails
|
|
str_data = dump(data)
|
|
with open(path, 'w', encoding='utf-8') as outfile:
|
|
outfile.write(str_data)
|
|
|
|
|
|
# From: https://gist.github.com/miracle2k/3184458
|
|
# pylint: disable=redefined-outer-name
|
|
def represent_odict(dump, tag, mapping, # type: ignore
|
|
flow_style=None) -> yaml.MappingNode:
|
|
"""Like BaseRepresenter.represent_mapping but does not issue the sort()."""
|
|
value = [] # type: list
|
|
node = yaml.MappingNode(tag, value, flow_style=flow_style)
|
|
if dump.alias_key is not None:
|
|
dump.represented_objects[dump.alias_key] = node
|
|
best_style = True
|
|
if hasattr(mapping, 'items'):
|
|
mapping = mapping.items()
|
|
for item_key, item_value in mapping:
|
|
node_key = dump.represent_data(item_key)
|
|
node_value = dump.represent_data(item_value)
|
|
if not (isinstance(node_key, yaml.ScalarNode) and not node_key.style):
|
|
best_style = False
|
|
if not (isinstance(node_value, yaml.ScalarNode) and
|
|
not node_value.style):
|
|
best_style = False
|
|
value.append((node_key, node_value))
|
|
if flow_style is None:
|
|
if dump.default_flow_style is not None:
|
|
node.flow_style = dump.default_flow_style
|
|
else:
|
|
node.flow_style = best_style
|
|
return node
|
|
|
|
|
|
yaml.SafeDumper.add_representer(
|
|
OrderedDict,
|
|
lambda dumper, value:
|
|
represent_odict(dumper, 'tag:yaml.org,2002:map', value))
|
|
|
|
yaml.SafeDumper.add_representer(
|
|
NodeListClass,
|
|
lambda dumper, value:
|
|
dumper.represent_sequence('tag:yaml.org,2002:seq', value))
|