[ty] Normalize recursive types using Any (#19003)

## Summary

This just replaces one temporary solution to recursive protocols (the
`SelfReference` mechanism) with another one (track seen types when
recursively descending in `normalize` and replace recursive references
with `Any`). But this temporary solution can handle mutually-recursive
types, not just self-referential ones, and it's sufficient for the
primer ecosystem and some other projects we are testing on to no longer
stack overflow.

The follow-up here will be to properly handle these self-references
instead of replacing them with `Any`.

We will also eventually need cycle detection on more recursive-descent
type transformations and tests.

## Test Plan

Existing tests (including recursive-protocol tests) and primer.

Added mdtest for mutually-recursive protocols that stack-overflowed
before this PR.
This commit is contained in:
Carl Meyer 2025-06-30 12:07:57 -07:00 committed by GitHub
parent 34052a1185
commit 2ae0bd9464
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 356 additions and 368 deletions

View file

@ -1729,6 +1729,21 @@ def _(r: Recursive):
reveal_type(r.method(r).callable1(1).direct.t[1][1]) # revealed: Recursive
```
### Mutually-recursive protocols
```py
from typing import Protocol
from ty_extensions import is_equivalent_to, static_assert
class Foo(Protocol):
x: "Bar"
class Bar(Protocol):
x: Foo
static_assert(is_equivalent_to(Foo, Bar))
```
### Regression test: narrowing with self-referential protocols
This snippet caused us to panic on an early version of the implementation for protocols.

View file

@ -10,7 +10,7 @@ jax # too many iterations
mypy # too many iterations (self-recursive type alias)
packaging # too many iterations
pandas # slow (9s)
pandera # stack overflow
pandera # too many iterations
pip # vendors packaging, see above
pylint # cycle panics (self-recursive type alias)
pyodide # too many cycle iterations
@ -19,5 +19,4 @@ setuptools # vendors packaging, see above
spack # slow, success, but mypy-primer hangs processing the output
spark # too many iterations
steam.py # hangs (single threaded)
tornado # bad use-def map (https://github.com/astral-sh/ty/issues/365)
xarray # too many iterations

View file

@ -110,6 +110,7 @@ strawberry
streamlit
svcs
sympy
tornado
trio
twine
typeshed-stats