What type can be used with annotations which won't affect static type checkers

104 Views Asked by At

I'd like to use Python (3.12) type annotations at runtime for my own purposes that have nothing to do with variable typing. The syntax is very convenient for adding metadata to variables.

So I'd like to annotate with a type that won't affect any static type analysis that may also be occurring. The annotation PEPs make reference to uses like mine (i.e., using annotations for things other than static type analysis), and in particular Annotated is close to what I need. The problem is that I'd like to skip the first argument to Annotated since I don't actually want to associate a type with an object—I just want to associate metadata.

Is there a type (or can one be built) that won't alter static type analysis? I know Any is a reasonable choice, but that changes static type analysis. For example, Pyright assigns variables with no type annotation the type Unknown (not Any).

2

There are 2 best solutions below

2
Mehedi Khan On
from typing import Annotated, Any

# Annotate a variable with metadata using Annotated with Any
my_variable: Annotated[Any, "metadata_key"] = "value"

# Access the metadata associated with the variable
metadata_value = my_variable.__annotations__["metadata_key"]
print(metadata_value)  # Output: "value"
1
Jasmijn On

While not wanting to use type annotations for annotating types suggests an XY problem to me, the most logical way to do this would be with type variables:

from typing import Annotated, get_args, TypeVar, Generic

Ta = TypeVar('Ta')
Tb = TypeVar('Tb')
Tc = TypeVar('Tc')

class Test(Generic[Ta, Tb, Tc]):
    a: Annotated[Ta, {'my-app': 1}]
    b: Annotated[Tb, {'my-app': 2}]
    c: Annotated[Tc, {'my-app': 3}]

_, my_app_data = get_args(Test.__annotations__['a'])

print(my_app_data['my-app']) # 1

I haven't tried pyright, so I don't know if that supports PEP 695, but if it does, you could simply write out your annotations like so:

from typing import Annotated, get_args

class Test[Ta, Tb, Tc]:
    a: Annotated[Ta, {'my-app': 1}]
    b: Annotated[Tb, {'my-app': 2}]
    c: Annotated[Tc, {'my-app': 3}]

Either way, it's important that within a single class or function, all variables with potentially different types get their own type variable.