Clean up ModuleWrapper from loader (#148488)

This commit is contained in:
Artur Pragacz 2025-07-16 15:19:22 +02:00 committed by GitHub
parent 412035b970
commit 840e0d1388
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -10,7 +10,6 @@ import asyncio
from collections.abc import Callable, Iterable from collections.abc import Callable, Iterable
from contextlib import suppress from contextlib import suppress
from dataclasses import dataclass from dataclasses import dataclass
import functools as ft
import importlib import importlib
import logging import logging
import os import os
@ -1650,77 +1649,6 @@ class CircularDependency(LoaderError):
self.args[1].insert(0, domain) self.args[1].insert(0, domain)
def _load_file(
hass: HomeAssistant, comp_or_platform: str, base_paths: list[str]
) -> ComponentProtocol | None:
"""Try to load specified file.
Looks in config dir first, then built-in components.
Only returns it if also found to be valid.
Async friendly.
"""
cache = hass.data[DATA_COMPONENTS]
if module := cache.get(comp_or_platform):
return cast(ComponentProtocol, module)
for path in (f"{base}.{comp_or_platform}" for base in base_paths):
try:
module = importlib.import_module(path)
# In Python 3 you can import files from directories that do not
# contain the file __init__.py. A directory is a valid module if
# it contains a file with the .py extension. In this case Python
# will succeed in importing the directory as a module and call it
# a namespace. We do not care about namespaces.
# This prevents that when only
# custom_components/switch/some_platform.py exists,
# the import custom_components.switch would succeed.
# __file__ was unset for namespaces before Python 3.7
if getattr(module, "__file__", None) is None:
continue
cache[comp_or_platform] = module
return cast(ComponentProtocol, module)
except ImportError as err:
# This error happens if for example custom_components/switch
# exists and we try to load switch.demo.
# Ignore errors for custom_components, custom_components.switch
# and custom_components.switch.demo.
white_listed_errors = []
parts = []
for part in path.split("."):
parts.append(part)
white_listed_errors.append(f"No module named '{'.'.join(parts)}'")
if str(err) not in white_listed_errors:
_LOGGER.exception(
"Error loading %s. Make sure all dependencies are installed", path
)
return None
class ModuleWrapper:
"""Class to wrap a Python module and auto fill in hass argument."""
def __init__(self, hass: HomeAssistant, module: ComponentProtocol) -> None:
"""Initialize the module wrapper."""
self._hass = hass
self._module = module
def __getattr__(self, attr: str) -> Any:
"""Fetch an attribute."""
value = getattr(self._module, attr)
if hasattr(value, "__bind_hass"):
value = ft.partial(value, self._hass)
setattr(self, attr, value)
return value
def bind_hass[_CallableT: Callable[..., Any]](func: _CallableT) -> _CallableT: def bind_hass[_CallableT: Callable[..., Any]](func: _CallableT) -> _CallableT:
"""Decorate function to indicate that first argument is hass. """Decorate function to indicate that first argument is hass.
@ -1744,13 +1672,6 @@ def _async_mount_config_dir(hass: HomeAssistant) -> None:
sys.path_importer_cache.pop(hass.config.config_dir, None) sys.path_importer_cache.pop(hass.config.config_dir, None)
def _lookup_path(hass: HomeAssistant) -> list[str]:
"""Return the lookup paths for legacy lookups."""
if hass.config.recovery_mode or hass.config.safe_mode:
return [PACKAGE_BUILTIN]
return [PACKAGE_CUSTOM_COMPONENTS, PACKAGE_BUILTIN]
def is_component_module_loaded(hass: HomeAssistant, module: str) -> bool: def is_component_module_loaded(hass: HomeAssistant, module: str) -> bool:
"""Test if a component module is loaded.""" """Test if a component module is loaded."""
return module in hass.data[DATA_COMPONENTS] return module in hass.data[DATA_COMPONENTS]