Align valid_entity_id with new slugify (#20231)

* slug

* ensure a dot

* fix

* schema_with_slug_keys

* lint

* test
This commit is contained in:
Johann Kellerman 2019-01-21 19:45:11 +02:00 committed by Paulus Schoutsen
parent 6ca0da5c52
commit c36c708068
5 changed files with 32 additions and 17 deletions

View File

@ -45,7 +45,7 @@ SCRIPT_ENTRY_SCHEMA = vol.Schema({
})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({cv.slug: SCRIPT_ENTRY_SCHEMA})
DOMAIN: cv.schema_with_slug_keys(SCRIPT_ENTRY_SCHEMA)
}, extra=vol.ALLOW_EXTRA)
SCRIPT_SERVICE_SCHEMA = vol.Schema(dict)

View File

@ -170,10 +170,9 @@ def _no_duplicate_auth_mfa_module(configs: Sequence[Dict[str, Any]]) \
return configs
PACKAGES_CONFIG_SCHEMA = vol.Schema({
cv.slug: vol.Schema( # Package names are slugs
{cv.string: vol.Any(dict, list, None)}) # Component configuration
})
PACKAGES_CONFIG_SCHEMA = cv.schema_with_slug_keys( # Package names are slugs
vol.Schema({cv.string: vol.Any(dict, list, None)}) # Component config
)
CUSTOMIZE_DICT_SCHEMA = vol.Schema({
vol.Optional(ATTR_FRIENDLY_NAME): cv.string,
@ -627,7 +626,7 @@ def _identify_config_schema(module: ModuleType) -> \
except (AttributeError, KeyError):
return None, None
t_schema = str(schema)
if t_schema.startswith('{'):
if t_schema.startswith('{') or 'schema_with_slug_keys' in t_schema:
return ('dict', schema)
if t_schema.startswith(('[', 'All(<function ensure_list')):
return ('list', schema)

View File

@ -12,7 +12,6 @@ import functools
import logging
import os
import pathlib
import re
import sys
import threading
from time import monotonic
@ -43,7 +42,7 @@ from homeassistant.util.async_ import (
fire_coroutine_threadsafe)
from homeassistant import util
import homeassistant.util.dt as dt_util
from homeassistant.util import location
from homeassistant.util import location, slugify
from homeassistant.util.unit_system import UnitSystem, METRIC_SYSTEM # NOQA
# Typing imports that create a circular dependency
@ -62,9 +61,6 @@ DOMAIN = 'homeassistant'
# How long we wait for the result of a service call
SERVICE_CALL_LIMIT = 10 # seconds
# Pattern for validating entity IDs (format: <domain>.<entity>)
ENTITY_ID_PATTERN = re.compile(r"^(\w+)\.(\w+)$")
# How long to wait till things that run on startup have to finish.
TIMEOUT_EVENT_START = 15
@ -77,8 +73,12 @@ def split_entity_id(entity_id: str) -> List[str]:
def valid_entity_id(entity_id: str) -> bool:
"""Test if an entity ID is a valid format."""
return ENTITY_ID_PATTERN.match(entity_id) is not None
"""Test if an entity ID is a valid format.
Format: <domain>.<entity> where both are slugs.
"""
return ('.' in entity_id and
slugify(entity_id) == entity_id.replace('.', '_', 1))
def valid_state(state: str) -> bool:

View File

@ -319,7 +319,23 @@ def service(value):
.format(value))
def slug(value):
def schema_with_slug_keys(value_schema: Union[T, Callable]) -> Callable:
"""Ensure dicts have slugs as keys.
Replacement of vol.Schema({cv.slug: value_schema}) to prevent misleading
"Extra keys" errors from voluptuous.
"""
schema = vol.Schema({str: value_schema})
def verify(value: Dict) -> Dict:
"""Validate all keys are slugs and then the value_schema."""
for key in value.keys():
slug(key)
return schema(value)
return verify
def slug(value: Any) -> str:
"""Validate value is a valid slug."""
if value is None:
raise vol.Invalid('Slug should not be None')
@ -330,7 +346,7 @@ def slug(value):
raise vol.Invalid('invalid slug {} (try {})'.format(value, slg))
def slugify(value):
def slugify(value: Any) -> str:
"""Coerce a value to a slug."""
if value is None:
raise vol.Invalid('Slug should not be None')

View File

@ -139,11 +139,11 @@ class TestWOLSwitch(unittest.TestCase):
'mac_address': '00-01-02-03-04-05',
'host': 'validhostname',
'turn_off': {
'service': 'shell_command.turn_off_TARGET',
'service': 'shell_command.turn_off_target',
},
}
})
calls = mock_service(self.hass, 'shell_command', 'turn_off_TARGET')
calls = mock_service(self.hass, 'shell_command', 'turn_off_target')
state = self.hass.states.get('switch.wake_on_lan')
assert STATE_OFF == state.state