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.29k stars 1.13k forks source link

Pylint doesn't understand async iterables. #2311

Closed twavv closed 6 years ago

twavv commented 6 years ago

Steps to reproduce

Consider the following example. I did it twice to make sure that the first example (as a method on a class) wasn't a weird edge case regarding self.

"""
Async iterator test.
"""

import asyncio

class AIter:
    """Async iterator test."""
    async def __aiter__(self):
        for value in range(20):
            yield value

    async def to_list(self):
        """Get the contents of the async iterator as a list."""
        # E1133: Non-iterable value my_aiter is used in an iterating context
        return [m async for m in self]

async def get_list():
    """Get the contents of an async iterator as a list."""
    my_aiter = AIter()
    # E1133: Non-iterable value my_aiter is used in an iterating context
    my_list = [m async for m in my_aiter]
    return my_list

def main():
    """Run the example."""
    loop = asyncio.get_event_loop()
    print(loop.run_until_complete(get_list()))
    my_aiter = AIter()
    print(loop.run_until_complete(my_aiter.to_list()))

if __name__ == "__main__":
    main()

Current behavior

Pylint complains:

pylint ./pylint_async_iter_test.py 
************* Module pylint_async_iter_test
pylint_async_iter_test.py:15:33: E1133: Non-iterable value self is used in an iterating context (not-an-iterable)
pylint_async_iter_test.py:21:32: E1133: Non-iterable value my_aiter is used in an iterating context (not-an-iterable)

Expected behavior

No errors.

pylint --version output

pylint --version
pylint 2.0.0
astroid 2.0.0.dev4
Python 3.6.5 (default, Apr  1 2018, 05:46:30) 
[GCC 7.3.0]

Addendum

This was discovered in a context where I have an async generator that yields messages from a message queue as they arrive.

PCManticore commented 6 years ago

Thanks for the report! I can confirm the bug, it's due to the fact that pylint is not aware of async iterators.

terrisgit commented 6 years ago

Problem seems to still exist in 2.1.0. It does not believe AsyncIterator used like so:

http://mypy.readthedocs.io/en/latest/python36.html#asynchronous-generators-pep-525-and-comprehensions-pep-530

2.0.1 doesn't complain.

r-darwish commented 6 years ago

I'm using 2.1.1 and still seeing this issue. In the following example error are reported for lines 7 and 8:

https://gist.github.com/r-darwish/7e40e2bf15d206e7edbf65bd1c0b49cd

Version 2.0.1 seems to work as expected.

terrisgit commented 6 years ago

I have the same issue with 2.1.1. I think the key to getting this resolved is via an open issue. I am not permitted to re-open this issue.

@r-darwish Your report is better report than mine. Would you mind creating a new issue with the same text?

dev-techmoe commented 6 years ago

v2.1.1, still seeing this issue.

test.py

import asyncio

async def iterfunc():
    for i in range(0, 5):
        yield i

async def main():
    async for num in iterfunc():
        print(num)

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(main()))

test:

$ python test.py
0
1
2
3
4

$ pylint test.py
test.py:12:0: C0304: Final newline missing (missing-final-newline)
test.py:1:0: C0111: Missing module docstring (missing-docstring)
test.py:3:6: C0111: Missing function docstring (missing-docstring)
test.py:7:6: C0111: Missing function docstring (missing-docstring)
test.py:8:21: E1133: Non-iterable value iterfunc() is used in an iterating context (not-an-iterable)
test.py:11:0: C0103: Constant name "loop" doesn't conform to UPPER_CASE naming style (invalid-name)

$ pylint --version
pylint 2.1.1
astroid 2.0.4
Python 3.7.0 (default, Jun 28 2018, 19:31:47)
[GCC 7.3.0]

$ python --version
Python 3.7.0
jab commented 6 years ago

@PCManticore, reopen?

jab commented 6 years ago

Also, the title of this issue is too narrow as this doesn't only occur in list comprehensions.

PCManticore commented 5 years ago

This is fixed in the master branch for astroid and pylint, so if you get those from Github you should no longer see this issue. I think the commit that fixed this was https://github.com/PyCQA/astroid/commit/6848ba49b2a99264e72de3e10c89d24eb8a253a2. I'll do a release soon for pylint 2.2.0 and astroid 2.1.

⋊> ~/p/p/playground cat a.py                                                                                                       14:04:42
import asyncio

async def iterfunc():
    for i in range(0, 5):
        yield i

async def main():
    async for num in iterfunc():
        print(num)

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(main()))
⋊> ~/p/p/playground pylint a.py                                                                                                    14:04:35
************* Module a
a.py:1:0: C0111: Missing module docstring (missing-docstring)
a.py:3:6: C0111: Missing function docstring (missing-docstring)
a.py:7:6: C0111: Missing function docstring (missing-docstring)
a.py:11:0: C0103: Constant name "loop" doesn't conform to UPPER_CASE naming style (invalid-name)

------------------------------------------------------------------
Your code has been rated at 5.56/10 (previous run: 0.00/10, +5.56)

⋊> ~/p/p/playground pylint --version
discosultan commented 5 years ago

Are there still any known issues to block 2.2 release?

PCManticore commented 5 years ago

@discosultan Other than me not having time to do the release, no, all is good for a 2.2 release! I'll try to do it this week.