astroid icon indicating copy to clipboard operation
astroid copied to clipboard

false not-an-iterable for class method with @overload (2.5.7 regression)

Open belm0 opened this issue 3 years ago • 5 comments

Steps to reproduce

Starting from astroid 2.5.7, I'm seeing false not-an-iterable when generator class methods are annotated with @overload.

from typing import overload, Iterator

class MyClass:
    @overload
    def transitions(self, foo: int, bar: int) -> Iterator[int]: ...
    @overload
    def transitions(self, baz: str) -> Iterator[str]: ...
    def transitions(self, foo_or_baz, bar=None):
        yield

for _ in MyClass().transitions('hello'):
    pass

If @overload is removed, or the function is moved to the module level, or I switch to astroid 2.5.6, the problem goes away.

It happens with pylint-2.8.3 or pylint-3.0.0a3.

Current behavior

E1133: Non-iterable value MyClass().transitions('hello') is used in an iterating context (not-an-iterable)

Expected behavior

no error

belm0 avatar Jun 08 '21 08:06 belm0

I can work around it by replacing ... with yield, but it doesn't seem that this should be necessary (and it wasn't in prior versions of Astroid). And it is hard to satisfy mypy with this workaround, because it expects yield [value] etc.

belm0 avatar Jun 08 '21 08:06 belm0

@belm0 thanks for the report.

hippo91 avatar Jun 14 '21 04:06 hippo91

The issue seems to have started with #934, the previous commit is fine: https://github.com/PyCQA/astroid/commit/2e8417ffc2285e798ccdef86f743abb75958e2c6. (Tested with pylint 2.8.3.) @nelfin Would you like to take a look at this?

cdce8p avatar Sep 12 '21 21:09 cdce8p

Sure, happy to take this one

nelfin avatar Sep 12 '21 23:09 nelfin

This doesn't seem to have anything to do with @overload. Here's a minimum reproducing example:

class A:
    def foo(self): ...
    def foo(self):
        yield

for _ in A().foo():
    pass

I think this has to do with the name resolution order, Attribute.foo is inferred as the foo(): ... instead of foo(): yield and the lack of a function body means infer_call_result infers None (arguably correct, just given incorrect inputs). If you switch the order of the functions in the class body then there's no pylint warning (which is a false-negative). I'm chasing down the root cause and a fix.

nelfin avatar Sep 15 '21 05:09 nelfin

https://github.com/PyCQA/pylint/issues/7624 have a use case related to this in pylint

Pierre-Sassoulas avatar Oct 16 '22 07:10 Pierre-Sassoulas

Getting a similar problem with pylint on torch.nn.Module.to():

from __future__ import annotations

from typing import Any, Union, overload


class device:
    ...


class Tensor:
    ...


class dtype:
    ...


class Module:
    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        ...

    @overload
    def to(self, dtype: Union[dtype, str]) -> Module:
        ...

    @overload
    def to(self, tensor: Tensor) -> Module:
        ...

    def to(self, *args, **kwargs):
        return self


tensor = Tensor()

module = Module()
_ = module(tensor)  # OK

module = module.to("cpu")
_ = module(tensor)  # a.py:46:4: E1102: module is not callable (not-callable)
astroid==2.15.4
pylint==2.17.4

ringohoffman avatar Jun 13 '23 21:06 ringohoffman

THANKS @jacobtylerwalls -- this was a major bug for pre-typing code that returned different things depending on settings (would not write new code for that today) and needed @overload to specify the proper return values.

mscuthbert avatar Apr 02 '24 20:04 mscuthbert