3.14. Typing Annotated
3.14.1. SetUp
>>> from typing import Annotated
3.14.2. Summary
Since Python 3.9 PEP 593 -- Flexible function and variable annotations
https://docs.python.org/3/library/typing.html#typing.Annotated
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
Annotated
>>> 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).