I have a function which takes a value, a type annotation, and validates that the value is of the type.
It only takes a certain subset of type annotations: int
, float
, str
, bool
, a list
of any of these, or a (potentially nested) list
of any of these.
def validate(value: Any, type_: ValidatableAnnotation):
...
I have no problem implementing the function, however, I have a problem typing it.
I'm trying to define the type ValidatableAnnotation
to allow any level of recursion in the list type e.g. list[int]
, list[list[int]]
etc. ad infinitum.
My best attempt:
ValidatableAnnotation = (
Type[str] | Type[int] | Type[float] | Type[bool] | Type[list["ValidatableAnnotation"]]
)
However, while it works for the atomic values, it doesn't work for lists:
validate([1], list[int]) # Incompatible types in assignment (expression has type "type[list[int]]", variable has type "ValidatableAnnotation")
Is the Python (3.10) type system capable of doing what I want here, and if so how do I do it?
Edit: similarity has been pointed out to What's the correct type hint for a type hint?. However, it is possible that an answer is available at a lower level of generality than at a higher level. I'm not asking for a general answer to "how to define a type hint for a type hint", but for a specific answer to "how to recursively define a type hint for a list
type hint. This specific scenario may have a solution even if the more general question doesn't.
I have a function which takes a value, a type annotation, and validates that the value is of the type.
It only takes a certain subset of type annotations: int
, float
, str
, bool
, a list
of any of these, or a (potentially nested) list
of any of these.
def validate(value: Any, type_: ValidatableAnnotation):
...
I have no problem implementing the function, however, I have a problem typing it.
I'm trying to define the type ValidatableAnnotation
to allow any level of recursion in the list type e.g. list[int]
, list[list[int]]
etc. ad infinitum.
My best attempt:
ValidatableAnnotation = (
Type[str] | Type[int] | Type[float] | Type[bool] | Type[list["ValidatableAnnotation"]]
)
However, while it works for the atomic values, it doesn't work for lists:
validate([1], list[int]) # Incompatible types in assignment (expression has type "type[list[int]]", variable has type "ValidatableAnnotation")
Is the Python (3.10) type system capable of doing what I want here, and if so how do I do it?
Edit: similarity has been pointed out to What's the correct type hint for a type hint?. However, it is possible that an answer is available at a lower level of generality than at a higher level. I'm not asking for a general answer to "how to define a type hint for a type hint", but for a specific answer to "how to recursively define a type hint for a list
type hint. This specific scenario may have a solution even if the more general question doesn't.
1 Answer
Reset to default 1What you need is a TypeForm
from https://peps.python./pep-0747/. Which has now been released with typing_extensions
4.13.
If want to strictly insist on a list
, the code at the bottom will.
Alternatively go for if any sequence is acceptable:
type ValidatableAnnotation = (
str | int | float | bool | Sequence["ValidatableAnnotation"]
)
Second note, that you have to differentiate between a TypeForm[ValidatableAnnotation]
which can just be a string representing a type and a ValidatableAnnotation
which will be a valid object.
Code sample in pyright playground
from typing_extensions import TypeForm, Type, Any, Sequence, TypeVar
# Define valid types here or in a variable/typea alias
type ValidatableAnnotation[typeT: str | int | float | bool] = (
typeT | list["ValidatableAnnotation[typeT]"]
)
def validate(value: Any, type_: TypeForm[ValidatableAnnotation]):
...
validate([1], list[int])
# Not a Type Expression, should be rejected
x_form: TypeForm[ValidatableAnnotation] = [int, float]
# Okay as a value
x_value: ValidatableAnnotation = [int, float]
y: TypeForm[ValidatableAnnotation] = list[int]
z: TypeForm[ValidatableAnnotation] = list[float]
u: TypeForm[ValidatableAnnotation] = list[list[int]]
Type[list["ValidatableAnnotation"]]
you should just havelist["ValidatableAnnotation"]
because it should be the list of types, not the type of the list of types. Also small side node, just usetype
instead ofType
, because it is easier and the old typing aliases were deprecated in version 3.9. docs.python./3/library/typing.html#typing.Type – JoniKauf Commented Mar 27 at 8:49x: ValidatableAnnotation = [int, float]
. I want to enablex: ValidatableAnnotation = list[int]
,x: ValidatableAnnotation = list[float]
x: ValidatableAnnotation = list[list[int]]
etc. – samfrances Commented Mar 27 at 9:22int
str
float
bool
work. Also, best not to use an alias comprisingtype[...]
s; you should aliasstr | int | ...
then dotype_: type[ValidatableAnnotation]
instead. Try this. – dROOOze Commented Mar 27 at 11:00