I’m excited to present PEP 849, which adds some new background capabilities to the way type annotations are processed in order to let us use arbitrary expressions while maintaining runtime introspection.
Currently, you technically can use arbitrary Python expressions in an annotation, so e.g. even var: f(x) if Something else 1 + 17 wouldn’t raise a compiler error. But if we inspect the annotations dict at runtime, we would only ever see the result of calling f or 18, depending on what Something evaluates to. This means that the typing spec can’t really assign any meaning to an expression like that because it wants to make sure that runtime tools will always see some representation of the full annotation.
This PEP introduces a new format for annotations, Format.AST. When an annotate function is called with it, it will not return the result of evaluating the annotation in some way, but the annotation’s AST and namespace. E.g. for the above example, we’d get something like this:
AnnotationAST(
ast=ast.IfExp(
test=ast.Name("Something"),
body=ast.Call(ast.Name("f"), [ast.Name("x")]),
orelse=ast.BinOp(ast.Constant(1), ast.Add(), ast.Constant(17)),
),
namespace={"f": ..., "Something": ..., "x", ...},
)
In the most common use case, runtime introspection tools will not work with this AST directly, but use it to construct the correct typing objects. To make that easier, we introduce helper functions similar to annotationlib.get_annotations, which perform those computations under the hood.
There are still some implementation details to iron out, most notably how exactly the annotate functions are stored and how they construct the AST objects. But even in the current implementation, import times and memory usage are reduced compared to the current behavior, but executing the annotate functions takes longer in some common cases. Note that this slow down is comparing just the time it takes to construct the relevant typing objects. In most use cases that is just a small step of whatever introspection machinery is using annotation data. So the actual performance impact will be positive for many users and only a marginal decrease in some situations.
The full PEP is published here, the reference implementation is being worked on in my CPython fork and previous discussion around this idea took place in this thread and this thread.