3.14. Typing Annotated

3.14.1. SetUp

>>> from typing import Annotated

3.14.2. Summary

Note

ValueRange, ctype, MatchesRegex, MaxLen does not exist in Python. It is used only as an example both here and in PEP 593.

>>> a = Annotated[str, 'this is just metadata']
>>> b = Annotated[str, 'this is just metadata', 'this is also metadata']
>>> c = Annotated[str, {'min': 18, 'max': 100}]

3.14.3. Numeric

>>> age = Annotated[int, {'min': 18, 'max': 100}]
>>> digit = Annotated[int, range(0,9)]
>>> int8 = Annotated[int, range(-128, 127)]
>>> uint8 = Annotated[int, range(0, 255)]
>>> kelvin = Annotated[float, range(0, 1000)]
>>> vector = Annotated[list[int], MaxLen(3)]  

3.14.4. Character

>>> firstname = Annotated[str, MaxLen(10)]  
>>> lastname = Annotated[str, MinLen(2), MaxLen(10)]  

3.14.5. Patterns

>>> jira_issuekey = Annotated[str, MatchesRegex('^[A-Z]{2,10}-[0-9]{1,6}$')]  
>>> email = Annotated[str, MatchesRegex('^[a-z]{1,20}@nasa.gov$')]  

3.14.6. Use Case - 1

>>> 
... EmailAddress = Annotated[str, MatchesRegex('^[a-z]{1,20}@nasa.gov$')]
...
... def send_email(recipient: EmailAddress):
...     ...
...
...
... send_email('mwatney@nasa.gov')  # ok
... send_email('avogel@esa.int')    # error

3.14.7. Use Case - 2

>>> 
... IssueKey = Annotated[str, MatchesRegex('^[A-Z]{2,10}-[0-9]{1,6}$')]
...
... def comment(issuekey: IssueKey, text: str):
...     ...
...
...
... comment('MYPROJ-1337', 'Issue was resolved successfully.')

3.14.8. Use Case - 3

>>> from functools import wraps
>>> from typing import get_type_hints, get_origin, get_args, Annotated
>>>
>>>
>>> class MaxLen:
...     def __init__(self, value):
...         self.value = value
>>>
>>>
>>> def check_annotations(func):
...     @wraps(func)
...     def wrapped(**kwargs):
...         type_hints = get_type_hints(func, include_extras=True)
...         for param, hint in type_hints.items():
...             if get_origin(hint) is not Annotated:
...                 continue
...             hint_type, *hint_args = get_args(hint)
...             if hint_type is str or get_origin(hint_type) is str:
...                 for arg in hint_args:
...                     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}).")
...         return func(**kwargs)
...     return wrapped
>>>
>>>
>>> word = Annotated[str, MaxLen(10)]
>>>
>>> @check_annotations
... def echo(text: word):
...     return text
>>>
>>>
>>> echo(text='abcdefghij')
'abcdefghij'
>>>
>>> echo(text='abcdefghijk')
Traceback (most recent call last):
ValueError: Parameter 'text' cannot have a length larger than 10 (got length 11).