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

Closed
ndjensen opened this issue Aug 22, 2024 · 7 comments · Fixed by #124557
Closed

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

ndjensen opened this issue Aug 22, 2024 · 7 comments · Fixed by #124557
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

Linked PRs

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

@picnixz
Copy link
Contributor

picnixz commented Aug 24, 2024

Well.. maybe amend the docs a little? or at least the error message? (though I don't have an better error message for that). Otherwise, we should close the issue. For beginners and for introspection tools, it could be undesirable (in Sphinx, I think we do getattr(obj, name, sentinel) even if we iterate over the names returned by dir but I should check that's indeed the case, otherwise we could have surprises).

@picnixz picnixz removed their assignment Aug 24, 2024
JelleZijlstra added a commit to JelleZijlstra/cpython that referenced this issue Sep 25, 2024
Closes python#123242. The real criterion is that the attribute does not
exist on heap types, but I don't think we should discuss heap vs.
static types in the language reference.
JelleZijlstra added a commit to JelleZijlstra/cpython that referenced this issue Sep 25, 2024
…ythonGH-124557)

Closes pythonGH-123242. The real criterion is that the attribute does not
exist on heap types, but I don't think we should discuss heap vs.
static types in the language reference.
(cherry picked from commit 99b23c6)

Co-authored-by: Jelle Zijlstra <[email protected]>
JelleZijlstra added a commit that referenced this issue Sep 25, 2024
…4557) (#124562)

Closes GH-123242. The real criterion is that the attribute does not
exist on heap types, but I don't think we should discuss heap vs.
static types in the language reference.
(cherry picked from commit 99b23c6)
emilyemorehouse added a commit to lysnikolaou/cpython that referenced this issue Sep 26, 2024
* main: (69 commits)
  Add "annotate" SET_FUNCTION_ATTRIBUTE bit to dis. (python#124566)
  pythongh-124412: Add helpers for converting annotations to source format (python#124551)
  pythongh-119180: Disallow instantiation of ConstEvaluator objects (python#124561)
  For-else deserves its own section in the tutorial (python#123946)
  Add 3.13 as a version option to the crash issue template (python#124560)
  pythongh-123242: Note that type.__annotations__ may not exist (python#124557)
  pythongh-119180: Make FORWARDREF format look at __annotations__ first (python#124479)
  pythonGH-58058: Add quick reference for `ArgumentParser` to argparse docs (pythongh-124227)
  pythongh-41431: Add `datetime.time.strptime()` and `datetime.date.strptime()` (python#120752)
  pythongh-102450: Add ISO-8601 alternative for midnight to `fromisoformat()` calls. (python#105856)
  pythongh-124370: Add "howto" for free-threaded Python (python#124371)
  pythongh-121277: Allow `.. versionadded:: next` in docs (pythonGH-121278)
  pythongh-119400:  make_ssl_certs: update reference test data automatically, pass in expiration dates as parameters python#119400  (pythonGH-119401)
  pythongh-119180: Avoid going through AST and eval() when possible in annotationlib (python#124337)
  pythongh-124448: Update Windows builds to use Tcl/Tk 8.6.15 (pythonGH-124449)
  pythongh-123884 Tee of tee was not producing n independent iterators (pythongh-124490)
  pythongh-124378: Update test_ttk for Tcl/Tk 8.6.15 (pythonGH-124542)
  pythongh-124513: Check args in framelocalsproxy_new() (python#124515)
  pythongh-101100: Add a table of class attributes to the "Custom classes" section of the data model docs (python#124480)
  Doc: Use ``major.minor`` for documentation distribution archive filenames (python#124489)
  ...
JelleZijlstra added a commit to JelleZijlstra/cpython that referenced this issue Sep 26, 2024
…ythonGH-124557)

Closes pythonGH-123242. The real criterion is that the attribute does not
exist on heap types, but I don't think we should discuss heap vs.
static types in the language reference.
(cherry picked from commit 99b23c6)

Co-authored-by: Jelle Zijlstra <[email protected]>
Yhg1s pushed a commit that referenced this issue Sep 26, 2024
…4557) (#124569)

Closes GH-123242. The real criterion is that the attribute does not
exist on heap types, but I don't think we should discuss heap vs.
static types in the language reference.
(cherry picked from commit 99b23c6)
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

Successfully merging a pull request may close this issue.

4 participants