Yaml use dict (#88977)

* Use built-in dict instead of OrderedDict

* Use dict instead of OrderedDict in YAML
This commit is contained in:
Paulus Schoutsen 2023-03-01 12:29:57 -05:00 committed by GitHub
parent 89c276bb6b
commit 3f32c5d2ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 190 additions and 180 deletions

View File

@ -4,7 +4,7 @@ from typing import Any
import yaml
from .objects import Input, NodeListClass
from .objects import Input, NodeDictClass, NodeListClass
# mypy: allow-untyped-calls, no-warn-return-any
@ -74,6 +74,11 @@ add_representer(
lambda dumper, value: represent_odict(dumper, "tag:yaml.org,2002:map", value),
)
add_representer(
NodeDictClass,
lambda dumper, value: represent_odict(dumper, "tag:yaml.org,2002:map", value),
)
add_representer(
NodeListClass,
lambda dumper, value: dumper.represent_sequence("tag:yaml.org,2002:seq", value),

View File

@ -1,7 +1,6 @@
"""Custom loader."""
from __future__ import annotations
from collections import OrderedDict
from collections.abc import Iterator
import fnmatch
from io import StringIO, TextIOWrapper
@ -25,7 +24,7 @@ except ImportError:
from homeassistant.exceptions import HomeAssistantError
from .const import SECRET_YAML
from .objects import Input, NodeListClass, NodeStrClass
from .objects import Input, NodeDictClass, NodeListClass, NodeStrClass
# mypy: allow-untyped-calls, no-warn-return-any
@ -205,7 +204,7 @@ def _parse_yaml(
# We convert that to an empty dict
return (
yaml.load(content, Loader=lambda stream: loader(stream, secrets))
or OrderedDict()
or NodeDictClass()
)
@ -276,9 +275,9 @@ def _find_files(directory: str, pattern: str) -> Iterator[str]:
yield filename
def _include_dir_named_yaml(loader: LoaderType, node: yaml.nodes.Node) -> OrderedDict:
def _include_dir_named_yaml(loader: LoaderType, node: yaml.nodes.Node) -> NodeDictClass:
"""Load multiple files from directory as a dictionary."""
mapping: OrderedDict = OrderedDict()
mapping = NodeDictClass()
loc = os.path.join(os.path.dirname(loader.get_name()), node.value)
for fname in _find_files(loc, "*.yaml"):
filename = os.path.splitext(os.path.basename(fname))[0]
@ -290,9 +289,9 @@ def _include_dir_named_yaml(loader: LoaderType, node: yaml.nodes.Node) -> Ordere
def _include_dir_merge_named_yaml(
loader: LoaderType, node: yaml.nodes.Node
) -> OrderedDict:
) -> NodeDictClass:
"""Load multiple files from directory as a merged dictionary."""
mapping: OrderedDict = OrderedDict()
mapping = NodeDictClass()
loc = os.path.join(os.path.dirname(loader.get_name()), node.value)
for fname in _find_files(loc, "*.yaml"):
if os.path.basename(fname) == SECRET_YAML:
@ -330,7 +329,9 @@ def _include_dir_merge_list_yaml(
return _add_reference(merged_list, loader, node)
def _ordered_dict(loader: LoaderType, node: yaml.nodes.MappingNode) -> OrderedDict:
def _handle_mapping_tag(
loader: LoaderType, node: yaml.nodes.MappingNode
) -> NodeDictClass:
"""Load YAML mappings into an ordered dictionary to preserve key order."""
loader.flatten_mapping(node)
nodes = loader.construct_pairs(node)
@ -361,7 +362,7 @@ def _ordered_dict(loader: LoaderType, node: yaml.nodes.MappingNode) -> OrderedDi
)
seen[key] = line
return _add_reference(OrderedDict(nodes), loader, node)
return _add_reference(NodeDictClass(nodes), loader, node)
def _construct_seq(loader: LoaderType, node: yaml.nodes.Node) -> JSON_TYPE:
@ -398,7 +399,7 @@ def add_constructor(tag: Any, constructor: Any) -> None:
add_constructor("!include", _include_yaml)
add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _ordered_dict)
add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _handle_mapping_tag)
add_constructor(yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, _construct_seq)
add_constructor("!env_var", _env_var_yaml)
add_constructor("!secret", secret_yaml)

View File

@ -14,6 +14,10 @@ class NodeStrClass(str):
"""Wrapper class to be able to add attributes on a string."""
class NodeDictClass(dict):
"""Wrapper class to be able to add attributes on a dict."""
@dataclass(frozen=True)
class Input:
"""Input that should be substituted."""

View File

@ -1,25 +1,79 @@
# serializer version: 1
# name: test_extract_blueprint_from_community_topic
OrderedDict({
'remote': OrderedDict({
'name': 'Remote',
'description': 'IKEA remote to use',
NodeDictClass({
'brightness': NodeDictClass({
'default': 50,
'description': 'Brightness of the light(s) when turning on',
'name': 'Brightness',
'selector': dict({
'device': OrderedDict({
'integration': 'zha',
'manufacturer': 'IKEA of Sweden',
'model': 'TRADFRI remote control',
'multiple': False,
'number': NodeDictClass({
'max': 100.0,
'min': 0.0,
'mode': 'slider',
'step': 1.0,
'unit_of_measurement': '%',
}),
}),
}),
'light': OrderedDict({
'name': 'Light(s)',
'description': 'The light(s) to control',
'button_left_long': NodeDictClass({
'default': NodeListClass([
]),
'description': 'Action to run on long left button press',
'name': 'Left button - long press',
'selector': dict({
'target': OrderedDict({
'action': dict({
}),
}),
}),
'button_left_short': NodeDictClass({
'default': NodeListClass([
]),
'description': 'Action to run on short left button press',
'name': 'Left button - short press',
'selector': dict({
'action': dict({
}),
}),
}),
'button_right_long': NodeDictClass({
'default': NodeListClass([
]),
'description': 'Action to run on long right button press',
'name': 'Right button - long press',
'selector': dict({
'action': dict({
}),
}),
}),
'button_right_short': NodeDictClass({
'default': NodeListClass([
]),
'description': 'Action to run on short right button press',
'name': 'Right button - short press',
'selector': dict({
'action': dict({
}),
}),
}),
'force_brightness': NodeDictClass({
'default': False,
'description': '''
Force the brightness to the set level below, when the "on" button on the remote is pushed and lights turn on.
''',
'name': 'Force turn on brightness',
'selector': dict({
'boolean': dict({
}),
}),
}),
'light': NodeDictClass({
'description': 'The light(s) to control',
'name': 'Light(s)',
'selector': dict({
'target': NodeDictClass({
'entity': list([
OrderedDict({
NodeDictClass({
'domain': list([
'light',
]),
@ -28,95 +82,95 @@
}),
}),
}),
'force_brightness': OrderedDict({
'name': 'Force turn on brightness',
'description': '''
Force the brightness to the set level below, when the "on" button on the remote is pushed and lights turn on.
''',
'default': False,
'remote': NodeDictClass({
'description': 'IKEA remote to use',
'name': 'Remote',
'selector': dict({
'boolean': dict({
}),
}),
}),
'brightness': OrderedDict({
'name': 'Brightness',
'description': 'Brightness of the light(s) when turning on',
'default': 50,
'selector': dict({
'number': OrderedDict({
'min': 0.0,
'max': 100.0,
'mode': 'slider',
'step': 1.0,
'unit_of_measurement': '%',
}),
}),
}),
'button_left_short': OrderedDict({
'name': 'Left button - short press',
'description': 'Action to run on short left button press',
'default': NodeListClass([
]),
'selector': dict({
'action': dict({
}),
}),
}),
'button_left_long': OrderedDict({
'name': 'Left button - long press',
'description': 'Action to run on long left button press',
'default': NodeListClass([
]),
'selector': dict({
'action': dict({
}),
}),
}),
'button_right_short': OrderedDict({
'name': 'Right button - short press',
'description': 'Action to run on short right button press',
'default': NodeListClass([
]),
'selector': dict({
'action': dict({
}),
}),
}),
'button_right_long': OrderedDict({
'name': 'Right button - long press',
'description': 'Action to run on long right button press',
'default': NodeListClass([
]),
'selector': dict({
'action': dict({
'device': NodeDictClass({
'integration': 'zha',
'manufacturer': 'IKEA of Sweden',
'model': 'TRADFRI remote control',
'multiple': False,
}),
}),
}),
})
# ---
# name: test_fetch_blueprint_from_community_url
OrderedDict({
'remote': OrderedDict({
'name': 'Remote',
'description': 'IKEA remote to use',
NodeDictClass({
'brightness': NodeDictClass({
'default': 50,
'description': 'Brightness of the light(s) when turning on',
'name': 'Brightness',
'selector': dict({
'device': OrderedDict({
'integration': 'zha',
'manufacturer': 'IKEA of Sweden',
'model': 'TRADFRI remote control',
'multiple': False,
'number': NodeDictClass({
'max': 100.0,
'min': 0.0,
'mode': 'slider',
'step': 1.0,
'unit_of_measurement': '%',
}),
}),
}),
'light': OrderedDict({
'name': 'Light(s)',
'description': 'The light(s) to control',
'button_left_long': NodeDictClass({
'default': NodeListClass([
]),
'description': 'Action to run on long left button press',
'name': 'Left button - long press',
'selector': dict({
'target': OrderedDict({
'action': dict({
}),
}),
}),
'button_left_short': NodeDictClass({
'default': NodeListClass([
]),
'description': 'Action to run on short left button press',
'name': 'Left button - short press',
'selector': dict({
'action': dict({
}),
}),
}),
'button_right_long': NodeDictClass({
'default': NodeListClass([
]),
'description': 'Action to run on long right button press',
'name': 'Right button - long press',
'selector': dict({
'action': dict({
}),
}),
}),
'button_right_short': NodeDictClass({
'default': NodeListClass([
]),
'description': 'Action to run on short right button press',
'name': 'Right button - short press',
'selector': dict({
'action': dict({
}),
}),
}),
'force_brightness': NodeDictClass({
'default': False,
'description': '''
Force the brightness to the set level below, when the "on" button on the remote is pushed and lights turn on.
''',
'name': 'Force turn on brightness',
'selector': dict({
'boolean': dict({
}),
}),
}),
'light': NodeDictClass({
'description': 'The light(s) to control',
'name': 'Light(s)',
'selector': dict({
'target': NodeDictClass({
'entity': list([
OrderedDict({
NodeDictClass({
'domain': list([
'light',
]),
@ -125,94 +179,26 @@
}),
}),
}),
'force_brightness': OrderedDict({
'name': 'Force turn on brightness',
'description': '''
Force the brightness to the set level below, when the "on" button on the remote is pushed and lights turn on.
''',
'default': False,
'remote': NodeDictClass({
'description': 'IKEA remote to use',
'name': 'Remote',
'selector': dict({
'boolean': dict({
}),
}),
}),
'brightness': OrderedDict({
'name': 'Brightness',
'description': 'Brightness of the light(s) when turning on',
'default': 50,
'selector': dict({
'number': OrderedDict({
'min': 0.0,
'max': 100.0,
'mode': 'slider',
'step': 1.0,
'unit_of_measurement': '%',
}),
}),
}),
'button_left_short': OrderedDict({
'name': 'Left button - short press',
'description': 'Action to run on short left button press',
'default': NodeListClass([
]),
'selector': dict({
'action': dict({
}),
}),
}),
'button_left_long': OrderedDict({
'name': 'Left button - long press',
'description': 'Action to run on long left button press',
'default': NodeListClass([
]),
'selector': dict({
'action': dict({
}),
}),
}),
'button_right_short': OrderedDict({
'name': 'Right button - short press',
'description': 'Action to run on short right button press',
'default': NodeListClass([
]),
'selector': dict({
'action': dict({
}),
}),
}),
'button_right_long': OrderedDict({
'name': 'Right button - long press',
'description': 'Action to run on long right button press',
'default': NodeListClass([
]),
'selector': dict({
'action': dict({
'device': NodeDictClass({
'integration': 'zha',
'manufacturer': 'IKEA of Sweden',
'model': 'TRADFRI remote control',
'multiple': False,
}),
}),
}),
})
# ---
# name: test_fetch_blueprint_from_github_gist_url
OrderedDict({
'motion_entity': OrderedDict({
'name': 'Motion Sensor',
'selector': dict({
'entity': OrderedDict({
'domain': list([
'binary_sensor',
]),
'device_class': list([
'motion',
]),
'multiple': False,
}),
}),
}),
'light_entity': OrderedDict({
NodeDictClass({
'light_entity': NodeDictClass({
'name': 'Light',
'selector': dict({
'entity': OrderedDict({
'entity': NodeDictClass({
'domain': list([
'light',
]),
@ -220,5 +206,19 @@
}),
}),
}),
'motion_entity': NodeDictClass({
'name': 'Motion Sensor',
'selector': dict({
'entity': NodeDictClass({
'device_class': list([
'motion',
]),
'domain': list([
'binary_sensor',
]),
'multiple': False,
}),
}),
}),
})
# ---