Skip to content

Commit 99ad742

Browse files
authored
bpo-44015: dataclasses should allow KW_ONLY to be specified only once per class (pythonGH-25841)
bpo-44015: Raise a TypeError if KW_ONLY is specified more than once.
1 parent 72720a2 commit 99ad742

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed

Lib/dataclasses.py

+5
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
930930
# we can.
931931
cls_fields = []
932932
# Get a reference to this module for the _is_kw_only() test.
933+
KW_ONLY_seen = False
933934
dataclasses = sys.modules[__name__]
934935
for name, type in cls_annotations.items():
935936
# See if this is a marker to change the value of kw_only.
@@ -939,6 +940,10 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
939940
_is_kw_only))):
940941
# Switch the default to kw_only=True, and ignore this
941942
# annotation: it's not a real field.
943+
if KW_ONLY_seen:
944+
raise TypeError(f'{name!r} is KW_ONLY, but KW_ONLY '
945+
'has already been specified')
946+
KW_ONLY_seen = True
942947
kw_only = True
943948
else:
944949
# Otherwise it's a field of some type.

Lib/test/test_dataclasses.py

+77
Original file line numberDiff line numberDiff line change
@@ -3699,6 +3699,83 @@ class C:
36993699
self.assertEqual(c.b, 3)
37003700
self.assertEqual(c.c, 2)
37013701

3702+
def test_KW_ONLY_as_string(self):
3703+
@dataclass
3704+
class A:
3705+
a: int
3706+
_: 'dataclasses.KW_ONLY'
3707+
b: int
3708+
c: int
3709+
A(3, c=5, b=4)
3710+
msg = "takes 2 positional arguments but 4 were given"
3711+
with self.assertRaisesRegex(TypeError, msg):
3712+
A(3, 4, 5)
3713+
3714+
def test_KW_ONLY_twice(self):
3715+
msg = "'Y' is KW_ONLY, but KW_ONLY has already been specified"
3716+
3717+
with self.assertRaisesRegex(TypeError, msg):
3718+
@dataclass
3719+
class A:
3720+
a: int
3721+
X: KW_ONLY
3722+
Y: KW_ONLY
3723+
b: int
3724+
c: int
3725+
3726+
with self.assertRaisesRegex(TypeError, msg):
3727+
@dataclass
3728+
class A:
3729+
a: int
3730+
X: KW_ONLY
3731+
b: int
3732+
Y: KW_ONLY
3733+
c: int
3734+
3735+
with self.assertRaisesRegex(TypeError, msg):
3736+
@dataclass
3737+
class A:
3738+
a: int
3739+
X: KW_ONLY
3740+
b: int
3741+
c: int
3742+
Y: KW_ONLY
3743+
3744+
# But this usage is okay, since it's not using KW_ONLY.
3745+
@dataclass
3746+
class A:
3747+
a: int
3748+
_: KW_ONLY
3749+
b: int
3750+
c: int = field(kw_only=True)
3751+
3752+
# And if inheriting, it's okay.
3753+
@dataclass
3754+
class A:
3755+
a: int
3756+
_: KW_ONLY
3757+
b: int
3758+
c: int
3759+
@dataclass
3760+
class B(A):
3761+
_: KW_ONLY
3762+
d: int
3763+
3764+
# Make sure the error is raised in a derived class.
3765+
with self.assertRaisesRegex(TypeError, msg):
3766+
@dataclass
3767+
class A:
3768+
a: int
3769+
_: KW_ONLY
3770+
b: int
3771+
c: int
3772+
@dataclass
3773+
class B(A):
3774+
X: KW_ONLY
3775+
d: int
3776+
Y: KW_ONLY
3777+
3778+
37023779
def test_post_init(self):
37033780
@dataclass
37043781
class A:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
In @dataclass(), raise a TypeError if KW_ONLY is specified more than once.

0 commit comments

Comments
 (0)