Minor improvement of frame helper (#101387)

* Minor improvement of frame helper

* Add new custom integration for testing

* Make IntegrationFrame kw_only
This commit is contained in:
Erik Montnemery 2023-10-04 21:43:00 +02:00 committed by GitHub
parent c495d607d8
commit 7e39acda37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 10 deletions

View File

@ -6,6 +6,7 @@ from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
import functools import functools
import logging import logging
import sys
from traceback import FrameSummary, extract_stack from traceback import FrameSummary, extract_stack
from typing import Any, TypeVar, cast from typing import Any, TypeVar, cast
@ -19,14 +20,15 @@ _REPORTED_INTEGRATIONS: set[str] = set()
_CallableT = TypeVar("_CallableT", bound=Callable) _CallableT = TypeVar("_CallableT", bound=Callable)
@dataclass @dataclass(kw_only=True)
class IntegrationFrame: class IntegrationFrame:
"""Integration frame container.""" """Integration frame container."""
custom_integration: bool custom_integration: bool
filename: str
frame: FrameSummary frame: FrameSummary
integration: str integration: str
module: str | None
relative_filename: str
def get_integration_frame(exclude_integrations: set | None = None) -> IntegrationFrame: def get_integration_frame(exclude_integrations: set | None = None) -> IntegrationFrame:
@ -55,11 +57,20 @@ def get_integration_frame(exclude_integrations: set | None = None) -> Integratio
if found_frame is None: if found_frame is None:
raise MissingIntegrationFrame raise MissingIntegrationFrame
found_module: str | None = None
for module, module_obj in dict(sys.modules).items():
if not hasattr(module_obj, "__file__"):
continue
if module_obj.__file__ == found_frame.filename:
found_module = module
break
return IntegrationFrame( return IntegrationFrame(
path == "custom_components/", custom_integration=path == "custom_components/",
found_frame.filename[index:], frame=found_frame,
found_frame, integration=integration,
integration, module=found_module,
relative_filename=found_frame.filename[index:],
) )
@ -121,7 +132,7 @@ def _report_integration(
what, what,
extra, extra,
integration_frame.integration, integration_frame.integration,
integration_frame.filename, integration_frame.relative_filename,
found_frame.lineno, found_frame.lineno,
(found_frame.line or "?").strip(), (found_frame.line or "?").strip(),
) )

View File

@ -1,10 +1,11 @@
"""Test the frame helper.""" """Test the frame helper."""
from collections.abc import Generator from collections.abc import Generator
from unittest.mock import Mock, patch from unittest.mock import ANY, Mock, patch
import pytest import pytest
from homeassistant.core import HomeAssistant
from homeassistant.helpers import frame from homeassistant.helpers import frame
@ -41,7 +42,28 @@ async def test_extract_frame_integration(
"""Test extracting the current frame from integration context.""" """Test extracting the current frame from integration context."""
integration_frame = frame.get_integration_frame() integration_frame = frame.get_integration_frame()
assert integration_frame == frame.IntegrationFrame( assert integration_frame == frame.IntegrationFrame(
False, "homeassistant/components/hue/light.py", mock_integration_frame, "hue" custom_integration=False,
frame=mock_integration_frame,
integration="hue",
module=None,
relative_filename="homeassistant/components/hue/light.py",
)
async def test_extract_frame_resolve_module(
hass: HomeAssistant, enable_custom_integrations
) -> None:
"""Test extracting the current frame from integration context."""
from custom_components.test_integration_frame import call_get_integration_frame
integration_frame = call_get_integration_frame()
assert integration_frame == frame.IntegrationFrame(
custom_integration=True,
frame=ANY,
integration="test_integration_frame",
module="custom_components.test_integration_frame",
relative_filename="custom_components/test_integration_frame/__init__.py",
) )
@ -80,7 +102,11 @@ async def test_extract_frame_integration_with_excluded_integration(
) )
assert integration_frame == frame.IntegrationFrame( assert integration_frame == frame.IntegrationFrame(
False, "homeassistant/components/mdns/light.py", correct_frame, "mdns" custom_integration=False,
frame=correct_frame,
integration="mdns",
module=None,
relative_filename="homeassistant/components/mdns/light.py",
) )

View File

@ -0,0 +1,8 @@
"""An integration which calls helpers.frame.get_integration_frame."""
from homeassistant.helpers import frame
def call_get_integration_frame() -> frame.IntegrationFrame:
"""Call get_integration_frame."""
return frame.get_integration_frame()

View File

@ -0,0 +1,9 @@
{
"domain": "test_integration_frame",
"name": "Test Integration Frame",
"documentation": "http://example.com",
"requirements": [],
"dependencies": [],
"codeowners": [],
"version": "1.2.3"
}