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 contextlib import suppress
from dataclasses import dataclass
import functools as ft
import importlib
import logging
import os
@ -1650,77 +1649,6 @@ class CircularDependency(LoaderError):
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:
"""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)
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:
"""Test if a component module is loaded."""
return module in hass.data[DATA_COMPONENTS]