-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
Bug Report
The setup:
I have a generic class (Instantiator) which contains a class method (instantiate()) which is passed a desired type (MyClass) and instantiates an instance of the generic type. I then have another function (returns_instance()) outside of the class which calls instantiate and returns an instance of the passed desired type.
What is happening:
When the definition for the generic type and the generic class lives in the same module as the calling function, there are no mypy errors. However, if the definition for the generic type and the generic class live in a different module from the calling function, mypy throws a error: Returning Any from function declared to return "MyClass" [no-any-return] error.
To Reproduce
File 1: a.py
from typing import Any, Dict, Generic, Type, TypeVar
from dataclasses import dataclass
from spotify_typed_config.b import InstantiatorB
TYPEVAR_A = TypeVar("TYPEVAR_A")
@dataclass
class MyClass:
i: int = 10
def print(self) -> None:
print(self.i)
class InstantiatorA(Generic[TYPEVAR_A]):
@classmethod
def instantiate(cls, class_to_instantiate: Type[TYPEVAR_A], x: Dict[str, Any]) -> TYPEVAR_A:
return class_to_instantiate(**x)
# This works fine -- no errors!
def returns_instance_a(i: int) -> MyClass:
return InstantiatorA.instantiate(MyClass, {"i": i})
# a.py:29: error: Returning Any from function declared to return "MyClass" [no-any-return]
def returns_instance_b(i: int) -> MyClass:
return InstantiatorB.instantiate(MyClass, {"i": i})
# b.py:14: error: Redundant cast to "TYPEVAR_B" [redundant-cast]
# a.py:33: error: Returning Any from function declared to return "MyClass" [no-any-return]
def returns_instance_b_with_cast(i: int) -> MyClass:
return InstantiatorB.instantiate_with_cast(MyClass, {"i": i})
# a.py:37: error: Returning Any from function declared to return "MyClass" [no-any-return]
def returns_instance_b_no_params(i: int) -> MyClass:
return InstantiatorB.instantiate_no_params(MyClass)
# This works fine -- no errors!
def returns_instance_b_reassignment(i: int) -> MyClass:
b: MyClass = InstantiatorB.instantiate_no_params(MyClass)
return b
# a.py:47: error: Returning Any from function declared to return "MyClass" [no-any-return]
def returns_instance_b_inner_reassign(i: int) -> MyClass:
return InstantiatorB.instantiate_with_reassign(MyClass)
# a.py:51: error: Returning Any from function declared to return "MyClass" [no-any-return]
def returns_instance_b_with_generic(i: int) -> MyClass:
return InstantiatorB[MyClass].instantiate(MyClass, {"i": i})File 2: b.py
from typing import Any, Dict, Generic, Type, TypeVar, cast
TYPEVAR_B = TypeVar("TYPEVAR_B")
class InstantiatorB(Generic[TYPEVAR_B]):
@classmethod
def instantiate(cls, class_to_instantiate: Type[TYPEVAR_B], x: Dict[str, Any]) -> TYPEVAR_B:
return class_to_instantiate(**x)
@classmethod
def instantiate_with_cast(cls, class_to_instantiate: Type[TYPEVAR_B], x: Dict[str, Any]) -> TYPEVAR_B:
return cast(TYPEVAR_B, class_to_instantiate(**x))
@classmethod
def instantiate_no_params(cls, class_to_instantiate: Type[TYPEVAR_B]) -> TYPEVAR_B:
return class_to_instantiate()
@classmethod
def instantiate_with_reassign(cls, class_to_instantiate: Type[TYPEVAR_B]) -> TYPEVAR_B:
result: TYPEVAR_B = class_to_instantiate()
return resultExpected Behavior
I expect that mypy would be able to correctly infer that the return type of the instantiate_* methods as it does for InstantiatorA, but for InstantiatorB it incorrectly reports Any as the return type.
I would expect that maybe passing the desired return type as a subscript was needed such as in returns_instance_b_with_generic, and that actually does result in VSCode showing the correct return type, but mypy still does not.
Actual Behavior
Errors are reported inline above.
Your Environment
- Mypy version used: 1.14.1
- Mypy command-line flags: none
- Mypy configuration options from
mypy.ini(and other config files):
# Mypy configuration equal to mypy --strict. Read more about the tools at
# https://backstage.spotify.net/docs/python/
[tool.mypy]
ignore_missing_imports = true
warn_unused_configs = true
disallow_any_generics = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
no_implicit_reexport = true
strict_equality = true
- Python version used: 3.8.12
<!-- You can freely edit this text, please remove all the lines you believe are unnecessary. -->