Warn for old slugs/entity ids (#20478)

* Warn for old slugs/entity ids

* add comments

* Lint

* LInt

* Lint

* Lint
pull/20488/head
Paulus Schoutsen 2019-01-26 14:09:41 -08:00 committed by GitHub
parent 38b1ce3fe0
commit 87316c4e83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 1 deletions

View File

@ -18,6 +18,7 @@ from homeassistant.util.logging import AsyncHandler
from homeassistant.util.package import async_get_user_site, is_virtual_env
from homeassistant.util.yaml import clear_secret_cache
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
_LOGGER = logging.getLogger(__name__)
@ -153,6 +154,34 @@ async def async_from_config_dict(config: Dict[str, Any],
stop = time()
_LOGGER.info("Home Assistant initialized in %.2fs", stop-start)
# TEMP: warn users for invalid slugs
# Remove after 0.94 or 1.0
if cv.INVALID_SLUGS_FOUND or cv.INVALID_ENTITY_IDS_FOUND:
msg = []
if cv.INVALID_ENTITY_IDS_FOUND:
msg.append(
"Your configuration contains invalid entity ID references. "
"Please find and update the following. "
"This will become a breaking change."
)
msg.append('\n'.join('- {} -> {}'.format(*item)
for item
in cv.INVALID_ENTITY_IDS_FOUND.items()))
if cv.INVALID_SLUGS_FOUND:
msg.append(
"Your configuration contains invalid slugs. "
"Please find and update the following. "
"This will become a breaking change."
)
msg.append('\n'.join('- {} -> {}'.format(*item)
for item in cv.INVALID_SLUGS_FOUND.items()))
hass.components.persistent_notification.async_create(
'\n\n'.join(msg), "Config Warning", "config_warning"
)
return hass

View File

@ -26,6 +26,13 @@ from homeassistant.helpers import template as template_helper
# pylint: disable=invalid-name
TIME_PERIOD_ERROR = "offset {} should be format 'HH:MM' or 'HH:MM:SS'"
OLD_SLUG_VALIDATION = r'^[a-z0-9_]+$'
OLD_ENTITY_ID_VALIDATION = r"^(\w+)\.(\w+)$"
# Keep track of invalid slugs and entity ids found so we can create a
# persistent notification. Rare temporary exception to use a global.
INVALID_SLUGS_FOUND = {}
INVALID_ENTITY_IDS_FOUND = {}
# Home Assistant types
byte = vol.All(vol.Coerce(int), vol.Range(min=0, max=255))
@ -149,6 +156,18 @@ def entity_id(value: Any) -> str:
value = string(value).lower()
if valid_entity_id(value):
return value
elif re.match(OLD_ENTITY_ID_VALIDATION, value):
# To ease the breaking change, we allow old slugs for now
# Remove after 0.94 or 1.0
fixed = '.'.join(util_slugify(part) for part in value.split('.', 1))
INVALID_ENTITY_IDS_FOUND[value] = fixed
logging.getLogger(__name__).warning(
"Found invalid entity_id %s, please update with %s. This "
"will become a breaking change.",
value, fixed
)
return value
raise vol.Invalid('Entity ID {} is an invalid entity id'.format(value))
@ -333,7 +352,22 @@ def schema_with_slug_keys(value_schema: Union[T, Callable]) -> Callable:
raise vol.Invalid('expected dictionary')
for key in value.keys():
slug(key)
try:
slug(key)
except vol.Invalid:
# To ease the breaking change, we allow old slugs for now
# Remove after 0.94 or 1.0
if re.match(OLD_SLUG_VALIDATION, key):
fixed = util_slugify(key)
INVALID_SLUGS_FOUND[key] = fixed
logging.getLogger(__name__).warning(
"Found invalid slug %s, please update with %s. This "
"will be come a breaking change.",
key, fixed
)
else:
raise
return schema(value)
return verify

View File

@ -602,3 +602,31 @@ def test_comp_entity_ids():
for invalid in (['light.kitchen', 'not-entity-id'], '*', ''):
with pytest.raises(vol.Invalid):
schema(invalid)
def test_schema_with_slug_keys_allows_old_slugs(caplog):
"""Test schema with slug keys allowing old slugs."""
schema = cv.schema_with_slug_keys(str)
with patch.dict(cv.INVALID_SLUGS_FOUND, clear=True):
for value in ('_world', 'wow__yeah'):
caplog.clear()
# Will raise if not allowing old slugs
schema({value: 'yo'})
assert "Found invalid slug {}".format(value) in caplog.text
assert len(cv.INVALID_SLUGS_FOUND) == 2
def test_entity_id_allow_old_validation(caplog):
"""Test schema allowing old entity_ids."""
schema = vol.Schema(cv.entity_id)
with patch.dict(cv.INVALID_ENTITY_IDS_FOUND, clear=True):
for value in ('hello.__world', 'great.wow__yeah'):
caplog.clear()
# Will raise if not allowing old entity ID
schema(value)
assert "Found invalid entity_id {}".format(value) in caplog.text
assert len(cv.INVALID_ENTITY_IDS_FOUND) == 2