Shorthand syntax for Annotated (Type @ [metadata])

I’m not sure if you’re saying the difference if arguments are forward references should be documented or if @ should make the assumption?

My view is that Format.FORWARDREF should be conservative and only resolve if it can be done in such a way that, when the ForwardRef objects are resolved it will give the same result as Format.VALUE[1].


I’ll note that although I also don’t make this assumption in reannotate, I do allow the user to subsequently make the assumption for a deferred annotation that all names are types with get_origin and get_args which does make it possible to extract the other types from the | syntax:

>>> from annotationlib import Format
>>> from reannotate import get_deferred_annotations, get_args, get_origin
>>> class Example:
...     a: unknown | str | int
...
>>> a_anno = get_deferred_annotations(Example)['a']
>>> print(a_anno)
DeferredAnnotation('unknown | str | int')

>>> a_anno.evaluate(format=Format.FORWARDREF)
ForwardRef('unknown | __annotationlib_name_1__ | __annotationlib_name_2__', is_class=True, owner=<class '__main__.Example'>)

>>> get_origin(a_anno).evaluate(format=Format.FORWARDREF)
<class 'typing.Union'>

>>> for arg in get_args(a_anno):
...     print(arg.evaluate(format=Format.FORWARDREF))
...
ForwardRef('unknown', is_class=True, owner=<class '__main__.Example'>)
<class 'str'>
<class 'int'>

  1. Except for some special corner cases like str(undefined) which fail due to implementation details. (Note the use of parentheses, not square brackets!) ↩︎

1 Like