mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 02:07:09 +00:00
Yaml use dict (#88977)
* Use built-in dict instead of OrderedDict * Use dict instead of OrderedDict in YAML
This commit is contained in:
parent
89c276bb6b
commit
3f32c5d2ad
@ -4,7 +4,7 @@ from typing import Any
|
|||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from .objects import Input, NodeListClass
|
from .objects import Input, NodeDictClass, NodeListClass
|
||||||
|
|
||||||
# mypy: allow-untyped-calls, no-warn-return-any
|
# 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),
|
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(
|
add_representer(
|
||||||
NodeListClass,
|
NodeListClass,
|
||||||
lambda dumper, value: dumper.represent_sequence("tag:yaml.org,2002:seq", value),
|
lambda dumper, value: dumper.represent_sequence("tag:yaml.org,2002:seq", value),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"""Custom loader."""
|
"""Custom loader."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections import OrderedDict
|
|
||||||
from collections.abc import Iterator
|
from collections.abc import Iterator
|
||||||
import fnmatch
|
import fnmatch
|
||||||
from io import StringIO, TextIOWrapper
|
from io import StringIO, TextIOWrapper
|
||||||
@ -25,7 +24,7 @@ except ImportError:
|
|||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
from .const import SECRET_YAML
|
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
|
# mypy: allow-untyped-calls, no-warn-return-any
|
||||||
|
|
||||||
@ -205,7 +204,7 @@ def _parse_yaml(
|
|||||||
# We convert that to an empty dict
|
# We convert that to an empty dict
|
||||||
return (
|
return (
|
||||||
yaml.load(content, Loader=lambda stream: loader(stream, secrets))
|
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
|
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."""
|
"""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)
|
loc = os.path.join(os.path.dirname(loader.get_name()), node.value)
|
||||||
for fname in _find_files(loc, "*.yaml"):
|
for fname in _find_files(loc, "*.yaml"):
|
||||||
filename = os.path.splitext(os.path.basename(fname))[0]
|
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(
|
def _include_dir_merge_named_yaml(
|
||||||
loader: LoaderType, node: yaml.nodes.Node
|
loader: LoaderType, node: yaml.nodes.Node
|
||||||
) -> OrderedDict:
|
) -> NodeDictClass:
|
||||||
"""Load multiple files from directory as a merged dictionary."""
|
"""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)
|
loc = os.path.join(os.path.dirname(loader.get_name()), node.value)
|
||||||
for fname in _find_files(loc, "*.yaml"):
|
for fname in _find_files(loc, "*.yaml"):
|
||||||
if os.path.basename(fname) == SECRET_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)
|
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."""
|
"""Load YAML mappings into an ordered dictionary to preserve key order."""
|
||||||
loader.flatten_mapping(node)
|
loader.flatten_mapping(node)
|
||||||
nodes = loader.construct_pairs(node)
|
nodes = loader.construct_pairs(node)
|
||||||
@ -361,7 +362,7 @@ def _ordered_dict(loader: LoaderType, node: yaml.nodes.MappingNode) -> OrderedDi
|
|||||||
)
|
)
|
||||||
seen[key] = line
|
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:
|
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("!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(yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, _construct_seq)
|
||||||
add_constructor("!env_var", _env_var_yaml)
|
add_constructor("!env_var", _env_var_yaml)
|
||||||
add_constructor("!secret", secret_yaml)
|
add_constructor("!secret", secret_yaml)
|
||||||
|
@ -14,6 +14,10 @@ class NodeStrClass(str):
|
|||||||
"""Wrapper class to be able to add attributes on a string."""
|
"""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)
|
@dataclass(frozen=True)
|
||||||
class Input:
|
class Input:
|
||||||
"""Input that should be substituted."""
|
"""Input that should be substituted."""
|
||||||
|
@ -1,25 +1,79 @@
|
|||||||
# serializer version: 1
|
# serializer version: 1
|
||||||
# name: test_extract_blueprint_from_community_topic
|
# name: test_extract_blueprint_from_community_topic
|
||||||
OrderedDict({
|
NodeDictClass({
|
||||||
'remote': OrderedDict({
|
'brightness': NodeDictClass({
|
||||||
'name': 'Remote',
|
'default': 50,
|
||||||
'description': 'IKEA remote to use',
|
'description': 'Brightness of the light(s) when turning on',
|
||||||
|
'name': 'Brightness',
|
||||||
'selector': dict({
|
'selector': dict({
|
||||||
'device': OrderedDict({
|
'number': NodeDictClass({
|
||||||
'integration': 'zha',
|
'max': 100.0,
|
||||||
'manufacturer': 'IKEA of Sweden',
|
'min': 0.0,
|
||||||
'model': 'TRADFRI remote control',
|
'mode': 'slider',
|
||||||
'multiple': False,
|
'step': 1.0,
|
||||||
|
'unit_of_measurement': '%',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'light': OrderedDict({
|
'button_left_long': NodeDictClass({
|
||||||
'name': 'Light(s)',
|
'default': NodeListClass([
|
||||||
'description': 'The light(s) to control',
|
]),
|
||||||
|
'description': 'Action to run on long left button press',
|
||||||
|
'name': 'Left button - long press',
|
||||||
'selector': dict({
|
'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([
|
'entity': list([
|
||||||
OrderedDict({
|
NodeDictClass({
|
||||||
'domain': list([
|
'domain': list([
|
||||||
'light',
|
'light',
|
||||||
]),
|
]),
|
||||||
@ -28,95 +82,95 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'force_brightness': OrderedDict({
|
'remote': NodeDictClass({
|
||||||
'name': 'Force turn on brightness',
|
'description': 'IKEA remote to use',
|
||||||
'description': '''
|
'name': 'Remote',
|
||||||
Force the brightness to the set level below, when the "on" button on the remote is pushed and lights turn on.
|
|
||||||
|
|
||||||
''',
|
|
||||||
'default': False,
|
|
||||||
'selector': dict({
|
'selector': dict({
|
||||||
'boolean': dict({
|
'device': NodeDictClass({
|
||||||
}),
|
'integration': 'zha',
|
||||||
}),
|
'manufacturer': 'IKEA of Sweden',
|
||||||
}),
|
'model': 'TRADFRI remote control',
|
||||||
'brightness': OrderedDict({
|
'multiple': False,
|
||||||
'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({
|
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_fetch_blueprint_from_community_url
|
# name: test_fetch_blueprint_from_community_url
|
||||||
OrderedDict({
|
NodeDictClass({
|
||||||
'remote': OrderedDict({
|
'brightness': NodeDictClass({
|
||||||
'name': 'Remote',
|
'default': 50,
|
||||||
'description': 'IKEA remote to use',
|
'description': 'Brightness of the light(s) when turning on',
|
||||||
|
'name': 'Brightness',
|
||||||
'selector': dict({
|
'selector': dict({
|
||||||
'device': OrderedDict({
|
'number': NodeDictClass({
|
||||||
'integration': 'zha',
|
'max': 100.0,
|
||||||
'manufacturer': 'IKEA of Sweden',
|
'min': 0.0,
|
||||||
'model': 'TRADFRI remote control',
|
'mode': 'slider',
|
||||||
'multiple': False,
|
'step': 1.0,
|
||||||
|
'unit_of_measurement': '%',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'light': OrderedDict({
|
'button_left_long': NodeDictClass({
|
||||||
'name': 'Light(s)',
|
'default': NodeListClass([
|
||||||
'description': 'The light(s) to control',
|
]),
|
||||||
|
'description': 'Action to run on long left button press',
|
||||||
|
'name': 'Left button - long press',
|
||||||
'selector': dict({
|
'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([
|
'entity': list([
|
||||||
OrderedDict({
|
NodeDictClass({
|
||||||
'domain': list([
|
'domain': list([
|
||||||
'light',
|
'light',
|
||||||
]),
|
]),
|
||||||
@ -125,94 +179,26 @@
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
'force_brightness': OrderedDict({
|
'remote': NodeDictClass({
|
||||||
'name': 'Force turn on brightness',
|
'description': 'IKEA remote to use',
|
||||||
'description': '''
|
'name': 'Remote',
|
||||||
Force the brightness to the set level below, when the "on" button on the remote is pushed and lights turn on.
|
|
||||||
|
|
||||||
''',
|
|
||||||
'default': False,
|
|
||||||
'selector': dict({
|
'selector': dict({
|
||||||
'boolean': dict({
|
'device': NodeDictClass({
|
||||||
}),
|
'integration': 'zha',
|
||||||
}),
|
'manufacturer': 'IKEA of Sweden',
|
||||||
}),
|
'model': 'TRADFRI remote control',
|
||||||
'brightness': OrderedDict({
|
'multiple': False,
|
||||||
'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({
|
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_fetch_blueprint_from_github_gist_url
|
# name: test_fetch_blueprint_from_github_gist_url
|
||||||
OrderedDict({
|
NodeDictClass({
|
||||||
'motion_entity': OrderedDict({
|
'light_entity': NodeDictClass({
|
||||||
'name': 'Motion Sensor',
|
|
||||||
'selector': dict({
|
|
||||||
'entity': OrderedDict({
|
|
||||||
'domain': list([
|
|
||||||
'binary_sensor',
|
|
||||||
]),
|
|
||||||
'device_class': list([
|
|
||||||
'motion',
|
|
||||||
]),
|
|
||||||
'multiple': False,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
'light_entity': OrderedDict({
|
|
||||||
'name': 'Light',
|
'name': 'Light',
|
||||||
'selector': dict({
|
'selector': dict({
|
||||||
'entity': OrderedDict({
|
'entity': NodeDictClass({
|
||||||
'domain': list([
|
'domain': list([
|
||||||
'light',
|
'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,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
Loading…
x
Reference in New Issue
Block a user