diff --git a/tests/benchmarks/python/test_component_import_bench.py b/tests/benchmarks/python/test_component_import_bench.py new file mode 100644 index 00000000000..a6d0a8ed0f1 --- /dev/null +++ b/tests/benchmarks/python/test_component_import_bench.py @@ -0,0 +1,42 @@ +"""CodSpeed benchmarks for importing entity components. + +Every ``esphome config`` or ``esphome compile`` imports the Python module of +each component the YAML uses. A component that imports its optional +integrations (mqtt, web_server, zigbee) at module scope drags their whole +import chains in for every user, so the cost of a fresh import is what these +benchmarks pin down. + +The interpreter is shared across iterations, so each run first drops the +esphome modules from ``sys.modules`` and imports the component again from +scratch; the import machinery and the module bodies run every time, as they +do in a new process. + +Skipped when ``pytest-codspeed`` isn't installed so the regular unit-test +suite keeps working unchanged. +""" + +from __future__ import annotations + +import importlib +import sys + +import pytest + +pytest.importorskip("pytest_codspeed") + +COMPONENTS = ("binary_sensor", "sensor", "logger") + + +def _forget_esphome_modules() -> None: + for name in [n for n in sys.modules if n == "esphome" or n.startswith("esphome.")]: + del sys.modules[name] + + +def _import_component(name: str) -> object: + _forget_esphome_modules() + return importlib.import_module(f"esphome.components.{name}") + + +@pytest.mark.parametrize("component", COMPONENTS) +def test_import_component(benchmark, component: str) -> None: + benchmark(_import_component, component)