[ty] Don't panic with argument that doesn't actually implement Iterable (#19602)

This eliminates the panic reported in
https://github.com/astral-sh/ty/issues/909, though it doesn't address
the underlying cause, which is that we aren't yet checking the types of
the fields of a protocol when checking whether a class implements the
protocol. And in particular, if a class explictly opts out of iteration
via

```py
class NotIterable:
    __iter__ = None
```

we currently treat that as "having an `__iter__`" member, and therefore
implementing `Iterable`.

Note that the assumption that was in the comment before is still
correct: call binding will have already checked that the argument
satisfies `Iterable`, and so it shouldn't be an error to iterate over
said argument. But arguably, the new logic in this PR is a better way to
discharge that assumption — instead of panicking if we happen to be
wrong, fall back on an unknown iteration result.
This commit is contained in:
Douglas Creager 2025-07-28 12:09:54 -04:00 committed by GitHub
parent e63dfa3d18
commit 130d4e1135
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1026,10 +1026,14 @@ impl<'db> Bindings<'db> {
// `tuple(range(42))` => `tuple[int, ...]`
// BUT `tuple((1, 2))` => `tuple[Literal[1], Literal[2]]` rather than `tuple[Literal[1, 2], ...]`
if let [Some(argument)] = overload.parameter_types() {
let tuple_spec = argument.try_iterate(db).expect(
let Ok(tuple_spec) = argument.try_iterate(db) else {
tracing::debug!(
"type" = %argument.display(db),
"try_iterate() should not fail on a type \
assignable to `Iterable`",
);
continue;
};
overload.set_return_type(Type::tuple(TupleType::new(
db,
tuple_spec.as_ref(),