What is the right way to extract PEP 593 annotation information attached with Annotated[]
from a class/type object?
I want to annotate the members of a class to indicate how to process objects of the class. Ideally I’d like to use a TypedDict as the base class for this.
There’s a suggestion here to use a mix of typing.get_origin
and typing.get_args
which looked promising.
But this doesn’t seem to play nicely with PEP 655 Required[]
and NotRequired[]
. Specifically PEP 655 states:
Required[]
andNotRequired[]
can be used withAnnotated[]
, in any nesting order
This means that get_origin
could return Required
and the Annotated
might come from get_args
or visa versa, or any mix of those.
I can write a function to iterate down those three specific wrappers but this leaves be with two concerns:
- Is this list of wrappers a closed set? will future versions of python add more? PEP 593 was python 3.9 and PEP 655 was Python 3.11 so I guess this is really not future proof.
- Am I re-inventing the wheel? PEP 593 seems to talk about how to annotate but doesn’t describe how to extract information from annotations. Am I missing something obvious here?
The iteration may look something like this:
while get_origin(foo) in (Required, NotRequired):
foo = get_args(foo)[0]
But this will obviously break the moment a PEP adds a fourth thing.
Worked Example:
from typing import TypedDict, Annotated, NamedTuple
class ExtractFrom(NamedTuple):
name: str
source_id: int
class UsefulInfo(TypedDict):
a: Raquired[Annotated[int, ExtractFrom("x", 1)]]
b: Annotated[Required[int], ExtractFrom("y", 2)]
c: Annotated[int, ExtractFrom("z", 3)]
Given this definition, all I really want is code that will search for ExtractFrom
instances and give me a dictionary:
{
"a": ExtractFrom("x", 1),
"b": ExtractFrom("y", 2),
"c": ExtractFrom("c", 3),
}
I feel like I must be over thinking this and missing something obvious.