I’m not sure if this is possible in Python, but I’d like to create a series of related typing.Annotated
referring to the same underlying type, but where each is constrained to a different set of values (ideally using enums, but that isn’t crucial), and annotated with metadata about what it accepts. I could manually specify them all, but it would be cleaner if this could be parameterized.
Here’s a simplified example of what I’m trying to do:
from dataclasses import dataclass
from enum import Enum
from typing import Annotated
from annotated_types import Predicate
class WildAnimals(Enum):
LION = 'lion'
BEAR = 'bear'
class PetAnimals(Enum):
CAT = 'cat'
DOG = 'dog'
def ConstrainedType(allowed_values: type[Enum]):
return Annotated[
str,
Predicate(lambda val: val in (m.value for m in allowed_values)),
f'Allowed: {list(allowed_values)}',
]
def ConstrainedContainer(allowed_values: type[Enum]):
CType = ConstrainedType(allowed_values)
@dataclass
class Container:
# error: Variable not allowed in type expression (reportInvalidTypeForm)
animal: CType
count: int
return Container
PetCarrier = ConstrainedContainer(PetAnimals)
WildCarrier = ConstrainedContainer(WildAnimals)
As far as I can tell, this code runs fine (as it should, since type hints are optional) but Pyright objects to line 29 where I’m assigning CType
as a type for animal
:
example.py:29:17 – error: Variable not allowed in type expression (reportInvalidTypeForm)
If I instead try to inline ConstrainedType()
I get this error:
example.py:29:17 – error: Call expression not allowed in type expression (reportInvalidTypeForm)
What am I missing here? Do I need to be using TypeVar
or Generic
somehow? Is there a way to have parameterized types that aren’t “variable” so the type checker can understand them?