diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 0f7dc9ae6b82f5..d1d217605e42db 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -409,12 +409,21 @@ def field(*, default=MISSING, default_factory=MISSING, init=True, repr=True, def _fields_in_init_order(fields): - # Returns the fields as __init__ will output them. It returns 2 tuples: - # the first for normal args, and the second for keyword args. + required_fields = [] + optional_fields = [] + kw_fields = [] + for f in fields: + if not f.init: + continue + if f.kw_only: + kw_fields.append(f) + elif f.default is MISSING and f.default_factory is MISSING: + required_fields.append(f) + else: + optional_fields.append(f) + + return tuple(required_fields + optional_fields), tuple(kw_fields) - return (tuple(f for f in fields if f.init and not f.kw_only), - tuple(f for f in fields if f.init and f.kw_only) - ) def _tuple_str(obj_name, fields): diff --git a/Lib/test/test_dataclass.py b/Lib/test/test_dataclass.py new file mode 100644 index 00000000000000..e39aa46c376858 --- /dev/null +++ b/Lib/test/test_dataclass.py @@ -0,0 +1,26 @@ +import unittest +from dataclasses import dataclass + +class TestDataclassInheritanceFieldOrder(unittest.TestCase): + def test_required_after_optional_in_subclass(self): + @dataclass + class Base: + x: int = 10 # optional field with default + + @dataclass + class Sub(Base): + y: int # required field in subclass + + # Should require y but allow optional x to be omitted + obj = Sub(y=5) + self.assertEqual(obj.x, 10) + self.assertEqual(obj.y, 5) + + # Should allow overriding x + obj2 = Sub(x=42, y=9) + self.assertEqual(obj2.x, 42) + self.assertEqual(obj2.y, 9) + + # Missing y should raise TypeError + with self.assertRaises(TypeError): + Sub()