From 07fa844c43e2540f61973ddce8d900834725d99d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 24 Feb 2020 08:35:02 -0800 Subject: [PATCH] Speed up validate_entity_id (#32137) * Speed up validate_entity_id * Add some more invalid entity IDs * Adjust regular expression * Extend and sort test cases * Update regular expression, more cases, faster * Adjust tests, allow start with number, disallow double underscore Co-authored-by: Franck Nijhof --- homeassistant/core.py | 8 ++++-- homeassistant/scripts/benchmark/__init__.py | 9 ++++++ tests/test_core.py | 31 +++++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/homeassistant/core.py b/homeassistant/core.py index c17c1f698ce..a1d9a83d1ad 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -12,6 +12,7 @@ import functools import logging import os import pathlib +import re import threading from time import monotonic from types import MappingProxyType @@ -63,7 +64,7 @@ from homeassistant.exceptions import ( ServiceNotFound, Unauthorized, ) -from homeassistant.util import location, slugify +from homeassistant.util import location from homeassistant.util.async_ import fire_coroutine_threadsafe, run_callback_threadsafe import homeassistant.util.dt as dt_util from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM, UnitSystem @@ -103,12 +104,15 @@ def split_entity_id(entity_id: str) -> List[str]: return entity_id.split(".", 1) +VALID_ENTITY_ID = re.compile(r"^(?!.+__)(?!_)[\da-z_]+(? bool: """Test if an entity ID is a valid format. Format: . where both are slugs. """ - return "." in entity_id and slugify(entity_id) == entity_id.replace(".", "_", 1) + return VALID_ENTITY_ID.match(entity_id) is not None def valid_state(state: str) -> bool: diff --git a/homeassistant/scripts/benchmark/__init__.py b/homeassistant/scripts/benchmark/__init__.py index 58125bc4829..4d7df6d7248 100644 --- a/homeassistant/scripts/benchmark/__init__.py +++ b/homeassistant/scripts/benchmark/__init__.py @@ -185,3 +185,12 @@ def _logbook_filtering(hass, last_changed, last_updated): list(logbook.humanify(None, yield_events(event))) return timer() - start + + +@benchmark +async def valid_entity_id(hass): + """Run valid entity ID a million times.""" + start = timer() + for _ in range(10 ** 6): + core.valid_entity_id("light.kitchen") + return timer() - start diff --git a/tests/test_core.py b/tests/test_core.py index 0c7acfbba0e..f5a6f4718cd 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1206,3 +1206,34 @@ async def test_async_functions_with_callback(hass): await hass.services.async_call("test_domain", "test_service", blocking=True) assert len(runs) == 3 + + +def test_valid_entity_id(): + """Test valid entity ID.""" + for invalid in [ + "_light.kitchen", + ".kitchen", + ".light.kitchen", + "light_.kitchen", + "light._kitchen", + "light.", + "light.kitchen__ceiling", + "light.kitchen_yo_", + "light.kitchen.", + "Light.kitchen", + "light.Kitchen", + "lightkitchen", + ]: + assert not ha.valid_entity_id(invalid), invalid + + for valid in [ + "1.a", + "1light.kitchen", + "a.1", + "a.a", + "input_boolean.hello_world_0123", + "light.1kitchen", + "light.kitchen", + "light.something_yoo", + ]: + assert ha.valid_entity_id(valid), valid