From 4004867edaa42e4f71c42e6089bebab6ac4adfb0 Mon Sep 17 00:00:00 2001 From: Ties de Kock Date: Thu, 9 May 2019 09:07:56 -0700 Subject: [PATCH] Split up yaml loaders into multiple files (#23774) * 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 --- homeassistant/scripts/check_config.py | 25 ++-- homeassistant/util/yaml/__init__.py | 15 +++ homeassistant/util/yaml/const.py | 4 + homeassistant/util/yaml/dumper.py | 60 +++++++++ .../util/{yaml.py => yaml/loader.py} | 116 ++++-------------- homeassistant/util/yaml/objects.py | 13 ++ mypy.ini | 6 +- tests/common.py | 6 +- tests/components/scene/test_init.py | 4 +- tests/helpers/test_entity_registry.py | 2 +- tests/util/test_yaml.py | 67 +++++----- 11 files changed, 179 insertions(+), 139 deletions(-) create mode 100644 homeassistant/util/yaml/__init__.py create mode 100644 homeassistant/util/yaml/const.py create mode 100644 homeassistant/util/yaml/dumper.py rename homeassistant/util/{yaml.py => yaml/loader.py} (83%) create mode 100644 homeassistant/util/yaml/objects.py diff --git a/homeassistant/scripts/check_config.py b/homeassistant/scripts/check_config.py index 5fe4e95a480..27b2738871c 100644 --- a/homeassistant/scripts/check_config.py +++ b/homeassistant/scripts/check_config.py @@ -17,7 +17,8 @@ from homeassistant.config import ( CONF_PACKAGES, merge_packages_config, _format_config_error, find_config_file, load_yaml_config_file, extract_domain_configs, config_per_platform) -from homeassistant.util import yaml + +import homeassistant.util.yaml.loader as yaml_loader from homeassistant.exceptions import HomeAssistantError REQUIREMENTS = ('colorlog==4.0.2',) @@ -25,12 +26,14 @@ REQUIREMENTS = ('colorlog==4.0.2',) _LOGGER = logging.getLogger(__name__) # pylint: disable=protected-access MOCKS = { - 'load': ("homeassistant.util.yaml.load_yaml", yaml.load_yaml), - 'load*': ("homeassistant.config.load_yaml", yaml.load_yaml), - 'secrets': ("homeassistant.util.yaml.secret_yaml", yaml.secret_yaml), + 'load': ("homeassistant.util.yaml.loader.load_yaml", + yaml_loader.load_yaml), + 'load*': ("homeassistant.config.load_yaml", yaml_loader.load_yaml), + 'secrets': ("homeassistant.util.yaml.loader.secret_yaml", + yaml_loader.secret_yaml), } SILENCE = ( - 'homeassistant.scripts.check_config.yaml.clear_secret_cache', + 'homeassistant.scripts.check_config.yaml_loader.clear_secret_cache', ) PATCHES = {} @@ -195,7 +198,8 @@ def check(config_dir, secrets=False): if secrets: # Ensure !secrets point to the patched function - yaml.yaml.SafeLoader.add_constructor('!secret', yaml.secret_yaml) + yaml_loader.yaml.SafeLoader.add_constructor('!secret', + yaml_loader.secret_yaml) try: hass = core.HomeAssistant() @@ -203,7 +207,7 @@ def check(config_dir, secrets=False): res['components'] = hass.loop.run_until_complete( check_ha_config_file(hass)) - res['secret_cache'] = OrderedDict(yaml.__SECRET_CACHE) + res['secret_cache'] = OrderedDict(yaml_loader.__SECRET_CACHE) for err in res['components'].errors: domain = err.domain or ERROR_STR @@ -221,7 +225,8 @@ def check(config_dir, secrets=False): pat.stop() if secrets: # Ensure !secrets point to the original function - yaml.yaml.SafeLoader.add_constructor('!secret', yaml.secret_yaml) + yaml_loader.yaml.SafeLoader.add_constructor( + '!secret', yaml_loader.secret_yaml) bootstrap.clear_secret_cache() return res @@ -239,7 +244,7 @@ def line_info(obj, **kwargs): def dump_dict(layer, indent_count=3, listi=False, **kwargs): """Display a dict. - A friendly version of print yaml.yaml.dump(config). + A friendly version of print yaml_loader.yaml.dump(config). """ def sort_dict_key(val): """Return the dict key for sorting.""" @@ -311,7 +316,7 @@ async def check_ha_config_file(hass): return result.add_error( "Error loading {}: {}".format(config_path, err)) finally: - yaml.clear_secret_cache() + yaml_loader.clear_secret_cache() # Extract and validate core [homeassistant] config try: diff --git a/homeassistant/util/yaml/__init__.py b/homeassistant/util/yaml/__init__.py new file mode 100644 index 00000000000..da797a23074 --- /dev/null +++ b/homeassistant/util/yaml/__init__.py @@ -0,0 +1,15 @@ +"""YAML utility functions.""" +from .const import ( + SECRET_YAML, _SECRET_NAMESPACE +) +from .dumper import dump, save_yaml +from .loader import ( + clear_secret_cache, load_yaml, secret_yaml +) + + +__all__ = [ + 'SECRET_YAML', '_SECRET_NAMESPACE', + 'dump', 'save_yaml', + 'clear_secret_cache', 'load_yaml', 'secret_yaml', +] diff --git a/homeassistant/util/yaml/const.py b/homeassistant/util/yaml/const.py new file mode 100644 index 00000000000..9bd08d99326 --- /dev/null +++ b/homeassistant/util/yaml/const.py @@ -0,0 +1,4 @@ +"""Constants.""" +SECRET_YAML = 'secrets.yaml' + +_SECRET_NAMESPACE = 'homeassistant' diff --git a/homeassistant/util/yaml/dumper.py b/homeassistant/util/yaml/dumper.py new file mode 100644 index 00000000000..d8f766c6c2b --- /dev/null +++ b/homeassistant/util/yaml/dumper.py @@ -0,0 +1,60 @@ +"""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)) diff --git a/homeassistant/util/yaml.py b/homeassistant/util/yaml/loader.py similarity index 83% rename from homeassistant/util/yaml.py rename to homeassistant/util/yaml/loader.py index f6d967b6e5a..7d228490c4c 100644 --- a/homeassistant/util/yaml.py +++ b/homeassistant/util/yaml/loader.py @@ -1,4 +1,4 @@ -"""YAML utility functions.""" +"""Custom loader.""" import logging import os import sys @@ -7,6 +7,7 @@ from collections import OrderedDict from typing import Union, List, Dict, Iterator, overload, TypeVar import yaml + try: import keyring except ImportError: @@ -19,25 +20,23 @@ except ImportError: from homeassistant.exceptions import HomeAssistantError +from .const import _SECRET_NAMESPACE, SECRET_YAML +from .objects import NodeListClass, NodeStrClass + + _LOGGER = logging.getLogger(__name__) -_SECRET_NAMESPACE = 'homeassistant' -SECRET_YAML = 'secrets.yaml' __SECRET_CACHE = {} # type: Dict[str, JSON_TYPE] JSON_TYPE = Union[List, Dict, str] # pylint: disable=invalid-name DICT_T = TypeVar('DICT_T', bound=Dict) # pylint: disable=invalid-name -class NodeListClass(list): - """Wrapper class to be able to add attributes on a list.""" +def clear_secret_cache() -> None: + """Clear the secret cache. - pass - - -class NodeStrClass(str): - """Wrapper class to be able to add attributes on a string.""" - - pass + Async friendly. + """ + __SECRET_CACHE.clear() # pylint: disable=too-many-ancestors @@ -54,6 +53,21 @@ class SafeLineLoader(yaml.SafeLoader): return node +def load_yaml(fname: str) -> JSON_TYPE: + """Load a YAML file.""" + try: + with open(fname, encoding='utf-8') as conf_file: + # If configuration file is empty YAML returns None + # We convert that to an empty dict + return yaml.load(conf_file, Loader=SafeLineLoader) or OrderedDict() + except yaml.YAMLError as exc: + _LOGGER.error(str(exc)) + raise HomeAssistantError(exc) + except UnicodeDecodeError as exc: + _LOGGER.error("Unable to read file %s: %s", fname, exc) + raise HomeAssistantError(exc) + + # pylint: disable=pointless-statement @overload def _add_reference(obj: Union[list, NodeListClass], @@ -86,44 +100,6 @@ def _add_reference(obj, loader: SafeLineLoader, # type: ignore # noqa: F811 return obj -def load_yaml(fname: str) -> JSON_TYPE: - """Load a YAML file.""" - try: - with open(fname, encoding='utf-8') as conf_file: - # If configuration file is empty YAML returns None - # We convert that to an empty dict - return yaml.load(conf_file, Loader=SafeLineLoader) or OrderedDict() - except yaml.YAMLError as exc: - _LOGGER.error(str(exc)) - raise HomeAssistantError(exc) - except UnicodeDecodeError as exc: - _LOGGER.error("Unable to read file %s: %s", fname, exc) - raise HomeAssistantError(exc) - - -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) - - -def clear_secret_cache() -> None: - """Clear the secret cache. - - Async friendly. - """ - __SECRET_CACHE.clear() - - def _include_yaml(loader: SafeLineLoader, node: yaml.nodes.Node) -> JSON_TYPE: """Load another YAML file and embeds it using the !include tag. @@ -331,43 +307,3 @@ yaml.SafeLoader.add_constructor('!include_dir_merge_list', yaml.SafeLoader.add_constructor('!include_dir_named', _include_dir_named_yaml) yaml.SafeLoader.add_constructor('!include_dir_merge_named', _include_dir_merge_named_yaml) - - -# 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)) diff --git a/homeassistant/util/yaml/objects.py b/homeassistant/util/yaml/objects.py new file mode 100644 index 00000000000..183c6c171d6 --- /dev/null +++ b/homeassistant/util/yaml/objects.py @@ -0,0 +1,13 @@ +"""Custom yaml object types.""" + + +class NodeListClass(list): + """Wrapper class to be able to add attributes on a list.""" + + pass + + +class NodeStrClass(str): + """Wrapper class to be able to add attributes on a string.""" + + pass diff --git a/mypy.ini b/mypy.ini index ee893476eed..2599eb079e0 100644 --- a/mypy.ini +++ b/mypy.ini @@ -17,7 +17,11 @@ disallow_untyped_defs = true [mypy-homeassistant.config_entries] disallow_untyped_defs = false -[mypy-homeassistant.util.yaml] +[mypy-homeassistant.util.yaml.dumper] +warn_return_any = false +disallow_untyped_calls = false + +[mypy-homeassistant.util.yaml.loader] warn_return_any = false disallow_untyped_calls = false diff --git a/tests/common.py b/tests/common.py index 46e30187d45..8b28d9db047 100644 --- a/tests/common.py +++ b/tests/common.py @@ -15,7 +15,8 @@ from io import StringIO from unittest.mock import MagicMock, Mock, patch import homeassistant.util.dt as date_util -import homeassistant.util.yaml as yaml +import homeassistant.util.yaml.loader as yaml_loader +import homeassistant.util.yaml.dumper as yaml_dumper from homeassistant import auth, config_entries, core as ha, loader from homeassistant.auth import ( @@ -680,7 +681,8 @@ def patch_yaml_files(files_dict, endswith=True): # Not found raise FileNotFoundError("File not found: {}".format(fname)) - return patch.object(yaml, 'open', mock_open_f, create=True) + return patch.object(yaml_loader, 'open', mock_open_f, create=True) + return patch.object(yaml_dumper, 'open', mock_open_f, create=True) def mock_coro(return_value=None, exception=None): diff --git a/tests/components/scene/test_init.py b/tests/components/scene/test_init.py index 940999c2dbe..99364d51e6c 100644 --- a/tests/components/scene/test_init.py +++ b/tests/components/scene/test_init.py @@ -4,7 +4,7 @@ import unittest from homeassistant.setup import setup_component from homeassistant.components import light, scene -from homeassistant.util import yaml +from homeassistant.util.yaml import loader as yaml_loader from tests.common import get_test_home_assistant from tests.components.light import common as common_light @@ -90,7 +90,7 @@ class TestScene(unittest.TestCase): self.light_1.entity_id, self.light_2.entity_id) with io.StringIO(config) as file: - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.load(file) assert setup_component(self.hass, scene.DOMAIN, doc) common.activate(self.hass, 'scene.test') diff --git a/tests/helpers/test_entity_registry.py b/tests/helpers/test_entity_registry.py index 624adbb8ea3..3af9394a202 100644 --- a/tests/helpers/test_entity_registry.py +++ b/tests/helpers/test_entity_registry.py @@ -11,7 +11,7 @@ from homeassistant.helpers import entity_registry from tests.common import mock_registry, flush_store -YAML__OPEN_PATH = 'homeassistant.util.yaml.open' +YAML__OPEN_PATH = 'homeassistant.util.yaml.loader.open' @pytest.fixture diff --git a/tests/util/test_yaml.py b/tests/util/test_yaml.py index c7d1be3d58c..01a64f17b86 100644 --- a/tests/util/test_yaml.py +++ b/tests/util/test_yaml.py @@ -8,7 +8,8 @@ from unittest.mock import patch import pytest from homeassistant.exceptions import HomeAssistantError -from homeassistant.util import yaml +from homeassistant.util.yaml import loader as yaml_loader +import homeassistant.util.yaml as yaml from homeassistant.config import YAML_CONFIG_FILE, load_yaml_config_file from tests.common import get_test_config_dir, patch_yaml_files @@ -16,7 +17,7 @@ from tests.common import get_test_config_dir, patch_yaml_files @pytest.fixture(autouse=True) def mock_credstash(): """Mock credstash so it doesn't connect to the internet.""" - with patch.object(yaml, 'credstash') as mock_credstash: + with patch.object(yaml_loader, 'credstash') as mock_credstash: mock_credstash.getSecret.return_value = None yield mock_credstash @@ -25,7 +26,7 @@ def test_simple_list(): """Test simple list.""" conf = "config:\n - simple\n - list" with io.StringIO(conf) as file: - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert doc['config'] == ["simple", "list"] @@ -33,7 +34,7 @@ def test_simple_dict(): """Test simple dict.""" conf = "key: value" with io.StringIO(conf) as file: - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert doc['key'] == 'value' @@ -58,7 +59,7 @@ def test_environment_variable(): os.environ["PASSWORD"] = "secret_password" conf = "password: !env_var PASSWORD" with io.StringIO(conf) as file: - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert doc['password'] == "secret_password" del os.environ["PASSWORD"] @@ -67,7 +68,7 @@ def test_environment_variable_default(): """Test config file with default value for environment variable.""" conf = "password: !env_var PASSWORD secret_password" with io.StringIO(conf) as file: - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert doc['password'] == "secret_password" @@ -76,7 +77,7 @@ def test_invalid_environment_variable(): conf = "password: !env_var PASSWORD" with pytest.raises(HomeAssistantError): with io.StringIO(conf) as file: - yaml.yaml.safe_load(file) + yaml_loader.yaml.safe_load(file) def test_include_yaml(): @@ -84,17 +85,17 @@ def test_include_yaml(): with patch_yaml_files({'test.yaml': 'value'}): conf = 'key: !include test.yaml' with io.StringIO(conf) as file: - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert doc["key"] == "value" with patch_yaml_files({'test.yaml': None}): conf = 'key: !include test.yaml' with io.StringIO(conf) as file: - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert doc["key"] == {} -@patch('homeassistant.util.yaml.os.walk') +@patch('homeassistant.util.yaml.loader.os.walk') def test_include_dir_list(mock_walk): """Test include dir list yaml.""" mock_walk.return_value = [ @@ -107,11 +108,11 @@ def test_include_dir_list(mock_walk): }): conf = "key: !include_dir_list /tmp" with io.StringIO(conf) as file: - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert doc["key"] == sorted(["one", "two"]) -@patch('homeassistant.util.yaml.os.walk') +@patch('homeassistant.util.yaml.loader.os.walk') def test_include_dir_list_recursive(mock_walk): """Test include dir recursive list yaml.""" mock_walk.return_value = [ @@ -129,13 +130,13 @@ def test_include_dir_list_recursive(mock_walk): with io.StringIO(conf) as file: assert '.ignore' in mock_walk.return_value[0][1], \ "Expecting .ignore in here" - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert 'tmp2' in mock_walk.return_value[0][1] assert '.ignore' not in mock_walk.return_value[0][1] assert sorted(doc["key"]) == sorted(["zero", "one", "two"]) -@patch('homeassistant.util.yaml.os.walk') +@patch('homeassistant.util.yaml.loader.os.walk') def test_include_dir_named(mock_walk): """Test include dir named yaml.""" mock_walk.return_value = [ @@ -149,11 +150,11 @@ def test_include_dir_named(mock_walk): conf = "key: !include_dir_named /tmp" correct = {'first': 'one', 'second': 'two'} with io.StringIO(conf) as file: - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert doc["key"] == correct -@patch('homeassistant.util.yaml.os.walk') +@patch('homeassistant.util.yaml.loader.os.walk') def test_include_dir_named_recursive(mock_walk): """Test include dir named yaml.""" mock_walk.return_value = [ @@ -172,13 +173,13 @@ def test_include_dir_named_recursive(mock_walk): with io.StringIO(conf) as file: assert '.ignore' in mock_walk.return_value[0][1], \ "Expecting .ignore in here" - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert 'tmp2' in mock_walk.return_value[0][1] assert '.ignore' not in mock_walk.return_value[0][1] assert doc["key"] == correct -@patch('homeassistant.util.yaml.os.walk') +@patch('homeassistant.util.yaml.loader.os.walk') def test_include_dir_merge_list(mock_walk): """Test include dir merge list yaml.""" mock_walk.return_value = [['/tmp', [], ['first.yaml', 'second.yaml']]] @@ -189,11 +190,11 @@ def test_include_dir_merge_list(mock_walk): }): conf = "key: !include_dir_merge_list /tmp" with io.StringIO(conf) as file: - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert sorted(doc["key"]) == sorted(["one", "two", "three"]) -@patch('homeassistant.util.yaml.os.walk') +@patch('homeassistant.util.yaml.loader.os.walk') def test_include_dir_merge_list_recursive(mock_walk): """Test include dir merge list yaml.""" mock_walk.return_value = [ @@ -211,14 +212,14 @@ def test_include_dir_merge_list_recursive(mock_walk): with io.StringIO(conf) as file: assert '.ignore' in mock_walk.return_value[0][1], \ "Expecting .ignore in here" - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert 'tmp2' in mock_walk.return_value[0][1] assert '.ignore' not in mock_walk.return_value[0][1] assert sorted(doc["key"]) == sorted(["one", "two", "three", "four"]) -@patch('homeassistant.util.yaml.os.walk') +@patch('homeassistant.util.yaml.loader.os.walk') def test_include_dir_merge_named(mock_walk): """Test include dir merge named yaml.""" mock_walk.return_value = [['/tmp', [], ['first.yaml', 'second.yaml']]] @@ -231,7 +232,7 @@ def test_include_dir_merge_named(mock_walk): with patch_yaml_files(files): conf = "key: !include_dir_merge_named /tmp" with io.StringIO(conf) as file: - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert doc["key"] == { "key1": "one", "key2": "two", @@ -239,7 +240,7 @@ def test_include_dir_merge_named(mock_walk): } -@patch('homeassistant.util.yaml.os.walk') +@patch('homeassistant.util.yaml.loader.os.walk') def test_include_dir_merge_named_recursive(mock_walk): """Test include dir merge named yaml.""" mock_walk.return_value = [ @@ -257,7 +258,7 @@ def test_include_dir_merge_named_recursive(mock_walk): with io.StringIO(conf) as file: assert '.ignore' in mock_walk.return_value[0][1], \ "Expecting .ignore in here" - doc = yaml.yaml.safe_load(file) + doc = yaml_loader.yaml.safe_load(file) assert 'tmp2' in mock_walk.return_value[0][1] assert '.ignore' not in mock_walk.return_value[0][1] assert doc["key"] == { @@ -268,12 +269,12 @@ def test_include_dir_merge_named_recursive(mock_walk): } -@patch('homeassistant.util.yaml.open', create=True) +@patch('homeassistant.util.yaml.loader.open', create=True) def test_load_yaml_encoding_error(mock_open): """Test raising a UnicodeDecodeError.""" mock_open.side_effect = UnicodeDecodeError('', b'', 1, 0, '') with pytest.raises(HomeAssistantError): - yaml.load_yaml('test') + yaml_loader.load_yaml('test') def test_dump(): @@ -392,16 +393,16 @@ class TestSecrets(unittest.TestCase): def test_secrets_keyring(self): """Test keyring fallback & get_password.""" - yaml.keyring = None # Ensure its not there + yaml_loader.keyring = None # Ensure its not there yaml_str = 'http:\n api_password: !secret http_pw_keyring' - with pytest.raises(yaml.HomeAssistantError): + with pytest.raises(HomeAssistantError): load_yaml(self._yaml_path, yaml_str) - yaml.keyring = FakeKeyring({'http_pw_keyring': 'yeah'}) + yaml_loader.keyring = FakeKeyring({'http_pw_keyring': 'yeah'}) _yaml = load_yaml(self._yaml_path, yaml_str) assert {'http': {'api_password': 'yeah'}} == _yaml - @patch.object(yaml, 'credstash') + @patch.object(yaml_loader, 'credstash') def test_secrets_credstash(self, mock_credstash): """Test credstash fallback & get_password.""" mock_credstash.getSecret.return_value = 'yeah' @@ -413,10 +414,10 @@ class TestSecrets(unittest.TestCase): def test_secrets_logger_removed(self): """Ensure logger: debug was removed.""" - with pytest.raises(yaml.HomeAssistantError): + with pytest.raises(HomeAssistantError): load_yaml(self._yaml_path, 'api_password: !secret logger') - @patch('homeassistant.util.yaml._LOGGER.error') + @patch('homeassistant.util.yaml.loader._LOGGER.error') def test_bad_logger_value(self, mock_error): """Ensure logger: debug was removed.""" yaml.clear_secret_cache()