]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-44015: dataclasses should allow KW_ONLY to be specified only once per class ...
authorEric V. Smith <ericvsmith@users.noreply.github.com>
Mon, 3 May 2021 07:24:53 +0000 (03:24 -0400)
committerGitHub <noreply@github.com>
Mon, 3 May 2021 07:24:53 +0000 (03:24 -0400)
bpo-44015: Raise a TypeError if KW_ONLY is specified more than once.

Lib/dataclasses.py
Lib/test/test_dataclasses.py
Misc/NEWS.d/next/Library/2021-05-03-03-03-49.bpo-44015.V5936k.rst [new file with mode: 0644]

index 363d0b66d208e4db3decd1fd6012cd63736f7eec..cbba320e01a54d6919f95f99a3aa08daddb00149 100644 (file)
@@ -930,6 +930,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
     # we can.
     cls_fields = []
     # Get a reference to this module for the _is_kw_only() test.
+    KW_ONLY_seen = False
     dataclasses = sys.modules[__name__]
     for name, type in cls_annotations.items():
         # 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,
                              _is_kw_only))):
             # Switch the default to kw_only=True, and ignore this
             # annotation: it's not a real field.
+            if KW_ONLY_seen:
+                raise TypeError(f'{name!r} is KW_ONLY, but KW_ONLY '
+                                'has already been specified')
+            KW_ONLY_seen = True
             kw_only = True
         else:
             # Otherwise it's a field of some type.
index 16ee4c7705d8cc8fa7fd60eeb6c50cc1ec73a4c8..8e645aeb4a75030f5940ea3075f78cf57003585a 100644 (file)
@@ -3699,6 +3699,83 @@ class TestKeywordArgs(unittest.TestCase):
         self.assertEqual(c.b, 3)
         self.assertEqual(c.c, 2)
 
+    def test_KW_ONLY_as_string(self):
+        @dataclass
+        class A:
+            a: int
+            _: 'dataclasses.KW_ONLY'
+            b: int
+            c: int
+        A(3, c=5, b=4)
+        msg = "takes 2 positional arguments but 4 were given"
+        with self.assertRaisesRegex(TypeError, msg):
+            A(3, 4, 5)
+
+    def test_KW_ONLY_twice(self):
+        msg = "'Y' is KW_ONLY, but KW_ONLY has already been specified"
+
+        with self.assertRaisesRegex(TypeError, msg):
+            @dataclass
+            class A:
+                a: int
+                X: KW_ONLY
+                Y: KW_ONLY
+                b: int
+                c: int
+
+        with self.assertRaisesRegex(TypeError, msg):
+            @dataclass
+            class A:
+                a: int
+                X: KW_ONLY
+                b: int
+                Y: KW_ONLY
+                c: int
+
+        with self.assertRaisesRegex(TypeError, msg):
+            @dataclass
+            class A:
+                a: int
+                X: KW_ONLY
+                b: int
+                c: int
+                Y: KW_ONLY
+
+        # But this usage is okay, since it's not using KW_ONLY.
+        @dataclass
+        class A:
+            a: int
+            _: KW_ONLY
+            b: int
+            c: int = field(kw_only=True)
+
+        # And if inheriting, it's okay.
+        @dataclass
+        class A:
+            a: int
+            _: KW_ONLY
+            b: int
+            c: int
+        @dataclass
+        class B(A):
+            _: KW_ONLY
+            d: int
+
+        # Make sure the error is raised in a derived class.
+        with self.assertRaisesRegex(TypeError, msg):
+            @dataclass
+            class A:
+                a: int
+                _: KW_ONLY
+                b: int
+                c: int
+            @dataclass
+            class B(A):
+                X: KW_ONLY
+                d: int
+                Y: KW_ONLY
+
+
     def test_post_init(self):
         @dataclass
         class A:
diff --git a/Misc/NEWS.d/next/Library/2021-05-03-03-03-49.bpo-44015.V5936k.rst b/Misc/NEWS.d/next/Library/2021-05-03-03-03-49.bpo-44015.V5936k.rst
new file mode 100644 (file)
index 0000000..4c4f543
--- /dev/null
@@ -0,0 +1 @@
+In @dataclass(), raise a TypeError if KW_ONLY is specified more than once.