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 <frenck@frenck.nl>
This commit is contained in:
Paulus Schoutsen 2020-02-24 08:35:02 -08:00 committed by GitHub
parent 15b4975681
commit 07fa844c43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 2 deletions

View File

@ -12,6 +12,7 @@ import functools
import logging import logging
import os import os
import pathlib import pathlib
import re
import threading import threading
from time import monotonic from time import monotonic
from types import MappingProxyType from types import MappingProxyType
@ -63,7 +64,7 @@ from homeassistant.exceptions import (
ServiceNotFound, ServiceNotFound,
Unauthorized, Unauthorized,
) )
from homeassistant.util import location, slugify from homeassistant.util import location
from homeassistant.util.async_ import fire_coroutine_threadsafe, run_callback_threadsafe from homeassistant.util.async_ import fire_coroutine_threadsafe, run_callback_threadsafe
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM, UnitSystem 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) return entity_id.split(".", 1)
VALID_ENTITY_ID = re.compile(r"^(?!.+__)(?!_)[\da-z_]+(?<!_)\.(?!_)[\da-z_]+(?<!_)$")
def valid_entity_id(entity_id: str) -> bool: def valid_entity_id(entity_id: str) -> bool:
"""Test if an entity ID is a valid format. """Test if an entity ID is a valid format.
Format: <domain>.<entity> where both are slugs. Format: <domain>.<entity> 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: def valid_state(state: str) -> bool:

View File

@ -185,3 +185,12 @@ def _logbook_filtering(hass, last_changed, last_updated):
list(logbook.humanify(None, yield_events(event))) list(logbook.humanify(None, yield_events(event)))
return timer() - start 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

View File

@ -1206,3 +1206,34 @@ async def test_async_functions_with_callback(hass):
await hass.services.async_call("test_domain", "test_service", blocking=True) await hass.services.async_call("test_domain", "test_service", blocking=True)
assert len(runs) == 3 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