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:
Nikita Sobolev 2023-10-12 16:05:23 +03:00 committed by GitHub
parent 7dd3c2b800
commit b6000d2874
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 107 additions and 4 deletions

View File

@ -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
# ourselves. In reversed order so that more derived classes
# 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
# 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
for b in cls.__mro__[-1:0:-1]:
# 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
for f in base_fields.values():
fields[f.name] = f
if getattr(b, _PARAMS).frozen:
any_frozen_base = True
if all_frozen_bases is None:
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).
#
@ -1025,7 +1031,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
'frozen one')
# 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 '
'non-frozen one')

View File

@ -2863,6 +2863,101 @@ class TestFrozen(unittest.TestCase):
class D(C):
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):
@dataclass
class C:

View File

@ -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.