Skip to content

Commit 6bb75f8

Browse files
hynekJelleZijlstra
andcommitted
Fix slotted reference cycles on 3.14
Ref python/cpython#136893 & python/cpython#135228 Co-authored-by: Jelle Zijlstra <906600+JelleZijlstra@users.noreply.github.com>
1 parent 69541ab commit 6bb75f8

File tree

2 files changed

+20
-2
lines changed

2 files changed

+20
-2
lines changed

src/attr/_make.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
PY_3_10_PLUS,
2525
PY_3_11_PLUS,
2626
PY_3_13_PLUS,
27+
PY_3_14_PLUS,
2728
_AnnotationExtractor,
2829
_get_annotations,
2930
get_generic_base,
@@ -618,6 +619,17 @@ def evolve(*args, **changes):
618619
return cls(**changes)
619620

620621

622+
# Hack to the get the underlying dict out of a mappingproxy
623+
# Use it with: cls.__dict__ | _deproxier
624+
# See: https://github.com/python/cpython/pull/136893
625+
class _Deproxier:
626+
def __ror__(self, other):
627+
return other
628+
629+
630+
_deproxier = _Deproxier()
631+
632+
621633
class _ClassBuilder:
622634
"""
623635
Iteratively build *one* class.
@@ -845,6 +857,13 @@ def _create_slots_class(self):
845857
if k not in (*tuple(self._attr_names), "__dict__", "__weakref__")
846858
}
847859

860+
if PY_3_14_PLUS:
861+
# Clean up old dict to avoid leaks.
862+
old_cls_dict = self._cls.__dict__ | _deproxier
863+
old_cls_dict.pop("__dict__", None)
864+
if "__weakref__" in self._cls.__dict__:
865+
del self._cls.__weakref__
866+
848867
# If our class doesn't have its own implementation of __setattr__
849868
# (either from the user or by us), check the bases, if one of them has
850869
# an attrs-made __setattr__, that needs to be reset. We don't walk the

tests/test_make.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import attr
2424

2525
from attr import _config
26-
from attr._compat import PY_3_10_PLUS, PY_3_14_PLUS
26+
from attr._compat import PY_3_10_PLUS
2727
from attr._make import (
2828
Attribute,
2929
Factory,
@@ -1896,7 +1896,6 @@ class C2(C):
18961896

18971897
assert [C2] == C.__subclasses__()
18981898

1899-
@pytest.mark.xfail(PY_3_14_PLUS, reason="Currently broken on nightly.")
19001899
def test_no_references_to_original_when_using_cached_property(self):
19011900
"""
19021901
When subclassing a slotted class and using cached property, there are

0 commit comments

Comments
 (0)