-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Change cache
/lru_cache
typing to retain function signatures
#12952
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Define overloads for `cache`/`lru_cache` that are constrained by the signature of the function they're called with. This assumes `self` as a first parameter is a function method, `cls` is a classmethod and everything else are either static methods or functions. Then direct each overload to a descriptor protocol that mirrors the binding behaviour of methods/functions/classmethods. The returned type is then a union with a callable that takes arbitrary arguments that are hashable. This means that the previous hashable requirement is retained (type checkers don't know which type was returned so calls must satisfy both, the union takes any arguments as long as they're hashable while the descriptor protocol mirrors the cached function's behaviour).
Diff from mypy_primer, showing the effect of this PR on open source code: dacite (https://github.com/konradhalas/dacite)
+ dacite/cache.py:11: error: Unused "type: ignore" comment [unused-ignore]
pylint (https://github.com/pycqa/pylint)
+ pylint/checkers/utils.py:2312: error: "_lru_cache_wrapper" expects 2 type arguments, but 1 given [type-arg]
+ pylint/checkers/utils.py:2312: error: Missing type parameters for generic type "_lru_cache_wrapper" [type-arg]
+ pylint/checkers/utils.py:2313: error: List item 0 has incompatible type "_FunctionDescriptor[[Any], bool] | _FunctionHasHashable[[Any], bool] | _lru_cache_wrapper[[Any], bool]"; expected "_lru_cache_wrapper[Any, Any]" [list-item]
+ pylint/checkers/utils.py:2314: error: List item 1 has incompatible type "_FunctionDescriptor[[Any, Any], bool] | _FunctionHasHashable[[Any, Any], bool] | _lru_cache_wrapper[[Any, Any], bool]"; expected "_lru_cache_wrapper[Any, Any]" [list-item]
+ pylint/checkers/utils.py:2315: error: List item 2 has incompatible type "_FunctionDescriptor[[Any, Any | None], list[Any]] | _FunctionHasHashable[[Any, Any | None], list[Any]] | _lru_cache_wrapper[[Any, Any | None], list[Any]]"; expected "_lru_cache_wrapper[Any, Any]" [list-item]
+ pylint/checkers/utils.py:2316: error: List item 3 has incompatible type "_FunctionDescriptor[[Any], bool] | _FunctionHasHashable[[Any], bool] | _lru_cache_wrapper[[Any], bool]"; expected "_lru_cache_wrapper[Any, Any]" [list-item]
+ pylint/checkers/utils.py:2317: error: List item 4 has incompatible type "_FunctionDescriptor[[Any, str | None], Any | None] | _FunctionHasHashable[[Any, str | None], Any | None] | _lru_cache_wrapper[[Any, str | None], Any | None]"; expected "_lru_cache_wrapper[Any, Any]" [list-item]
+ pylint/checkers/utils.py:2318: error: List item 5 has incompatible type "_FunctionDescriptor[[Any, Any | None], dict[str, Any]] | _FunctionHasHashable[[Any, Any | None], dict[str, Any]] | _lru_cache_wrapper[[Any, Any | None], dict[str, Any]]"; expected "_lru_cache_wrapper[Any, Any]" [list-item]
+ pylint/checkers/utils.py:2319: error: List item 6 has incompatible type "_FunctionDescriptor[[Any, Any | None, DefaultNamedArg(bool, 'compare_constants'), DefaultNamedArg(bool, 'compare_constructors')], Any | None] | _FunctionHasHashable[[Any, Any | None, DefaultNamedArg(bool, 'compare_constants'), DefaultNamedArg(bool, 'compare_constructors')], Any | None] | _lru_cache_wrapper[[Any, Any | None, DefaultNamedArg(bool, 'compare_constants'), DefaultNamedArg(bool, 'compare_constructors')], Any | None]"; expected "_lru_cache_wrapper[Any, Any]" [list-item]
psycopg (https://github.com/psycopg/psycopg)
+ psycopg/psycopg/rows.py:143: error: Argument 2 to "__call__" of "_FunctionDescriptor" has incompatible type "*Generator[bytes | None, None, None]"; expected "bytes" [arg-type]
+ psycopg/psycopg/types/enum.py:156: error: Incompatible types in assignment (expression has type "type[_BaseEnumLoader[E@register_enum]] | type[_BaseEnumLoader[E@_make_binary_loader]]", variable has type "type[_BaseEnumLoader[E@register_enum]] | type[_BaseEnumLoader[E@_make_loader]]") [assignment]
+ psycopg/psycopg/types/enum.py:164: error: Incompatible types in assignment (expression has type "type[_BaseEnumDumper[E@register_enum]] | type[_BaseEnumDumper[E@_make_binary_dumper]]", variable has type "type[_BaseEnumDumper[E@register_enum]] | type[_BaseEnumDumper[E@_make_dumper]]") [assignment]
prefect (https://github.com/PrefectHQ/prefect)
- src/prefect/settings/legacy.py:105: error: Argument 1 to "__call__" of "_lru_cache_wrapper" has incompatible type "type[PrefectBaseSettings]"; expected "Hashable" [arg-type]
- src/prefect/settings/legacy.py:105: note: Following member(s) of "PrefectBaseSettings" have conflicts:
- src/prefect/settings/legacy.py:105: note: Expected:
- src/prefect/settings/legacy.py:105: note: def __hash__() -> int
- src/prefect/settings/legacy.py:105: note: Got:
- src/prefect/settings/legacy.py:105: note: def __hash__(self: object) -> int
- src/prefect/settings/legacy.py:105: note: Expected:
- src/prefect/settings/legacy.py:105: note: def __hash__() -> int
- src/prefect/settings/legacy.py:105: note: Got:
- src/prefect/settings/legacy.py:105: note: def __hash__(self: object) -> int
- src/prefect/settings/legacy.py:136: error: Argument 1 to "__call__" of "_lru_cache_wrapper" has incompatible type "type[PrefectBaseSettings]"; expected "Hashable" [arg-type]
+ src/prefect/settings/legacy.py:136: error: Argument 1 to "__call__" of "_FunctionHasHashable" has incompatible type "type[PrefectBaseSettings]"; expected "Hashable" [arg-type]
ibis (https://github.com/ibis-project/ibis)
+ ibis/backends/tests/test_temporal.py:2162: error: Argument "exclude" to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[str, str]"; expected "tuple[str]" [arg-type]
+ ibis/backends/tests/test_temporal.py:2180: error: Argument "exclude" to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[str, str, str, str, str, str]"; expected "tuple[str]" [arg-type]
twine (https://github.com/pypa/twine)
+ twine/settings.py:135: error: Incompatible return value type (got "_BoundDescriptor[[Resolver], [], str | None]", expected "str | None") [return-value]
+ twine/settings.py:140: error: Incompatible return value type (got "_BoundDescriptor[[Resolver], [], str | None]", expected "str | None") [return-value]
jinja (https://github.com/pallets/jinja)
+ src/jinja2/environment.py:1189: error: Unused "type: ignore" comment [unused-ignore]
+ src/jinja2/environment.py:1204: error: Unused "type: ignore" comment [unused-ignore]
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/proxy/mode_specs.py:167: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/proxy/mode_specs.py:167: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/connection.py:193: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/connection.py:193: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/utils/asyncio_utils.py:62: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "Any | str"; expected "tuple[Any, ...] | None" [arg-type]
+ mitmproxy/tools/console/common.py:811: error: Argument "error_message" to "__call__" of "_FunctionDescriptor" has incompatible type "str | None"; expected "str" [arg-type]
+ mitmproxy/tools/console/common.py:848: error: Incompatible types in assignment (expression has type "_FunctionDescriptor[[NamedArg(RenderMode, 'render_mode'), NamedArg(bool, 'focused'), NamedArg(str, 'marked'), NamedArg(str | None, 'is_replay'), NamedArg(str, 'request_method'), NamedArg(str, 'request_scheme'), NamedArg(str, 'request_host'), NamedArg(str, 'request_path'), NamedArg(str, 'request_url'), NamedArg(str, 'request_http_version'), NamedArg(float, 'request_timestamp'), NamedArg(bool, 'request_is_push_promise'), NamedArg(bool, 'intercepted'), NamedArg(int | None, 'response_code'), NamedArg(str | None, 'response_reason'), NamedArg(int | None, 'response_content_length'), NamedArg(str | None, 'response_content_type'), NamedArg(float | None, 'duration'), NamedArg(str | None, 'error_message')], Any] | _FunctionHasHashable[[NamedArg(RenderMode, 'render_mode'), NamedArg(bool, 'focused'), NamedArg(str, 'marked'), NamedArg(str | None, 'is_replay'), NamedArg(str, 'request_method'), NamedArg(str, 'request_scheme'), NamedArg(str, 'request_host'), NamedArg(str, 'request_path'), NamedArg(str, 'request_url'), NamedArg(str, 'request_http_version'), NamedArg(float, 'request_timestamp'), NamedArg(bool, 'request_is_push_promise'), NamedArg(bool, 'intercepted'), NamedArg(int | None, 'response_code'), NamedArg(str | None, 'response_reason'), NamedArg(int | None, 'response_content_length'), NamedArg(str | None, 'response_content_type'), NamedArg(float | None, 'duration'), NamedArg(str | None, 'error_message')], Any] | _lru_cache_wrapper[[NamedArg(RenderMode, 'render_mode'), NamedArg(bool, 'focused'), NamedArg(str, 'marked'), NamedArg(str | None, 'is_replay'), NamedArg(str, 'request_method'), NamedArg(str, 'request_scheme'), NamedArg(str, 'request_host'), NamedArg(str, 'request_path'), NamedArg(str, 'request_url'), NamedArg(str, 'request_http_version'), NamedArg(float, 'request_timestamp'), NamedArg(bool, 'request_is_push_promise'), NamedArg(bool, 'intercepted'), NamedArg(int | None, 'response_code'), NamedArg(str | None, 'response_reason'), NamedArg(int | None, 'response_content_length'), NamedArg(str | None, 'response_content_type'), NamedArg(float | None, 'duration'), NamedArg(str | None, 'error_message')], Any]", variable has type "_FunctionDescriptor[[NamedArg(RenderMode, 'render_mode'), NamedArg(bool, 'focused'), NamedArg(str, 'marked'), NamedArg(bool, 'is_replay'), NamedArg(str, 'request_method'), NamedArg(str, 'request_scheme'), NamedArg(str, 'request_host'), NamedArg(str, 'request_path'), NamedArg(str, 'request_url'), NamedArg(str, 'request_http_version'), NamedArg(float, 'request_timestamp'), NamedArg(bool, 'request_is_push_promise'), NamedArg(bool, 'intercepted'), NamedArg(int | None, 'response_code'), NamedArg(str | None, 'response_reason'), NamedArg(int | None, 'response_content_length'), NamedArg(str | None, 'response_content_type'), NamedArg(float | None, 'duration'), NamedArg(str | None, 'error_message')], Any] | _FunctionHasHashable[[NamedArg(RenderMode, 'render_mode'), NamedArg(bool, 'focused'), NamedArg(str, 'marked'), NamedArg(bool, 'is_replay'), NamedArg(str, 'request_method'), NamedArg(str, 'request_scheme'), NamedArg(str, 'request_host'), NamedArg(str, 'request_path'), NamedArg(str, 'request_url'), NamedArg(str, 'request_http_version'), NamedArg(float, 'request_timestamp'), NamedArg(bool, 'request_is_push_promise'), NamedArg(bool, 'intercepted'), NamedArg(int | None, 'response_code'), NamedArg(str | None, 'response_reason'), NamedArg(int | None, 'response_content_length'), NamedArg(str | None, 'response_content_type'), NamedArg(float | None, 'duration'), NamedArg(str | None, 'error_message')], Any] | _lru_cache_wrapper[[NamedArg(RenderMode, 'render_mode'), NamedArg(bool, 'focused'), NamedArg(str, 'marked'), NamedArg(bool, 'is_replay'), NamedArg(str, 'request_method'), NamedArg(str, 'request_scheme'), NamedArg(str, 'request_host'), NamedArg(str, 'request_path'), NamedArg(str, 'request_url'), NamedArg(str, 'request_http_version'), NamedArg(float, 'request_timestamp'), NamedArg(bool, 'request_is_push_promise'), NamedArg(bool, 'intercepted'), NamedArg(int | None, 'response_code'), NamedArg(str | None, 'response_reason'), NamedArg(int | None, 'response_content_length'), NamedArg(str | None, 'response_content_type'), NamedArg(float | None, 'duration'), NamedArg(str | None, 'error_message')], Any]") [assignment]
+ mitmproxy/tools/console/common.py:853: error: Argument "is_replay" to "__call__" of "_FunctionDescriptor" has incompatible type "str | None"; expected "bool" [arg-type]
+ mitmproxy/test/tflow.py:134: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/test/tflow.py:134: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/test/tflow.py:235: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/test/tflow.py:235: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/proxy/server.py:582: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/proxy/server.py:582: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/proxy/mode_servers.py:115: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/proxy/mode_servers.py:115: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/addons/clientplayback.py:98: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[UpstreamMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/addons/clientplayback.py:98: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[UpstreamMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/addons/clientplayback.py:98: error: Need type annotation for "mode" [var-annotated]
+ mitmproxy/addons/proxyserver.py:109: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/addons/proxyserver.py:109: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/addons/proxyserver.py:255: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/addons/proxyserver.py:255: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/addons/proxyserver.py:299: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/addons/proxyserver.py:299: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[ProxyMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/master.py:135: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[ReverseMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/master.py:135: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[ReverseMode]"; expected "type[Never]" [arg-type]
+ mitmproxy/master.py:135: error: Need type annotation for "mode" [var-annotated]
rich (https://github.com/Textualize/rich)
+ rich/progress_bar.py:145: error: Argument 3 to "__call__" of "_BoundDescriptor" has incompatible type "str | None"; expected "str" [arg-type]
schemathesis (https://github.com/schemathesis/schemathesis)
+ src/schemathesis/stateful/state_machine.py: note: In class "APIStateMachine":
+ src/schemathesis/stateful/state_machine.py:68: error: Signature of "_to_test_case" incompatible with supertype "RuleBasedStateMachine" [override]
+ src/schemathesis/stateful/state_machine.py:68: note: Superclass:
+ src/schemathesis/stateful/state_machine.py:68: note: Union[_ClassmethodDescriptor[RuleBasedStateMachine, [], Any], _ClassmethodHasHashableDescriptor[RuleBasedStateMachine, [], Any], _lru_cache_wrapper[[Type[RuleBasedStateMachine]], Any]]
+ src/schemathesis/stateful/state_machine.py:68: note: Subclass:
+ src/schemathesis/stateful/state_machine.py:68: note: Union[_ClassmethodDescriptor[APIStateMachine, [], type], _ClassmethodHasHashableDescriptor[APIStateMachine, [], type], _lru_cache_wrapper[[Type[APIStateMachine]], type]]
+ src/schemathesis/specs/openapi/stateful/__init__.py: note: In function "create_state_machine":
+ src/schemathesis/specs/openapi/stateful/__init__.py:79: error: Argument 2 to "__call__" of "_FunctionDescriptor" has incompatible type "Tuple[Any, ...]"; expected "Iterator[str]" [arg-type]
+ src/schemathesis/specs/openapi/stateful/__init__.py:79: note: "tuple" is missing following "Iterator" protocol member:
+ src/schemathesis/specs/openapi/stateful/__init__.py:79: note: __next__
+ src/schemathesis/specs/openapi/stateful/__init__.py: note: At top level:
pip (https://github.com/pypa/pip)
+ src/pip/_internal/index/package_finder.py:902: error: Argument 1 to "__call__" of "_BoundDescriptor" has incompatible type "str | None"; expected "str" [arg-type]
+ src/pip/_internal/resolution/resolvelib/provider.py:242: error: Signature of "is_satisfied_by" incompatible with supertype "AbstractProvider" [override]
+ src/pip/_internal/resolution/resolvelib/provider.py:242: note: Superclass:
+ src/pip/_internal/resolution/resolvelib/provider.py:242: note: def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> bool
+ src/pip/_internal/resolution/resolvelib/provider.py:242: note: Subclass:
+ src/pip/_internal/resolution/resolvelib/provider.py:242: note: _MethodDescriptor[PipProvider, [Requirement, Candidate], bool] | _MethodHasHashableDescriptor[PipProvider, [Requirement, Candidate], bool] | _lru_cache_wrapper[[PipProvider, Requirement, Candidate], bool]
manticore (https://github.com/trailofbits/manticore)
+ manticore/core/smtlib/solver.py:505: error: Signature of "can_be_true" incompatible with supertype "Solver" [override]
+ manticore/core/smtlib/solver.py:505: note: Superclass:
+ manticore/core/smtlib/solver.py:505: note: def can_be_true(self, constraints: Any, expression: Any = ...) -> bool
+ manticore/core/smtlib/solver.py:505: note: Subclass:
+ manticore/core/smtlib/solver.py:505: note: _MethodDescriptor[SMTLIBSolver, [ConstraintSet, bool | Bool], bool] | _MethodHasHashableDescriptor[SMTLIBSolver, [ConstraintSet, bool | Bool], bool] | _lru_cache_wrapper[[SMTLIBSolver, ConstraintSet, bool | Bool], bool]
+ manticore/core/smtlib/solver.py:605: error: Signature of "get_all_values" incompatible with supertype "Solver" [override]
+ manticore/core/smtlib/solver.py:605: note: Superclass:
+ manticore/core/smtlib/solver.py:605: note: def get_all_values(self, constraints: Any, x: Any, maxcnt: Any = ..., silent: Any = ...) -> Any
+ manticore/core/smtlib/solver.py:605: note: Subclass:
+ manticore/core/smtlib/solver.py:605: note: _MethodDescriptor[SMTLIBSolver, [ConstraintSet, Any, int | None, bool], Any] | _MethodHasHashableDescriptor[SMTLIBSolver, [ConstraintSet, Any, int | None, bool], Any] | _lru_cache_wrapper[[SMTLIBSolver, ConstraintSet, Any, int | None, bool], Any]
core (https://github.com/home-assistant/core)
+ homeassistant/util/unit_conversion.py:367: error: Signature of "converter_factory" incompatible with supertype "BaseUnitConverter" [override]
+ homeassistant/util/unit_conversion.py:367: note: Superclass:
+ homeassistant/util/unit_conversion.py:367: note: _ClassmethodDescriptor[BaseUnitConverter, [str | None, str | None], Callable[[float], float]] | _ClassmethodHasHashableDescriptor[BaseUnitConverter, [str | None, str | None], Callable[[float], float]] | _lru_cache_wrapper[[type[BaseUnitConverter], str | None, str | None], Callable[[float], float]]
+ homeassistant/util/unit_conversion.py:367: note: Subclass:
+ homeassistant/util/unit_conversion.py:367: note: _ClassmethodDescriptor[SpeedConverter, [str | None, str | None], Callable[[float], float]] | _ClassmethodHasHashableDescriptor[SpeedConverter, [str | None, str | None], Callable[[float], float]] | _lru_cache_wrapper[[type[SpeedConverter], str | None, str | None], Callable[[float], float]]
+ homeassistant/util/unit_conversion.py:381: error: Signature of "converter_factory_allow_none" incompatible with supertype "BaseUnitConverter" [override]
+ homeassistant/util/unit_conversion.py:381: note: Superclass:
+ homeassistant/util/unit_conversion.py:381: note: _ClassmethodDescriptor[BaseUnitConverter, [str | None, str | None], Callable[[float | None], float | None]] | _ClassmethodHasHashableDescriptor[BaseUnitConverter, [str | None, str | None], Callable[[float | None], float | None]] | _lru_cache_wrapper[[type[BaseUnitConverter], str | None, str | None], Callable[[float | None], float | None]]
+ homeassistant/util/unit_conversion.py:381: note: Subclass:
+ homeassistant/util/unit_conversion.py:381: note: _ClassmethodDescriptor[SpeedConverter, [str | None, str | None], Callable[[float | None], float | None]] | _ClassmethodHasHashableDescriptor[SpeedConverter, [str | None, str | None], Callable[[float | None], float | None]] | _lru_cache_wrapper[[type[SpeedConverter], str | None, str | None], Callable[[float | None], float | None]]
+ homeassistant/util/unit_conversion.py:447: error: Signature of "converter_factory" incompatible with supertype "BaseUnitConverter" [override]
+ homeassistant/util/unit_conversion.py:447: note: Superclass:
+ homeassistant/util/unit_conversion.py:447: note: _ClassmethodDescriptor[BaseUnitConverter, [str | None, str | None], Callable[[float], float]] | _ClassmethodHasHashableDescriptor[BaseUnitConverter, [str | None, str | None], Callable[[float], float]] | _lru_cache_wrapper[[type[BaseUnitConverter], str | None, str | None], Callable[[float], float]]
+ homeassistant/util/unit_conversion.py:447: note: Subclass:
+ homeassistant/util/unit_conversion.py:447: note: _ClassmethodDescriptor[TemperatureConverter, [str | None, str | None], Callable[[float], float]] | _ClassmethodHasHashableDescriptor[TemperatureConverter, [str | None, str | None], Callable[[float], float]] | _lru_cache_wrapper[[type[TemperatureConverter], str | None, str | None], Callable[[float], float]]
+ homeassistant/util/unit_conversion.py:461: error: Signature of "converter_factory_allow_none" incompatible with supertype "BaseUnitConverter" [override]
+ homeassistant/util/unit_conversion.py:461: note: Superclass:
+ homeassistant/util/unit_conversion.py:461: note: _ClassmethodDescriptor[BaseUnitConverter, [str | None, str | None], Callable[[float | None], float | None]] | _ClassmethodHasHashableDescriptor[BaseUnitConverter, [str | None, str | None], Callable[[float | None], float | None]] | _lru_cache_wrapper[[type[BaseUnitConverter], str | None, str | None], Callable[[float | None], float | None]]
+ homeassistant/util/unit_conversion.py:461: note: Subclass:
+ homeassistant/util/unit_conversion.py:461: note: _ClassmethodDescriptor[TemperatureConverter, [str | None, str | None], Callable[[float | None], float | None]] | _ClassmethodHasHashableDescriptor[TemperatureConverter, [str | None, str | None], Callable[[float | None], float | None]] | _lru_cache_wrapper[[type[TemperatureConverter], str | None, str | None], Callable[[float | None], float | None]]
+ homeassistant/components/xiaomi_aqara/config_flow.py:216: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "str | None"; expected "str" [arg-type]
+ homeassistant/components/mqtt_room/sensor.py:199: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "Any | None"; expected "str" [arg-type]
+ homeassistant/components/esphome/update.py:63: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[DomainData]"; expected "type[Never]" [arg-type]
+ homeassistant/components/esphome/update.py:63: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[DomainData]"; expected "type[Never]" [arg-type]
+ homeassistant/components/esphome/update.py:63: error: "Never" has no attribute "get_entry_data" [attr-defined]
+ homeassistant/components/esphome/update.py:64: error: Statement is unreachable [unreachable]
+ homeassistant/components/esphome/light.py:178: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[int, ...]"; expected "list[int]" [arg-type]
+ homeassistant/components/esphome/light.py:187: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[int, ...]"; expected "list[int]" [arg-type]
+ homeassistant/components/esphome/light.py:198: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[int, ...]"; expected "list[int]" [arg-type]
+ homeassistant/components/esphome/light.py:207: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[int, ...]"; expected "list[int]" [arg-type]
+ homeassistant/components/esphome/light.py:208: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[int, ...]"; expected "list[int]" [arg-type]
+ homeassistant/components/esphome/light.py:213: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[int, ...]"; expected "list[int]" [arg-type]
+ homeassistant/components/esphome/light.py:225: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[int, ...]"; expected "list[int]" [arg-type]
+ homeassistant/components/esphome/light.py:242: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[int, ...]"; expected "list[int]" [arg-type]
+ homeassistant/components/esphome/light.py:247: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[int, ...]"; expected "list[int]" [arg-type]
+ homeassistant/components/esphome/light.py:261: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[int, ...]"; expected "list[int]" [arg-type]
+ homeassistant/components/esphome/light.py:342: error: Argument 1 to "__call__" of "_FunctionDescriptor" has incompatible type "tuple[int, ...]"; expected "list[int]" [arg-type]
+ homeassistant/components/esphome/__init__.py:61: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[DomainData]"; expected "type[Never]" [arg-type]
+ homeassistant/components/esphome/__init__.py:61: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[DomainData]"; expected "type[Never]" [arg-type]
+ homeassistant/components/esphome/__init__.py:61: error: Need type annotation for "domain_data" [var-annotated]
+ homeassistant/components/esphome/__init__.py:62: error: Statement is unreachable [unreachable]
+ homeassistant/components/esphome/__init__.py:89: error: Argument 2 to "__get__" of "_ClassmethodDescriptor" has incompatible type "type[DomainData]"; expected "type[Never]" [arg-type]
+ homeassistant/components/esphome/__init__.py:89: error: Argument 2 to "__get__" of "_ClassmethodHasHashableDescriptor" has incompatible type "type[DomainData]"; expected "type[Never]" [arg-type]
+ homeassistant/components/esphome/__init__.py:89: error: "Never" has no attribute "get_or_create_store" [attr-defined]
|
My summary of the mypy primer errors:
There is an error in Testing something similar locally, I get However, I think this just highlights a bigger issue - the descriptor protocol isn't easily implemented or wrapped. The current conversation to fix this was about changing how Lines 137 to 138 in 5243d30
descriptors and decorators are a pain. |
I've asked a question at microsoft/pyright#9384 as I think it might be possible to simplify this, but possibly some work elsewhere needed. |
This works but I also don't think this is the right way to do it. |
My (second - #12945 my comments there aren't really relevant) attempt at fixing the
cache
&lru_cache
typing problem. This is based on a local wrapper I wrote using newer generics syntax, see my comment for the newer generics approach. I'm not sure on type variance and have a few ignores. Mypy and pyright disagree in places too. I defer to maintainers as to what is right, my understanding of type variance is limited.Note: the current implementation is quite clunky. I have attempted to meet the following constraints:
Hashable
argument requirement (requires aHashable
descriptor class)cache_info
,cache_clear
andcache_parameters
function_lru_cache_wrapper
and so this must be part of the cached return type._lru_cache_wrapper
at runtime isn't subclassable so one of the tests expect it to be annotated withfinal
. Making the descriptor protocols subclasses of this would simplify typing slightly but require ignores.It might be possible to simplify the code as is or some of these constraints may be considered not necessary. My takeaway from other conversations is that 2 is a hard requirement of maintainers, 5 is what I want. The other requirements may be negotiable.
But using this typeshed fork locally the below code example correctly type checks in pyright without any errors (and all type ignore errors are correctly ignoring an error).
To do this I've defined overloads for
cache
/lru_cache
that are constrained by the signature of the function they're called with. This assumesself
as a first parameter is an object method,cls
is a classmethod and everything else is either a static method or a function.Then direct each overload to a descriptor protocol that mirrors the binding behaviour of methods/functions/classmethods.
The returned type is then a union with a callable that takes arbitrary arguments that are hashable. This means that the previous hashable requirement is retained (type checkers don't know which type was returned so calls must satisfy both, the union takes any arguments as long as they're hashable while the descriptor protocol mirrors the cached function's behaviour).