From 1c7ca4bc6f01460dadd4affe64144844fba19327 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Tue, 26 Feb 2019 19:22:33 +0100 Subject: [PATCH] Recommend similar keys for spelling errors (#458) * Recommend similar keys for spelling errors Fixes https://github.com/esphome/feature-requests/issues/68 * Fixes * Fix --- esphome/automation.py | 12 +- esphome/components/ads1115.py | 2 +- esphome/components/apds9960.py | 2 +- esphome/components/api.py | 10 +- esphome/components/binary_sensor/__init__.py | 2 +- .../binary_sensor/remote_receiver.py | 16 +- esphome/components/binary_sensor/template.py | 2 +- esphome/components/cover/template.py | 2 +- esphome/components/custom_component.py | 4 +- esphome/components/dallas.py | 2 +- esphome/components/debug.py | 5 +- esphome/components/deep_sleep.py | 4 +- esphome/components/esp32_ble_beacon.py | 2 +- esphome/components/esp32_ble_tracker.py | 2 +- esphome/components/esp32_touch.py | 2 +- esphome/components/ethernet.py | 2 +- esphome/components/fan/speed.py | 2 +- esphome/components/font.py | 6 +- esphome/components/globals.py | 2 +- esphome/components/i2c.py | 2 +- esphome/components/image.py | 2 +- esphome/components/interval.py | 2 +- esphome/components/light/__init__.py | 28 +-- esphome/components/logger.py | 8 +- esphome/components/mpr121.py | 2 +- esphome/components/mqtt.py | 8 +- esphome/components/my9231.py | 2 +- esphome/components/ota.py | 2 +- esphome/components/output/__init__.py | 4 +- esphome/components/pca9685.py | 2 +- esphome/components/pcf8574.py | 2 +- esphome/components/pn532.py | 2 +- esphome/components/power_supply.py | 2 +- esphome/components/rdm6300.py | 4 +- esphome/components/remote_receiver.py | 2 +- esphome/components/remote_transmitter.py | 14 +- esphome/components/sensor/__init__.py | 4 +- esphome/components/sensor/bme680.py | 2 +- esphome/components/sensor/ina3221.py | 2 +- esphome/components/sensor/pulse_counter.py | 2 +- esphome/components/sensor/template.py | 2 +- esphome/components/spi.py | 2 +- esphome/components/status_led.py | 2 +- esphome/components/stepper/__init__.py | 6 +- esphome/components/substitutions.py | 2 +- .../components/switch/remote_transmitter.py | 18 +- esphome/components/switch/template.py | 2 +- esphome/components/switch/uart.py | 2 +- esphome/components/text_sensor/template.py | 2 +- esphome/components/uart.py | 2 +- esphome/components/web_server.py | 2 +- esphome/components/wifi.py | 6 +- esphome/config.py | 23 ++- esphome/config_validation.py | 14 +- esphome/core_config.py | 8 +- esphome/pins.py | 6 +- esphome/voluptuous_schema.py | 161 ++++++++++++++++++ 57 files changed, 302 insertions(+), 137 deletions(-) create mode 100644 esphome/voluptuous_schema.py diff --git a/esphome/automation.py b/esphome/automation.py index 7e880d1ea7..349628d3c4 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -131,7 +131,7 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): # Next try as a sequence of automations try: - return vol.Schema([schema])(value) + return cv.Schema([schema])(value) except vol.Invalid as err2: if 'Unable to find action' in str(err): raise err2 @@ -146,7 +146,7 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): def validator(value): value = validator_(value) if extra_validators is not None: - value = vol.Schema([extra_validators])(value) + value = cv.Schema([extra_validators])(value) if single: if len(value) != 1: raise vol.Invalid("Cannot have more than 1 automation for templates") @@ -156,7 +156,7 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): return validator -AUTOMATION_SCHEMA = vol.Schema({ +AUTOMATION_SCHEMA = cv.Schema({ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_variable_id(Trigger), cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_variable_id(Automation), vol.Optional(CONF_IF): validate_recursive_condition, @@ -187,7 +187,7 @@ def or_condition_to_code(config, condition_id, arg_type, template_arg): yield Pvariable(condition_id, rhs, type=type) -RANGE_CONDITION_SCHEMA = vol.All(vol.Schema({ +RANGE_CONDITION_SCHEMA = vol.All(cv.Schema({ vol.Optional(CONF_ABOVE): cv.templatable(cv.float_), vol.Optional(CONF_BELOW): cv.templatable(cv.float_), }), cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)) @@ -250,7 +250,7 @@ def if_action_to_code(config, action_id, arg_type, template_arg): yield action -WHILE_ACTION_SCHEMA = vol.Schema({ +WHILE_ACTION_SCHEMA = cv.Schema({ vol.Required(CONF_CONDITION): validate_recursive_condition, vol.Required(CONF_THEN): validate_recursive_action, }) @@ -270,7 +270,7 @@ def while_action_to_code(config, action_id, arg_type, template_arg): def validate_wait_until(value): - schema = vol.Schema({ + schema = cv.Schema({ vol.Required(CONF_CONDITION): validate_recursive_condition }) if isinstance(value, dict) and CONF_CONDITION in value: diff --git a/esphome/components/ads1115.py b/esphome/components/ads1115.py index 0bf25125b1..62386ef7b6 100644 --- a/esphome/components/ads1115.py +++ b/esphome/components/ads1115.py @@ -12,7 +12,7 @@ MULTI_CONF = True ADS1115Component = sensor.sensor_ns.class_('ADS1115Component', Component, i2c.I2CDevice) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(ADS1115Component), vol.Required(CONF_ADDRESS): cv.i2c_address, }).extend(cv.COMPONENT_SCHEMA.schema) diff --git a/esphome/components/apds9960.py b/esphome/components/apds9960.py index bebe71e3fa..c8a38cc231 100644 --- a/esphome/components/apds9960.py +++ b/esphome/components/apds9960.py @@ -13,7 +13,7 @@ MULTI_CONF = True CONF_APDS9960_ID = 'apds9960_id' APDS9960 = sensor.sensor_ns.class_('APDS9960', PollingComponent, i2c.I2CDevice) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(APDS9960), vol.Optional(CONF_ADDRESS): cv.i2c_address, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, diff --git a/esphome/components/api.py b/esphome/components/api.py index 3acf0b9143..00a578be9d 100644 --- a/esphome/components/api.py +++ b/esphome/components/api.py @@ -15,7 +15,7 @@ HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', KeyValuePair = api_ns.class_('KeyValuePair') TemplatableKeyValuePair = api_ns.class_('TemplatableKeyValuePair') -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(APIServer), vol.Optional(CONF_PORT, default=6053): cv.port, vol.Optional(CONF_PASSWORD, default=''): cv.string_strict, @@ -49,16 +49,16 @@ def lib_deps(config): CONF_HOMEASSISTANT_SERVICE = 'homeassistant.service' -HOMEASSISTANT_SERVIC_ACTION_SCHEMA = vol.Schema({ +HOMEASSISTANT_SERVIC_ACTION_SCHEMA = cv.Schema({ cv.GenerateID(): cv.use_variable_id(APIServer), vol.Required(CONF_SERVICE): cv.string, - vol.Optional(CONF_DATA): vol.Schema({ + vol.Optional(CONF_DATA): cv.Schema({ cv.string: cv.string, }), - vol.Optional(CONF_DATA_TEMPLATE): vol.Schema({ + vol.Optional(CONF_DATA_TEMPLATE): cv.Schema({ cv.string: cv.string, }), - vol.Optional(CONF_VARIABLES): vol.Schema({ + vol.Optional(CONF_VARIABLES): cv.Schema({ cv.string: cv.lambda_, }), }) diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 2cb2b2ef15..45e129853f 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -62,7 +62,7 @@ FILTERS_SCHEMA = cv.ensure_list({ vol.Optional(CONF_HEARTBEAT): cv.invalid("The heartbeat filter has been removed in 1.11.0"), }, cv.has_exactly_one_key(*FILTER_KEYS)) -MULTI_CLICK_TIMING_SCHEMA = vol.Schema({ +MULTI_CLICK_TIMING_SCHEMA = cv.Schema({ vol.Optional(CONF_STATE): cv.boolean, vol.Optional(CONF_MIN_LENGTH): cv.positive_time_period_milliseconds, vol.Optional(CONF_MAX_LENGTH): cv.positive_time_period_milliseconds, diff --git a/esphome/components/binary_sensor/remote_receiver.py b/esphome/components/binary_sensor/remote_receiver.py index 1089eb4da4..9d06d22fc3 100644 --- a/esphome/components/binary_sensor/remote_receiver.py +++ b/esphome/components/binary_sensor/remote_receiver.py @@ -41,7 +41,7 @@ RCSwitchTypeDReceiver = remote_ns.class_('RCSwitchTypeDReceiver', RCSwitchRawRec def validate_raw(value): if isinstance(value, dict): - return vol.Schema({ + return cv.Schema({ cv.GenerateID(): cv.declare_variable_id(int32), vol.Required(CONF_DATA): [vol.Any(vol.Coerce(int), cv.time_period_microseconds)], })(value) @@ -52,29 +52,29 @@ def validate_raw(value): PLATFORM_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(RemoteReceiver), - vol.Optional(CONF_JVC): vol.Schema({ + vol.Optional(CONF_JVC): cv.Schema({ vol.Required(CONF_DATA): cv.hex_uint32_t, }), - vol.Optional(CONF_LG): vol.Schema({ + vol.Optional(CONF_LG): cv.Schema({ vol.Required(CONF_DATA): cv.hex_uint32_t, vol.Optional(CONF_NBITS, default=28): cv.one_of(28, 32, int=True), }), - vol.Optional(CONF_NEC): vol.Schema({ + vol.Optional(CONF_NEC): cv.Schema({ vol.Required(CONF_ADDRESS): cv.hex_uint16_t, vol.Required(CONF_COMMAND): cv.hex_uint16_t, }), - vol.Optional(CONF_SAMSUNG): vol.Schema({ + vol.Optional(CONF_SAMSUNG): cv.Schema({ vol.Required(CONF_DATA): cv.hex_uint32_t, }), - vol.Optional(CONF_SONY): vol.Schema({ + vol.Optional(CONF_SONY): cv.Schema({ vol.Required(CONF_DATA): cv.hex_uint32_t, vol.Optional(CONF_NBITS, default=12): cv.one_of(12, 15, 20, int=True), }), - vol.Optional(CONF_PANASONIC): vol.Schema({ + vol.Optional(CONF_PANASONIC): cv.Schema({ vol.Required(CONF_ADDRESS): cv.hex_uint16_t, vol.Required(CONF_COMMAND): cv.hex_uint32_t, }), - vol.Optional(CONF_RC5): vol.Schema({ + vol.Optional(CONF_RC5): cv.Schema({ vol.Required(CONF_ADDRESS): vol.All(cv.hex_int, vol.Range(min=0, max=0x1F)), vol.Required(CONF_COMMAND): vol.All(cv.hex_int, vol.Range(min=0, max=0x3F)), }), diff --git a/esphome/components/binary_sensor/template.py b/esphome/components/binary_sensor/template.py index a02709626d..1cd7c59a07 100644 --- a/esphome/components/binary_sensor/template.py +++ b/esphome/components/binary_sensor/template.py @@ -36,7 +36,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_BINARY_SENSOR' CONF_BINARY_SENSOR_TEMPLATE_PUBLISH = 'binary_sensor.template.publish' -BINARY_SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA = vol.Schema({ +BINARY_SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA = cv.Schema({ vol.Required(CONF_ID): cv.use_variable_id(binary_sensor.BinarySensor), vol.Required(CONF_STATE): cv.templatable(cv.boolean), }) diff --git a/esphome/components/cover/template.py b/esphome/components/cover/template.py index 5f278da7bc..11c2b85e5f 100644 --- a/esphome/components/cover/template.py +++ b/esphome/components/cover/template.py @@ -55,7 +55,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_COVER' CONF_COVER_TEMPLATE_PUBLISH = 'cover.template.publish' -COVER_TEMPLATE_PUBLISH_ACTION_SCHEMA = vol.Schema({ +COVER_TEMPLATE_PUBLISH_ACTION_SCHEMA = cv.Schema({ vol.Required(CONF_ID): cv.use_variable_id(cover.Cover), vol.Required(CONF_STATE): cv.templatable(cover.validate_cover_state), }) diff --git a/esphome/components/custom_component.py b/esphome/components/custom_component.py index 2af824f50e..6a94934ed5 100644 --- a/esphome/components/custom_component.py +++ b/esphome/components/custom_component.py @@ -9,10 +9,10 @@ from esphome.cpp_types import Component, ComponentPtr, esphome_ns, std_vector CustomComponentConstructor = esphome_ns.class_('CustomComponentConstructor') MULTI_CONF = True -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(CustomComponentConstructor), vol.Required(CONF_LAMBDA): cv.lambda_, - vol.Optional(CONF_COMPONENTS): cv.ensure_list(vol.Schema({ + vol.Optional(CONF_COMPONENTS): cv.ensure_list(cv.Schema({ cv.GenerateID(): cv.declare_variable_id(Component) }).extend(cv.COMPONENT_SCHEMA.schema)), }) diff --git a/esphome/components/dallas.py b/esphome/components/dallas.py index 74eb1cebf3..bce923bbed 100644 --- a/esphome/components/dallas.py +++ b/esphome/components/dallas.py @@ -11,7 +11,7 @@ from esphome.cpp_types import App, PollingComponent DallasComponent = sensor.sensor_ns.class_('DallasComponent', PollingComponent) MULTI_CONF = True -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(DallasComponent), vol.Required(CONF_PIN): pins.input_pin, vol.Optional(CONF_UPDATE_INTERVAL): cv.update_interval, diff --git a/esphome/components/debug.py b/esphome/components/debug.py index 1105105914..550d5856c0 100644 --- a/esphome/components/debug.py +++ b/esphome/components/debug.py @@ -1,11 +1,10 @@ -import voluptuous as vol - +import esphome.config_validation as cv from esphome.cpp_generator import add from esphome.cpp_types import App DEPENDENCIES = ['logger'] -CONFIG_SCHEMA = vol.Schema({}) +CONFIG_SCHEMA = cv.Schema({}) def to_code(config): diff --git a/esphome/components/deep_sleep.py b/esphome/components/deep_sleep.py index 9313130b70..34c69fd29e 100644 --- a/esphome/components/deep_sleep.py +++ b/esphome/components/deep_sleep.py @@ -38,14 +38,14 @@ EXT1_WAKEUP_MODES = { CONF_WAKEUP_PIN_MODE = 'wakeup_pin_mode' CONF_ESP32_EXT1_WAKEUP = 'esp32_ext1_wakeup' -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(DeepSleepComponent), vol.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, vol.Optional(CONF_WAKEUP_PIN): vol.All(cv.only_on_esp32, pins.internal_gpio_input_pin_schema, validate_pin_number), vol.Optional(CONF_WAKEUP_PIN_MODE): vol.All(cv.only_on_esp32, cv.one_of(*WAKEUP_PIN_MODES), upper=True), - vol.Optional(CONF_ESP32_EXT1_WAKEUP): vol.All(cv.only_on_esp32, vol.Schema({ + vol.Optional(CONF_ESP32_EXT1_WAKEUP): vol.All(cv.only_on_esp32, cv.Schema({ vol.Required(CONF_PINS): cv.ensure_list(pins.shorthand_input_pin, validate_pin_number), vol.Required(CONF_MODE): cv.one_of(*EXT1_WAKEUP_MODES, upper=True), })), diff --git a/esphome/components/esp32_ble_beacon.py b/esphome/components/esp32_ble_beacon.py index 3996f298d8..adb348e548 100644 --- a/esphome/components/esp32_ble_beacon.py +++ b/esphome/components/esp32_ble_beacon.py @@ -13,7 +13,7 @@ ESP32BLEBeacon = esphome_ns.class_('ESP32BLEBeacon', Component) CONF_MAJOR = 'major' CONF_MINOR = 'minor' -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(ESP32BLEBeacon), vol.Required(CONF_TYPE): cv.one_of('IBEACON', upper=True), vol.Required(CONF_UUID): cv.uuid, diff --git a/esphome/components/esp32_ble_tracker.py b/esphome/components/esp32_ble_tracker.py index 2e1f3c9d51..01c8c8d3fe 100644 --- a/esphome/components/esp32_ble_tracker.py +++ b/esphome/components/esp32_ble_tracker.py @@ -18,7 +18,7 @@ XIAOMI_SENSOR_SCHEMA = sensor.SENSOR_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(XiaomiSensor) }) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(ESP32BLETracker), vol.Optional(CONF_SCAN_INTERVAL): cv.positive_time_period_seconds, }).extend(cv.COMPONENT_SCHEMA.schema) diff --git a/esphome/components/esp32_touch.py b/esphome/components/esp32_touch.py index 02eedcdc8e..9374326162 100644 --- a/esphome/components/esp32_touch.py +++ b/esphome/components/esp32_touch.py @@ -46,7 +46,7 @@ VOLTAGE_ATTENUATION = { ESP32TouchComponent = binary_sensor.binary_sensor_ns.class_('ESP32TouchComponent', Component) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(ESP32TouchComponent), vol.Optional(CONF_SETUP_MODE): cv.boolean, vol.Optional(CONF_IIR_FILTER): cv.positive_time_period_milliseconds, diff --git a/esphome/components/ethernet.py b/esphome/components/ethernet.py index 0ce83d3586..94578d272f 100644 --- a/esphome/components/ethernet.py +++ b/esphome/components/ethernet.py @@ -46,7 +46,7 @@ def validate(config): return config -CONFIG_SCHEMA = vol.All(vol.Schema({ +CONFIG_SCHEMA = vol.All(cv.Schema({ cv.GenerateID(): cv.declare_variable_id(EthernetComponent), vol.Required(CONF_TYPE): cv.one_of(*ETHERNET_TYPES, upper=True), vol.Required(CONF_MDC_PIN): pins.output_pin, diff --git a/esphome/components/fan/speed.py b/esphome/components/fan/speed.py index 62a1274487..5a2bc61140 100644 --- a/esphome/components/fan/speed.py +++ b/esphome/components/fan/speed.py @@ -14,7 +14,7 @@ PLATFORM_SCHEMA = cv.nameable(fan.FAN_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_SPEED_STATE_TOPIC): cv.publish_topic, vol.Optional(CONF_SPEED_COMMAND_TOPIC): cv.subscribe_topic, vol.Optional(CONF_OSCILLATION_OUTPUT): cv.use_variable_id(output.BinaryOutput), - vol.Optional(CONF_SPEED): vol.Schema({ + vol.Optional(CONF_SPEED): cv.Schema({ vol.Required(CONF_LOW): cv.percentage, vol.Required(CONF_MEDIUM): cv.percentage, vol.Required(CONF_HIGH): cv.percentage, diff --git a/esphome/components/font.py b/esphome/components/font.py index a6907240fd..ed8c47fe2c 100644 --- a/esphome/components/font.py +++ b/esphome/components/font.py @@ -19,8 +19,8 @@ Glyph = display.display_ns.class_('Glyph') def validate_glyphs(value): if isinstance(value, list): - value = vol.Schema([cv.string])(value) - value = vol.Schema([cv.string])(list(value)) + value = cv.Schema([cv.string])(value) + value = cv.Schema([cv.string])(list(value)) def comparator(x, y): x_ = x.encode('utf-8') @@ -69,7 +69,7 @@ def validate_truetype_file(value): DEFAULT_GLYPHS = u' !"%()+,-.:0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°' CONF_RAW_DATA_ID = 'raw_data_id' -FONT_SCHEMA = vol.Schema({ +FONT_SCHEMA = cv.Schema({ vol.Required(CONF_ID): cv.declare_variable_id(Font), vol.Required(CONF_FILE): validate_truetype_file, vol.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs, diff --git a/esphome/components/globals.py b/esphome/components/globals.py index 749c63d923..e56aa1b136 100644 --- a/esphome/components/globals.py +++ b/esphome/components/globals.py @@ -10,7 +10,7 @@ GlobalVariableComponent = esphome_ns.class_('GlobalVariableComponent', Component MULTI_CONF = True -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ vol.Required(CONF_ID): cv.declare_variable_id(GlobalVariableComponent), vol.Required(CONF_TYPE): cv.string_strict, vol.Optional(CONF_INITIAL_VALUE): cv.string_strict, diff --git a/esphome/components/i2c.py b/esphome/components/i2c.py index 2226b51f83..0ee857f47b 100644 --- a/esphome/components/i2c.py +++ b/esphome/components/i2c.py @@ -11,7 +11,7 @@ from esphome.cpp_types import App, Component, esphome_ns I2CComponent = esphome_ns.class_('I2CComponent', Component) I2CDevice = pins.I2CDevice -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(I2CComponent), vol.Optional(CONF_SDA, default='SDA'): pins.input_pin, vol.Optional(CONF_SCL, default='SCL'): pins.input_pin, diff --git a/esphome/components/image.py b/esphome/components/image.py index 01b469b382..4b6cb622c4 100644 --- a/esphome/components/image.py +++ b/esphome/components/image.py @@ -20,7 +20,7 @@ Image_ = display.display_ns.class_('Image') CONF_RAW_DATA_ID = 'raw_data_id' -IMAGE_SCHEMA = vol.Schema({ +IMAGE_SCHEMA = cv.Schema({ vol.Required(CONF_ID): cv.declare_variable_id(Image_), vol.Required(CONF_FILE): cv.file_, vol.Optional(CONF_RESIZE): cv.dimensions, diff --git a/esphome/components/interval.py b/esphome/components/interval.py index f51fd163af..49aa9b9163 100644 --- a/esphome/components/interval.py +++ b/esphome/components/interval.py @@ -9,7 +9,7 @@ from esphome.cpp_types import App, NoArg, PollingComponent, Trigger, esphome_ns IntervalTrigger = esphome_ns.class_('IntervalTrigger', Trigger.template(NoArg), PollingComponent) -CONFIG_SCHEMA = automation.validate_automation(vol.Schema({ +CONFIG_SCHEMA = automation.validate_automation(cv.Schema({ cv.GenerateID(): cv.declare_variable_id(IntervalTrigger), vol.Required(CONF_INTERVAL): cv.positive_time_period_milliseconds, }).extend(cv.COMPONENT_SCHEMA.schema)) diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index 783f1400b6..13b40e19d4 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -88,22 +88,22 @@ ADDRESSABLE_EFFECTS = RGB_EFFECTS + [CONF_ADDRESSABLE_LAMBDA, CONF_ADDRESSABLE_R CONF_ADDRESSABLE_TWINKLE, CONF_ADDRESSABLE_RANDOM_TWINKLE, CONF_ADDRESSABLE_FIREWORKS, CONF_ADDRESSABLE_FLICKER] -EFFECTS_SCHEMA = vol.Schema({ - vol.Optional(CONF_LAMBDA): vol.Schema({ +EFFECTS_SCHEMA = cv.Schema({ + vol.Optional(CONF_LAMBDA): cv.Schema({ vol.Required(CONF_NAME): cv.string, vol.Required(CONF_LAMBDA): cv.lambda_, vol.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.positive_time_period_milliseconds, }), - vol.Optional(CONF_RANDOM): vol.Schema({ + vol.Optional(CONF_RANDOM): cv.Schema({ cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(RandomLightEffect), vol.Optional(CONF_NAME, default="Random"): cv.string, vol.Optional(CONF_TRANSITION_LENGTH): cv.positive_time_period_milliseconds, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, }), - vol.Optional(CONF_STROBE): vol.Schema({ + vol.Optional(CONF_STROBE): cv.Schema({ cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(StrobeLightEffect), vol.Optional(CONF_NAME, default="Strobe"): cv.string, - vol.Optional(CONF_COLORS): vol.All(cv.ensure_list(vol.Schema({ + vol.Optional(CONF_COLORS): vol.All(cv.ensure_list(cv.Schema({ vol.Optional(CONF_STATE, default=True): cv.boolean, vol.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, vol.Optional(CONF_RED, default=1.0): cv.percentage, @@ -114,24 +114,24 @@ EFFECTS_SCHEMA = vol.Schema({ }), cv.has_at_least_one_key(CONF_STATE, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE)), vol.Length(min=2)), }), - vol.Optional(CONF_FLICKER): vol.Schema({ + vol.Optional(CONF_FLICKER): cv.Schema({ cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(FlickerLightEffect), vol.Optional(CONF_NAME, default="Flicker"): cv.string, vol.Optional(CONF_ALPHA): cv.percentage, vol.Optional(CONF_INTENSITY): cv.percentage, }), - vol.Optional(CONF_ADDRESSABLE_LAMBDA): vol.Schema({ + vol.Optional(CONF_ADDRESSABLE_LAMBDA): cv.Schema({ vol.Required(CONF_NAME): cv.string, vol.Required(CONF_LAMBDA): cv.lambda_, vol.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.positive_time_period_milliseconds, }), - vol.Optional(CONF_ADDRESSABLE_RAINBOW): vol.Schema({ + vol.Optional(CONF_ADDRESSABLE_RAINBOW): cv.Schema({ cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableRainbowLightEffect), vol.Optional(CONF_NAME, default="Rainbow"): cv.string, vol.Optional(CONF_SPEED): cv.uint32_t, vol.Optional(CONF_WIDTH): cv.uint32_t, }), - vol.Optional(CONF_ADDRESSABLE_COLOR_WIPE): vol.Schema({ + vol.Optional(CONF_ADDRESSABLE_COLOR_WIPE): cv.Schema({ cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableColorWipeEffect), vol.Optional(CONF_NAME, default="Color Wipe"): cv.string, vol.Optional(CONF_COLORS): cv.ensure_list({ @@ -145,24 +145,24 @@ EFFECTS_SCHEMA = vol.Schema({ vol.Optional(CONF_ADD_LED_INTERVAL): cv.positive_time_period_milliseconds, vol.Optional(CONF_REVERSE): cv.boolean, }), - vol.Optional(CONF_ADDRESSABLE_SCAN): vol.Schema({ + vol.Optional(CONF_ADDRESSABLE_SCAN): cv.Schema({ cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableScanEffect), vol.Optional(CONF_NAME, default="Scan"): cv.string, vol.Optional(CONF_MOVE_INTERVAL): cv.positive_time_period_milliseconds, }), - vol.Optional(CONF_ADDRESSABLE_TWINKLE): vol.Schema({ + vol.Optional(CONF_ADDRESSABLE_TWINKLE): cv.Schema({ cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableTwinkleEffect), vol.Optional(CONF_NAME, default="Twinkle"): cv.string, vol.Optional(CONF_TWINKLE_PROBABILITY): cv.percentage, vol.Optional(CONF_PROGRESS_INTERVAL): cv.positive_time_period_milliseconds, }), - vol.Optional(CONF_ADDRESSABLE_RANDOM_TWINKLE): vol.Schema({ + vol.Optional(CONF_ADDRESSABLE_RANDOM_TWINKLE): cv.Schema({ cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableRandomTwinkleEffect), vol.Optional(CONF_NAME, default="Random Twinkle"): cv.string, vol.Optional(CONF_TWINKLE_PROBABILITY): cv.percentage, vol.Optional(CONF_PROGRESS_INTERVAL): cv.positive_time_period_milliseconds, }), - vol.Optional(CONF_ADDRESSABLE_FIREWORKS): vol.Schema({ + vol.Optional(CONF_ADDRESSABLE_FIREWORKS): cv.Schema({ cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableFireworksEffect), vol.Optional(CONF_NAME, default="Fireworks"): cv.string, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, @@ -170,7 +170,7 @@ EFFECTS_SCHEMA = vol.Schema({ vol.Optional(CONF_USE_RANDOM_COLOR): cv.boolean, vol.Optional(CONF_FADE_OUT_RATE): cv.uint8_t, }), - vol.Optional(CONF_ADDRESSABLE_FLICKER): vol.Schema({ + vol.Optional(CONF_ADDRESSABLE_FLICKER): cv.Schema({ cv.GenerateID(CONF_EFFECT_ID): cv.declare_variable_id(AddressableFlickerEffect), vol.Optional(CONF_NAME, default="Addressable Flicker"): cv.string, vol.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, diff --git a/esphome/components/logger.py b/esphome/components/logger.py index 3115930a0b..8cf6b6dbf7 100644 --- a/esphome/components/logger.py +++ b/esphome/components/logger.py @@ -73,13 +73,13 @@ def validate_local_no_higher_than_global(value): LogComponent = esphome_ns.class_('LogComponent', Component) -CONFIG_SCHEMA = vol.All(vol.Schema({ +CONFIG_SCHEMA = vol.All(cv.Schema({ cv.GenerateID(): cv.declare_variable_id(LogComponent), vol.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int, vol.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.validate_bytes, vol.Optional(CONF_HARDWARE_UART, default='UART0'): uart_selection, vol.Optional(CONF_LEVEL): is_log_level, - vol.Optional(CONF_LOGS): vol.Schema({ + vol.Optional(CONF_LOGS): cv.Schema({ cv.string: is_log_level, }) }), validate_local_no_higher_than_global) @@ -129,8 +129,8 @@ def required_build_flags(config): def maybe_simple_message(schema): def validator(value): if isinstance(value, dict): - return vol.Schema(schema)(value) - return vol.Schema(schema)({CONF_FORMAT: value}) + return cv.Schema(schema)(value) + return cv.Schema(schema)({CONF_FORMAT: value}) return validator diff --git a/esphome/components/mpr121.py b/esphome/components/mpr121.py index dab4d75e51..d46a419d5f 100644 --- a/esphome/components/mpr121.py +++ b/esphome/components/mpr121.py @@ -13,7 +13,7 @@ MULTI_CONF = True CONF_MPR121_ID = 'mpr121_id' MPR121Component = binary_sensor.binary_sensor_ns.class_('MPR121Component', Component, i2c.I2CDevice) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(MPR121Component), vol.Optional(CONF_ADDRESS): cv.i2c_address }).extend(cv.COMPONENT_SCHEMA.schema) diff --git a/esphome/components/mqtt.py b/esphome/components/mqtt.py index b98898c54f..1cd01583ab 100644 --- a/esphome/components/mqtt.py +++ b/esphome/components/mqtt.py @@ -26,7 +26,7 @@ def validate_message_just_topic(value): return MQTT_MESSAGE_BASE({CONF_TOPIC: value}) -MQTT_MESSAGE_BASE = vol.Schema({ +MQTT_MESSAGE_BASE = cv.Schema({ vol.Required(CONF_TOPIC): cv.publish_topic, vol.Optional(CONF_QOS, default=0): cv.mqtt_qos, vol.Optional(CONF_RETAIN, default=True): cv.boolean, @@ -67,7 +67,7 @@ def validate_fingerprint(value): return value -CONFIG_SCHEMA = vol.All(vol.Schema({ +CONFIG_SCHEMA = vol.All(cv.Schema({ cv.GenerateID(): cv.declare_variable_id(MQTTClientComponent), vol.Required(CONF_BROKER): cv.string_strict, vol.Optional(CONF_PORT): cv.port, @@ -193,7 +193,7 @@ def to_code(config): CONF_MQTT_PUBLISH = 'mqtt.publish' -MQTT_PUBLISH_ACTION_SCHEMA = vol.Schema({ +MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema({ cv.GenerateID(): cv.use_variable_id(MQTTClientComponent), vol.Required(CONF_TOPIC): cv.templatable(cv.publish_topic), vol.Required(CONF_PAYLOAD): cv.templatable(cv.mqtt_payload), @@ -228,7 +228,7 @@ def mqtt_publish_action_to_code(config, action_id, arg_type, template_arg): CONF_MQTT_PUBLISH_JSON = 'mqtt.publish_json' -MQTT_PUBLISH_JSON_ACTION_SCHEMA = vol.Schema({ +MQTT_PUBLISH_JSON_ACTION_SCHEMA = cv.Schema({ cv.GenerateID(): cv.use_variable_id(MQTTClientComponent), vol.Required(CONF_TOPIC): cv.templatable(cv.publish_topic), vol.Required(CONF_PAYLOAD): cv.lambda_, diff --git a/esphome/components/my9231.py b/esphome/components/my9231.py index 096a782875..c950bcae7c 100644 --- a/esphome/components/my9231.py +++ b/esphome/components/my9231.py @@ -12,7 +12,7 @@ from esphome.cpp_types import App, Component MY9231OutputComponent = output.output_ns.class_('MY9231OutputComponent', Component) MULTI_CONF = True -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(MY9231OutputComponent), vol.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, vol.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, diff --git a/esphome/components/ota.py b/esphome/components/ota.py index d1e522741d..18dd5cbd85 100644 --- a/esphome/components/ota.py +++ b/esphome/components/ota.py @@ -12,7 +12,7 @@ _LOGGER = logging.getLogger(__name__) OTAComponent = esphome_ns.class_('OTAComponent', Component) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(OTAComponent), vol.Optional(CONF_SAFE_MODE, default=True): cv.boolean, vol.Optional(CONF_PORT): cv.port, diff --git a/esphome/components/output/__init__.py b/esphome/components/output/__init__.py index a79a88ff04..aa5609b6af 100644 --- a/esphome/components/output/__init__.py +++ b/esphome/components/output/__init__.py @@ -13,7 +13,7 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ }) -BINARY_OUTPUT_SCHEMA = vol.Schema({ +BINARY_OUTPUT_SCHEMA = cv.Schema({ vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent), vol.Optional(CONF_INVERTED): cv.boolean, }) @@ -95,7 +95,7 @@ def output_turn_off_to_code(config, action_id, arg_type, template_arg): CONF_OUTPUT_SET_LEVEL = 'output.set_level' -OUTPUT_SET_LEVEL_ACTION = vol.Schema({ +OUTPUT_SET_LEVEL_ACTION = cv.Schema({ vol.Required(CONF_ID): cv.use_variable_id(FloatOutput), vol.Required(CONF_LEVEL): cv.templatable(cv.percentage), }) diff --git a/esphome/components/pca9685.py b/esphome/components/pca9685.py index 0906db90d0..d565e47443 100644 --- a/esphome/components/pca9685.py +++ b/esphome/components/pca9685.py @@ -13,7 +13,7 @@ MULTI_CONF = True PCA9685OutputComponent = output.output_ns.class_('PCA9685OutputComponent', Component, i2c.I2CDevice) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(PCA9685OutputComponent), vol.Required(CONF_FREQUENCY): vol.All(cv.frequency, vol.Range(min=23.84, max=1525.88)), diff --git a/esphome/components/pcf8574.py b/esphome/components/pcf8574.py index a5ab90a441..4e2cf2eabb 100644 --- a/esphome/components/pcf8574.py +++ b/esphome/components/pcf8574.py @@ -20,7 +20,7 @@ PCF8675_GPIO_MODES = { PCF8574GPIOInputPin = io_ns.class_('PCF8574GPIOInputPin', GPIOInputPin) PCF8574GPIOOutputPin = io_ns.class_('PCF8574GPIOOutputPin', GPIOOutputPin) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ vol.Required(CONF_ID): cv.declare_variable_id(pins.PCF8574Component), vol.Optional(CONF_ADDRESS, default=0x21): cv.i2c_address, vol.Optional(CONF_PCF8575, default=False): cv.boolean, diff --git a/esphome/components/pn532.py b/esphome/components/pn532.py index ea188d3ae1..58f44520a9 100644 --- a/esphome/components/pn532.py +++ b/esphome/components/pn532.py @@ -17,7 +17,7 @@ PN532Component = binary_sensor.binary_sensor_ns.class_('PN532Component', Polling spi.SPIDevice) PN532Trigger = binary_sensor.binary_sensor_ns.class_('PN532Trigger', Trigger.template(std_string)) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(PN532Component), cv.GenerateID(CONF_SPI_ID): cv.use_variable_id(SPIComponent), vol.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, diff --git a/esphome/components/power_supply.py b/esphome/components/power_supply.py index bb8501eb0d..620fa60d80 100644 --- a/esphome/components/power_supply.py +++ b/esphome/components/power_supply.py @@ -11,7 +11,7 @@ PowerSupplyComponent = esphome_ns.class_('PowerSupplyComponent', Component) MULTI_CONF = True -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ vol.Required(CONF_ID): cv.declare_variable_id(PowerSupplyComponent), vol.Required(CONF_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_ENABLE_TIME): cv.positive_time_period_milliseconds, diff --git a/esphome/components/rdm6300.py b/esphome/components/rdm6300.py index 5c89d83fb3..46ba52fb20 100644 --- a/esphome/components/rdm6300.py +++ b/esphome/components/rdm6300.py @@ -1,5 +1,3 @@ -import voluptuous as vol - from esphome.components import binary_sensor, uart import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_UART_ID @@ -12,7 +10,7 @@ DEPENDENCIES = ['uart'] RDM6300Component = binary_sensor.binary_sensor_ns.class_('RDM6300Component', Component, uart.UARTDevice) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(RDM6300Component), cv.GenerateID(CONF_UART_ID): cv.use_variable_id(uart.UARTComponent), }).extend(cv.COMPONENT_SCHEMA.schema) diff --git a/esphome/components/remote_receiver.py b/esphome/components/remote_receiver.py index cb909ef72f..cb49833bc3 100644 --- a/esphome/components/remote_receiver.py +++ b/esphome/components/remote_receiver.py @@ -40,7 +40,7 @@ def validate_dumpers_all(value): raise vol.Invalid("Not valid dumpers") -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(RemoteReceiverComponent), vol.Required(CONF_PIN): pins.gpio_input_pin_schema, vol.Optional(CONF_DUMP, default=[]): diff --git a/esphome/components/remote_transmitter.py b/esphome/components/remote_transmitter.py index d0e82826ee..12ab4cc3f9 100644 --- a/esphome/components/remote_transmitter.py +++ b/esphome/components/remote_transmitter.py @@ -39,7 +39,7 @@ RC_SWITCH_TIMING_SCHEMA = vol.All([cv.uint8_t], vol.Length(min=2, max=2)) RC_SWITCH_PROTOCOL_SCHEMA = vol.Any( vol.All(vol.Coerce(int), vol.Range(min=1, max=7)), - vol.Schema({ + cv.Schema({ vol.Required(CONF_PULSE_LENGTH): cv.uint32_t, vol.Optional(CONF_SYNC, default=[1, 31]): RC_SWITCH_TIMING_SCHEMA, vol.Optional(CONF_ZERO, default=[1, 3]): RC_SWITCH_TIMING_SCHEMA, @@ -48,23 +48,23 @@ RC_SWITCH_PROTOCOL_SCHEMA = vol.Any( }) ) -RC_SWITCH_RAW_SCHEMA = vol.Schema({ +RC_SWITCH_RAW_SCHEMA = cv.Schema({ vol.Required(CONF_CODE): validate_rc_switch_code, vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, }) -RC_SWITCH_TYPE_A_SCHEMA = vol.Schema({ +RC_SWITCH_TYPE_A_SCHEMA = cv.Schema({ vol.Required(CONF_GROUP): vol.All(validate_rc_switch_code, vol.Length(min=5, max=5)), vol.Required(CONF_DEVICE): vol.All(validate_rc_switch_code, vol.Length(min=5, max=5)), vol.Required(CONF_STATE): cv.boolean, vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, }) -RC_SWITCH_TYPE_B_SCHEMA = vol.Schema({ +RC_SWITCH_TYPE_B_SCHEMA = cv.Schema({ vol.Required(CONF_ADDRESS): vol.All(cv.uint8_t, vol.Range(min=1, max=4)), vol.Required(CONF_CHANNEL): vol.All(cv.uint8_t, vol.Range(min=1, max=4)), vol.Required(CONF_STATE): cv.boolean, vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, }) -RC_SWITCH_TYPE_C_SCHEMA = vol.Schema({ +RC_SWITCH_TYPE_C_SCHEMA = cv.Schema({ vol.Required(CONF_FAMILY): cv.one_of('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', lower=True), vol.Required(CONF_GROUP): vol.All(cv.uint8_t, vol.Range(min=1, max=4)), @@ -72,14 +72,14 @@ RC_SWITCH_TYPE_C_SCHEMA = vol.Schema({ vol.Required(CONF_STATE): cv.boolean, vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, }) -RC_SWITCH_TYPE_D_SCHEMA = vol.Schema({ +RC_SWITCH_TYPE_D_SCHEMA = cv.Schema({ vol.Required(CONF_GROUP): cv.one_of('a', 'b', 'c', 'd', lower=True), vol.Required(CONF_DEVICE): vol.All(cv.uint8_t, vol.Range(min=1, max=3)), vol.Required(CONF_STATE): cv.boolean, vol.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, }) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(RemoteTransmitterComponent), vol.Required(CONF_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_CARRIER_DUTY_PERCENT): vol.All(cv.percentage_int, diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 0ade5e1de7..4fae480ef9 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -44,12 +44,12 @@ FILTERS_SCHEMA = cv.ensure_list({ vol.Optional(CONF_MULTIPLY): cv.float_, vol.Optional(CONF_FILTER_OUT): cv.float_, vol.Optional(CONF_FILTER_NAN): None, - vol.Optional(CONF_SLIDING_WINDOW_MOVING_AVERAGE): vol.All(vol.Schema({ + vol.Optional(CONF_SLIDING_WINDOW_MOVING_AVERAGE): vol.All(cv.Schema({ vol.Optional(CONF_WINDOW_SIZE, default=15): cv.positive_not_null_int, vol.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int, vol.Optional(CONF_SEND_FIRST_AT): cv.positive_not_null_int, }), validate_send_first_at), - vol.Optional(CONF_EXPONENTIAL_MOVING_AVERAGE): vol.Schema({ + vol.Optional(CONF_EXPONENTIAL_MOVING_AVERAGE): cv.Schema({ vol.Optional(CONF_ALPHA, default=0.1): cv.positive_float, vol.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int, }), diff --git a/esphome/components/sensor/bme680.py b/esphome/components/sensor/bme680.py index bc32f8d132..285e91564f 100644 --- a/esphome/components/sensor/bme680.py +++ b/esphome/components/sensor/bme680.py @@ -64,7 +64,7 @@ PLATFORM_SCHEMA = sensor.PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(BME680GasResistanceSensor), })), vol.Optional(CONF_IIR_FILTER): cv.one_of(*IIR_FILTER_OPTIONS, upper=True), - vol.Optional(CONF_HEATER): vol.Any(None, vol.All(vol.Schema({ + vol.Optional(CONF_HEATER): vol.Any(None, vol.All(cv.Schema({ vol.Optional(CONF_TEMPERATURE, default=320): vol.All(vol.Coerce(int), vol.Range(200, 400)), vol.Optional(CONF_DURATION, default='150ms'): vol.All( cv.positive_time_period_milliseconds, vol.Range(max=core.TimePeriod(milliseconds=4032))) diff --git a/esphome/components/sensor/ina3221.py b/esphome/components/sensor/ina3221.py index cac822fbfb..197ba8c6fc 100644 --- a/esphome/components/sensor/ina3221.py +++ b/esphome/components/sensor/ina3221.py @@ -24,7 +24,7 @@ INA3221PowerSensor = sensor.sensor_ns.class_('INA3221PowerSensor', sensor.EmptyP SENSOR_KEYS = [CONF_BUS_VOLTAGE, CONF_SHUNT_VOLTAGE, CONF_CURRENT, CONF_POWER] -INA3221_CHANNEL_SCHEMA = vol.All(vol.Schema({ +INA3221_CHANNEL_SCHEMA = vol.All(cv.Schema({ vol.Optional(CONF_BUS_VOLTAGE): cv.nameable(sensor.SENSOR_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(INA3221VoltageSensor), })), diff --git a/esphome/components/sensor/pulse_counter.py b/esphome/components/sensor/pulse_counter.py index b1ae21b2c6..067fab5bcb 100644 --- a/esphome/components/sensor/pulse_counter.py +++ b/esphome/components/sensor/pulse_counter.py @@ -41,7 +41,7 @@ def validate_internal_filter(value): PLATFORM_SCHEMA = cv.nameable(sensor.SENSOR_PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(PulseCounterSensorComponent), vol.Required(CONF_PIN): pins.internal_gpio_input_pin_schema, - vol.Optional(CONF_COUNT_MODE): vol.Schema({ + vol.Optional(CONF_COUNT_MODE): cv.Schema({ vol.Required(CONF_RISING_EDGE): COUNT_MODE_SCHEMA, vol.Required(CONF_FALLING_EDGE): COUNT_MODE_SCHEMA, }), diff --git a/esphome/components/sensor/template.py b/esphome/components/sensor/template.py index 7182ae672e..c238aed080 100644 --- a/esphome/components/sensor/template.py +++ b/esphome/components/sensor/template.py @@ -35,7 +35,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_SENSOR' CONF_SENSOR_TEMPLATE_PUBLISH = 'sensor.template.publish' -SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA = vol.Schema({ +SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA = cv.Schema({ vol.Required(CONF_ID): cv.use_variable_id(sensor.Sensor), vol.Required(CONF_STATE): cv.templatable(cv.float_), }) diff --git a/esphome/components/spi.py b/esphome/components/spi.py index 247a30635e..eeadacf880 100644 --- a/esphome/components/spi.py +++ b/esphome/components/spi.py @@ -12,7 +12,7 @@ SPIComponent = esphome_ns.class_('SPIComponent', Component) SPIDevice = esphome_ns.class_('SPIDevice') MULTI_CONF = True -CONFIG_SCHEMA = vol.All(vol.Schema({ +CONFIG_SCHEMA = vol.All(cv.Schema({ cv.GenerateID(): cv.declare_variable_id(SPIComponent), vol.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, vol.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema, diff --git a/esphome/components/status_led.py b/esphome/components/status_led.py index fc71c678da..4afe92695e 100644 --- a/esphome/components/status_led.py +++ b/esphome/components/status_led.py @@ -8,7 +8,7 @@ from esphome.cpp_types import App, Component, esphome_ns StatusLEDComponent = esphome_ns.class_('StatusLEDComponent', Component) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(StatusLEDComponent), vol.Optional(CONF_PIN): pins.gpio_output_pin_schema, }).extend(cv.COMPONENT_SCHEMA.schema) diff --git a/esphome/components/stepper/__init__.py b/esphome/components/stepper/__init__.py index fa601d8330..ff9cb0252c 100644 --- a/esphome/components/stepper/__init__.py +++ b/esphome/components/stepper/__init__.py @@ -60,7 +60,7 @@ def validate_speed(value): return value -STEPPER_SCHEMA = vol.Schema({ +STEPPER_SCHEMA = cv.Schema({ vol.Required(CONF_MAX_SPEED): validate_speed, vol.Optional(CONF_ACCELERATION): validate_acceleration, vol.Optional(CONF_DECELERATION): validate_acceleration, @@ -85,7 +85,7 @@ def setup_stepper(stepper_var, config): BUILD_FLAGS = '-DUSE_STEPPER' CONF_STEPPER_SET_TARGET = 'stepper.set_target' -STEPPER_SET_TARGET_ACTION_SCHEMA = vol.Schema({ +STEPPER_SET_TARGET_ACTION_SCHEMA = cv.Schema({ vol.Required(CONF_ID): cv.use_variable_id(Stepper), vol.Required(CONF_TARGET): cv.templatable(cv.int_), }) @@ -105,7 +105,7 @@ def stepper_set_target_to_code(config, action_id, arg_type, template_arg): CONF_STEPPER_REPORT_POSITION = 'stepper.report_position' -STEPPER_REPORT_POSITION_ACTION_SCHEMA = vol.Schema({ +STEPPER_REPORT_POSITION_ACTION_SCHEMA = cv.Schema({ vol.Required(CONF_ID): cv.use_variable_id(Stepper), vol.Required(CONF_POSITION): cv.templatable(cv.int_), }) diff --git a/esphome/components/substitutions.py b/esphome/components/substitutions.py index 3177ed8a44..9e7d7bce9d 100644 --- a/esphome/components/substitutions.py +++ b/esphome/components/substitutions.py @@ -31,7 +31,7 @@ def validate_substitution_key(value): return value -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ validate_substitution_key: cv.string_strict, }) diff --git a/esphome/components/switch/remote_transmitter.py b/esphome/components/switch/remote_transmitter.py index 217eec0c00..ef87c8ed83 100644 --- a/esphome/components/switch/remote_transmitter.py +++ b/esphome/components/switch/remote_transmitter.py @@ -43,7 +43,7 @@ RCSwitchTypeDTransmitter = remote_ns.class_('RCSwitchTypeDTransmitter', RCSwitch def validate_raw(value): if isinstance(value, dict): - return vol.Schema({ + return cv.Schema({ cv.GenerateID(): cv.declare_variable_id(int32), vol.Required(CONF_DATA): [vol.Any(vol.Coerce(int), cv.time_period_microseconds)], vol.Optional(CONF_CARRIER_FREQUENCY): vol.All(cv.frequency, vol.Coerce(int)), @@ -55,29 +55,29 @@ def validate_raw(value): PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ cv.GenerateID(): cv.declare_variable_id(RemoteTransmitter), - vol.Optional(CONF_JVC): vol.Schema({ + vol.Optional(CONF_JVC): cv.Schema({ vol.Required(CONF_DATA): cv.hex_uint32_t, }), - vol.Optional(CONF_LG): vol.Schema({ + vol.Optional(CONF_LG): cv.Schema({ vol.Required(CONF_DATA): cv.hex_uint32_t, vol.Optional(CONF_NBITS, default=28): cv.one_of(28, 32, int=True), }), - vol.Optional(CONF_NEC): vol.Schema({ + vol.Optional(CONF_NEC): cv.Schema({ vol.Required(CONF_ADDRESS): cv.hex_uint16_t, vol.Required(CONF_COMMAND): cv.hex_uint16_t, }), - vol.Optional(CONF_SAMSUNG): vol.Schema({ + vol.Optional(CONF_SAMSUNG): cv.Schema({ vol.Required(CONF_DATA): cv.hex_uint32_t, }), - vol.Optional(CONF_SONY): vol.Schema({ + vol.Optional(CONF_SONY): cv.Schema({ vol.Required(CONF_DATA): cv.hex_uint32_t, vol.Optional(CONF_NBITS, default=12): cv.one_of(12, 15, 20, int=True), }), - vol.Optional(CONF_PANASONIC): vol.Schema({ + vol.Optional(CONF_PANASONIC): cv.Schema({ vol.Required(CONF_ADDRESS): cv.hex_uint16_t, vol.Required(CONF_COMMAND): cv.hex_uint32_t, }), - vol.Optional(CONF_RC5): vol.Schema({ + vol.Optional(CONF_RC5): cv.Schema({ vol.Required(CONF_ADDRESS): vol.All(cv.hex_int, vol.Range(min=0, max=0x1F)), vol.Required(CONF_COMMAND): vol.All(cv.hex_int, vol.Range(min=0, max=0x3F)), }), @@ -88,7 +88,7 @@ PLATFORM_SCHEMA = cv.nameable(switch.SWITCH_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_RC_SWITCH_TYPE_C): RC_SWITCH_TYPE_C_SCHEMA, vol.Optional(CONF_RC_SWITCH_TYPE_D): RC_SWITCH_TYPE_D_SCHEMA, - vol.Optional(CONF_REPEAT): vol.Any(cv.positive_not_null_int, vol.Schema({ + vol.Optional(CONF_REPEAT): vol.Any(cv.positive_not_null_int, cv.Schema({ vol.Required(CONF_TIMES): cv.positive_not_null_int, vol.Required(CONF_WAIT_TIME): cv.positive_time_period_microseconds, })), diff --git a/esphome/components/switch/template.py b/esphome/components/switch/template.py index 6fd34dd462..4e4868acc2 100644 --- a/esphome/components/switch/template.py +++ b/esphome/components/switch/template.py @@ -55,7 +55,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_SWITCH' CONF_SWITCH_TEMPLATE_PUBLISH = 'switch.template.publish' -SWITCH_TEMPLATE_PUBLISH_ACTION_SCHEMA = vol.Schema({ +SWITCH_TEMPLATE_PUBLISH_ACTION_SCHEMA = cv.Schema({ vol.Required(CONF_ID): cv.use_variable_id(switch.Switch), vol.Required(CONF_STATE): cv.templatable(cv.boolean), }) diff --git a/esphome/components/switch/uart.py b/esphome/components/switch/uart.py index 4c5852c38e..82c79d13d3 100644 --- a/esphome/components/switch/uart.py +++ b/esphome/components/switch/uart.py @@ -20,7 +20,7 @@ def validate_data(value): if isinstance(value, str): return value if isinstance(value, list): - return vol.Schema([cv.hex_uint8_t])(value) + return cv.Schema([cv.hex_uint8_t])(value) raise vol.Invalid("data must either be a string wrapped in quotes or a list of bytes") diff --git a/esphome/components/text_sensor/template.py b/esphome/components/text_sensor/template.py index cde833f175..1d6932eef6 100644 --- a/esphome/components/text_sensor/template.py +++ b/esphome/components/text_sensor/template.py @@ -35,7 +35,7 @@ def to_code(config): BUILD_FLAGS = '-DUSE_TEMPLATE_TEXT_SENSOR' CONF_TEXT_SENSOR_TEMPLATE_PUBLISH = 'text_sensor.template.publish' -TEXT_SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA = vol.Schema({ +TEXT_SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA = cv.Schema({ vol.Required(CONF_ID): cv.use_variable_id(text_sensor.TextSensor), vol.Required(CONF_STATE): cv.templatable(cv.string_strict), }) diff --git a/esphome/components/uart.py b/esphome/components/uart.py index 65d416ce57..0b15bd66bd 100644 --- a/esphome/components/uart.py +++ b/esphome/components/uart.py @@ -11,7 +11,7 @@ UARTComponent = esphome_ns.class_('UARTComponent', Component) UARTDevice = esphome_ns.class_('UARTDevice') MULTI_CONF = True -CONFIG_SCHEMA = vol.All(vol.Schema({ +CONFIG_SCHEMA = vol.All(cv.Schema({ cv.GenerateID(): cv.declare_variable_id(UARTComponent), vol.Optional(CONF_TX_PIN): pins.output_pin, vol.Optional(CONF_RX_PIN): pins.input_pin, diff --git a/esphome/components/web_server.py b/esphome/components/web_server.py index e8b7420f0c..17da72faac 100644 --- a/esphome/components/web_server.py +++ b/esphome/components/web_server.py @@ -9,7 +9,7 @@ from esphome.cpp_types import App, Component, StoringController, esphome_ns WebServer = esphome_ns.class_('WebServer', Component, StoringController) -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(WebServer), vol.Optional(CONF_PORT): cv.port, vol.Optional(CONF_CSS_URL): cv.string, diff --git a/esphome/components/wifi.py b/esphome/components/wifi.py index bf2e5a92b8..c509917c18 100644 --- a/esphome/components/wifi.py +++ b/esphome/components/wifi.py @@ -42,7 +42,7 @@ def validate_channel(value): return value -AP_MANUAL_IP_SCHEMA = vol.Schema({ +AP_MANUAL_IP_SCHEMA = cv.Schema({ vol.Required(CONF_STATIC_IP): cv.ipv4, vol.Required(CONF_GATEWAY): cv.ipv4, vol.Required(CONF_SUBNET): cv.ipv4, @@ -53,7 +53,7 @@ STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend({ vol.Optional(CONF_DNS2, default="1.0.0.1"): cv.ipv4, }) -WIFI_NETWORK_BASE = vol.Schema({ +WIFI_NETWORK_BASE = cv.Schema({ cv.GenerateID(): cv.declare_variable_id(WiFiAP), vol.Optional(CONF_SSID): cv.ssid, vol.Optional(CONF_PASSWORD): validate_password, @@ -105,7 +105,7 @@ def validate(config): return config -CONFIG_SCHEMA = vol.All(vol.Schema({ +CONFIG_SCHEMA = vol.All(cv.Schema({ cv.GenerateID(): cv.declare_variable_id(WiFiComponent), vol.Optional(CONF_NETWORKS): cv.ensure_list(WIFI_NETWORK_STA), diff --git a/esphome/config.py b/esphome/config.py index 2b517b6301..469b1305a6 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -19,6 +19,7 @@ from esphome.util import safe_print from typing import List, Optional, Tuple, Union # noqa from esphome.core import ConfigType # noqa from esphome.yaml_util import is_secret +from esphome.voluptuous_schema import ExtraKeysInvalid _LOGGER = logging.getLogger(__name__) @@ -414,17 +415,21 @@ def humanize_error(config, validation_error): def _format_vol_invalid(ex, config, path, domain): # type: (vol.Invalid, ConfigType, ConfigPath, basestring) -> unicode message = u'' - if u'extra keys not allowed' in ex.error_message: - try: - paren = ex.path[-2] - except IndexError: - paren = domain + try: + paren = ex.path[-2] + except IndexError: + paren = domain + + if isinstance(ex, ExtraKeysInvalid): + if ex.candidates: + message += u'[{}] is an invalid option for [{}]. Did you mean {}?'.format( + ex.path[-1], paren, u', '.join(u'[{}]'.format(x) for x in ex.candidates)) + else: + message += u'[{}] is an invalid option for [{}]. Please check the indentation.'.format( + ex.path[-1], paren) + elif u'extra keys not allowed' in ex.error_message: message += u'[{}] is an invalid option for [{}].'.format(ex.path[-1], paren) elif u'required key not provided' in ex.error_message: - try: - paren = ex.path[-2] - except IndexError: - paren = domain message += u"'{}' is a required option for [{}].".format(ex.path[-1], paren) else: message += humanize_error(_nested_getitem(config, path), ex) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 16be903262..55e9b71cbd 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -17,11 +17,13 @@ from esphome.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, from esphome.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \ TimePeriodMilliseconds, TimePeriodSeconds from esphome.py_compat import integer_types, string_types, text_type +from esphome.voluptuous_schema import _Schema _LOGGER = logging.getLogger(__name__) # pylint: disable=invalid-name +Schema = _Schema port = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)) float_ = vol.Coerce(float) positive_float = vol.All(float_, vol.Range(min=0)) @@ -203,7 +205,7 @@ def templatable(other_validators): if isinstance(value, Lambda): return value if isinstance(other_validators, dict): - return vol.Schema(other_validators)(value) + return Schema(other_validators)(value) return other_validators(value) return validator @@ -273,7 +275,7 @@ def has_at_most_one_key(*keys): TIME_PERIOD_ERROR = "Time period {} should be format number + unit, for example 5ms, 5s, 5min, 5h" time_period_dict = vol.All( - dict, vol.Schema({ + dict, Schema({ 'days': float_, 'hours': float_, 'minutes': float_, @@ -727,17 +729,17 @@ def nameable(*schemas): return validator -PLATFORM_SCHEMA = vol.Schema({ +PLATFORM_SCHEMA = Schema({ vol.Required(CONF_PLATFORM): valid, }) -MQTT_COMPONENT_AVAILABILITY_SCHEMA = vol.Schema({ +MQTT_COMPONENT_AVAILABILITY_SCHEMA = Schema({ vol.Required(CONF_TOPIC): subscribe_topic, vol.Optional(CONF_PAYLOAD_AVAILABLE, default='online'): mqtt_payload, vol.Optional(CONF_PAYLOAD_NOT_AVAILABLE, default='offline'): mqtt_payload, }) -MQTT_COMPONENT_SCHEMA = vol.Schema({ +MQTT_COMPONENT_SCHEMA = Schema({ vol.Optional(CONF_NAME): string, vol.Optional(CONF_RETAIN): vol.All(requires_component('mqtt'), boolean), vol.Optional(CONF_DISCOVERY): vol.All(requires_component('mqtt'), boolean), @@ -751,6 +753,6 @@ MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend({ vol.Optional(CONF_COMMAND_TOPIC): vol.All(requires_component('mqtt'), subscribe_topic), }) -COMPONENT_SCHEMA = vol.Schema({ +COMPONENT_SCHEMA = Schema({ vol.Optional(CONF_SETUP_PRIORITY): float_ }) diff --git a/esphome/core_config.py b/esphome/core_config.py index e948c304c1..c003158b83 100644 --- a/esphome/core_config.py +++ b/esphome/core_config.py @@ -87,11 +87,11 @@ def validate_commit(value): ESPHOME_CORE_VERSION_SCHEMA = vol.Any( validate_simple_esphome_core_version, - vol.Schema({ + cv.Schema({ vol.Required(CONF_LOCAL): validate_local_esphome_core_version, }), vol.All( - vol.Schema({ + cv.Schema({ vol.Optional(CONF_REPOSITORY, default=LIBRARY_URI_REPO): cv.string, vol.Optional(CONF_COMMIT): validate_commit, vol.Optional(CONF_BRANCH): cv.string, @@ -159,7 +159,7 @@ def default_build_path(): return CORE.name -CONFIG_SCHEMA = vol.Schema({ +CONFIG_SCHEMA = cv.Schema({ vol.Required(CONF_NAME): cv.valid_name, vol.Required(CONF_PLATFORM): cv.one_of('ESP8266', 'ESPRESSIF8266', 'ESP32', 'ESPRESSIF32', upper=True), @@ -168,7 +168,7 @@ CONFIG_SCHEMA = vol.Schema({ vol.Optional(CONF_ARDUINO_VERSION, default='recommended'): validate_arduino_version, vol.Optional(CONF_USE_CUSTOM_CODE, default=False): cv.boolean, vol.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string, - vol.Optional(CONF_PLATFORMIO_OPTIONS): vol.Schema({ + vol.Optional(CONF_PLATFORMIO_OPTIONS): cv.Schema({ cv.string_strict: vol.Any([cv.string], cv.string), }), vol.Optional(CONF_ESP8266_RESTORE_FROM_FLASH): vol.All(cv.only_on_esp8266, cv.boolean), diff --git a/esphome/pins.py b/esphome/pins.py index e2c0bc6d17..8049c8bf86 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -320,13 +320,13 @@ def pin_mode(value): raise NotImplementedError -GPIO_FULL_OUTPUT_PIN_SCHEMA = vol.Schema({ +GPIO_FULL_OUTPUT_PIN_SCHEMA = cv.Schema({ vol.Required(CONF_NUMBER): output_pin, vol.Optional(CONF_MODE): pin_mode, vol.Optional(CONF_INVERTED): cv.boolean, }) -GPIO_FULL_INPUT_PIN_SCHEMA = vol.Schema({ +GPIO_FULL_INPUT_PIN_SCHEMA = cv.Schema({ vol.Required(CONF_NUMBER): input_pin, vol.Optional(CONF_MODE): pin_mode, vol.Optional(CONF_INVERTED): cv.boolean, @@ -351,7 +351,7 @@ def shorthand_input_pullup_pin(value): I2CDevice = esphome_ns.class_('I2CDevice') PCF8574Component = io_ns.class_('PCF8574Component', Component, I2CDevice) -PCF8574_OUTPUT_PIN_SCHEMA = vol.Schema({ +PCF8574_OUTPUT_PIN_SCHEMA = cv.Schema({ vol.Required(CONF_PCF8574): cv.use_variable_id(PCF8574Component), vol.Required(CONF_NUMBER): vol.Coerce(int), vol.Optional(CONF_MODE): cv.one_of("OUTPUT", upper=True), diff --git a/esphome/voluptuous_schema.py b/esphome/voluptuous_schema.py new file mode 100644 index 0000000000..6f293adf29 --- /dev/null +++ b/esphome/voluptuous_schema.py @@ -0,0 +1,161 @@ +import difflib +import itertools + +import voluptuous as vol + +from esphome.py_compat import string_types + + +class ExtraKeysInvalid(vol.Invalid): + def __init__(self, *arg, **kwargs): + self.candidates = kwargs.pop('candidates') + vol.Invalid.__init__(self, *arg, **kwargs) + + +# pylint: disable=protected-access, unidiomatic-typecheck +class _Schema(vol.Schema): + """Custom cv.Schema that prints similar keys on error.""" + def _compile_mapping(self, schema, invalid_msg=None): + invalid_msg = invalid_msg or 'mapping value' + + # Keys that may be required + all_required_keys = set(key for key in schema + if key is not vol.Extra and + ((self.required and not isinstance(key, (vol.Optional, vol.Remove))) + or isinstance(key, vol.Required))) + + # Keys that may have defaults + all_default_keys = set(key for key in schema + if isinstance(key, (vol.Required, vol.Optional))) + + _compiled_schema = {} + for skey, svalue in vol.iteritems(schema): + new_key = self._compile(skey) + new_value = self._compile(svalue) + _compiled_schema[skey] = (new_key, new_value) + + candidates = list(vol.schema_builder._iterate_mapping_candidates(_compiled_schema)) + + # After we have the list of candidates in the correct order, we want to apply some + # optimization so that each + # key in the data being validated will be matched against the relevant schema keys only. + # No point in matching against different keys + additional_candidates = [] + candidates_by_key = {} + for skey, (ckey, cvalue) in candidates: + if type(skey) in vol.primitive_types: + candidates_by_key.setdefault(skey, []).append((skey, (ckey, cvalue))) + elif isinstance(skey, vol.Marker) and type(skey.schema) in vol.primitive_types: + candidates_by_key.setdefault(skey.schema, []).append((skey, (ckey, cvalue))) + else: + # These are wildcards such as 'int', 'str', 'Remove' and others which should be + # applied to all keys + additional_candidates.append((skey, (ckey, cvalue))) + + key_names = [] + for skey in schema: + if isinstance(skey, string_types): + key_names.append(skey) + elif isinstance(skey, vol.Marker) and isinstance(skey.schema, string_types): + key_names.append(skey.schema) + + def validate_mapping(path, iterable, out): + required_keys = all_required_keys.copy() + + # Build a map of all provided key-value pairs. + # The type(out) is used to retain ordering in case a ordered + # map type is provided as input. + key_value_map = type(out)() + for key, value in iterable: + key_value_map[key] = value + + # Insert default values for non-existing keys. + for key in all_default_keys: + if not isinstance(key.default, vol.Undefined) and \ + key.schema not in key_value_map: + # A default value has been specified for this missing + # key, insert it. + key_value_map[key.schema] = key.default() + + error = None + errors = [] + for key, value in key_value_map.items(): + key_path = path + [key] + remove_key = False + + # Optimization. Validate against the matching key first, then fallback to the rest + relevant_candidates = itertools.chain(candidates_by_key.get(key, []), + additional_candidates) + + # compare each given key/value against all compiled key/values + # schema key, (compiled key, compiled value) + for skey, (ckey, cvalue) in relevant_candidates: + try: + new_key = ckey(key_path, key) + except vol.Invalid as e: + if len(e.path) > len(key_path): + raise + if not error or len(e.path) > len(error.path): + error = e + continue + # Backtracking is not performed once a key is selected, so if + # the value is invalid we immediately throw an exception. + exception_errors = [] + # check if the key is marked for removal + is_remove = new_key is vol.Remove + try: + cval = cvalue(key_path, value) + # include if it's not marked for removal + if not is_remove: + out[new_key] = cval + else: + remove_key = True + continue + except vol.MultipleInvalid as e: + exception_errors.extend(e.errors) + except vol.Invalid as e: + exception_errors.append(e) + + if exception_errors: + if is_remove or remove_key: + continue + for err in exception_errors: + if len(err.path) <= len(key_path): + err.error_type = invalid_msg + errors.append(err) + # If there is a validation error for a required + # key, this means that the key was provided. + # Discard the required key so it does not + # create an additional, noisy exception. + required_keys.discard(skey) + break + + # Key and value okay, mark as found in case it was + # a Required() field. + required_keys.discard(skey) + + break + else: + if remove_key: + # remove key + continue + elif self.extra == vol.ALLOW_EXTRA: + out[key] = value + elif self.extra != vol.REMOVE_EXTRA: + if isinstance(key, string_types) and key_names: + matches = difflib.get_close_matches(key, key_names) + errors.append(ExtraKeysInvalid('extra keys not allowed', key_path, + candidates=matches)) + else: + errors.append(vol.Invalid('extra keys not allowed', key_path)) + + # for any required keys left that weren't found and don't have defaults: + for key in required_keys: + msg = key.msg if hasattr(key, 'msg') and key.msg else 'required key not provided' + errors.append(vol.RequiredFieldInvalid(msg, path + [key])) + if errors: + raise vol.MultipleInvalid(errors) + + return out + + return validate_mapping