As stated by AntiNeutronicPlasma, Maxlen
is just an example so you'll need to create it yourself.
Here's an example for how to create and parse a custom annotation such as MaxLen
to get you started.
First, we define the annotation class itself. It's a very simple class, we only need to store the relevant metadata, in this case, the max value:
class MaxLen:
def __init__(self, value):
self.value = value
Now, we can define a function that uses this annotation, such as the following:
def sum_nums(nums: Annotated[List[int], MaxLen(10)]):
return sum(nums)
But it's going to be of little use if nobody checks for it. So, one option could be to implement a decorator that checks your custom annotations at runtime. The functions get_type_hints
, get_origin
and get_args
from the typing
module are going to be your best friends. Below is an example of such a decorator, which parses and enforces the MaxLen
annotation on list
types:
from functools import wraps
from typing import get_type_hints, get_origin, get_args, Annotated
def check_annotations(func):
@wraps(func)
def wrapped(**kwargs):
# perform runtime annotation checking
# first, get type hints from function
type_hints = get_type_hints(func, include_extras=True)
for param, hint in type_hints.items():
# only process annotated types
if get_origin(hint) is not Annotated:
continue
# get base type and additional arguments
hint_type, *hint_args = get_args(hint)
# if a list type is detected, process the args
if hint_type is list or get_origin(hint_type) is list:
for arg in hint_args:
# if MaxLen arg is detected, process it
if isinstance(arg, MaxLen):
max_len = arg.value
actual_len = len(kwargs[param])
if actual_len > max_len:
raise ValueError(f"Parameter '{param}' cannot have a length "
f"larger than {max_len} (got length {actual_len}).")
# execute function once all checks passed
return func(**kwargs)
return wrapped
(Note that this particular example only works with keyword arguments, but you could probably find a way to make it work for normal arguments too).
Now, you can apply this decorator to any function, and your custom annotation will get parsed:
from typing import Annotated, List
@check_annotations
def sum_nums_strict(nums: Annotated[List[int], MaxLen(10)]):
return sum(nums)
Below is an example of the code in action:
>>> sum_nums(nums=list(range(5)))
10
>>> sum_nums(nums=list(range(15)))
105
>>> sum_nums_strict(nums=list(range(5)))
10
>>> sum_nums_strict(nums=list(range(15)))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "annotated_test.py", line 29, in wrapped
raise ValueError(f"Parameter '{param}' cannot have a length "
ValueError: Parameter 'nums' cannot have a length larger than 10 (got length 15).