summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric V. Smith <ericvsmith@users.noreply.github.com>2018-03-20 01:07:51 (GMT)
committerGitHub <noreply@github.com>2018-03-20 01:07:51 (GMT)
commit7389fd935c95b4b6f094312294e703ee0de18719 (patch)
treedb6bb8cbf62b96d09d532d21f3bd939e1b64e8b0
parent4573820d2a9156346392838d455e89f33067e9dd (diff)
downloadcpython-7389fd935c95b4b6f094312294e703ee0de18719.zip
cpython-7389fd935c95b4b6f094312294e703ee0de18719.tar.gz
cpython-7389fd935c95b4b6f094312294e703ee0de18719.tar.bz2
bpo-33100: Dataclasses now handles __slots__ and default values correctly. (GH-6152)
If the class has a member that's a MemberDescriptorType, it's not a default value, it's from that member being in __slots__.
-rw-r--r--Lib/dataclasses.py3
-rwxr-xr-xLib/test/test_dataclasses.py42
-rw-r--r--Misc/NEWS.d/next/Library/2018-03-19-20-47-00.bpo-33100.chyIO4.rst2
3 files changed, 47 insertions, 0 deletions
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index 8ab04dd..a4afd50 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -519,6 +519,9 @@ def _get_field(cls, a_name, a_type):
if isinstance(default, Field):
f = default
else:
+ if isinstance(default, types.MemberDescriptorType):
+ # This is a field in __slots__, so it has no default value.
+ default = MISSING
f = field(default=default)
# Assume it's a normal field until proven otherwise.
diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py
index 3e67263..db03ec1 100755
--- a/Lib/test/test_dataclasses.py
+++ b/Lib/test/test_dataclasses.py
@@ -2564,5 +2564,47 @@ class TestFrozen(unittest.TestCase):
self.assertEqual(s.cached, True)
+class TestSlots(unittest.TestCase):
+ def test_simple(self):
+ @dataclass
+ class C:
+ __slots__ = ('x',)
+ x: Any
+
+ # There was a bug where a variable in a slot was assumed
+ # to also have a default value (of type types.MemberDescriptorType).
+ with self.assertRaisesRegex(TypeError,
+ "__init__\(\) missing 1 required positional argument: 'x'"):
+ C()
+
+ # We can create an instance, and assign to x.
+ c = C(10)
+ self.assertEqual(c.x, 10)
+ c.x = 5
+ self.assertEqual(c.x, 5)
+
+ # We can't assign to anything else.
+ with self.assertRaisesRegex(AttributeError, "'C' object has no attribute 'y'"):
+ c.y = 5
+
+ def test_derived_added_field(self):
+ # See bpo-33100.
+ @dataclass
+ class Base:
+ __slots__ = ('x',)
+ x: Any
+
+ @dataclass
+ class Derived(Base):
+ x: int
+ y: int
+
+ d = Derived(1, 2)
+ self.assertEqual((d.x, d.y), (1, 2))
+
+ # We can add a new field to the derived instance.
+ d.z = 10
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2018-03-19-20-47-00.bpo-33100.chyIO4.rst b/Misc/NEWS.d/next/Library/2018-03-19-20-47-00.bpo-33100.chyIO4.rst
new file mode 100644
index 0000000..080a55c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-03-19-20-47-00.bpo-33100.chyIO4.rst
@@ -0,0 +1,2 @@
+Dataclasses: If a field has a default value that's a MemberDescriptorType,
+then it's from that field being in __slots__, not an actual default value.