Строгая нотация типов в python

Задавал как то вопрос про нотации типов в функциях в python в стиле

def incr(number: int) -> int:
    return number + 10

В таком случае, работая в IDE я буду получать соответствующие подсказки типов, по сути информируя среду о типах данных, с которыми работаю. И мне известно, что в отличии от условного с++, принимаемый на вход тип данных может не соответствовать тому что указан в нотации, IDE может и что-то скажет об этом, но сам интерпретатор никаким образом ругаться не станет. Вопрос вот в чём:

Существуют ли библиотеки с декораторами(?), автоматизирующую проверку типов, и вызывающие исключения, в случае несоответствии проверки?

Выражаясь псевдоязыком, в моей голове это должно выглядеть так:

def incr(number: int) -> int:
    assert isinstance(number, int), TypeError
    result = number + 10
    assert isinstance(result, int), TypeError
    return result

Только разумеется, я имею в виду автоматизацию процедуры проверки всех принимаемых и возвращаемых значений


Ответы (2 шт):

Автор решения: Kram

Python - динамически типизированый язык, максимум можно сделать типа такого костыля:

from typing import get_type_hints


def static(func):
    def wrapper(*args):
        typehints = list(get_type_hints(func).values())
        with_return = False
        if len(args) > len(typehints):
            raise SyntaxError('invalid syntax.')
        elif len(args) < len(typehints):
            typehints = typehints[:-1]
            with_return = True
        else:
            pass
        for i in args:
            for j in typehints:
                if not isinstance(i, j):
                    raise TypeError(f'{j} type is not {i} type')
        if with_return:
            return func(*args)
        else:
            func(*args)

    return wrapper

Но данный декоратор обязывает человека использовать тайпхинты, плюс у него есть ещё пара минусов.

→ Ссылка
Автор решения: insolor

Есть библиотека typeguard (вроде бы слышал про нее раньше, но сейчас нашел через этот ответ), которая умеет в том числе добавлять проверки типов получаемых и возвращаемых значений функций.

Пример из документации:

from typeguard import typechecked

@typechecked
def some_function(a: int, b: float, c: str, *args: str) -> bool:
    ...
    return retval

@typechecked
class SomeClass:
    # All type annotated methods (including static and class methods and properties)
    # are type checked.
    # Does not apply to inner classes!
    def method(x: int) -> int:
        ...

Мой пример:

from typeguard import typechecked


@typechecked
def test(x: int) -> int:
    return x


print(test(10))  # Выведет 10
print(test("abcd"))  # TypeError: type of argument "x" must be int; got str instead

В том числе умеет проверять, например, что передан список целых чисел, а не произвольных значений:

from typing import List

from typeguard import typechecked


@typechecked
def test(x: List[int]) -> int:
    return sum(x)


print(test([1, 2, 3, "A"]))  # TypeError: type of argument "x"[3] must be int; got str instead

Похожая библиотека: strongtyping, работает похожим образом (правда, не умеет проверять тип возвращаемого значения):

from strongtyping.strong_typing import match_typing


@match_typing
def test(x: int) -> int:
    return x


print(test("abcd"))
# Incorrect parameter: [x] `'abcd'`
#         required: <class 'int'>
from strongtyping.strong_typing import match_typing
from typing import List

@match_typing
def test(x: List[int]) -> int:
    return sum(x)

print(test([1, 2, 3, "A"]))
# Incorrect parameter: [x] `[1, 2, 3, 'A']`
#         required: typing.List[int]
→ Ссылка