Speed up the frame helper (#112562)
This commit is contained in:
parent
3ccbb2c87a
commit
1fb9cfe37e
12 changed files with 460 additions and 338 deletions
|
@ -6,15 +6,21 @@ from collections.abc import Callable
|
|||
from contextlib import suppress
|
||||
from dataclasses import dataclass
|
||||
import functools
|
||||
import linecache
|
||||
import logging
|
||||
import sys
|
||||
from traceback import FrameSummary, extract_stack
|
||||
from typing import Any, TypeVar, cast
|
||||
from types import FrameType
|
||||
from typing import TYPE_CHECKING, Any, TypeVar, cast
|
||||
|
||||
from homeassistant.core import HomeAssistant, async_get_hass
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.loader import async_suggest_report_issue
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from functools import cached_property
|
||||
else:
|
||||
from homeassistant.backports.functools import cached_property
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# Keep track of integrations already reported to prevent flooding
|
||||
|
@ -28,10 +34,25 @@ class IntegrationFrame:
|
|||
"""Integration frame container."""
|
||||
|
||||
custom_integration: bool
|
||||
frame: FrameSummary
|
||||
integration: str
|
||||
module: str | None
|
||||
relative_filename: str
|
||||
_frame: FrameType
|
||||
|
||||
@cached_property
|
||||
def line_number(self) -> int:
|
||||
"""Return the line number of the frame."""
|
||||
return self._frame.f_lineno
|
||||
|
||||
@cached_property
|
||||
def filename(self) -> str:
|
||||
"""Return the filename of the frame."""
|
||||
return self._frame.f_code.co_filename
|
||||
|
||||
@cached_property
|
||||
def line(self) -> str:
|
||||
"""Return the line of the frame."""
|
||||
return (linecache.getline(self.filename, self.line_number) or "?").strip()
|
||||
|
||||
|
||||
def get_integration_logger(fallback_name: str) -> logging.Logger:
|
||||
|
@ -54,19 +75,28 @@ def get_integration_logger(fallback_name: str) -> logging.Logger:
|
|||
return logging.getLogger(logger_name)
|
||||
|
||||
|
||||
def get_current_frame(depth: int = 0) -> FrameType:
|
||||
"""Return the current frame."""
|
||||
# Add one to depth since get_current_frame is included
|
||||
return sys._getframe(depth + 1) # pylint: disable=protected-access
|
||||
|
||||
|
||||
def get_integration_frame(exclude_integrations: set | None = None) -> IntegrationFrame:
|
||||
"""Return the frame, integration and integration path of the current stack frame."""
|
||||
found_frame = None
|
||||
if not exclude_integrations:
|
||||
exclude_integrations = set()
|
||||
|
||||
for frame in reversed(extract_stack()):
|
||||
frame: FrameType | None = get_current_frame()
|
||||
while frame is not None:
|
||||
filename = frame.f_code.co_filename
|
||||
|
||||
for path in ("custom_components/", "homeassistant/components/"):
|
||||
try:
|
||||
index = frame.filename.index(path)
|
||||
index = filename.index(path)
|
||||
start = index + len(path)
|
||||
end = frame.filename.index("/", start)
|
||||
integration = frame.filename[start:end]
|
||||
end = filename.index("/", start)
|
||||
integration = filename[start:end]
|
||||
if integration not in exclude_integrations:
|
||||
found_frame = frame
|
||||
|
||||
|
@ -77,6 +107,8 @@ def get_integration_frame(exclude_integrations: set | None = None) -> Integratio
|
|||
if found_frame is not None:
|
||||
break
|
||||
|
||||
frame = frame.f_back
|
||||
|
||||
if found_frame is None:
|
||||
raise MissingIntegrationFrame
|
||||
|
||||
|
@ -84,16 +116,16 @@ def get_integration_frame(exclude_integrations: set | None = None) -> Integratio
|
|||
for module, module_obj in dict(sys.modules).items():
|
||||
if not hasattr(module_obj, "__file__"):
|
||||
continue
|
||||
if module_obj.__file__ == found_frame.filename:
|
||||
if module_obj.__file__ == found_frame.f_code.co_filename:
|
||||
found_module = module
|
||||
break
|
||||
|
||||
return IntegrationFrame(
|
||||
custom_integration=path == "custom_components/",
|
||||
frame=found_frame,
|
||||
integration=integration,
|
||||
module=found_module,
|
||||
relative_filename=found_frame.filename[index:],
|
||||
relative_filename=found_frame.f_code.co_filename[index:],
|
||||
_frame=found_frame,
|
||||
)
|
||||
|
||||
|
||||
|
@ -137,9 +169,8 @@ def _report_integration(
|
|||
|
||||
Async friendly.
|
||||
"""
|
||||
found_frame = integration_frame.frame
|
||||
# Keep track of integrations already reported to prevent flooding
|
||||
key = f"{found_frame.filename}:{found_frame.lineno}"
|
||||
key = f"{integration_frame.filename}:{integration_frame.line_number}"
|
||||
if key in _REPORTED_INTEGRATIONS:
|
||||
return
|
||||
_REPORTED_INTEGRATIONS.add(key)
|
||||
|
@ -160,8 +191,8 @@ def _report_integration(
|
|||
integration_frame.integration,
|
||||
what,
|
||||
integration_frame.relative_filename,
|
||||
found_frame.lineno,
|
||||
(found_frame.line or "?").strip(),
|
||||
integration_frame.line_number,
|
||||
integration_frame.line,
|
||||
report_issue,
|
||||
)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue