Skip to content

Commit ceef876

Browse files
committed
Kwonly attributes are moved to the end in all cases, this behaviour does not depend on parent classes...
1 parent daf8922 commit ceef876

File tree

2 files changed

+25
-11
lines changed

2 files changed

+25
-11
lines changed

mypy/plugins/dataclasses.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,11 @@ def transform(self) -> bool:
263263
args = [
264264
attr.to_argument(info, of="__init__")
265265
for attr in attributes
266-
if attr.is_in_init and not self._is_kw_only_type(attr.type)
266+
if attr.is_in_init and not self._is_kw_only_type(attr.type) and not attr.kw_only
267+
] + [
268+
attr.to_argument(info, of="__init__")
269+
for attr in attributes
270+
if attr.is_in_init and not self._is_kw_only_type(attr.type) and attr.kw_only
267271
]
268272

269273
if info.fallback_to_any:
@@ -546,7 +550,6 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
546550
# in the parent. We can implement this via a dict without disrupting the attr order
547551
# because dicts preserve insertion order in Python 3.7+.
548552
found_attrs: dict[str, DataclassAttribute] = {}
549-
found_dataclass_supertype = False
550553
for info in reversed(cls.info.mro[1:-1]):
551554
if "dataclass_tag" in info.metadata and "dataclass" not in info.metadata:
552555
# We haven't processed the base class yet. Need another pass.
@@ -556,7 +559,6 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
556559

557560
# Each class depends on the set of attributes in its dataclass ancestors.
558561
self._api.add_plugin_dependency(make_wildcard_trigger(info.fullname))
559-
found_dataclass_supertype = True
560562

561563
for data in info.metadata["dataclass"]["attributes"]:
562564
name: str = data["name"]
@@ -720,8 +722,7 @@ def collect_attributes(self) -> list[DataclassAttribute] | None:
720722
)
721723

722724
all_attrs = list(found_attrs.values())
723-
if found_dataclass_supertype:
724-
all_attrs.sort(key=lambda a: a.kw_only)
725+
all_attrs.sort(key=lambda a: a.kw_only)
725726

726727
# Third, ensure that arguments without a default don't follow
727728
# arguments that have a default and that the KW_ONLY sentinel

test-data/unit/check-dataclasses.test

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -460,14 +460,16 @@ from dataclasses import dataclass, field, KW_ONLY
460460
class Application:
461461
_: KW_ONLY
462462
name: str = 'Unnamed'
463-
rating: int = field(kw_only=False) # E: Attributes without a default cannot follow attributes with one
463+
rating: int = field(kw_only=False)
464464

465465
Application(name='name', rating=5)
466-
Application() # E: Missing positional argument "name" in call to "Application"
467-
Application('name') # E: Too many positional arguments for "Application" # E: Too few arguments for "Application"
468-
Application('name', 123) # E: Too many positional arguments for "Application"
469-
Application('name', rating=123) # E: Too many positional arguments for "Application"
470-
466+
Application() # E: Missing positional argument "rating" in call to "Application"
467+
Application(123)
468+
Application('name') # E: Argument 1 to "Application" has incompatible type "str"; expected "int"
469+
Application('name', 123) # E: Too many positional arguments for "Application" \
470+
# E: Argument 1 to "Application" has incompatible type "str"; expected "int" \
471+
# E: Argument 2 to "Application" has incompatible type "int"; expected "str"
472+
Application(123, rating=123) # E: "Application" gets multiple values for keyword argument "rating"
471473
[builtins fixtures/dataclasses.pyi]
472474

473475
[case testDataclassesOrderingKwOnlyWithSentinelAndSubclass]
@@ -2610,3 +2612,14 @@ class B2(B1): # E: A NamedTuple cannot be a dataclass
26102612
pass
26112613

26122614
[builtins fixtures/tuple.pyi]
2615+
2616+
[case testDataclassKwOnlyArgsLast]
2617+
from dataclasses import dataclass, field
2618+
2619+
@dataclass
2620+
class User:
2621+
id: int = field(kw_only=True)
2622+
name: str
2623+
2624+
User("Foo", id=0)
2625+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)