From 602eb472f9e7e95e332dcf7ec9e5909421155f15 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 13 Oct 2017 22:02:41 +0200 Subject: [PATCH] Allow to set a option als optional (#218) * Update validate.py * Update validate.py * Update validate.py * Update validate.py * Update validate.py * fix bug * Extend schema * Update validate.py * fix lint * Update validate.py * Update validate.py * Fix deepmerge * Update setup.py * Update validate.py --- hassio/addons/addon.py | 11 ++++----- hassio/addons/validate.py | 50 +++++++++++++++++++++++---------------- setup.py | 3 +-- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/hassio/addons/addon.py b/hassio/addons/addon.py index fbfb7d4ad..2a5ed4438 100644 --- a/hassio/addons/addon.py +++ b/hassio/addons/addon.py @@ -8,7 +8,6 @@ import shutil import tarfile from tempfile import TemporaryDirectory -from deepmerge import Merger import voluptuous as vol from voluptuous.humanize import humanize_error @@ -33,8 +32,6 @@ RE_WEBUI = re.compile( r"^(?:(?Phttps?)|\[PROTO:(?P\w+)\])" r":\/\/\[HOST\]:\[PORT:(?P\d+)\](?P.*)$") -MERGE_OPT = Merger([(dict, ['merge'])], ['override'], ['override']) - class Addon(object): """Hold data for addon inside HassIO.""" @@ -109,10 +106,10 @@ class Addon(object): def options(self): """Return options with local changes.""" if self.is_installed: - return MERGE_OPT.merge( - self.data.system[self._id][ATTR_OPTIONS], - self.data.user[self._id][ATTR_OPTIONS], - ) + return { + **self.data.system[self._id][ATTR_OPTIONS], + **self.data.user[self._id][ATTR_OPTIONS] + } return self.data.cache[self._id][ATTR_OPTIONS] @options.setter diff --git a/hassio/addons/validate.py b/hassio/addons/validate.py index 3f02c4738..36a34fedb 100644 --- a/hassio/addons/validate.py +++ b/hassio/addons/validate.py @@ -38,7 +38,7 @@ RE_SCHEMA_ELEMENT = re.compile( r"|int(?:\((?P\d+)?,(?P\d+)?\))?" r"|float(?:\((?P[\d\.]+)?,(?P[\d\.]+)?\))?" r"|match\((?P.*)\)" - r")$" + r")\??$" ) SCHEMA_ELEMENT = vol.Match(RE_SCHEMA_ELEMENT) @@ -105,8 +105,13 @@ SCHEMA_ADDON_CONFIG = vol.Schema({ vol.Required(ATTR_OPTIONS): dict, vol.Required(ATTR_SCHEMA): vol.Any(vol.Schema({ vol.Coerce(str): vol.Any(SCHEMA_ELEMENT, [ - vol.Any(SCHEMA_ELEMENT, {vol.Coerce(str): SCHEMA_ELEMENT}) - ], vol.Schema({vol.Coerce(str): SCHEMA_ELEMENT})) + vol.Any( + SCHEMA_ELEMENT, + {vol.Coerce(str): vol.Any(SCHEMA_ELEMENT, [SCHEMA_ELEMENT])} + ), + ], vol.Schema({ + vol.Coerce(str): vol.Any(SCHEMA_ELEMENT, [SCHEMA_ELEMENT]) + })) }), False), vol.Optional(ATTR_IMAGE): vol.Match(r"^[\-\w{}]+/[\-\w{}]+$"), vol.Optional(ATTR_TIMEOUT, default=10): @@ -199,6 +204,7 @@ def validate_options(raw_schema): raise vol.Invalid( "Type error for {}.".format(key)) from None + _check_missing_options(raw_schema, options, 'root') return options return validate @@ -246,25 +252,10 @@ def _nested_validate_list(typ, data_list, key): options = [] for element in data_list: - # dict list + # Nested? if isinstance(typ, dict): - c_options = {} - for c_key, c_value in element.items(): - # Ignore unknown options / remove from list - if c_key not in typ: - _LOGGER.warning("Unknown options %s", c_key) - continue - - c_options[c_key] = _single_validate(typ[c_key], c_value, c_key) - - # check if all options are exists - missing = set(typ) - set(c_options) - if missing: - raise vol.Invalid( - "Missing {} options inside nested list".format(missing)) - + c_options = _nested_validate_dict(typ, element, key) options.append(c_options) - # normal list else: options.append(_single_validate(typ, element, key)) @@ -281,6 +272,23 @@ def _nested_validate_dict(typ, data_dict, key): _LOGGER.warning("Unknown options %s", c_key) continue - options[c_key] = _single_validate(typ[c_key], c_value, c_key) + # Nested? + if isinstance(typ[c_key], list): + options[c_key] = _nested_validate_list(typ[c_key][0], + c_value, c_key) + else: + options[c_key] = _single_validate(typ[c_key], c_value, c_key) + _check_missing_options(typ, options, key) return options + + +def _check_missing_options(origin, exists, root): + """Check if all options are exists.""" + missing = set(origin) - set(exists) + for miss_opt in missing: + if isinstance(origin[miss_opt], str) and \ + origin[miss_opt].endswith("?"): + continue + raise vol.Invalid( + "Missing option {} in {}".format(miss_opt, root)) diff --git a/setup.py b/setup.py index d265d8f11..179b6831d 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,6 @@ setup( 'pyotp', 'pyqrcode', 'pytz', - 'pyudev', - 'deepmerge' + 'pyudev' ] )