-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
check_config script evolution #12792
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@@ -88,6 +92,9 @@ def run(script_args: List) -> int: | |||
'-s', '--secrets', | |||
action='store_true', | |||
help="Show secret information") | |||
parser.add_argument( | |||
'--new', nargs='?', const=True, default=False, | |||
help="Proof of concept config_checker") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be simplified :)
parser.add_argument('--new', action='store_true', help="...")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, it will be removed eventually, but good to know :-)
homeassistant/config.py
Outdated
@@ -304,7 +304,12 @@ def load_yaml_config_file(config_path): | |||
_LOGGER.error(msg) | |||
raise HomeAssistantError(msg) | |||
|
|||
return conf_dict | |||
# Make a copy because we are mutating it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to copy it here, we just created the dict.
homeassistant/config.py
Outdated
@@ -669,6 +674,39 @@ def async_process_component_config(hass, config, domain): | |||
return None | |||
|
|||
|
|||
async def async_check_ha_config_file_new(hass, config_path=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't call it "new", that name doesn't age…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will completely replace the function above without the "_new" once done
homeassistant/config.py
Outdated
clear_secret_cache() | ||
|
||
# Validate and extract core config | ||
core_config = CORE_CONFIG_SCHEMA(config.get(CONF_CORE, {})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use config.pop(CONF_CORE, {})
to remove it from the config
dict.
homeassistant/config.py
Outdated
|
||
# Filter out the repeating and common config section [homeassistant] | ||
components = set(key.split(' ')[0] for key in config.keys()) | ||
print(components) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stale print
homeassistant/config.py
Outdated
# To ADD config entries? | ||
# hass.config_entries = config_entries.ConfigEntries(hass, config) | ||
# yield from hass.config_entries.async_load() | ||
# components.update(hass.config_entries.async_domains()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Config entries are a comple 8000 te standalone configuration channel from normal config. They also don't have validation as they are read and written by code.
@@ -264,9 +265,17 @@ def mock_logger_exception_bootstrap(msg, *params): | |||
yaml.yaml.SafeLoader.add_constructor('!secret', yaml._secret_yaml) | |||
|
|||
try: | |||
with patch('homeassistant.util.logging.AsyncHandler._process'): | |||
if new_method: | |||
hass = core.HomeAssistant() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would we even need to instantiate hass
to check configuration? That is not necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, only need loop and config
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would you need loop or config? You really only need config path.
You only need loop
if you plan on using await
, but just validating some dicts doesn't require any awaits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking load_yaml_config_file
should actually be awaited, sice it does IO?
But otherwise there is no IO (except for importing all the plaforms, which I would think also needs awaiting)
May I suggest you take a slightly different approach to this problem: Build a standalone config checker (without hass dependency) from scratch that validates CONFIG_SCHEMA and PLATFORM_SCHEMA without trying to share any code with the current bootstrap process (just copy paste the pieces from |
This is the framework for *_SCHEMA validation. The standalone checker will be a single method in
This method can then be (a) used by
I can start that work as a second commit to this PR (preferred) or as another PR based of this one? |
If you pass in You can do it as a second commit to this PR. |
# Merge packages | ||
conf_util.merge_packages_config( | ||
config, core_config.get(conf_util.CONF_PACKAGES, {})) | ||
|
||
# Make a copy because we are mutating it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was already mutated by merge_packages
in the past. Loading will ensure values are {} and never None
homeassistant/config.py
Outdated
|
||
# Merge packages | ||
with patch('homeassistant.config._log_pkg_error', side_effect=_pack_error): | ||
merge_packages_config(config, core_config.get(CONF_PACKAGES, {})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Patching the _log_pkg_error
else I have to rewrite the complete method.
Another approach is to add the logging function to the funtion definition
def merge_packages_config(config, packages, _log_pkg_error=_log_pkg_error):
and then call it with _log_pkg_error=_pack_error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
passing method in is a 1000x better solution than patching.
homeassistant/config.py
Outdated
@@ -669,6 +680,116 @@ def async_process_component_config(hass, config, domain): | |||
return None | |||
|
|||
|
|||
def check_ha_config_file(config_dir): | |||
"""Check if Home Assistant configuration file is valid.""" | |||
from unittest.mock import patch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should never use the unittest module in normal code. It is a big red flag of poor design.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggested another aproach at the patch...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I saw it later. Like it!
homeassistant/config.py
Outdated
return {}, [("File configuration.yaml not found.", None, None)] | ||
config = load_yaml_config_file(config_path) | ||
except HomeAssistantError as err: | ||
return {}, [(str(err), None, None)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not return tuples, they are confusing and also mean that any code depending on this function will need to be updated if we want to add another parameter. Please consider using a namedtuple or an attr class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea behind namedtuples. The current double tuple approach can be confusing, especially in the unit tests
Thanks for the feedback @balloob. I think this is turning out quite well now, In this form it can even become a drop in replacement for loading config, validated and errors removed. (something says there might still be some border cases when we have no SCHEMA defined in components/platforms, but can't think of an example now, maybe automation...) |
This should address flaky testconfig tests from #10966 |
homeassistant/config.py
Outdated
@@ -669,6 +680,128 @@ def async_process_component_config(hass, config, domain): | |||
return None | |||
|
|||
|
|||
def check_ha_config_file(config_dir): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we keep this code inside the check config script for now?
homeassistant/config.py
Outdated
CheckConfigError = namedtuple( # pylint: disable=invalid-name | ||
'CheckConfigError', "message domain config") | ||
|
||
class ConfigResult(OrderedDict): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not define this inside function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With attr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will also move it to check_config.py.
@pvizeli not sure I'm following you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think he means using the new attrs library which has recently been added as a global requirement (home-assistant/architecture#4)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, thanks. And @attr.s will call super() for me?
Ready for review (Coveralls seems to be down) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh this is so much better 😍 🐬
* Initial async_check_ha_config_file * check_ha_config_file * Various fixes * feedback - return the config * move_to_check_config
Description:
The
check_config
script #2657 relies on some serious patching to test user configuration. It certainly helped me to understand unit testing... In #5609 it got incorporated into services/check upon restart, spawning another process and capturing the script output. It works, but it's slow... this also contains a record amount of slowest entries in all Travis jobs.This PR will
replaceremoving the need for patches, or the speed penalty of the current script method.async_check_ha_config_file
used by the check_config service,Using the commandline script you can still access
---files
,--secrets
and--info all
to print options. These however still require patching to extract the interesting parts from the yaml processor.Test behaviour using:
Todo:
Eventually make--new
the standard and remove the old parts of the script.