astroid
astroid copied to clipboard
false not-an-iterable for class method with @overload (2.5.7 regression)
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
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 thanks for the report.
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?
Sure, happy to take this one
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.
https://github.com/PyCQA/pylint/issues/7624 have a use case related to this in pylint
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
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.