Similar use case as to why they are used in first place. To delay evaluation for things like recursive types. And yes I’ve written code to handle them as well. And popular libraries like pydantic have also implemented support for some quoted types. You evaluate them and allow user to register types as needed. Forward Annotations - Pydantic is an example of this.
Isn’t that solved in Python 3.14 though? From the example here:
>>> from annotationlib import get_annotations, Format
>>> def func(arg: Undefined):
... pass
>>> get_annotations(func, format=Format.FORWARDREF)
{'arg': ForwardRef('Undefined', owner=<function func at 0x...>)}
So, it should always be possible to get a ForwardRef
instead of a str
.
Indeed would be great if string annotations would be supported (afaict the mentioned mypy limitations are working as expected with pyright (playground – also the error message is a bit confusing as it mentions Literal
)).
3.14 only “solves” this if using type expressions as type hints. The idea would be to be able to support this:
from pydantic import TypeAdapter
ta_f = TypeAdapter('MyList[int]')
MyList = list
assert_type(ta_f, TypeAdapter[list[int]])
# Currently, using `ta_f` to validate data should work, as Pydantic fetches
# annotations from the current namespace.
One possible solution without stringified types:
from pydantic import TypeAdapter
type _MyList[T] = MyList[T]
ta_f = TypeAdapter(_MyList[int])
MyList = list
assert_type(ta_f, TypeAdapter[list[int]])
This works in pyright: code sample in pyright playground.
It looks quite silly of course, but I imagine in a more realistic example, it would be more like:
type _User = User
ta_f = TypeAdapter(_User)
class User(TypedDict):
name: str
id: int
In general, I think it would be great if with Python 3.12’s lazyily-evaluated type aliases and with Python 3.14’s deferred evaluation of type annotation, that Python’s typing ecosystem can move beyond stringified types.
That’s certainly my goal, though there’s still a handful of cases where types are eagerly evaluated at runtime and quoted types may therefore be necessary (cast()
, NewType()
, type arguments to generic bases, the function-based TypedDict definition off the top of my head; I think there are a few others). And of course, there’s lots of existing code that uses strings in annotations, even though that should become unnecessary once 3.14 is in wide use. TypeForm will have to support code that is already in use, so it must be able to include stringified annotations.
Update:
- The second mypy PR containing TypeForm support is in review. It incorporates all of the major feedback from the first PR.
- A request for the Typing Council to provide a yes/no recommendation on the PEP is in progress.