[ty] Do not consider a type T to satisfy a method member on a protocol unless the method is available on the meta-type of T (#19187)

This commit is contained in:
Alex Waygood 2025-07-25 11:16:04 +01:00 committed by GitHub
parent b124e182ca
commit f722bfa9e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 132 additions and 19 deletions

View file

@ -754,3 +754,42 @@ def f(never: Never):
for x in never:
reveal_type(x) # revealed: Unknown
```
## A class literal is iterable if it inherits from `Any`
A class literal can be iterated over if it has `Any` or `Unknown` in its MRO, since the
`Any`/`Unknown` element in the MRO could materialize to a class with a custom metaclass that defines
`__iter__` for all instances of the metaclass:
```py
from unresolved_module import SomethingUnknown # error: [unresolved-import]
from typing import Any, Iterable
from ty_extensions import static_assert, is_assignable_to, TypeOf, Unknown
class Foo(SomethingUnknown): ...
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
# TODO: these should pass
static_assert(is_assignable_to(TypeOf[Foo], Iterable[Unknown])) # error: [static-assert-error]
static_assert(is_assignable_to(type[Foo], Iterable[Unknown])) # error: [static-assert-error]
# TODO: should not error
# error: [not-iterable]
for x in Foo:
reveal_type(x) # revealed: Unknown
class Bar(Any): ...
reveal_type(Bar.__mro__) # revealed: tuple[<class 'Bar'>, Any, <class 'object'>]
# TODO: these should pass
static_assert(is_assignable_to(TypeOf[Bar], Iterable[Any])) # error: [static-assert-error]
static_assert(is_assignable_to(type[Bar], Iterable[Any])) # error: [static-assert-error]
# TODO: should not error
# error: [not-iterable]
for x in Bar:
# TODO: should reveal `Any`
reveal_type(x) # revealed: Unknown
```