Skip to content

Conversation

@encukou
Copy link
Member

@encukouencukou commented Jul 22, 2025

Here's another possible way to solve #135228:
The __dict__ and __weakref__ descriptors don't really need to hold a strong reference to "their" type; they are generic and could be created for object instead.
Here's a proof of concept. A cleaner way to do this would be creating the descriptor objects once and caching them in the interpreter state.

This does involve a behaviour change -- before:

>>>classC: pass>>>C.__dict__['__dict__'] <attribute'__dict__'of'C'objects>>>>C.__dict__['__dict__'].__objclass__<class'__main__.C'>

After:

>>> class C: pass >>> C.__dict__['__dict__'] <attribute '__dict__' of 'object' objects> >>> C.__dict__['__dict__'].__objclass__ <class 'object'> 

JelleZijlstraand others added 5 commits July 20, 2025 16:57
…ke the original class collectible An interesting hack, but more localized in scope than python#135230. This may be a breaking change if people intentionally keep the original class around when using `@dataclass(slots=True)`, and then use `__dict__` or `__weakref__` on the original class.
@encukou
Copy link
MemberAuthor

I can polish this, but I think it'd be better to target 3.15, and follow through a bit more.
The descriptor objects themselves could be shared across all classes that need them (within an interpreter), but it's not as straightforward to plug that into the type creation machinery.

@encukou
Copy link
MemberAuthor

Here's a polished version: the descriptors are made for type, which means thet skip type-checking (they work on any object). The getter/setter functions already check if their argument has a __dict__/__weakref__.

And since they're the same for all objects, they can be shared across all types in an interpreter -- that is, cached in interpreter state.

@encukou
Copy link
MemberAuthor

This now partially reverts #137047. I don't think keeping sys._clear_type_descriptors is worth it. If removed, power users can test its presence to see whether it's needed.

Copy link
Member

@JelleZijlstraJelleZijlstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is a nice improvement! I think there's risk this will break some advanced introspection code, similar to the change you had to make to inspect, but if it goes in early in the 3.15 cycle, that should be OK.

@serhiy-storchaka
Copy link
Member

I suggest to wait some time, maybe until 3.14.1, so we can have chance to get some feedback about an alternate approach and decide whether it is worth to backport this approach to 3.14. We may also find completely different solution of the original problem in 3.15.

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
@encukou
Copy link
MemberAuthor

I suggest to wait some time [before backporting]

Definitely! Personally, I don't think this should be backported at all. (But I wouldn't have backported #137047 to a RC either if that was up to me...)

@encukouencukou merged commit 7dfa048 into python:mainAug 18, 2025
44 checks passed
@encukouencukou deleted the hackweakref-descr branch August 18, 2025 12:25
Agent-Hellboy pushed a commit to Agent-Hellboy/cpython that referenced this pull request Aug 19, 2025
…ct (pythonGH-136966) This partially reverts python#137047, keeping the tests for GC collectability of the original class that dataclass adds `__slots__` to. The reference leaks solved there are instead solved by having the `__dict__` & `__weakref__` descriptors not tied to (and referencing) their class. Instead, they're shared between all classes that need them (within an interpreter). The `__objclass__` ol the descriptors is set to `object`, since these descriptors work with *any* object. (The appropriate checks were already made in the get/set code, so the `__objclass__` check was redundant.) The repr of these descriptors (and any others whose `__objclass__` is `object`) now doesn't mention the objclass. This change required adjustment of introspection code that checks `__objclass__` to determine an object's “own” (i.e. not inherited) `__dict__`. Third-party code that does similar introspection of the internals will also need adjusting. Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@encukou@serhiy-storchaka@JelleZijlstra@ZeroIntensity