Задача 6D. Проверка типов

Входной файл:Стандартный вход   Ограничение времени:1 сек
Выходной файл:Стандартный выход   Ограничение памяти:512 Мб

Условие

Требуется реализовать на языке Python функцию-декоратор check_types, выполняющую проверку параметров и возвращаемого значения функции на соответствие аннотации при вызове функции. Функция должна поддерживать list, dict, Any, tuple, Optional, Union, set (см. typing) и проверку на полное соответствие типу, при этом объекты классов наследников разрешается передавать как объекты классов родителей. Если вызов функции не проходит проверку, необходимо вызвать исключение TypeCheckError, имеющее следующий интерфейс

class TypeCheckError(BaseException):
    
    # Имя аргумента, которому было передано значение неверного типа. `return`, если функция вернула значение неверного типа
    name: str

Функция также должна иметь возможность декорировать классы, при этом добавляется проверка на каждую функцию, определённую в классе. После декорирования такой класс должен быть доступен для использования в аннотациях других классов и функций.

Все тесты используют следующий функционал

from __future__ import annotations

Формат выходных данных

Код решения должен содержать определение функции check_types и класса TypeCheckError, а также все вспомогательные переменные, функции и классы.

Примеры тестов

Стандартный вход Стандартный выход
1 @check_types def add(a: int, b: int) -> int: return a + b try: print(add(1, 2)) print(add('1', '2')) except TypeCheckError as e: print(f'Failed {e.name}')
3
Failed a
2 @check_types def sqr(a: float) -> int: return a * a try: sqr(2.) except TypeCheckError as e: print(f'Failed {e.name}')
Failed return
3 @check_types class Number: def __init__(self: Number, value: int): self.value = value def __add__(self: Number, other: Number) -> Number: return Number(self.value + other.value) try: print((Number(1) + Number(2)).value) print((Number(1) + 2).value) except TypeCheckError as e: print(f'Failed {e.name}')
3
Failed other
4 @check_types def pow(x: int | list[int], p: int) -> int | list[int]: if isinstance(x, list): return [i ** p for i in x] return x ** p try: print(pow(2, 2)) print(*pow([1, 2, 3], 3)) print(*pow((1, 2, 3), 3)) except TypeCheckError as e: print(f'Failed {e.name}')
4
1 8 27
Failed x

0.084s 0.012s 13