Skip to content
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

Fix types of inherited attributes in generic dataclasses #12656

Merged
merged 4 commits into from Apr 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 8 additions & 5 deletions mypy/plugins/dataclasses.py
Expand Up @@ -19,6 +19,7 @@
get_proper_type, AnyType, TypeOfAny,
)
from mypy.server.trigger import make_wildcard_trigger
from mypy.state import state

# The set of decorators that generate dataclasses.
dataclass_makers: Final = {
Expand Down Expand Up @@ -101,10 +102,8 @@ def deserialize(
def expand_typevar_from_subtype(self, sub_type: TypeInfo) -> None:
"""Expands type vars in the context of a subtype when an attribute is inherited
from a generic super type."""
if not isinstance(self.type, TypeVarType):
return

self.type = map_type_from_supertype(self.type, sub_type, self.info)
if self.type is not None:
self.type = map_type_from_supertype(self.type, sub_type, self.info)


class DataclassTransformer:
Expand Down Expand Up @@ -402,7 +401,11 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]:
name: str = data["name"]
if name not in known_attrs:
attr = DataclassAttribute.deserialize(info, data, ctx.api)
attr.expand_typevar_from_subtype(ctx.cls.info)
# TODO: We shouldn't be performing type operations during the main
# semantic analysis pass, since some TypeInfo attributes might
# still be in flux. This should be performed in a later phase.
with state.strict_optional_set(ctx.api.options.strict_optional):
attr.expand_typevar_from_subtype(ctx.cls.info)
known_attrs.add(name)
super_attrs.append(attr)
elif all_attrs:
Expand Down
51 changes: 51 additions & 0 deletions test-data/unit/check-dataclasses.test
Expand Up @@ -1568,3 +1568,54 @@ class B:
class Derived(A, B):
pass
[builtins fixtures/dataclasses.pyi]

[case testDataclassGenericInheritance2]
# flags: --python-version 3.7
from dataclasses import dataclass
from typing import Any, Callable, Generic, TypeVar, List

T = TypeVar("T")
S = TypeVar("S")

@dataclass
class Parent(Generic[T]):
f: Callable[[T], Any]

@dataclass
class Child(Parent[T]): ...

class A: ...
def func(obj: A) -> bool: ...

reveal_type(Child[A](func).f) # N: Revealed type is "def (__main__.A) -> Any"

@dataclass
class Parent2(Generic[T]):
a: List[T]

@dataclass
class Child2(Generic[T, S], Parent2[S]):
b: List[T]

reveal_type(Child2([A()], [1]).a) # N: Revealed type is "builtins.list[__main__.A]"
reveal_type(Child2[int, A]([A()], [1]).b) # N: Revealed type is "builtins.list[builtins.int]"
[builtins fixtures/dataclasses.pyi]

[case testDataclassInheritOptionalType]
# flags: --python-version 3.7 --strict-optional
from dataclasses import dataclass
from typing import Any, Callable, Generic, TypeVar, List, Optional

T = TypeVar("T")

@dataclass
class Parent(Generic[T]):
x: Optional[str]
@dataclass
class Child(Parent):
y: Optional[int]
Child(x=1, y=1) # E: Argument "x" to "Child" has incompatible type "int"; expected "Optional[str]"
Child(x='', y='') # E: Argument "y" to "Child" has incompatible type "str"; expected "Optional[int]"
Child(x='', y=1)
Child(x=None, y=None)
[builtins fixtures/dataclasses.pyi]