Skip to content

Commit 1e25e05

Browse files
authored
Don't create incomplete __init__ signatures for deferred dataclasses (#7382)
1 parent f9396ef commit 1e25e05

File tree

2 files changed

+41
-5
lines changed

2 files changed

+41
-5
lines changed

mypy/plugins/dataclasses.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from collections import OrderedDict
44

5-
from typing import Dict, List, Set, Tuple
5+
from typing import Dict, List, Set, Tuple, Optional
66
from typing_extensions import Final
77

88
from mypy.nodes import (
@@ -79,7 +79,9 @@ def transform(self) -> None:
7979
ctx = self._ctx
8080
info = self._ctx.cls.info
8181
attributes = self.collect_attributes()
82-
# Check if attribute types are ready.
82+
if attributes is None:
83+
# Some definitions are not ready, defer() should be already called.
84+
return
8385
for attr in attributes:
8486
if info[attr.name].type is None:
8587
ctx.api.defer()
@@ -95,7 +97,9 @@ def transform(self) -> None:
9597
# processed them yet. In order to work around this, we can simply skip generating
9698
# __init__ if there are no attributes, because if the user truly did not define any,
9799
# then the object default __init__ with an empty signature will be present anyway.
98-
if decorator_arguments['init'] and '__init__' not in info.names and attributes:
100+
if (decorator_arguments['init'] and
101+
('__init__' not in info.names or info.names['__init__'].plugin_generated) and
102+
attributes):
99103
add_method(
100104
ctx,
101105
'__init__',
@@ -189,7 +193,7 @@ def reset_init_only_vars(self, info: TypeInfo, attributes: List[DataclassAttribu
189193
# recreate a symbol node for this attribute.
190194
lvalue.node = None
191195

192-
def collect_attributes(self) -> List[DataclassAttribute]:
196+
def collect_attributes(self) -> Optional[List[DataclassAttribute]]:
193197
"""Collect all attributes declared in the dataclass and its parents.
194198
195199
All assignments of the form
@@ -225,7 +229,7 @@ def collect_attributes(self) -> List[DataclassAttribute]:
225229
node = sym.node
226230
if isinstance(node, PlaceholderNode):
227231
# This node is not ready yet.
228-
continue
232+
return None
229233
assert isinstance(node, Var)
230234

231235
# x: ClassVar[int] is ignored by dataclasses.

test-data/unit/check-dataclasses.test

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,3 +698,35 @@ C('no') # E: Argument 1 to "C" has incompatible type "str"; expected "int"
698698
[file other.py]
699699
import lib
700700
[builtins fixtures/bool.pyi]
701+
702+
[case testDeferredDataclassInitSignature]
703+
from dataclasses import dataclass
704+
from typing import Optional, Type
705+
706+
@dataclass
707+
class C:
708+
x: Optional[int] = None
709+
y: Type[Deferred] = Deferred
710+
711+
@classmethod
712+
def default(cls) -> C:
713+
return cls(x=None, y=None)
714+
715+
class Deferred: pass
716+
[builtins fixtures/classmethod.pyi]
717+
718+
[case testDeferredDataclassInitSignatureSubclass]
719+
# flags: --strict-optional
720+
from dataclasses import dataclass
721+
from typing import Optional
722+
723+
@dataclass
724+
class B:
725+
x: Optional[C]
726+
727+
@dataclass
728+
class C(B):
729+
y: str
730+
731+
a = C(None, 'abc')
732+
[builtins fixtures/bool.pyi]

0 commit comments

Comments
 (0)