Skip to content

Commit 7389fd9

Browse files
authored
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__.
1 parent 4573820 commit 7389fd9

File tree

3 files changed

+47
-0
lines changed

3 files changed

+47
-0
lines changed

Lib/dataclasses.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,9 @@ def _get_field(cls, a_name, a_type):
519519
if isinstance(default, Field):
520520
f = default
521521
else:
522+
if isinstance(default, types.MemberDescriptorType):
523+
# This is a field in __slots__, so it has no default value.
524+
default = MISSING
522525
f = field(default=default)
523526

524527
# Assume it's a normal field until proven otherwise.

Lib/test/test_dataclasses.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2564,5 +2564,47 @@ class S(D):
25642564
self.assertEqual(s.cached, True)
25652565

25662566

2567+
class TestSlots(unittest.TestCase):
2568+
def test_simple(self):
2569+
@dataclass
2570+
class C:
2571+
__slots__ = ('x',)
2572+
x: Any
2573+
2574+
# There was a bug where a variable in a slot was assumed
2575+
# to also have a default value (of type types.MemberDescriptorType).
2576+
with self.assertRaisesRegex(TypeError,
2577+
"__init__\(\) missing 1 required positional argument: 'x'"):
2578+
C()
2579+
2580+
# We can create an instance, and assign to x.
2581+
c = C(10)
2582+
self.assertEqual(c.x, 10)
2583+
c.x = 5
2584+
self.assertEqual(c.x, 5)
2585+
2586+
# We can't assign to anything else.
2587+
with self.assertRaisesRegex(AttributeError, "'C' object has no attribute 'y'"):
2588+
c.y = 5
2589+
2590+
def test_derived_added_field(self):
2591+
# See bpo-33100.
2592+
@dataclass
2593+
class Base:
2594+
__slots__ = ('x',)
2595+
x: Any
2596+
2597+
@dataclass
2598+
class Derived(Base):
2599+
x: int
2600+
y: int
2601+
2602+
d = Derived(1, 2)
2603+
self.assertEqual((d.x, d.y), (1, 2))
2604+
2605+
# We can add a new field to the derived instance.
2606+
d.z = 10
2607+
2608+
25672609
if __name__ == '__main__':
25682610
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Dataclasses: If a field has a default value that's a MemberDescriptorType,
2+
then it's from that field being in __slots__, not an actual default value.

0 commit comments

Comments
 (0)