astroid icon indicating copy to clipboard operation
astroid copied to clipboard

Add the ``__annotations__`` attribute to the ``ClassDef`` object model.

Open mbyrnepr2 opened this issue 1 year ago • 4 comments

Type of Changes

Type
:bug: Bug fix
:sparkles: New feature
:hammer: Refactoring
:scroll: Docs

Description

Add the __annotations__ attribute to the ClassDef object model. Pylint now does not emit a no-member error when accessing __annotations__ in the following cases:

case 1.

class Test:
    print(__annotations__)

case 2.

from typing import TypedDict

OtherTypedDict = TypedDict('OtherTypedDict', {'a': int, 'b': str})
print(OtherTypedDict.__annotations__)

case 1 is similar to the behaviour of some of the other special attributes:

class Test:
    print(__module__)
    print(__qualname__)
    print(__annotations__)

It turns out that this fix also fixed case 2. However, it's still unclear why case 2 was only a false positive for Python 3.9 and older 😬

Closes pylint-dev/pylint#7126

mbyrnepr2 avatar May 12 '24 17:05 mbyrnepr2

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 92.72%. Comparing base (2c38c02) to head (90f6f67). Report is 185 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2431      +/-   ##
==========================================
- Coverage   92.78%   92.72%   -0.06%     
==========================================
  Files          94       94              
  Lines       11098    10996     -102     
==========================================
- Hits        10297    10196     -101     
+ Misses        801      800       -1     
Flag Coverage Δ
linux 92.60% <100.00%> (+0.01%) :arrow_up:
pypy 92.72% <100.00%> (-0.06%) :arrow_down:
windows 92.70% <100.00%> (+0.02%) :arrow_up:

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
astroid/interpreter/objectmodel.py 93.27% <100.00%> (+0.02%) :arrow_up:
astroid/nodes/scoped_nodes/scoped_nodes.py 92.77% <100.00%> (+0.25%) :arrow_up:

... and 70 files with indirect coverage changes

codecov[bot] avatar May 12 '24 17:05 codecov[bot]

I'm fine with the change, but should this behind a version flag?

See https://docs.python.org/3/howto/annotations.html. It seems it was only added to all objects in 3.10+?

I see what you mean @DanielNoord great point. On Python 3.8, for example, we can demonstrate your point (this corresponds to case 2 in the description above):

>>> class Fruit:
...     pass
... 
>>> Fruit().__annotations__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Fruit' object has no attribute '__annotations__'

Now, let's look at case 1 on Python 3.8:

>>> class Fruit:
...     print(__annotations__)
... 
{}
>>> 

Without the fix, Pylint does emit the error here. At this moment I don't know if it's better to make a distinction or keep it as-is but I'm open to ideas.

mbyrnepr2 avatar Jun 25 '24 10:06 mbyrnepr2

I think a distinction would be better as that better represents the actual versions right?

DanielNoord avatar Jun 26 '24 07:06 DanielNoord

I would need to investigate a bit further @DanielNoord but my thinking is that we need the attribute anyway to satisfy both cases; so is the distinction necessary? Possibly but I need to see how that could work

mbyrnepr2 avatar Jun 26 '24 07:06 mbyrnepr2

However, it's still unclear why case 2 was only a false positive for Python 3.9 and older 😬

At a glance, I expect that's because on 3.9 and below, the base class __annotations__ was returned.

jacobtylerwalls avatar Jul 28 '24 14:07 jacobtylerwalls