mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Fix performance regression with SignalType (#117920)
This commit is contained in:
parent
5229f0d0ef
commit
5c9c71ba2c
@ -61,15 +61,15 @@ repos:
|
|||||||
name: mypy
|
name: mypy
|
||||||
entry: script/run-in-env.sh mypy
|
entry: script/run-in-env.sh mypy
|
||||||
language: script
|
language: script
|
||||||
types: [python]
|
types_or: [python, pyi]
|
||||||
require_serial: true
|
require_serial: true
|
||||||
files: ^(homeassistant|pylint)/.+\.(py|pyi)$
|
files: ^(homeassistant|pylint)/.+\.(py|pyi)$
|
||||||
- id: pylint
|
- id: pylint
|
||||||
name: pylint
|
name: pylint
|
||||||
entry: script/run-in-env.sh pylint -j 0 --ignore-missing-annotations=y
|
entry: script/run-in-env.sh pylint -j 0 --ignore-missing-annotations=y
|
||||||
language: script
|
language: script
|
||||||
types: [python]
|
types_or: [python, pyi]
|
||||||
files: ^homeassistant/.+\.py$
|
files: ^homeassistant/.+\.(py|pyi)$
|
||||||
- id: gen_requirements_all
|
- id: gen_requirements_all
|
||||||
name: gen_requirements_all
|
name: gen_requirements_all
|
||||||
entry: script/run-in-env.sh python3 -m script.gen_requirements_all
|
entry: script/run-in-env.sh python3 -m script.gen_requirements_all
|
||||||
|
@ -2,40 +2,20 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
|
class _SignalTypeBase[*_Ts](str):
|
||||||
@dataclass(frozen=True)
|
|
||||||
class _SignalTypeBase[*_Ts]:
|
|
||||||
"""Generic base class for SignalType."""
|
"""Generic base class for SignalType."""
|
||||||
|
|
||||||
name: str
|
__slots__ = ()
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
|
||||||
"""Return hash of name."""
|
|
||||||
|
|
||||||
return hash(self.name)
|
|
||||||
|
|
||||||
def __eq__(self, other: object) -> bool:
|
|
||||||
"""Check equality for dict keys to be compatible with str."""
|
|
||||||
|
|
||||||
if isinstance(other, str):
|
|
||||||
return self.name == other
|
|
||||||
if isinstance(other, SignalType):
|
|
||||||
return self.name == other.name
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, eq=False)
|
|
||||||
class SignalType[*_Ts](_SignalTypeBase[*_Ts]):
|
class SignalType[*_Ts](_SignalTypeBase[*_Ts]):
|
||||||
"""Generic string class for signal to improve typing."""
|
"""Generic string class for signal to improve typing."""
|
||||||
|
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, eq=False)
|
|
||||||
class SignalTypeFormat[*_Ts](_SignalTypeBase[*_Ts]):
|
class SignalTypeFormat[*_Ts](_SignalTypeBase[*_Ts]):
|
||||||
"""Generic string class for signal. Requires call to 'format' before use."""
|
"""Generic string class for signal. Requires call to 'format' before use."""
|
||||||
|
|
||||||
def format(self, *args: Any, **kwargs: Any) -> SignalType[*_Ts]:
|
__slots__ = ()
|
||||||
"""Format name and return new SignalType instance."""
|
|
||||||
return SignalType(self.name.format(*args, **kwargs))
|
|
||||||
|
69
homeassistant/util/signal_type.pyi
Normal file
69
homeassistant/util/signal_type.pyi
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
"""Stub file for signal_type. Provide overload for type checking."""
|
||||||
|
# ruff: noqa: PYI021 # Allow docstring
|
||||||
|
|
||||||
|
from typing import Any, assert_type
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"SignalType",
|
||||||
|
"SignalTypeFormat",
|
||||||
|
]
|
||||||
|
|
||||||
|
class _SignalTypeBase[*_Ts]:
|
||||||
|
"""Custom base class for SignalType. At runtime delegate to str.
|
||||||
|
|
||||||
|
For type checkers pretend to be its own separate class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, value: str, /) -> None: ...
|
||||||
|
def __hash__(self) -> int: ...
|
||||||
|
def __eq__(self, other: object, /) -> bool: ...
|
||||||
|
|
||||||
|
class SignalType[*_Ts](_SignalTypeBase[*_Ts]):
|
||||||
|
"""Generic string class for signal to improve typing."""
|
||||||
|
|
||||||
|
class SignalTypeFormat[*_Ts](_SignalTypeBase[*_Ts]):
|
||||||
|
"""Generic string class for signal. Requires call to 'format' before use."""
|
||||||
|
|
||||||
|
def format(self, *args: Any, **kwargs: Any) -> SignalType[*_Ts]: ...
|
||||||
|
|
||||||
|
def _test_signal_type_typing() -> None: # noqa: PYI048
|
||||||
|
"""Test SignalType and dispatcher overloads work as intended.
|
||||||
|
|
||||||
|
This is tested during the mypy run. Do not move it to 'tests'!
|
||||||
|
"""
|
||||||
|
# pylint: disable=import-outside-toplevel
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.dispatcher import (
|
||||||
|
async_dispatcher_connect,
|
||||||
|
async_dispatcher_send,
|
||||||
|
)
|
||||||
|
|
||||||
|
hass: HomeAssistant
|
||||||
|
def test_func(a: int) -> None: ...
|
||||||
|
def test_func_other(a: int, b: str) -> None: ...
|
||||||
|
|
||||||
|
# No type validation for str signals
|
||||||
|
signal_str = "signal"
|
||||||
|
async_dispatcher_connect(hass, signal_str, test_func)
|
||||||
|
async_dispatcher_connect(hass, signal_str, test_func_other)
|
||||||
|
async_dispatcher_send(hass, signal_str, 2)
|
||||||
|
async_dispatcher_send(hass, signal_str, 2, "Hello World")
|
||||||
|
|
||||||
|
# Using SignalType will perform type validation on target and args
|
||||||
|
signal_1: SignalType[int] = SignalType("signal")
|
||||||
|
assert_type(signal_1, SignalType[int])
|
||||||
|
async_dispatcher_connect(hass, signal_1, test_func)
|
||||||
|
async_dispatcher_connect(hass, signal_1, test_func_other) # type: ignore[arg-type]
|
||||||
|
async_dispatcher_send(hass, signal_1, 2)
|
||||||
|
async_dispatcher_send(hass, signal_1, "Hello World") # type: ignore[misc]
|
||||||
|
|
||||||
|
# SignalTypeFormat cannot be used for dispatcher_connect / dispatcher_send
|
||||||
|
# Call format() on it first to convert it to a SignalType
|
||||||
|
signal_format: SignalTypeFormat[int] = SignalTypeFormat("signal_")
|
||||||
|
signal_2 = signal_format.format("2")
|
||||||
|
assert_type(signal_format, SignalTypeFormat[int])
|
||||||
|
assert_type(signal_2, SignalType[int])
|
||||||
|
async_dispatcher_connect(hass, signal_format, test_func) # type: ignore[call-overload]
|
||||||
|
async_dispatcher_connect(hass, signal_2, test_func)
|
||||||
|
async_dispatcher_send(hass, signal_format, 2) # type: ignore[call-overload]
|
||||||
|
async_dispatcher_send(hass, signal_2, 2)
|
Loading…
x
Reference in New Issue
Block a user