pylint-dev / pylint

It's not just a linter that annoys you!
https://pylint.readthedocs.io/en/latest/
GNU General Public License v2.0
5.31k stars 1.14k forks source link

``not-an-iterable`` false positive on ``ExceptionGroup.exceptions`` #8985

Open gszabo opened 1 year ago

gszabo commented 1 year ago

Bug description

hello.py

# pylint: disable=missing-module-docstring
eg = ExceptionGroup("Hello", [Exception("A"), Exception("B")])
for e in eg.exceptions:
    print(e)

Configuration

No response

Command used

pylint hello.py

Pylint output

************* Module hello
hello.py:3:9: E1133: Non-iterable value eg.exceptions is used in an iterating context (not-an-iterable)

Expected behavior

I would expect pylint to accept this code as correct. ExceptionGroup.exceptions is a tuple, which is an iterable type.

If I run the code with python hello.py, I get no errors and the messages of the two exception instances:

$ python hello.py
A
B

Pylint version

pylint 2.17.5
astroid 2.15.6
Python 3.11.4 (main, Jun 16 2023, 10:32:21) [GCC 11.3.0]

OS / Environment

Ubuntu 22.04

Additional dependencies

astroid==2.15.6
dill==0.3.7
isort==5.12.0
lazy-object-proxy==1.9.0
mccabe==0.7.0
platformdirs==3.10.0
pylint==2.17.5
tomlkit==0.12.1
wrapt==1.15.0
jnsnow commented 3 months ago

+1

pylint 3.2.5
astroid 3.2.2
Python 3.12.3 (main, Apr 17 2024, 00:00:00) [GCC 14.0.1 20240411 (Red Hat 14.0.1-0)]
def main():
    try:
        raise ExceptionGroup()
    except ExceptionGroup as exc_group:
        for exc in exc_group.exceptions:
            print(exc)
repro.py:5:19: E1133: Non-iterable value exc_group.exceptions is used in an iterating context (not-an-iterable)

Not clear on root cause; mypy seems to tolerate this construct. Should I go digging?

DanielNoord commented 3 months ago

If you have the time to do so, yes please! I think this is clearly a bug.

jacobtylerwalls commented 3 months ago

It's a tuple...

>>> try:
...   raise ExceptionGroup('', [TypeError(), TypeError()])
... except ExceptionGroup as eg:
...   print(type(eg.exceptions))
... 
<class 'tuple'>

But astroid think's it's a class:

>>> from astroid import extract_node
>>> exc = extract_node("""
... try:
...     raise ExceptionGroup('', [TypeError(), TypeError()])
... except ExceptionGroup as eg:
...     eg.exceptions #@
...     
...     """)
>>> exc
<Attribute.exceptions l.5 at 0x102675090>
>>> exc.inferred()
[<ClassDef.exceptions l.0 at 0x1015b57c0>]

@jnsnow you might want to look into #2333.

My guess is that here we need to instantiate a new ExceptionGroupInstance.