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.2k stars 1.1k forks source link

Incorrect type detected when @contextmanager uses a subgenerator #9252

Open Enteee opened 8 months ago

Enteee commented 8 months ago

Bug description

The following raises a E1101: Generator 'generator' has no 'bit_count' member (no-member):

#pylint: disable=C0114,C0116
from contextlib import contextmanager
from typing import Generator

def subgenerator() -> Generator[int, None, None]:
    yield 0

@contextmanager
def context_manager() -> Generator[int, None, None]:
    # does not work:
    yield from subgenerator()

    # works:
    #for x in subgenerator():
    #    yield x

with context_manager() as cm:
    print(cm.bit_count())

Configuration

No response

Command used

pylint test.py

Pylint output

************* Module test
test.py:17:10: E1101: Generator 'generator' has no 'bit_count' member (no-member)

------------------------------------------------------------------
Your code has been rated at 3.75/10 (previous run: 7.85/10, -4.10)

Expected behavior

Same as commented code path : Pylint should detect that the context type is int and not Generator and hence it should not raise no-member

Pylint version

$ pylint --version
pylint 3.0.2
astroid 3.0.1
Python 3.11.6 (main, Oct  3 2023, 00:00:00) [GCC 13.2.1 20230728 (Red Hat 13.2.1-1)]

OS / Environment

No response

Additional dependencies

No response

PascalHonegger commented 2 months ago

With the latest version (pylint 3.2.0) this issue got worse because using the workaround mentioned above now results in the following pylint error:

R1737: Use 'yield from' directly instead of yielding each element one by one (use-yield-from)
Pierre-Sassoulas commented 2 months ago

Thank you for update @PascalHonegger. I think you should disable the message locally with # pylint: disable=no-member if it's a false positive instead of doing a workaround that can make the code worst.

PascalHonegger commented 2 months ago

Thank you for update @PascalHonegger. I think you should disable the message locally with # pylint: disable=no-member if it's a false positive instead of doing a workaround that can make the code worst.

The problem there is that you need to disable it at every call site, also isn't ideal :disappointed:

Pierre-Sassoulas commented 2 months ago

If something is generated dynamically, pylint won't be able to understand the code from your library (c-extension or not). You can then specify generated attributes with the generated-members option. For example if cv2.LINE_AA and sphinx.generated_member create false positives for no-member, you can do: $ pylint --generated-member=cv2.LINE_AA,sphinx.generated_member

https://pylint.readthedocs.io/en/stable/user_guide/messages/error/no-member.html