Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ABCMeta adds __weakref__ but not to __slots__ #128866

Open
barakatzir opened this issue Jan 15, 2025 · 3 comments
Open

ABCMeta adds __weakref__ but not to __slots__ #128866

barakatzir opened this issue Jan 15, 2025 · 3 comments
Labels
extension-modules C modules in the Modules dir stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@barakatzir
Copy link

barakatzir commented Jan 15, 2025

Bug report

Bug description:

I stumbled upon the following error (code snippet from python 3.12.3 with gcc 13.2.0, but I also checked with 3.11 and 3.13):

>> from abc import ABCMeta
>>> class Abstract(metaclass=ABCMeta):
...  ...
... 
>>> class Concrete(Abstract):
...  __slots__ = ('__weakref__',)
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<frozen abc>", line 106, in __new__
TypeError: __weakref__ slot disallowed: we already got one

This also happens when inheriting from abc.ABC. This surprised me since the documentation does not mention __weakref__ being added.
Adding to my confusion, the common abstract base classes from collections.abc do not have this behavior (I only checked Mapping, Sequence and Collection), i.e., __weakref__ attribute is not added to the class.

Another peculiarity here is that while a class with metaclass =ABCMeta is automatically weakref-able, and has a __weakref__ attribute, the __weakref__ cannot be found in its __slots__, and in fact there might not even be a __slots__ attribute anywhere in the MRO!

>>> import abc
>>> class A(abc.ABC): ...
... 
>>> A.__weakref__
<attribute '__weakref__' of 'A' objects>
>>> A.__slots__  # __slots__ attribute exist but no __weakref__
()
>>> class B(metaclass=abc.ABCMeta): ...
... 
>>> B.__weakref__
<attribute '__weakref__' of 'B' objects>
>>> B.__slots__  # __slots__ attribute does not exist
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'B' has no attribute '__slots__'. Did you mean: '__class__'?

At the very least this warrant adding documentation to abc.ABC and abc.ABCMeta. A better fix IMO is adding __weakref__ to __slots__.
Another solution (which I like the most) is not adding __weakref__ to abstract classes to begin with, but I suspect this might break some code.

CPython versions tested on:

3.11, 3.12, 3.13

Operating systems tested on:

Linux

@barakatzir barakatzir added the type-bug An unexpected behavior, bug, or error label Jan 15, 2025
@sobolevn
Copy link
Member

I am pretty sure that it is too late to "fix" this, a lot of users already adapted to the current behavior. Correct me if I am wrong, but I feel like that the only thing we can do here is to improve the docs.

@picnixz picnixz added stdlib Python modules in the Lib dir extension-modules C modules in the Modules dir labels Jan 15, 2025
@barakatzir
Copy link
Author

The simplest thing to do is improve the docs.

Another option is to make ABCMeta add slots if it doesn't exist and add weakref to it. This is a breaking change, but I don't think that it is a big one.

The third option ai mentioned of not adding weakref to begin with sound like too much of a change indeed.

Originally I thought that rhis behavior causes a bug in dataclasses with weakref_slot=True, so I opened a bug and not a documentation change. But, after quick testing it seemed to that dataclasses handles this by looking for either slots or weakref in entire mro. I forgot to change this to a documentation change.

@picnixz
Copy link
Member

picnixz commented Jan 15, 2025

But, after quick testing it seemed to that dataclasses handles this by looking for either slots or weakref in entire mro.

Yes, dataclasses handles this discrepency.

Adding to my confusion, the common abstract base classes from collections.abc do not have this behavior (I only checked Mapping, Sequence and Collection), i.e., weakref attribute is not added to the class.

Isn't it because we explicitly have __slots__ = ()? The data model says:

Without a weakref variable for each instance, classes defining slots do not support weak references to its instances. If weak reference support is needed, then add 'weakref' to the sequence of strings in the slots declaration.

Or am I missing something?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
extension-modules C modules in the Modules dir stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

3 participants