Skip to content

Do not iterate via __getitem__ when __iter__ is explicitly set - #11645

Open
Henry Su (hsusul) wants to merge 1 commit into
microsoft:mainfrom
hsusul:fix/iter-none-getitem-fallback
Open

Do not iterate via __getitem__ when __iter__ is explicitly set#11645
Henry Su (hsusul) wants to merge 1 commit into
microsoft:mainfrom
hsusul:fix/iter-none-getitem-fallback

Conversation

@hsusul

Copy link
Copy Markdown
Contributor

Summary

Test plan

  • npx jest typeEvaluator3.test.ts -t ForLoop --forceExit (from packages/pyright-internal)
  • Runtime: for _ in Foo() with __iter__ = None raises TypeError; a __getitem__-only class still iterates
  • Confirm a for loop over such a class reports a not-iterable error in the language server

CPython raises TypeError when __iter__ is None even if __getitem__ exists;
the legacy sequence protocol applies only when __iter__ is missing.
@rchiodo

Rich Chiodo (rchiodo) commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

🔒 Automated review in progress — Rich Chiodo (@rchiodo) is auto-reviewing this PR.

Comment thread packages/pyright-internal/src/analyzer/typeEvaluator.ts

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

@rchiodo Rich Chiodo (rchiodo) added the review-auto:approved Automated review: no blocking findings (approval posted). label Aug 17, 2026

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

@github-actions

Copy link
Copy Markdown
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

sympy (https://github.com/sympy/sympy)
-   .../projects/sympy/sympy/solvers/diophantine/diophantine.py:1460:50 - error: Cannot access attribute "as_coeff_Mul" for class "Basic*"
-     Attribute "as_coeff_Mul" is unknown (reportAttributeAccessIssue)
-   .../projects/sympy/sympy/solvers/diophantine/diophantine.py:1460:50 - error: Cannot access attribute "as_coeff_Mul" for class "Relational*"
-     Attribute "as_coeff_Mul" is unknown (reportAttributeAccessIssue)
-   .../projects/sympy/sympy/solvers/ode/lie_group.py:619:61 - error: Operator "-" not supported for type "Unknown | Basic" (reportOperatorIssue)
-   .../projects/sympy/sympy/solvers/ode/nonhomogeneous.py:225:45 - error: Cannot access attribute "has" for class "tuple[Expr, int]"
-     Attribute "has" is unknown (reportAttributeAccessIssue)
+   .../projects/sympy/sympy/solvers/ode/nonhomogeneous.py:223:22 - error: No overloads for "__new__" match the provided arguments (reportCallIssue)
+   .../projects/sympy/sympy/solvers/ode/nonhomogeneous.py:223:25 - error: Argument of type "Unknown | One | NegativeOne | Zero | Integer | tuple[Expr, int]" cannot be assigned to parameter "arg" of type "Expr" in function "__new__"
+     Type "Unknown | One | NegativeOne | Zero | Integer | tuple[Expr, int]" is not assignable to type "Expr"
+       "tuple[Expr, int]" is not assignable to "Expr" (reportArgumentType)
+   .../projects/sympy/sympy/solvers/ode/nonhomogeneous.py:238:40 - error: No overloads for "__new__" match the provided arguments (reportCallIssue)
+   .../projects/sympy/sympy/solvers/ode/nonhomogeneous.py:238:50 - error: Argument of type "Unknown | One | NegativeOne | Zero | Integer | tuple[Expr, int]" cannot be assigned to parameter "arg" of type "Expr" in function "__new__"
+     Type "Unknown | One | NegativeOne | Zero | Integer | tuple[Expr, int]" is not assignable to type "Expr"
+       "tuple[Expr, int]" is not assignable to "Expr" (reportArgumentType)
-   .../projects/sympy/sympy/solvers/ode/ode.py:1433:17 - error: Argument of type "One | NegativeOne | Zero | Integer | NaN | ComplexInfinity | Rational | Infinity | NegativeInfinity | Float | Number | Expr" cannot be assigned to parameter "value" of type "Zero" in function "__setitem__"
+   .../projects/sympy/sympy/solvers/ode/ode.py:1433:17 - error: Argument of type "One | NegativeOne | Zero | Integer | Unknown | Rational | Number | Expr" cannot be assigned to parameter "value" of type "Zero" in function "__setitem__"
-     Type "One | NegativeOne | Zero | Integer | NaN | ComplexInfinity | Rational | Infinity | NegativeInfinity | Float | Number | Expr" is not assignable to type "Zero"
+     Type "One | NegativeOne | Zero | Integer | Unknown | Rational | Number | Expr" is not assignable to type "Zero"
-   .../projects/sympy/sympy/solvers/ode/ode.py:2961:17 - error: Argument of type "One | NegativeOne | Zero | Integer | NaN | ComplexInfinity | Rational | Infinity | NegativeInfinity | Float | Number | Expr" cannot be assigned to parameter "value" of type "Zero" in function "__setitem__"
+   .../projects/sympy/sympy/solvers/ode/ode.py:2961:17 - error: Argument of type "One | NegativeOne | Zero | Integer | Unknown | Rational | Number | Expr" cannot be assigned to parameter "value" of type "Zero" in function "__setitem__"
-     Type "One | NegativeOne | Zero | Integer | NaN | ComplexInfinity | Rational | Infinity | NegativeInfinity | Float | Number | Expr" is not assignable to type "Zero"
+     Type "One | NegativeOne | Zero | Integer | Unknown | Rational | Number | Expr" is not assignable to type "Zero"
+   .../projects/sympy/sympy/solvers/ode/riccati.py:275:12 - error: No overloads for "__init__" match the provided arguments (reportCallIssue)
+   .../projects/sympy/sympy/solvers/ode/riccati.py:275:17 - error: Argument of type "zip[Unknown]" cannot be assigned to parameter "iterable" of type "Iterable[list[bytes]]" in function "__init__"
+     "zip[Unknown]" is incompatible with protocol "Iterable[list[bytes]]"
+       "__iter__" is an incompatible type
+         Type "() -> zip[Unknown]" is not assignable to type "() -> Iterator[_T_co@Iterable]"
+           Function return type "zip[Unknown]" is incompatible with type "Iterator[_T_co@Iterable]"
+             "zip[Unknown]" is incompatible with protocol "Iterator[_T_co@Iterable]" (reportArgumentType)
-   .../projects/sympy/sympy/solvers/ode/riccati.py:874:51 - error: Cannot access attribute "diff" for class "Basic*"
-     Attribute "diff" is unknown (reportAttributeAccessIssue)
+   .../projects/sympy/sympy/solvers/ode/single.py:867:9 - error: Expression with type "tuple[ComplexInfinity | Unknown | Any, list[tuple[Unknown, Unknown]] | list[Unknown]] | tuple[ComplexInfinity | Unknown | Any, list[tuple[Unknown, Unknown]] | list[Unknown], list[tuple[Unknown, Unknown]] | list[Unknown]]" cannot be assigned to target tuple
+     Type "tuple[ComplexInfinity | Unknown | Any, list[tuple[Unknown, Unknown]] | list[Unknown], list[tuple[Unknown, Unknown]] | list[Unknown]]" is incompatible with target tuple
+       Tuple size mismatch; expected 2 but received 3 (reportAssignmentType)
-   .../projects/sympy/sympy/solvers/ode/single.py:2646:27 - error: Argument of type "One | NegativeOne | Zero | Integer | NaN | ComplexInfinity | Rational | Infinity | NegativeInfinity | Float | Number | Expr | Unknown | int" cannot be assigned to parameter "stop" of type "SupportsIndex" in function "__new__"
+   .../projects/sympy/sympy/solvers/ode/single.py:2646:27 - error: Argument of type "One | NegativeOne | Zero | Integer | Unknown | Rational | Number | Expr | NaN | NegativeInfinity | int" cannot be assigned to parameter "stop" of type "SupportsIndex" in function "__new__"
-     Type "One | NegativeOne | Zero | Integer | NaN | ComplexInfinity | Rational | Infinity | NegativeInfinity | Float | Number | Expr | Unknown | int" is not assignable to type "SupportsIndex"
+     Type "One | NegativeOne | Zero | Integer | Unknown | Rational | Number | Expr | NaN | NegativeInfinity | int" is not assignable to type "SupportsIndex"
-   .../projects/sympy/sympy/solvers/ode/subscheck.py:270:32 - error: Operator "-" not supported for types "Basic | Unknown | Zero | Self@Basic" and "Basic | Unknown | Zero | Self@Basic"
+   .../projects/sympy/sympy/solvers/ode/subscheck.py:270:32 - error: Operator "-" not supported for types "Basic | Unknown | Zero" and "Basic | Unknown | Zero"
-     Operator "-" not supported for types "Basic" and "Basic"
-     Operator "-" not supported for types "Basic" and "Basic*"
-     Operator "-" not supported for types "Basic*" and "Basic"
-     Operator "-" not supported for types "Basic*" and "Basic*" (reportOperatorIssue)
+     Operator "-" not supported for types "Basic" and "Basic" (reportOperatorIssue)
-   .../projects/sympy/sympy/solvers/ode/systems.py:1167:37 - error: Operator "*" not supported for types "Literal[2]" and "Unknown | Self@Basic | bool | Expr | Basic"
+   .../projects/sympy/sympy/solvers/ode/systems.py:1167:37 - error: Operator "*" not supported for types "Literal[2]" and "Unknown | Expr | Basic | bool"
-     Operator "*" not supported for types "Literal[2]" and "Basic*"
-   .../projects/sympy/sympy/solvers/ode/systems.py:1167:37 - error: Operator "*" not supported for types "Unknown | int | Expr" and "Unknown | Self@Basic | bool | Expr | Basic"
+   .../projects/sympy/sympy/solvers/ode/systems.py:1167:37 - error: Operator "*" not supported for types "Unknown | Expr | int" and "Unknown | Expr | Basic | bool"
-     Operator "*" not supported for types "int" and "Basic*"
-     Operator "*" not supported for types "int" and "Basic"
-     Operator "*" not supported for types "Expr" and "Basic*"
+     Operator "*" not supported for types "Expr" and "Basic"
-     Operator "*" not supported for types "Expr" and "Basic" (reportOperatorIssue)
+     Operator "*" not supported for types "int" and "Basic" (reportOperatorIssue)
-   .../projects/sympy/sympy/solvers/ode/systems.py:1169:30 - error: Operator "*" not supported for types "Literal[2]" and "Unknown | Self@Basic | bool | Expr | Basic"
+   .../projects/sympy/sympy/solvers/ode/systems.py:1169:30 - error: Operator "*" not supported for types "Literal[2]" and "Unknown | Expr | Basic | bool"
-     Operator "*" not supported for types "Literal[2]" and "Basic*"
-   .../projects/sympy/sympy/solvers/ode/systems.py:1169:30 - error: Operator "*" not supported for types "Unknown | int | Expr" and "Unknown | Self@Basic | bool | Expr | Basic"
+   .../projects/sympy/sympy/solvers/ode/systems.py:1169:30 - error: Operator "*" not supported for types "Unknown | Expr | int" and "Unknown | Expr | Basic | bool"
-     Operator "*" not supported for types "int" and "Basic*"
-     Operator "*" not supported for types "int" and "Basic"
-     Operator "*" not supported for types "Expr" and "Basic*"
+     Operator "*" not supported for types "Expr" and "Basic"
+     Operator "*" not supported for types "int" and "Basic" (reportOperatorIssue)
-   .../projects/sympy/sympy/solvers/ode/systems.py:1169:49 - error: Operator "*" not supported for types "Literal[2]" and "Unknown | Self@Basic | bool | Expr | Basic"
+   .../projects/sympy/sympy/solvers/ode/systems.py:1169:49 - error: Operator "*" not supported for types "Literal[2]" and "Unknown | Expr | Basic | bool"
-     Operator "*" not supported for types "Literal[2]" and "Basic*"
-   .../projects/sympy/sympy/solvers/ode/systems.py:1169:49 - error: Operator "*" not supported for types "Unknown | int | Expr" and "Unknown | Self@Basic | bool | Expr | Basic"
+   .../projects/sympy/sympy/solvers/ode/systems.py:1169:49 - error: Operator "*" not supported for types "Unknown | Expr | int" and "Unknown | Expr | Basic | bool"
-     Operator "*" not supported for types "int" and "Basic*"
-     Operator "*" not supported for types "int" and "Basic"
-     Operator "*" not supported for types "Expr" and "Basic*"
+     Operator "*" not supported for types "Expr" and "Basic"

... (truncated 1777 lines) ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-auto:approved Automated review: no blocking findings (approval posted).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Instances of classes that define __iter__ = None and __getitem__ should not be iterable.

3 participants