[ty] Support stringified annotations in value-position Annotated instances (#21447)

## Summary

Infer the first argument `type` inside `Annotated[type, …]` as a type
expression. This allows us to support stringified annotations inside
`Annotated`.

## Ecosystem

* The removed diagnostic on `prefect` shows that we now understand the
`State.data` type annotation in
`src/prefect/client/schemas/objects.py:230`, which uses a stringified
annotation in `Annoated`. The other diagnostics are downstream changes
that result from this, it seems to be a commonly used data type.
* `artigraph` does something like `Annotated[cast(Any,
field_info.annotation), *field_info.metadata]` which I'm not sure we
need to allow? It's unfortunate since this is probably supported at
runtime, but it seems reasonable that they need to add a `# type:
ignore` for that.
* `pydantic` uses something like `Annotated[(self.annotation,
*self.metadata)]` but adds a `# type: ignore`

## Test Plan

New Markdown test
This commit is contained in:
David Peter 2025-11-14 14:09:09 +01:00 committed by GitHub
parent 05cf53aae8
commit e9a5337136
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 28 additions and 9 deletions

View file

@ -76,7 +76,18 @@ from ty_extensions import reveal_mro
class C(Annotated[int, "foo"]): ...
reveal_mro(C) # revealed: (<class 'C'>, <class 'int'>, <class 'object'>)
# revealed: (<class 'C'>, <class 'int'>, <class 'object'>)
reveal_mro(C)
class D(Annotated[list[str], "foo"]): ...
# revealed: (<class 'D'>, <class 'list[str]'>, <class 'MutableSequence[str]'>, <class 'Sequence[str]'>, <class 'Reversible[str]'>, <class 'Collection[str]'>, <class 'Iterable[str]'>, <class 'Container[str]'>, typing.Protocol, typing.Generic, <class 'object'>)
reveal_mro(D)
class E(Annotated[list["E"], "metadata"]): ...
# error: [revealed-type] "Revealed MRO: (<class 'E'>, <class 'list[E]'>, <class 'MutableSequence[E]'>, <class 'Sequence[E]'>, <class 'Reversible[E]'>, <class 'Collection[E]'>, <class 'Iterable[E]'>, <class 'Container[E]'>, typing.Protocol, typing.Generic, <class 'object'>)"
reveal_mro(E)
```
### Not parameterized

View file

@ -974,13 +974,14 @@ We *do* support stringified annotations if they appear in a position where a typ
syntactically expected:
```py
from typing import Union, List, Dict
from typing import Union, List, Dict, Annotated
ListOfInts1 = list["int"]
ListOfInts2 = List["int"]
StrOrStyle = Union[str, "Style"]
SubclassOfStyle = type["Style"]
DictStrToStyle = Dict[str, "Style"]
AnnotatedStyle = Annotated["Style", "metadata"]
class Style: ...
@ -990,12 +991,14 @@ def _(
str_or_style: StrOrStyle,
subclass_of_style: SubclassOfStyle,
dict_str_to_style: DictStrToStyle,
annotated_style: AnnotatedStyle,
):
reveal_type(list_of_ints1) # revealed: list[int]
reveal_type(list_of_ints2) # revealed: list[int]
reveal_type(str_or_style) # revealed: str | Style
reveal_type(subclass_of_style) # revealed: type[Style]
reveal_type(dict_str_to_style) # revealed: dict[str, Style]
reveal_type(annotated_style) # revealed: Style
```
## Recursive