mirror of
https://github.com/python/cpython.git
synced 2025-09-03 15:31:08 +00:00
gh-109409: Fix inheritance of frozen dataclass from non-frozen dataclass mixins (gh-109437)
Fix inheritance of frozen dataclass from non-frozen dataclass mixins
This commit is contained in:
parent
7dd3c2b800
commit
b6000d2874
3 changed files with 107 additions and 4 deletions
|
@ -944,8 +944,11 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
|
||||||
# Find our base classes in reverse MRO order, and exclude
|
# Find our base classes in reverse MRO order, and exclude
|
||||||
# ourselves. In reversed order so that more derived classes
|
# ourselves. In reversed order so that more derived classes
|
||||||
# override earlier field definitions in base classes. As long as
|
# override earlier field definitions in base classes. As long as
|
||||||
# we're iterating over them, see if any are frozen.
|
# we're iterating over them, see if all or any of them are frozen.
|
||||||
any_frozen_base = False
|
any_frozen_base = False
|
||||||
|
# By default `all_frozen_bases` is `None` to represent a case,
|
||||||
|
# where some dataclasses does not have any bases with `_FIELDS`
|
||||||
|
all_frozen_bases = None
|
||||||
has_dataclass_bases = False
|
has_dataclass_bases = False
|
||||||
for b in cls.__mro__[-1:0:-1]:
|
for b in cls.__mro__[-1:0:-1]:
|
||||||
# Only process classes that have been processed by our
|
# Only process classes that have been processed by our
|
||||||
|
@ -955,8 +958,11 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
|
||||||
has_dataclass_bases = True
|
has_dataclass_bases = True
|
||||||
for f in base_fields.values():
|
for f in base_fields.values():
|
||||||
fields[f.name] = f
|
fields[f.name] = f
|
||||||
if getattr(b, _PARAMS).frozen:
|
if all_frozen_bases is None:
|
||||||
any_frozen_base = True
|
all_frozen_bases = True
|
||||||
|
current_frozen = getattr(b, _PARAMS).frozen
|
||||||
|
all_frozen_bases = all_frozen_bases and current_frozen
|
||||||
|
any_frozen_base = any_frozen_base or current_frozen
|
||||||
|
|
||||||
# Annotations defined specifically in this class (not in base classes).
|
# Annotations defined specifically in this class (not in base classes).
|
||||||
#
|
#
|
||||||
|
@ -1025,7 +1031,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
|
||||||
'frozen one')
|
'frozen one')
|
||||||
|
|
||||||
# Raise an exception if we're frozen, but none of our bases are.
|
# Raise an exception if we're frozen, but none of our bases are.
|
||||||
if not any_frozen_base and frozen:
|
if all_frozen_bases is False and frozen:
|
||||||
raise TypeError('cannot inherit frozen dataclass from a '
|
raise TypeError('cannot inherit frozen dataclass from a '
|
||||||
'non-frozen one')
|
'non-frozen one')
|
||||||
|
|
||||||
|
|
|
@ -2863,6 +2863,101 @@ class TestFrozen(unittest.TestCase):
|
||||||
class D(C):
|
class D(C):
|
||||||
j: int
|
j: int
|
||||||
|
|
||||||
|
def test_inherit_frozen_mutliple_inheritance(self):
|
||||||
|
@dataclass
|
||||||
|
class NotFrozen:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Frozen:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class NotDataclass:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for bases in (
|
||||||
|
(NotFrozen, Frozen),
|
||||||
|
(Frozen, NotFrozen),
|
||||||
|
(Frozen, NotDataclass),
|
||||||
|
(NotDataclass, Frozen),
|
||||||
|
):
|
||||||
|
with self.subTest(bases=bases):
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
TypeError,
|
||||||
|
'cannot inherit non-frozen dataclass from a frozen one',
|
||||||
|
):
|
||||||
|
@dataclass
|
||||||
|
class NotFrozenChild(*bases):
|
||||||
|
pass
|
||||||
|
|
||||||
|
for bases in (
|
||||||
|
(NotFrozen, Frozen),
|
||||||
|
(Frozen, NotFrozen),
|
||||||
|
(NotFrozen, NotDataclass),
|
||||||
|
(NotDataclass, NotFrozen),
|
||||||
|
):
|
||||||
|
with self.subTest(bases=bases):
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
TypeError,
|
||||||
|
'cannot inherit frozen dataclass from a non-frozen one',
|
||||||
|
):
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class FrozenChild(*bases):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_inherit_frozen_mutliple_inheritance_regular_mixins(self):
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Frozen:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class NotDataclass:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class C1(Frozen, NotDataclass):
|
||||||
|
pass
|
||||||
|
self.assertEqual(C1.__mro__, (C1, Frozen, NotDataclass, object))
|
||||||
|
|
||||||
|
class C2(NotDataclass, Frozen):
|
||||||
|
pass
|
||||||
|
self.assertEqual(C2.__mro__, (C2, NotDataclass, Frozen, object))
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class C3(Frozen, NotDataclass):
|
||||||
|
pass
|
||||||
|
self.assertEqual(C3.__mro__, (C3, Frozen, NotDataclass, object))
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class C4(NotDataclass, Frozen):
|
||||||
|
pass
|
||||||
|
self.assertEqual(C4.__mro__, (C4, NotDataclass, Frozen, object))
|
||||||
|
|
||||||
|
def test_multiple_frozen_dataclasses_inheritance(self):
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class FrozenA:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class FrozenB:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class C1(FrozenA, FrozenB):
|
||||||
|
pass
|
||||||
|
self.assertEqual(C1.__mro__, (C1, FrozenA, FrozenB, object))
|
||||||
|
|
||||||
|
class C2(FrozenB, FrozenA):
|
||||||
|
pass
|
||||||
|
self.assertEqual(C2.__mro__, (C2, FrozenB, FrozenA, object))
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class C3(FrozenA, FrozenB):
|
||||||
|
pass
|
||||||
|
self.assertEqual(C3.__mro__, (C3, FrozenA, FrozenB, object))
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class C4(FrozenB, FrozenA):
|
||||||
|
pass
|
||||||
|
self.assertEqual(C4.__mro__, (C4, FrozenB, FrozenA, object))
|
||||||
|
|
||||||
def test_inherit_nonfrozen_from_empty(self):
|
def test_inherit_nonfrozen_from_empty(self):
|
||||||
@dataclass
|
@dataclass
|
||||||
class C:
|
class C:
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix error when it was possible to inherit a frozen dataclass from multiple
|
||||||
|
parents some of which were possibly not frozen.
|
Loading…
Add table
Add a link
Reference in a new issue