Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix line number if __iter__ is incorrectly reported as missing #14893

Merged
merged 3 commits into from
Mar 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3299,14 +3299,14 @@ def check_assignment_to_multiple_lvalues(
rvalues.extend([TempNode(typ) for typ in typs.items])
elif self.type_is_iterable(typs) and isinstance(typs, Instance):
if iterable_type is not None and iterable_type != self.iterable_item_type(
typs
typs, rvalue
):
self.fail(message_registry.CONTIGUOUS_ITERABLE_EXPECTED, context)
else:
if last_idx is None or last_idx + 1 == idx_rval:
rvalues.append(rval)
last_idx = idx_rval
iterable_type = self.iterable_item_type(typs)
iterable_type = self.iterable_item_type(typs, rvalue)
else:
self.fail(message_registry.CONTIGUOUS_ITERABLE_EXPECTED, context)
else:
Expand Down Expand Up @@ -3635,7 +3635,7 @@ def check_multi_assignment_from_iterable(
if self.type_is_iterable(rvalue_type) and isinstance(
rvalue_type, (Instance, CallableType, TypeType, Overloaded)
):
item_type = self.iterable_item_type(rvalue_type)
item_type = self.iterable_item_type(rvalue_type, context)
for lv in lvalues:
if isinstance(lv, StarExpr):
items_type = self.named_generic_type("builtins.list", [item_type])
Expand Down Expand Up @@ -6392,7 +6392,9 @@ def note(
return
self.msg.note(msg, context, offset=offset, code=code)

def iterable_item_type(self, it: Instance | CallableType | TypeType | Overloaded) -> Type:
def iterable_item_type(
self, it: Instance | CallableType | TypeType | Overloaded, context: Context
) -> Type:
if isinstance(it, Instance):
iterable = map_instance_to_supertype(it, self.lookup_typeinfo("typing.Iterable"))
item_type = iterable.args[0]
Expand All @@ -6401,7 +6403,7 @@ def iterable_item_type(self, it: Instance | CallableType | TypeType | Overloaded
# in case there is no explicit base class.
return item_type
# Try also structural typing.
return self.analyze_iterable_item_type_without_expression(it, it)[1]
return self.analyze_iterable_item_type_without_expression(it, context)[1]

def function_type(self, func: FuncBase) -> FunctionLike:
return function_type(func, self.named_type("builtins.function"))
Expand Down
10 changes: 5 additions & 5 deletions mypy/checkpattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from mypy.maptype import map_instance_to_supertype
from mypy.meet import narrow_declared_type
from mypy.messages import MessageBuilder
from mypy.nodes import ARG_POS, Expression, NameExpr, TypeAlias, TypeInfo, Var
from mypy.nodes import ARG_POS, Context, Expression, NameExpr, TypeAlias, TypeInfo, Var
from mypy.patterns import (
AsPattern,
ClassPattern,
Expand Down Expand Up @@ -242,7 +242,7 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType:
elif size_diff > 0 and star_position is None:
return self.early_non_match()
else:
inner_type = self.get_sequence_type(current_type)
inner_type = self.get_sequence_type(current_type, o)
if inner_type is None:
inner_type = self.chk.named_type("builtins.object")
inner_types = [inner_type] * len(o.patterns)
Expand Down Expand Up @@ -309,12 +309,12 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType:
new_type = current_type
return PatternType(new_type, rest_type, captures)

def get_sequence_type(self, t: Type) -> Type | None:
def get_sequence_type(self, t: Type, context: Context) -> Type | None:
t = get_proper_type(t)
if isinstance(t, AnyType):
return AnyType(TypeOfAny.from_another_any, t)
if isinstance(t, UnionType):
items = [self.get_sequence_type(item) for item in t.items]
items = [self.get_sequence_type(item, context) for item in t.items]
not_none_items = [item for item in items if item is not None]
if len(not_none_items) > 0:
return make_simplified_union(not_none_items)
Expand All @@ -324,7 +324,7 @@ def get_sequence_type(self, t: Type) -> Type | None:
if self.chk.type_is_iterable(t) and isinstance(t, (Instance, TupleType)):
if isinstance(t, TupleType):
t = tuple_fallback(t)
return self.chk.iterable_item_type(t)
return self.chk.iterable_item_type(t, context)
else:
return None

Expand Down
16 changes: 16 additions & 0 deletions test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -7797,3 +7797,19 @@ class Element(Generic[_T]):
class Bar(Foo): ...
e: Element[Bar]
reveal_type(e.elements) # N: Revealed type is "typing.Sequence[__main__.Element[__main__.Bar]]"

[case testIterableUnpackingWithGetAttr]
from typing import Union, Tuple

class C:
def __getattr__(self, name):
pass

class D:
def f(self) -> C:
return C()

def g(self) -> None:
# TODO: This is a false positive
a, b = self.f() # E: "C" has no attribute "__iter__" (not iterable)
[builtins fixtures/tuple.pyi]