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

ModuleType.__annotations__ and type.__annotations__ result in AttributeError #123242

Open
ndjensen opened this issue Aug 22, 2024 · 6 comments
Open
Assignees
Labels
topic-typing type-bug An unexpected behavior, bug, or error

Comments

@ndjensen
Copy link

ndjensen commented Aug 22, 2024

Bug report

Bug description:

If you call dir() on ModuleType or type, it shows __annotations__ as one of the attributes. However, when you attempt to access that attribute, for ModuleType you get AttributeError: type object 'module' has no attribute '__annotations__' and for type you get AttributeError: type object 'type' has no attribute '__annotations__'.

>>> from types import ModuleType
>>> dir(ModuleType)
['__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> ModuleType.__annotations__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'module' has no attribute '__annotations__'
>>> 
>>> dir(type)
['__abstractmethods__', '__annotations__', '__base__', '__bases__', '__basicsize__', '__call__', '__class__', '__delattr__', '__dict__', '__dictoffset__', '__dir__', '__doc__', '__eq__', '__flags__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__instancecheck__', '__itemsize__', '__le__', '__lt__', '__module__', '__mro__', '__name__', '__ne__', '__new__', '__or__', '__prepare__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__setattr__', '__sizeof__', '__str__', '__subclasscheck__', '__subclasses__', '__subclasshook__', '__text_signature__', '__weakrefoffset__', 'mro']
>>> type.__annotations__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'type' has no attribute '__annotations__'

I found this bug because I have a method that iterates over the attributes returned from dir and gets each attribute. After upgrading from Python 3.8 to 3.11, this caused an interesting failure. I would expect that attributes returned by dir() would be accessible and not result in an AttributeError.

CPython versions tested on:

3.11, 3.13

Operating systems tested on:

Linux

@ndjensen ndjensen added the type-bug An unexpected behavior, bug, or error label Aug 22, 2024
@sobolevn
Copy link
Member

sobolevn commented Aug 23, 2024

Docs say:

With an argument, attempt to return a list of valid attributes for that object.
https://docs.python.org/3/library/functions.html#dir

So, this seems like a bug to me. __annotations__ is not a valid attribute for this object. It should not be returned.

I can see __abstractmethods__ there as well, which also raises:

>>> type.__abstractmethods__
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    type.__abstractmethods__
AttributeError: __abstractmethods__

And __annotate__ for 3.14+

@picnixz
Copy link
Contributor

picnixz commented Aug 23, 2024

Fun fact:

>>> type.__annotations__
Traceback (most recent call last):
  File "<python-input-62>", line 1, in <module>
    type.__annotations__
AttributeError: type object 'type' has no attribute '__annotations__'. Did you mean: '__annotate__'?
>>> type.__annotate__
Traceback (most recent call last):
  File "<python-input-63>", line 1, in <module>
    type.__annotate__
AttributeError: type object 'type' has no attribute '__annotate__'. Did you mean: '__annotations__'?

@picnixz
Copy link
Contributor

picnixz commented Aug 23, 2024

@sobolevn Do you want to work on it? or can I take it perhaps?

@sobolevn
Copy link
Member

@picnixz sure, go ahead!

@picnixz picnixz self-assigned this Aug 23, 2024
@picnixz
Copy link
Contributor

picnixz commented Aug 23, 2024

Ok, I see what happens:

static PyObject *
type_get_annotations(PyTypeObject *type, void *context)
{
    if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
        PyErr_Format(PyExc_AttributeError, "type object '%s' has no attribute '__annotations__'", type->tp_name);
        return NULL;
    }
	...
}

So the attribute does exist. It's just that it immediately raises an error. The reason why it's being listed in __dir__ is that it's actually set in the __dict__. So the question is: is it really a bug or not? it results in an AttributeError, yes, but maybe we could return an empty dict in the case of type? or maybe change the error message? (and for non-heap types, we would just return empty dicts everytime, but still disallow to set __annotations__).

We could outright filter the output of type___dir___impl by rejecting __annotations__ and __annotate__ if we are dealing with a heap type, but only if a parent does not already have __annotations__.

cc @JelleZijlstra

@JelleZijlstra
Copy link
Member

I don't think the issue reported here is severe enough to justify changing this behavior. As Nikita noted, type.__abstractmethods__ behaves similarly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic-typing type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

4 participants