Module pedantic.tests.tests_pedantic

Expand source code
import os.path
import sys
import types
import typing
import unittest
from dataclasses import dataclass
from datetime import datetime, date
from functools import wraps
from io import BytesIO, StringIO
from typing import List, Tuple, Callable, Any, Optional, Union, Dict, Set, FrozenSet, NewType, TypeVar, Sequence, \
    AbstractSet, Iterator, NamedTuple, Collection, Type, Generator, Generic, BinaryIO, TextIO, Iterable, Container, \
    NoReturn, ClassVar, Literal
from enum import Enum, IntEnum

from pedantic import pedantic_class
from pedantic.exceptions import PedanticTypeCheckException, PedanticException, PedanticCallWithArgsException, \
    PedanticTypeVarMismatchException
from pedantic.decorators.fn_deco_pedantic import pedantic

TEST_FILE = 'test.txt'


class Parent:
    pass


class Child(Parent):
    def method(self, a: int):
        pass


class TestDecoratorRequireKwargsAndTypeCheck(unittest.TestCase):
    def tearDown(self) -> None:
        if os.path.isfile(TEST_FILE):
            os.remove(TEST_FILE)

    def test_no_kwargs(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> int:
            return n + m + i

        with self.assertRaises(expected_exception=PedanticCallWithArgsException):
            calc(42, 40, 38)
        with self.assertRaises(expected_exception=PedanticCallWithArgsException):
            calc(42, m=40, i=38)
        calc(n=42, m=40, i=38)

    def test_nested_type_hints_1(self):
        @pedantic
        def calc(n: int) -> List[List[float]]:
            return [0.0 * n]

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42)

    def test_nested_type_hints_1_corrected(self):
        @pedantic
        def calc(n: int) -> List[List[float]]:
            return [[0.0 * n]]

        calc(n=42)

    def test_nested_type_hints_2(self):
        """Problem here: int != float"""
        @pedantic
        def calc(n: int) -> List[Tuple[float, str]]:
            return [(n, str(n))]

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42)

    def test_nested_type_hints_2_corrected(self):
        @pedantic
        def calc(n: int) -> List[Tuple[int, str]]:
            return [(n, str(n))]

        @pedantic
        def calc_2(n: float) -> List[Tuple[float, str]]:
            return [(n, str(n))]

        calc(n=42)
        calc_2(n=42.0)

    def test_nested_type_hints_3(self):
        """Problem here: inner function actually returns Tuple[int, str]"""
        @pedantic
        def calc(n: int) -> Callable[[int, float], Tuple[float, str]]:
            @pedantic
            def f(x: int, y: float) -> Tuple[float, str]:
                return n * x, str(y)
            return f

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42)(x=3, y=3.14)

    def test_nested_type_hints_3_corrected(self):
        @pedantic
        def calc(n: int) -> Callable[[int, float], Tuple[int, str]]:
            @pedantic
            def f(x: int, y: float) -> Tuple[int, str]:
                return n * x, str(y)

            return f

        calc(n=42)(x=3, y=3.14)

    def test_nested_type_hints_4(self):
        """Problem here: return type is actually float"""
        @pedantic
        def calc(n: List[List[float]]) -> int:
            return n[0][0]

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=[[42.0]])

    def test_nested_type_hints_corrected(self):
        @pedantic
        def calc(n: List[List[float]]) -> int:
            return int(n[0][0])

        calc(n=[[42.0]])

    def test_nested_type_hints_5(self):
        """Problem here: Tuple[float, str] != Tuple[float, float]"""

        @pedantic
        def calc(n: int) -> Callable[[int, float], Tuple[float, str]]:
            @pedantic
            def f(x: int, y: float) -> Tuple[float, float]:
                return n * float(x), y
            return f

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42)

    def test_nested_type_hints_5_corrected(self):
        @pedantic
        def calc(n: int) -> Callable[[int, float], Tuple[float, float]]:
            @pedantic
            def f(x: int, y: float) -> Tuple[float, float]:
                return n * float(x), y
            return f

        calc(n=42)

    def test_missing_type_hint_1(self):
        """Problem here: type hint for n missed"""
        @pedantic
        def calc(n) -> float:
            return 42.0 * n

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42)

    def test_missing_type_hint_1_corrected(self):
        @pedantic
        def calc(n: int) -> float:
            return 42.0 * n

        calc(n=42)

    def test_missing_type_hint_2(self):
        """Problem here: Return type annotation missed"""
        @pedantic
        def calc(n: int):
            return 'Hi' + str(n)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42)

    def test_missing_type_hint_2_corrected(self):
        @pedantic
        def calc(n: int) -> str:
            return 'Hi' + str(n)

        calc(n=42)

    def test_missing_type_hint_3(self):
        """Problem here: type hint for i missed"""
        @pedantic
        def calc(n: int, m: int, i) -> int:
            return n + m + i

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=38)

    def test_missing_type_hint_3_corrected(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> int:
            return n + m + i

        calc(n=42, m=40, i=38)

    def test_all_ok_2(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> str:
            return str(n + m + i)

        calc(n=42, m=40, i=38)

    def test_all_ok_3(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> None:
            str(n + m + i)

        calc(n=42, m=40, i=38)

    def test_all_ok_4(self):
        @pedantic
        def calc(n: int) -> List[List[int]]:
            return [[n]]

        calc(n=42)

    def test_all_ok_5(self):
        @pedantic
        def calc(n: int) -> List[Tuple[float, str]]:
            return [(float(n), str(n))]

        calc(n=42)

    def test_all_ok_6(self):
        @pedantic
        def calc(n: int) -> Callable[[int, float], Tuple[float, str]]:
            @pedantic
            def f(x: int, y: float) -> Tuple[float, str]:
                return n * float(x), str(y)
            return f

        calc(n=42)(x=72, y=3.14)

    def test_all_ok_7(self):
        @pedantic
        def calc(n: List[List[float]]) -> Any:
            return n[0][0]

        calc(n=[[42.0]])

    def test_all_ok_8(self):
        @pedantic
        def calc(n: int) -> Callable[[int, float], Tuple[float, str]]:
            @pedantic
            def f(x: int, y: float) -> Tuple[float, str]:
                return n * float(x), str(y)

            return f

        calc(n=42)(x=3, y=3.14)

    def test_wrong_type_hint_1(self):
        """Problem here: str != int"""
        @pedantic
        def calc(n: int, m: int, i: int) -> str:
            return n + m + i

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=38)

    def test_wrong_type_hint_1_corrected(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> str:
            return str(n + m + i)

        calc(n=42, m=40, i=38)

    def test_wrong_type_hint_2(self):
        """Problem here: str != int"""
        @pedantic
        def calc(n: int, m: int, i: str) -> int:
            return n + m + i

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=38)

    def test_wrong_type_hint_2_corrected(self):
        @pedantic
        def calc(n: int, m: int, i: str) -> int:
            return n + m + int(i)

        calc(n=42, m=40, i='38')

    def test_wrong_type_hint_3(self):
        """Problem here: None != int"""
        @pedantic
        def calc(n: int, m: int, i: int) -> None:
            return n + m + i

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=38)

    def test_wrong_type_hint_corrected(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> None:
            print(n + m + i)

        calc(n=42, m=40, i=38)

    def test_wrong_type_hint_4(self):
        """Problem here: None != int"""
        @pedantic
        def calc(n: int, m: int, i: int) -> int:
            print(n + m + i)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=38)

    def test_wrong_type_hint_4_corrected(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> int:
            return n + m + i

        calc(n=42, m=40, i=38)

    def test_none_1(self):
        """Problem here: None is not accepted"""
        @pedantic
        def calc(n: int, m: int, i: int) -> int:
            return n + m + i

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=None)

    def test_none_2(self):
        @pedantic
        def calc(n: int, m: int, i: Optional[int]) -> int:
            return n + m + i if i is not None else n + m

        calc(n=42, m=40, i=None)

    def test_none_3(self):
        @pedantic
        def calc(n: int, m: int, i: Union[int, None]) -> int:
            return n + m + i if i is not None else n + m

        calc(n=42, m=40, i=None)

    def test_none_4(self):
        """Problem here: function may return None"""
        @pedantic
        def calc(n: int, m: int, i: Union[int, None]) -> int:
            return n + m + i if i is not None else None

        calc(n=42, m=40, i=42)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=None)

    def test_none_5(self):
        @pedantic
        def calc(n: int, m: int, i: Union[int, None]) -> Optional[int]:
            return n + m + i if i is not None else None

        calc(n=42, m=40, i=None)

    def test_inheritance_1(self):
        class MyClassA:
            pass

        class MyClassB(MyClassA):
            pass

        @pedantic
        def calc(a: MyClassA) -> str:
            return str(a)

        calc(a=MyClassA())
        calc(a=MyClassB())

    def test_inheritance_2(self):
        """Problem here: A is not a subtype of B"""
        class MyClassA:
            pass

        class MyClassB(MyClassA):
            pass

        @pedantic
        def calc(a: MyClassB) -> str:
            return str(a)

        calc(a=MyClassB())
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(a=MyClassA())

    def test_instance_method_1(self):
        class MyClassA:
            @pedantic
            def calc(self, i: int) -> str:
                return str(i)

        a = MyClassA()
        a.calc(i=42)

    def test_instance_method_2(self):
        """Problem here: 'i' has no type annotation"""
        class MyClassA:
            @pedantic
            def calc(self, i) -> str:
                return str(i)

        a = MyClassA()
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            a.calc(i=42)

    def test_instance_method_2_corrected(self):
        class MyClassA:
            @pedantic
            def calc(self, i: int) -> str:
                return str(i)

        a = MyClassA()
        a.calc(i=42)

    def test_instance_method_int_is_not_float(self):
        class MyClassA:
            @pedantic
            def calc(self, i: float) -> str:
                return str(i)

        a = MyClassA()
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            a.calc(i=42)

    def test_instance_method_3_corrected(self):
        class MyClassA:
            @pedantic
            def calc(self, i: float) -> str:
                return str(i)

        a = MyClassA()
        a.calc(i=42.0)

    def test_instance_method_no_kwargs(self):
        class MyClassA:
            @pedantic
            def calc(self, i: int) -> str:
                return str(i)

        a = MyClassA()
        with self.assertRaises(expected_exception=PedanticCallWithArgsException):
            a.calc(42)

    def test_instance_method_5(self):
        """Problem here: instance methods is not called with kwargs"""
        class MyClassA:
            @pedantic
            def calc(self, i: int) -> str:
                return str(i)

        a = MyClassA()
        a.calc(i=42)

    def test_lambda_1(self):
        @pedantic
        def calc(i: float) -> Callable[[float], str]:
            return lambda x: str(x * i)

        calc(i=42.0)(10.0)

    def test_lambda_3(self):
        @pedantic
        def calc(i: float) -> Callable[[float], str]:
            def res(x: float) -> str:
                return str(x * i)
            return res

        calc(i=42.0)(10.0)

    def test_lambda_int_is_not_float(self):
        @pedantic
        def calc(i: float) -> Callable[[float], str]:
            def res(x: int) -> str:
                return str(x * i)
            return res

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(i=42.0)(x=10)

    def test_lambda_4_almost_corrected(self):
        """Problem here: float != str"""
        @pedantic
        def calc(i: float) -> Callable[[float], str]:
            @pedantic
            def res(x: int) -> str:
                return str(x * i)
            return res

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(i=42.0)(x=10)

    def test_lambda_4_almost_corrected_2(self):
        @pedantic
        def calc(i: float) -> Callable[[int], str]:
            @pedantic
            def res(x: int) -> str:
                return str(x * i)
            return res

        calc(i=42.0)(x=10)

    def test_lambda_5(self):
        """Problem here: float != int"""
        @pedantic
        def calc(i: float) -> Callable[[float], str]:
            @pedantic
            def res(x: float) -> str:
                return str(x * i)
            return res

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(i=42.0)(x=10)

    def test_lambda_corrected(self):
        @pedantic
        def calc(i: float) -> Callable[[float], str]:
            @pedantic
            def res(x: float) -> str:
                return str(x * i)

            return res

        calc(i=42.0)(x=10.0)

    def test_tuple_without_type_args(self):
        @pedantic
        def calc(i: Tuple) -> str:
            return str(i)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(i=(42.0, 43, 'hi'))

    def test_tuple_without_args_corrected(self):
        @pedantic
        def calc(i: Tuple[Any, ...]) -> str:
            return str(i)

        calc(i=(42.0, 43, 'hi'))

    def test_callable_without_type_args(self):
        @pedantic
        def calc(i: Callable) -> str:
            return str(i(' you'))

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(i=lambda x: (42.0, 43, 'hi', x))

    def test_callable_without_args_correct_with_lambdas(self):
        @pedantic
        def calc(i: Callable[[Any], Tuple[Any, ...]]) -> str:
            return str(i(x=' you'))

        calc(i=lambda x: (42.0, 43, 'hi', x))

    def test_callable_without_args_corrected(self):
        @pedantic
        def calc(i: Callable[[Any], Tuple[Any, ...]]) -> str:
            return str(i(x=' you'))

        @pedantic
        def arg(x: Any) -> Tuple[Any, ...]:
            return 42.0, 43, 'hi', x
        calc(i=arg)

    def test_list_without_args(self):
        @pedantic
        def calc(i: List) -> Any:
            return [i]

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(i=[42.0, 43, 'hi'])

    def test_list_without_args_corrected(self):
        @pedantic
        def calc(i: List[Any]) -> List[List[Any]]:
            return [i]

        calc(i=[42.0, 43, 'hi'])

    def test_ellipsis_in_callable_1(self):
        @pedantic
        def calc(i: Callable[..., int]) -> int:
            return i()

        @pedantic
        def call() -> int:
            return 42

        calc(i=call)

    def test_ellipsis_in_callable_2(self):
        @pedantic
        def calc(i: Callable[..., int]) -> int:
            return i(x=3.14, y=5)

        @pedantic
        def call(x: float, y: int) -> int:
            return 42

        calc(i=call)

    def test_ellipsis_in_callable_3(self):
        """Problem here: call to "call" misses one argument"""
        @pedantic
        def calc(i: Callable[..., int]) -> int:
            return i(x=3.14)

        @pedantic
        def call(x: float, y: int) -> int:
            return 42

        with self.assertRaises(expected_exception=PedanticException):
            calc(i=call)

    def test_optional_args_1(self):
        @pedantic
        def calc(a: int, b: int = 42) -> int:
            return a + b

        calc(a=2)

    def test_optional_args_2(self):
        @pedantic
        def calc(a: int = 3, b: int = 42, c: float = 5.0) -> float:
            return a + b + c

        calc()
        calc(a=1)
        calc(b=1)
        calc(c=1.0)
        calc(a=1, b=1)
        calc(a=1, c=1.0)
        calc(b=1, c=1.0)
        calc(a=1, b=1, c=1.0)

    def test_optional_args_3(self):
        """Problem here: optional argument c: 5 is not a float"""
        @pedantic
        def calc(a: int = 3, b: int = 42, c: float = 5) -> float:
            return a + b + c

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc()

    def test_optional_args_3_corrected(self):
        @pedantic
        def calc(a: int = 3, b: int = 42, c: float = 5.0) -> float:
            return a + b + c

        calc()

    def test_optional_args_4(self):
        class MyClass:
            @pedantic
            def foo(self, a: int, b: Optional[int] = 1) -> int:
                return a + b

        my_class = MyClass()
        my_class.foo(a=10)

    def test_optional_args_5(self):
        @pedantic
        def calc(d: Optional[Dict[int, int]] = None) -> Optional[int]:
            if d is None:
                return None
            return sum(d.keys())

        calc(d=None)
        calc()
        calc(d={42: 3})

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(d={42: 3.14})

    def test_optional_args_6(self):
        """"Problem here: str != int"""
        @pedantic
        def calc(d: int = 42) -> int:
            return int(d)

        calc(d=99999)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(d='999999')

    def test_enum_1(self):
        """Problem here: Type hint for 'a' should be MyEnum instead of MyEnum.GAMMA"""
        class MyEnum(Enum):
            ALPHA = 'startEvent'
            BETA = 'task'
            GAMMA = 'sequenceFlow'

        class MyClass:
            @pedantic
            def operation(self, a: MyEnum.GAMMA) -> None:
                print(a)

        m = MyClass()
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            m.operation(a=MyEnum.GAMMA)

    def test_enum_1_corrected(self):
        class MyEnum(Enum):
            ALPHA = 'startEvent'
            BETA = 'task'
            GAMMA = 'sequenceFlow'

        @pedantic
        def operation(a: MyEnum) -> None:
            print(a)

        operation(a=MyEnum.GAMMA)

    def test_sloppy_types_dict(self):
        @pedantic
        def operation(d: dict) -> int:
            return len(d.keys())

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d={1: 1, 2: 2})

    def test_sloppy_types_dict_almost_corrected_no_type_args(self):
        @pedantic
        def operation(d: Dict) -> int:
            return len(d.keys())

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d={1: 1, 2: 2})

    def test_sloppy_types_dict_corrected(self):
        @pedantic
        def operation(d: Dict[int, int]) -> int:
            return len(d.keys())

        operation(d={1: 1, 2: 2})

    def test_sloppy_types_list(self):
        @pedantic
        def operation(d: list) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d=[1, 2, 3, 4])

    def test_sloppy_types_list_almost_corrected_no_type_args(self):
        @pedantic
        def operation(d: List) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d=[1, 2, 3, 4])

    def test_sloppy_types_list_corrected(self):
        @pedantic
        def operation(d: List[int]) -> int:
            return len(d)

        operation(d=[1, 2, 3, 4])

    def test_sloppy_types_tuple(self):
        @pedantic
        def operation(d: tuple) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d=(1, 2, 3))

    def test_sloppy_types_tuple_almost_corrected_no_type_args(self):
        @pedantic
        def operation(d: Tuple) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d=(1, 2, 3))

    def test_sloppy_types_tuple_corrected(self):
        @pedantic
        def operation(d: Tuple[int, int, int]) -> int:
            return len(d)

        operation(d=(1, 2, 3))

    def test_sloppy_types_set(self):
        @pedantic
        def operation(d: set) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d={1, 2, 3})

    def test_sloppy_types_set_almost_corrected_to_type_args(self):
        @pedantic
        def operation(d: Set) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d={1, 2, 3})

    def test_sloppy_types_set_corrected(self):
        @pedantic
        def operation(d: Set[int]) -> int:
            return len(d)

        operation(d={1, 2, 3})

    def test_sloppy_types_frozenset(self):
        @pedantic
        def operation(d: frozenset) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d=frozenset({1, 2, 3}))

    def test_sloppy_types_frozenset_almost_corrected_no_type_args(self):
        @pedantic
        def operation(d: FrozenSet) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d=frozenset({1, 2, 3}))

    def test_sloppy_types_frozenset_corrected(self):
        @pedantic
        def operation(d: FrozenSet[int]) -> int:
            return len(d)

        operation(d=frozenset({1, 2, 3}))

    def test_type_list_but_got_tuple(self):
        @pedantic
        def calc(ls: List[Any]) -> int:
            return len(ls)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(ls=(1, 2, 3))

    def test_type_list_corrected(self):
        @pedantic
        def calc(ls: Tuple[Any, ...]) -> int:
            return len(ls)

        calc(ls=(1, 2, 3))

    def test_any(self):
        @pedantic
        def calc(ls: List[Any]) -> Dict[int, Any]:
            return {i: ls[i] for i in range(0, len(ls))}

        calc(ls=[1, 2, 3])
        calc(ls=[1.11, 2.0, 3.0])
        calc(ls=['1', '2', '3'])
        calc(ls=[10.5, '2', (3, 4, 5)])

    def test_aliases(self):
        Vector = List[float]

        @pedantic
        def scale(scalar: float, vector: Vector) -> Vector:
            return [scalar * num for num in vector]

        scale(scalar=2.0, vector=[1.0, -4.2, 5.4])

    def test_new_type(self):
        UserId = NewType('UserId', int)

        @pedantic
        def get_user_name(user_id: UserId) -> str:
            return str(user_id)

        some_id = UserId(524313)
        get_user_name(user_id=some_id)

        # the following would be desirable but impossible to check at runtime:
        # with self.assertRaises(expected_exception=AssertionError):
        #     get_user_name(user_id=-1)

    def test_list_of_new_type(self):
        UserId = NewType('UserId', int)

        @pedantic
        def get_user_name(user_ids: List[UserId]) -> str:
            return str(user_ids)

        get_user_name(user_ids=[UserId(524313), UserId(42)])
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            get_user_name(user_ids=[UserId(524313), UserId(42), 430.0])

    def test_callable_no_args(self):
        @pedantic
        def f(g: Callable[[], str]) -> str:
            return g()

        @pedantic
        def greetings() -> str:
            return 'hello world'

        f(g=greetings)

    def test_type_var(self):
        T = TypeVar('T')

        @pedantic
        def first(ls: List[T]) -> T:
            return ls[0]

        first(ls=[1, 2, 3])

    def test_type_var_wrong(self):
        T = TypeVar('T')

        @pedantic
        def first(ls: List[T]) -> T:
            return str(ls[0])

        with self.assertRaises(expected_exception=PedanticTypeVarMismatchException):
            first(ls=[1, 2, 3])

    def test_type_var_wrong_sequence(self):
        T = TypeVar('T')

        @pedantic
        def first(ls: Sequence[T]) -> T:
            return str(ls[0])

        with self.assertRaises(expected_exception=PedanticTypeVarMismatchException):
            first(ls=[1, 2, 3])

    def test_double_pedantic(self):
        @pedantic
        @pedantic
        def f(x: int, y: float) -> Tuple[float, str]:
            return float(x), str(y)

        f(x=5, y=3.14)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            f(x=5.0, y=3.14)
        with self.assertRaises(expected_exception=PedanticCallWithArgsException):
            f(5, 3.14)

    def test_args_kwargs(self):
        @pedantic
        def some_method(a: int = 0, b: float = 0.0) -> float:
            return a * b

        @pedantic
        def wrapper_method(*args: Union[int, float], **kwargs: Union[int, float]) -> float:
            return some_method(*args, **kwargs)

        some_method()
        with self.assertRaises(expected_exception=PedanticCallWithArgsException):
            some_method(3, 3.0)
        some_method(a=3, b=3.0)
        wrapper_method()
        with self.assertRaises(expected_exception=PedanticCallWithArgsException):
            wrapper_method(3, 3.0)
        wrapper_method(a=3, b=3.0)

    def test_args_kwargs_no_type_hint(self):
        @pedantic
        def method_no_type_hint(*args, **kwargs) -> None:
            print(args)
            print(kwargs)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            method_no_type_hint(a=3, b=3.0)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            method_no_type_hint()

    def test_args_kwargs_wrong_type_hint(self):
        """See: https://www.python.org/dev/peps/pep-0484/#arbitrary-argument-lists-and-default-argument-values"""
        @pedantic
        def wrapper_method(*args: str, **kwargs: str) -> None:
            print(args)
            print(kwargs)

        wrapper_method()
        wrapper_method('hi', 'you', ':)')
        wrapper_method(a='hi', b='you', c=':)')
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            wrapper_method('hi', 'you', ':)', 7)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            wrapper_method(3, 3.0)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            wrapper_method(a=3, b=3.0)

    def test_additional_kwargs(self):
        @pedantic
        def some_method(a: int, b: float = 0.0, **kwargs: int) -> float:
            return sum([a, b])

        some_method(a=5)
        some_method(a=5, b=0.1)
        some_method(a=5, b=0.1, c=4)
        some_method(a=5, b=0.1, c=4, d=5, e=6)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            some_method(a=5, b=0.1, c=4, d=5.0, e=6)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            some_method(a=5.0, b=0.1, c=4, d=5, e=6)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            some_method(a=5, b=0, c=4, d=5, e=6)

    def test_args_kwargs_different_types(self):
        @pedantic
        def foo(*args: str, **kwds: int) -> None:
            print(args)
            print(kwds)

        foo('a', 'b', 'c')
        foo(x=1, y=2)
        foo('', z=0)

    def test_pedantic_on_class(self):
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            @pedantic
            class MyClass:
                pass
            MyClass()

    def test_is_subtype_tuple(self):
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            @pedantic
            def foo() -> Callable[[Tuple[float, str]], Tuple[int]]:
                def bar(a: Tuple[float]) -> Tuple[int]:
                    return len(a[1]) + int(a[0]),
                return bar
            foo()

    def test_is_subtype_tuple_corrected(self):
        @pedantic
        def foo() -> Callable[[Tuple[float, str]], Tuple[int]]:
            def bar(a: Tuple[float, str]) -> Tuple[int]:
                return len(a[1]) + int(a[0]),
            return bar
        foo()

    def test_forward_ref(self):
        class Conversation:
            pass

        @pedantic
        def get_conversations() -> List['Conversation']:
            return [Conversation(), Conversation()]

        get_conversations()

    def test_alternative_list_type_hint(self):
        @pedantic
        def _is_digit_in_int(digit: [int], num: int) -> bool:
            num_str = str(num)
            for i in num_str:
                if int(i) == digit:
                    return True
            return False

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            _is_digit_in_int(digit=4, num=42)

    def test_callable_with_union_return(self):
        class MyClass:
            pass

        @pedantic
        def admin_required(func: Callable[..., Union[str, MyClass]]) -> Callable[..., Union[str, MyClass]]:
            @wraps(func)
            def decorated_function(*args, **kwargs):
                return func(*args, **kwargs)
            return decorated_function

        @admin_required
        @pedantic
        def get_server_info() -> str:
            return 'info'

        get_server_info()

    def test_pedantic(self):
        @pedantic
        def foo(a: int, b: str) -> str:
            return 'abc'

        self.assertEqual('abc', foo(a=4, b='abc'))

    def test_pedantic_always(self):
        @pedantic
        def foo(a: int, b: str) -> str:
            return 'abc'

        self.assertEqual('abc', foo(a=4, b='abc'))

    def test_pedantic_arguments_fail(self):
        @pedantic
        def foo(a: int, b: str) -> str:
            return 'abc'

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            foo(a=4, b=5)

    def test_pedantic_return_type_fail(self):
        @pedantic
        def foo(a: int, b: str) -> str:
            return 6

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            foo(a=4, b='abc')

    def test_return_type_none(self):
        @pedantic
        def foo() -> None:
            return 'a'
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            foo()

    def test_marco(self):
        @pedantic_class
        class A:
            def __init__(self, val: int) -> None:
                self.val = val

            def __eq__(self, other: 'A') -> bool:  # other: A and all subclasses
                return self.val == other.val

        @pedantic_class
        class B(A):
            def __init__(self, val: int) -> None:
                super().__init__(val=val)

        @pedantic_class
        class C(A):
            def __init__(self, val: int) -> None:
                super().__init__(val=val)

        a = A(val=42)
        b = B(val=42)
        c = C(val=42)

        assert a == b  # works
        assert a == c  # works
        assert b == c  # error

    def test_date_datetime(self):
        @pedantic
        def foo(a: datetime, b: date) -> None:
            pass

        foo(a=datetime(1995, 2, 5), b=date(1987, 8, 7))
        foo(a=datetime(1995, 2, 5), b=datetime(1987, 8, 7))

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            foo(a=date(1995, 2, 5), b=date(1987, 8, 7))

    def test_any_type(self):
        @pedantic
        def foo(a: Any) -> None:
            pass

        foo(a='aa')

    def test_callable_exact_arg_count(self):
        @pedantic
        def foo(a: Callable[[int, str], int]) -> None:
            pass

        def some_callable(x: int, y: str) -> int:
            pass

        foo(a=some_callable)

    def test_callable_bad_type(self):
        @pedantic
        def foo(a: Callable[..., int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_callable_too_few_arguments(self):
        @pedantic
        def foo(a: Callable[[int, str], int]) -> None:
            pass

        def some_callable(x: int) -> int:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=some_callable)

    def test_callable_mandatory_kwonlyargs(self):
        @pedantic
        def foo(a: Callable[[int, str], int]) -> None:
            pass

        def some_callable(x: int, y: str, *, z: float, bar: str) -> int:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=some_callable)

    def test_callable_class(self):
        """
        Test that passing a class as a callable does not count the "self" argument "a"gainst the
        ones declared in the Callable specification.

        """
        @pedantic
        def foo(a: Callable[[int, str], Any]) -> None:
            pass

        class SomeClass:
            def __init__(self, x: int, y: str):
                pass

        foo(a=SomeClass)

    def test_callable_plain(self):
        @pedantic
        def foo(a: Callable[..., Any]) -> None:
            pass

        def callback(a):
            pass

        foo(a=callback)

    def test_callable_bound_method(self):
        @pedantic
        def foo(callback: Callable[[int], Any]) -> None:
            pass

        foo(callback=Child().method)

    def test_callable_defaults(self):
        """
        Test that a callable having "too many" arguments don't raise an error if the extra
        arguments have default values.

        """
        @pedantic
        def foo(callback: Callable[[int, str], Any]) -> None:
            pass

        def some_callable(x: int, y: str, z: float = 1.2) -> int:
            pass

        foo(callback=some_callable)

    def test_callable_builtin(self):
        @pedantic
        def foo(callback: types.BuiltinFunctionType) -> None:
            pass

        foo(callback=[].append)

    def test_dict_bad_type(self):
        @pedantic
        def foo(a: Dict[str, int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_dict_bad_key_type(self):
        @pedantic
        def foo(a: Dict[str, int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a={1: 2})

    def test_dict_bad_value_type(self):
        @pedantic
        def foo(a: Dict[str, int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a={'x': 'a'})

    def test_list_bad_type(self):
        @pedantic
        def foo(a: List[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_list_bad_element(self):
        @pedantic
        def foo(a: List[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=[1, 2, 'bb'])

    def test_sequence_bad_type(self):
        @pedantic
        def foo(a: Sequence[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_sequence_bad_element(self):
        @pedantic
        def foo(a: Sequence[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=[1, 2, 'bb'])

    def test_abstractset_custom_type(self):
        T = TypeVar('T')

        @pedantic_class
        class DummySet(AbstractSet[T]):
            def __contains__(self, x: object) -> bool:
                return x == 1

            def __len__(self) -> T:
                return 1

            def __iter__(self) -> Iterator[T]:
                yield 1

        @pedantic
        def foo(a: AbstractSet[int]) -> None:
            pass

        foo(a=DummySet[int]())

    def test_abstractset_bad_type(self):
        @pedantic
        def foo(a: AbstractSet[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_set_bad_type(self):
        @pedantic
        def foo(a: Set[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_abstractset_bad_element(self):
        @pedantic
        def foo(a: AbstractSet[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a={1, 2, 'bb'})

    def test_set_bad_element(self):
        @pedantic
        def foo(a: Set[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a={1, 2, 'bb'})

    def test_tuple_bad_type(self):
        @pedantic
        def foo(a: Tuple[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_tuple_too_many_elements(self):
        @pedantic
        def foo(a: Tuple[int, str]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=(1, 'aa', 2))

    def test_tuple_too_few_elements(self):
        @pedantic
        def foo(a: Tuple[int, str]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=(1,))

    def test_tuple_bad_element(self):
        @pedantic
        def foo(a: Tuple[int, str]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=(1, 2))

    def test_tuple_ellipsis_bad_element(self):
        @pedantic
        def foo(a: Tuple[int, ...]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=(1, 2, 'blah'))

    def test_namedtuple(self):
        Employee = NamedTuple('Employee', [('name', str), ('id', int)])

        @pedantic
        def foo(bar: Employee) -> None:
            print(bar)

        foo(bar=Employee('bob', 1))

    def test_namedtuple_key_mismatch(self):
        Employee1 = NamedTuple('Employee', [('name', str), ('id', int)])
        Employee2 = NamedTuple('Employee', [('firstname', str), ('id', int)])

        @pedantic
        def foo(bar: Employee1) -> None:
            print(bar)

        with self.assertRaises(PedanticTypeCheckException):
            foo(bar=Employee2('bob', 1))

    def test_namedtuple_type_mismatch(self):
        Employee = NamedTuple('Employee', [('name', str), ('id', int)])

        @pedantic
        def foo(bar: Employee) -> None:
            print(bar)

        with self.assertRaises(PedanticTypeCheckException):
            foo(bar=('bob', 1))

    def test_namedtuple_huge_type_mismatch(self):
        Employee = NamedTuple('Employee', [('name', str), ('id', int)])

        @pedantic
        def foo(bar: int) -> None:
            print(bar)

        with self.assertRaises(PedanticTypeCheckException):
            foo(bar=foo(bar=Employee('bob', 1)))

    def test_namedtuple_wrong_field_type(self):
        Employee = NamedTuple('Employee', [('name', str), ('id', int)])

        @pedantic
        def foo(bar: Employee) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(bar=Employee(2, 1))

    def test_union(self):
        @pedantic
        def foo(a: Union[str, int]) -> None:
            pass

        for value in [6, 'xa']:
            foo(a=value)

    def test_union_new_syntax(self):
        @pedantic
        def foo(a: str | int) -> None:
            pass

        for value in [6, 'xa']:
            foo(a=value)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=1.7)

    def test_union_typing_type(self):
        @pedantic
        def foo(a: Union[str, Collection]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=1)

    def test_union_fail(self):
        @pedantic
        def foo(a: Union[str, int]) -> None:
            pass

        for value in [5.6, b'xa']:
            with self.assertRaises(PedanticTypeCheckException):
                foo(a=value)

    def test_type_var_constraints(self):
        T = TypeVar('T', int, str)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        for values in [
            {'a': 6, 'b': 7},
            {'a': 'aa', 'b': "bb"},
        ]:
            foo(**values)

    def test_type_var_constraints_fail_typing_type(self):
        T = TypeVar('T', int, Collection)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a='aa', b='bb')

    def test_typevar_constraints_fail(self):
        T = TypeVar('T', int, str)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=2.5, b='aa')

    def test_typevar_bound(self):
        T = TypeVar('T', bound=Parent)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        foo(a=Child(), b=Child())

    def test_type_var_bound_fail(self):
        T = TypeVar('T', bound=Child)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=Parent(), b=Parent())

    def test_type_var_invariant_fail(self):
        T = TypeVar('T', int, str)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=2, b=3.6)

    def test_type_var_covariant(self):
        T = TypeVar('T', covariant=True)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        foo(a=Parent(), b=Child())

    def test_type_var_covariant_fail(self):
        T = TypeVar('T', covariant=True)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        with self.assertRaises(PedanticTypeVarMismatchException):
            foo(a=Child(), b=Parent())

    def test_type_var_contravariant(self):
        T = TypeVar('T', contravariant=True)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        foo(a=Child(), b=Parent())

    def test_type_var_contravariant_fail(self):
        T = TypeVar('T', contravariant=True)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        with self.assertRaises(PedanticTypeVarMismatchException):
            foo(a=Parent(), b=Child())

    def test_class_bad_subclass(self):
        @pedantic
        def foo(a: Type[Child]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=Parent)

    def test_class_any(self):
        @pedantic
        def foo(a: Type[Any]) -> None:
            pass

        foo(a=str)

    def test_wrapped_function(self):
        def decorator(func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                return func(*args, **kwargs)
            return wrapper

        @pedantic
        @decorator
        def foo(a: 'Child') -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=Parent())

    def test_mismatching_default_type(self):
        @pedantic
        def foo(a: str = 1) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo()

    def test_implicit_default_none(self):
        """
        Test that if the default value is ``None``, a ``None`` argument can be passed.

        """
        @pedantic
        def foo(a: Optional[str] = None) -> None:
            pass

        foo()

    def test_generator_simple(self):
        """Test that argument type checking works in a generator function too."""
        @pedantic
        def generate(a: int) -> Generator[int, int, None]:
            yield a
            yield a + 1

        gen = generate(a=1)
        next(gen)

    def test_wrapped_generator_no_return_type_annotation(self):
        """Test that return type checking works in a generator function too."""
        @pedantic
        def generate(a: int) -> Generator[int, int, None]:
            yield a
            yield a + 1

        gen = generate(a=1)
        next(gen)

    def test_varargs(self):
        @pedantic
        def foo(*args: int) -> None:
            pass

        foo(1, 2)

    def test_varargs_fail(self):
        @pedantic
        def foo(*args: int) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(1, 'a')

    def test_kwargs(self):
        @pedantic
        def foo(**kwargs: int) -> None:
            pass

        foo(a=1, b=2)

    def test_kwargs_fail(self):
        @pedantic
        def foo(**kwargs: int) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=1, b='a')

    def test_generic(self):
        T_Foo = TypeVar('T_Foo')

        class FooGeneric(Generic[T_Foo]):
            pass

        @pedantic
        def foo(a: FooGeneric[str]) -> None:
            print(a)

        foo(a=FooGeneric[str]())

    def test_newtype(self):
        myint = NewType("myint", int)

        @pedantic
        def foo(a: myint) -> int:
            return 42

        assert foo(a=1) == 42

        with self.assertRaises(PedanticTypeCheckException):
            foo(a="a")

    def test_collection(self):
        @pedantic
        def foo(a: Collection) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=True)

    def test_binary_io(self):
        @pedantic
        def foo(a: BinaryIO) -> None:
            print(a)

        foo(a=BytesIO())

    def test_text_io(self):
        @pedantic
        def foo(a: TextIO) -> None:
            print(a)

        foo(a=StringIO())

    def test_binary_io_fail(self):
        @pedantic
        def foo(a: TextIO) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=BytesIO())

    def test_text_io_fail(self):
        @pedantic
        def foo(a: BinaryIO) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=StringIO())

    def test_binary_io_real_file(self):
        @pedantic
        def foo(a: BinaryIO) -> None:
            print(a)

        with open(file=TEST_FILE, mode='wb') as f:
            foo(a=f)

    def test_text_io_real_file(self):
        @pedantic
        def foo(a: TextIO) -> None:
            print(a)

        with open(file=TEST_FILE, mode='w') as f:
            foo(a=f)

    def test_pedantic_return_type_var_fail(self):
        T = TypeVar('T', int, float)

        @pedantic
        def foo(a: T, b: T) -> T:
            return 'a'

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=4, b=2)

    def test_callable(self):
        @pedantic
        def foo_1(a: Callable[..., int]) -> None:
            print(a)

        @pedantic
        def foo_2(a: Callable) -> None:
            print(a)

        def some_callable() -> int:
            return 4

        foo_1(a=some_callable)

        with self.assertRaises(PedanticTypeCheckException):
            foo_2(a=some_callable)

    def test_list(self):
        @pedantic
        def foo_1(a: List[int]) -> None:
            print(a)

        @pedantic
        def foo_2(a: List) -> None:
            print(a)

        @pedantic
        def foo_3(a: list) -> None:
            print(a)

        @pedantic
        def foo_4(a: list[int]) -> None:
            print(a)

        foo_1(a=[1, 2])

        with self.assertRaises(PedanticTypeCheckException):
            foo_2(a=[1, 2])

        with self.assertRaises(PedanticTypeCheckException):
            foo_3(a=[1, 2])


        foo_4(a=[1, 2])

    def test_dict(self):
        @pedantic
        def foo_1(a: Dict[str, int]) -> None:
            print(a)

        @pedantic
        def foo_2(a: Dict) -> None:
            print(a)

        @pedantic
        def foo_3(a: dict) -> None:
            print(a)

        @pedantic
        def foo_4(a: dict[str, int]) -> None:
            print(a)

        foo_1(a={'x': 2})

        with self.assertRaises(PedanticTypeCheckException):
            foo_2(a={'x': 2})

        with self.assertRaises(PedanticTypeCheckException):
            foo_3(a={'x': 2})

        foo_4(a={'x': 2})

    def test_sequence(self):
        @pedantic
        def foo(a: Sequence[str]) -> None:
            print(a)

        for value in [('a', 'b'), ['a', 'b'], 'abc']:
            foo(a=value)

    def test_sequence_no_type_args(self):
        @pedantic
        def foo(a: Sequence) -> None:
            print(a)

        for value in [('a', 'b'), ['a', 'b'], 'abc']:
            with self.assertRaises(PedanticTypeCheckException):
                foo(a=value)

    def test_iterable(self):
        @pedantic
        def foo(a: Iterable[str]) -> None:
            print(a)

        for value in [('a', 'b'), ['a', 'b'], 'abc']:
            foo(a=value)

    def test_iterable_no_type_args(self):
        @pedantic
        def foo(a: Iterable) -> None:
            print(a)

        for value in [('a', 'b'), ['a', 'b'], 'abc']:
            with self.assertRaises(PedanticTypeCheckException):
                foo(a=value)

    def test_container(self):
        @pedantic
        def foo(a: Container[str]) -> None:
            print(a)

        for value in [('a', 'b'), ['a', 'b'], 'abc']:
            foo(a=value)

    def test_container_no_type_args(self):
        @pedantic
        def foo(a: Container) -> None:
            print(a)

        for value in [('a', 'b'), ['a', 'b'], 'abc']:
            with self.assertRaises(PedanticTypeCheckException):
                foo(a=value)

    def test_set(self):
        @pedantic
        def foo_1(a: AbstractSet[int]) -> None:
            print(a)

        @pedantic
        def foo_2(a: Set[int]) -> None:
            print(a)

        for value in [set(), {6}]:
            foo_1(a=value)
            foo_2(a=value)

    def test_set_no_type_args(self):
        @pedantic
        def foo_1(a: AbstractSet) -> None:
            print(a)

        @pedantic
        def foo_2(a: Set) -> None:
            print(a)

        @pedantic
        def foo_3(a: set) -> None:
            print(a)

        for value in [set(), {6}]:
            with self.assertRaises(PedanticTypeCheckException):
                foo_1(a=value)

            with self.assertRaises(PedanticTypeCheckException):
                foo_2(a=value)

            with self.assertRaises(PedanticTypeCheckException):
                foo_3(a=value)

    def test_tuple(self):
        @pedantic
        def foo_1(a: Tuple[int, int]) -> None:
            print(a)

        @pedantic
        def foo_2(a: Tuple[int, ...]) -> None:
            print(a)

        foo_1(a=(1, 2))
        foo_2(a=(1, 2))

    def test_tuple_no_type_args(self):
        @pedantic
        def foo_1(a: Tuple) -> None:
            print(a)

        @pedantic
        def foo_2(a: tuple) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo_1(a=(1, 2))

        with self.assertRaises(PedanticTypeCheckException):
            foo_2(a=(1, 2))

    def test_empty_tuple(self):
        @pedantic
        def foo(a: Tuple[()]) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=())

    def test_class(self):
        @pedantic
        def foo_1(a: Type[Parent]) -> None:
            print(a)

        @pedantic
        def foo_2(a: Type[TypeVar('UnboundType')]) -> None:
            print(a)

        @pedantic
        def foo_3(a: Type[TypeVar('BoundType', bound=Parent)]) -> None:
            print(a)

        foo_1(a=Child)
        foo_2(a=Child)
        foo_3(a=Child)

    def test_class_no_type_vars(self):
        @pedantic
        def foo_1(a: Type) -> None:
            print(a)

        @pedantic
        def foo_2(a: type) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo_1(a=Child)

        with self.assertRaises(PedanticTypeCheckException):
            foo_2(a=Child)

    def test_class_not_a_class(self):
        @pedantic
        def foo(a: Type[Parent]) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=1)

    def test_complex(self):
        @pedantic
        def foo(a: complex) -> None:
            print(a)

        foo(a=complex(1, 5))

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=1.0)

    def test_float(self):
        @pedantic
        def foo(a: float) -> None:
            print(a)

        foo(a=1.5)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=1)

    def test_coroutine_correct_return_type(self):
        @pedantic
        async def foo() -> str:
            return 'foo'

        coro = foo()

        with self.assertRaises(StopIteration):
            coro.send(None)

    def test_coroutine_wrong_return_type(self):
        @pedantic
        async def foo() -> str:
            return 1

        coro = foo()

        with self.assertRaises(PedanticTypeCheckException):
            coro.send(None)

    def test_bytearray_bytes(self):
        @pedantic
        def foo(x: bytearray) -> None:
            pass

        foo(x=bytearray([1]))

    def test_class_decorator(self):
        @pedantic_class
        class Foo:
            @staticmethod
            def staticmethod() -> int:
                return 'foo'

            @classmethod
            def classmethod(cls) -> int:
                return 'foo'

            def method(self) -> int:
                return 'foo'

        with self.assertRaises(PedanticTypeCheckException):
            Foo.staticmethod()

        with self.assertRaises(PedanticTypeCheckException):
            Foo.classmethod()

        with self.assertRaises(PedanticTypeCheckException):
            Foo().method()

    def test_generator(self):
        @pedantic
        def genfunc() -> Generator[int, str, List[str]]:
            val1 = yield 2
            val2 = yield 3
            val3 = yield 4
            return [val1, val2, val3]

        gen = genfunc()

        with self.assertRaises(StopIteration):
            value = next(gen)
            while True:
                value = gen.send(str(value))
                assert isinstance(value, int)

    def test_generator_no_type_args(self):
        @pedantic
        def genfunc() -> Generator:
            val1 = yield 2
            val2 = yield 3
            val3 = yield 4
            return [val1, val2, val3]

        with self.assertRaises(PedanticTypeCheckException):
            genfunc()

    def test_iterator(self):
        @pedantic
        def genfunc() -> Iterator[int]:
            val1 = yield 2
            val2 = yield 3
            val3 = yield 4
            return [val1, val2, val3]

        gen = genfunc()

        with self.assertRaises(PedanticTypeCheckException):
            value = next(gen)
            while True:
                value = gen.send(str(value))
                assert isinstance(value, int)

    def test_iterator_no_type_args(self):
        @pedantic
        def genfunc() -> Iterator:
            val1 = yield 2
            val2 = yield 3
            val3 = yield 4
            return [val1, val2, val3]

        with self.assertRaises(PedanticTypeCheckException):
            genfunc()

    def test_iterable_advanced(self):
        @pedantic
        def genfunc() -> Iterable[int]:
            val1 = yield 2
            val2 = yield 3
            val3 = yield 4
            return [val1, val2, val3]

        gen = genfunc()

        with self.assertRaises(PedanticTypeCheckException):
            value = next(gen)
            while True:
                value = gen.send(str(value))
                assert isinstance(value, int)

    def test_iterable_advanced_no_type_args(self):
        @pedantic
        def genfunc() -> Iterable:
            val1 = yield 2
            val2 = yield 3
            val3 = yield 4
            return [val1, val2, val3]

        with self.assertRaises(PedanticTypeCheckException):
            genfunc()

    def test_generator_bad_yield(self):
        @pedantic
        def genfunc_1() -> Generator[int, str, None]:
            yield 'foo'

        @pedantic
        def genfunc_2() -> Iterable[int]:
            yield 'foo'

        @pedantic
        def genfunc_3() -> Iterator[int]:
            yield 'foo'

        gen = genfunc_1()

        with self.assertRaises(PedanticTypeCheckException):
            next(gen)

        gen = genfunc_2()

        with self.assertRaises(PedanticTypeCheckException):
            next(gen)

        gen = genfunc_3()

        with self.assertRaises(PedanticTypeCheckException):
            next(gen)

    def test_generator_bad_send(self):
        @pedantic
        def genfunc() -> Generator[int, str, None]:
            yield 1
            yield 2

        gen = genfunc()
        next(gen)

        with self.assertRaises(PedanticTypeCheckException):
            gen.send(2)

    def test_generator_bad_return(self):
        @pedantic
        def genfunc() -> Generator[int, str, str]:
            yield 1
            return 6

        gen = genfunc()
        next(gen)

        with self.assertRaises(PedanticTypeCheckException):
            gen.send('foo')

    def test_return_generator(self):
        @pedantic
        def genfunc() -> Generator[int, None, None]:
            yield 1

        @pedantic
        def foo() -> Generator[int, None, None]:
            return genfunc()

        foo()

    def test_local_class(self):
        @pedantic_class
        class LocalClass:
            class Inner:
                pass

            def create_inner(self) -> 'Inner':
                return self.Inner()

        retval = LocalClass().create_inner()
        assert isinstance(retval, LocalClass.Inner)

    def test_local_class_async(self):
        @pedantic_class
        class LocalClass:
            class Inner:
                pass

            async def create_inner(self) -> 'Inner':
                return self.Inner()

        coro = LocalClass().create_inner()

        with self.assertRaises(StopIteration):
            coro.send(None)

    def test_callable_nonmember(self):
        class CallableClass:
            def __call__(self):
                pass

        @pedantic_class
        class LocalClass:
            some_callable = CallableClass()

    def test_inherited_class_method(self):
        @pedantic_class
        class Parent:
            @classmethod
            def foo(cls, x: str) -> str:
                return cls.__name__

        @pedantic_class
        class Child(Parent):
            pass

        self.assertEqual('Parent', Child.foo(x='bar'))

        with self.assertRaises(PedanticTypeCheckException):
            Child.foo(x=1)

    def test_type_var_forward_ref_bound(self):
        TBound = TypeVar('TBound', bound='Parent')

        @pedantic
        def func(x: TBound) -> None:
            pass

        func(x=Parent())

        with self.assertRaises(PedanticTypeCheckException):
            func(x='foo')

    def test_noreturn(self):
        @pedantic
        def foo() -> NoReturn:
            pass

        @pedantic
        def bar() -> NoReturn:
            raise ZeroDivisionError('bar')

        with self.assertRaises(expected_exception=ZeroDivisionError):
            bar()

        with self.assertRaises(PedanticTypeCheckException):
            foo()

    def test_literal(self):
        @pedantic
        def foo(a: Literal[1, True, 'x', b'y', 404]) -> None:
            print(a)

        foo(a=404)
        foo(a=True)
        foo(a='x')

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=4)

    def test_literal_union(self):
        @pedantic
        def foo(a: Union[str, Literal[1, 6, 8]]) -> None:
            print(a)

        foo(a=6)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=4)

    def test_literal_illegal_value(self):
        @pedantic
        def foo(a: Literal[1, 1.1]) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=4)

    def test_enum(self):
        with self.assertRaises(PedanticTypeCheckException):
            @pedantic_class
            class MyEnum(Enum):
                A = 'a'

    def test_enum_aggregate(self):
        T = TypeVar('T', bound=IntEnum)

        @pedantic_class
        class EnumAggregate(Generic[T]):
            enum: ClassVar[Type[T]]

            def __init__(self, value: Union[int, str, List[T]]) -> None:
                assert len(self.enum) < 10

                if value == '':
                    raise ValueError(f'Parameter "value" cannot be empty!')

                if isinstance(value, list):
                    self._value = ''.join([str(x.value) for x in value])
                else:
                    self._value = str(value)

                self._value = ''.join(sorted(self._value))  # sort characters in string
                self.to_list()  # check if is valid

            def __contains__(self, item: T) -> bool:
                return item in self.to_list()

            def __eq__(self, other: Union['EnumAggregate', str]) -> bool:
                if isinstance(other, str):
                    return self._value == other

                return self._value == other._value

            def __str__(self) -> str:
                return self._value

            def to_list(self) -> List[T]:
                return [self.enum(int(character)) for character in self._value]

            @property
            def value(self) -> str:
                return self._value

            @classmethod
            def all(cls) -> str:
                return ''.join([str(x.value) for x in cls.enum])

        class Gender(IntEnum):
            MALE = 1
            FEMALE = 2
            DIVERS = 3

        @pedantic_class
        class Genders(EnumAggregate[Gender]):
            enum = Gender

        Genders(value=12)

        with self.assertRaises(PedanticTypeCheckException):
            Genders(value=Child())

    def test_primitive_list_dict_tuple(self):
        @pedantic
        def f(x: list[dict[int, tuple[float, str]]]) -> list[Any]:
            return x

        f(x=[{3: (3.24, 'hi')}])

        for value in [
            [{3, (3, 'hi')}],
            [{3: (3, 'hi')}],
            [{3: (3.24, 3)}],
            [{3: (3.24, 'hi')}, {0}],
        ]:
            with self.assertRaises(PedanticTypeCheckException):
                f(x=value)

    def test_dataclass_protocol(self):
        class IsDataclass(typing.Protocol):
            __dataclass_fields__: ClassVar[Dict]

        @dataclass
        class Foo:
            v: int

        @pedantic
        def foo(x: IsDataclass) -> IsDataclass:
            return x

        foo(x=Foo(v=42))

    def test_dataclass_protocol_in_type(self):
        class IsDataclass(typing.Protocol):
            __dataclass_fields__: ClassVar[Dict]

        @dataclass
        class Foo:
            v: int

        @pedantic
        def foo(x: type[IsDataclass]) -> IsDataclass:
            return x

        assert foo(x=Foo) == Foo

    def test_dataclass_protocol_in_type_with_union(self):
        class IsDataclass(typing.Protocol):
            __dataclass_fields__: ClassVar[Dict]

        @dataclass
        class Foo:
            v: int

        @pedantic
        def foo(x: type[None | bool | IsDataclass]) -> IsDataclass:
            return x

        assert foo(x=Foo) == Foo

Classes

class Child
Expand source code
class Child(Parent):
    def method(self, a: int):
        pass

Ancestors

Methods

def method(self, a: int)
Expand source code
def method(self, a: int):
    pass
class Parent
Expand source code
class Parent:
    pass

Subclasses

class TestDecoratorRequireKwargsAndTypeCheck (methodName='runTest')

A class whose instances are single test cases.

By default, the test code itself should be placed in a method named 'runTest'.

If the fixture may be used for many test cases, create as many test methods as are needed. When instantiating such a TestCase subclass, specify in the constructor arguments the name of the test method that the instance is to execute.

Test authors should subclass TestCase for their own tests. Construction and deconstruction of the test's environment ('fixture') can be implemented by overriding the 'setUp' and 'tearDown' methods respectively.

If it is necessary to override the init method, the base class init method must always be called. It is important that subclasses should not change the signature of their init method, since instances of the classes are instantiated automatically by parts of the framework in order to be run.

When subclassing TestCase, you can set these attributes: * failureException: determines which exception will be raised when the instance's assertion methods fail; test methods raising this exception will be deemed to have 'failed' rather than 'errored'. * longMessage: determines whether long messages (including repr of objects used in assert methods) will be printed on failure in addition to any explicit message passed. * maxDiff: sets the maximum length of a diff in failure messages by assert methods using difflib. It is looked up as an instance attribute so can be configured by individual tests if required.

Create an instance of the class that will use the named test method when executed. Raises a ValueError if the instance does not have a method with the specified name.

Expand source code
class TestDecoratorRequireKwargsAndTypeCheck(unittest.TestCase):
    def tearDown(self) -> None:
        if os.path.isfile(TEST_FILE):
            os.remove(TEST_FILE)

    def test_no_kwargs(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> int:
            return n + m + i

        with self.assertRaises(expected_exception=PedanticCallWithArgsException):
            calc(42, 40, 38)
        with self.assertRaises(expected_exception=PedanticCallWithArgsException):
            calc(42, m=40, i=38)
        calc(n=42, m=40, i=38)

    def test_nested_type_hints_1(self):
        @pedantic
        def calc(n: int) -> List[List[float]]:
            return [0.0 * n]

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42)

    def test_nested_type_hints_1_corrected(self):
        @pedantic
        def calc(n: int) -> List[List[float]]:
            return [[0.0 * n]]

        calc(n=42)

    def test_nested_type_hints_2(self):
        """Problem here: int != float"""
        @pedantic
        def calc(n: int) -> List[Tuple[float, str]]:
            return [(n, str(n))]

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42)

    def test_nested_type_hints_2_corrected(self):
        @pedantic
        def calc(n: int) -> List[Tuple[int, str]]:
            return [(n, str(n))]

        @pedantic
        def calc_2(n: float) -> List[Tuple[float, str]]:
            return [(n, str(n))]

        calc(n=42)
        calc_2(n=42.0)

    def test_nested_type_hints_3(self):
        """Problem here: inner function actually returns Tuple[int, str]"""
        @pedantic
        def calc(n: int) -> Callable[[int, float], Tuple[float, str]]:
            @pedantic
            def f(x: int, y: float) -> Tuple[float, str]:
                return n * x, str(y)
            return f

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42)(x=3, y=3.14)

    def test_nested_type_hints_3_corrected(self):
        @pedantic
        def calc(n: int) -> Callable[[int, float], Tuple[int, str]]:
            @pedantic
            def f(x: int, y: float) -> Tuple[int, str]:
                return n * x, str(y)

            return f

        calc(n=42)(x=3, y=3.14)

    def test_nested_type_hints_4(self):
        """Problem here: return type is actually float"""
        @pedantic
        def calc(n: List[List[float]]) -> int:
            return n[0][0]

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=[[42.0]])

    def test_nested_type_hints_corrected(self):
        @pedantic
        def calc(n: List[List[float]]) -> int:
            return int(n[0][0])

        calc(n=[[42.0]])

    def test_nested_type_hints_5(self):
        """Problem here: Tuple[float, str] != Tuple[float, float]"""

        @pedantic
        def calc(n: int) -> Callable[[int, float], Tuple[float, str]]:
            @pedantic
            def f(x: int, y: float) -> Tuple[float, float]:
                return n * float(x), y
            return f

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42)

    def test_nested_type_hints_5_corrected(self):
        @pedantic
        def calc(n: int) -> Callable[[int, float], Tuple[float, float]]:
            @pedantic
            def f(x: int, y: float) -> Tuple[float, float]:
                return n * float(x), y
            return f

        calc(n=42)

    def test_missing_type_hint_1(self):
        """Problem here: type hint for n missed"""
        @pedantic
        def calc(n) -> float:
            return 42.0 * n

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42)

    def test_missing_type_hint_1_corrected(self):
        @pedantic
        def calc(n: int) -> float:
            return 42.0 * n

        calc(n=42)

    def test_missing_type_hint_2(self):
        """Problem here: Return type annotation missed"""
        @pedantic
        def calc(n: int):
            return 'Hi' + str(n)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42)

    def test_missing_type_hint_2_corrected(self):
        @pedantic
        def calc(n: int) -> str:
            return 'Hi' + str(n)

        calc(n=42)

    def test_missing_type_hint_3(self):
        """Problem here: type hint for i missed"""
        @pedantic
        def calc(n: int, m: int, i) -> int:
            return n + m + i

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=38)

    def test_missing_type_hint_3_corrected(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> int:
            return n + m + i

        calc(n=42, m=40, i=38)

    def test_all_ok_2(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> str:
            return str(n + m + i)

        calc(n=42, m=40, i=38)

    def test_all_ok_3(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> None:
            str(n + m + i)

        calc(n=42, m=40, i=38)

    def test_all_ok_4(self):
        @pedantic
        def calc(n: int) -> List[List[int]]:
            return [[n]]

        calc(n=42)

    def test_all_ok_5(self):
        @pedantic
        def calc(n: int) -> List[Tuple[float, str]]:
            return [(float(n), str(n))]

        calc(n=42)

    def test_all_ok_6(self):
        @pedantic
        def calc(n: int) -> Callable[[int, float], Tuple[float, str]]:
            @pedantic
            def f(x: int, y: float) -> Tuple[float, str]:
                return n * float(x), str(y)
            return f

        calc(n=42)(x=72, y=3.14)

    def test_all_ok_7(self):
        @pedantic
        def calc(n: List[List[float]]) -> Any:
            return n[0][0]

        calc(n=[[42.0]])

    def test_all_ok_8(self):
        @pedantic
        def calc(n: int) -> Callable[[int, float], Tuple[float, str]]:
            @pedantic
            def f(x: int, y: float) -> Tuple[float, str]:
                return n * float(x), str(y)

            return f

        calc(n=42)(x=3, y=3.14)

    def test_wrong_type_hint_1(self):
        """Problem here: str != int"""
        @pedantic
        def calc(n: int, m: int, i: int) -> str:
            return n + m + i

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=38)

    def test_wrong_type_hint_1_corrected(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> str:
            return str(n + m + i)

        calc(n=42, m=40, i=38)

    def test_wrong_type_hint_2(self):
        """Problem here: str != int"""
        @pedantic
        def calc(n: int, m: int, i: str) -> int:
            return n + m + i

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=38)

    def test_wrong_type_hint_2_corrected(self):
        @pedantic
        def calc(n: int, m: int, i: str) -> int:
            return n + m + int(i)

        calc(n=42, m=40, i='38')

    def test_wrong_type_hint_3(self):
        """Problem here: None != int"""
        @pedantic
        def calc(n: int, m: int, i: int) -> None:
            return n + m + i

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=38)

    def test_wrong_type_hint_corrected(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> None:
            print(n + m + i)

        calc(n=42, m=40, i=38)

    def test_wrong_type_hint_4(self):
        """Problem here: None != int"""
        @pedantic
        def calc(n: int, m: int, i: int) -> int:
            print(n + m + i)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=38)

    def test_wrong_type_hint_4_corrected(self):
        @pedantic
        def calc(n: int, m: int, i: int) -> int:
            return n + m + i

        calc(n=42, m=40, i=38)

    def test_none_1(self):
        """Problem here: None is not accepted"""
        @pedantic
        def calc(n: int, m: int, i: int) -> int:
            return n + m + i

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=None)

    def test_none_2(self):
        @pedantic
        def calc(n: int, m: int, i: Optional[int]) -> int:
            return n + m + i if i is not None else n + m

        calc(n=42, m=40, i=None)

    def test_none_3(self):
        @pedantic
        def calc(n: int, m: int, i: Union[int, None]) -> int:
            return n + m + i if i is not None else n + m

        calc(n=42, m=40, i=None)

    def test_none_4(self):
        """Problem here: function may return None"""
        @pedantic
        def calc(n: int, m: int, i: Union[int, None]) -> int:
            return n + m + i if i is not None else None

        calc(n=42, m=40, i=42)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(n=42, m=40, i=None)

    def test_none_5(self):
        @pedantic
        def calc(n: int, m: int, i: Union[int, None]) -> Optional[int]:
            return n + m + i if i is not None else None

        calc(n=42, m=40, i=None)

    def test_inheritance_1(self):
        class MyClassA:
            pass

        class MyClassB(MyClassA):
            pass

        @pedantic
        def calc(a: MyClassA) -> str:
            return str(a)

        calc(a=MyClassA())
        calc(a=MyClassB())

    def test_inheritance_2(self):
        """Problem here: A is not a subtype of B"""
        class MyClassA:
            pass

        class MyClassB(MyClassA):
            pass

        @pedantic
        def calc(a: MyClassB) -> str:
            return str(a)

        calc(a=MyClassB())
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(a=MyClassA())

    def test_instance_method_1(self):
        class MyClassA:
            @pedantic
            def calc(self, i: int) -> str:
                return str(i)

        a = MyClassA()
        a.calc(i=42)

    def test_instance_method_2(self):
        """Problem here: 'i' has no type annotation"""
        class MyClassA:
            @pedantic
            def calc(self, i) -> str:
                return str(i)

        a = MyClassA()
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            a.calc(i=42)

    def test_instance_method_2_corrected(self):
        class MyClassA:
            @pedantic
            def calc(self, i: int) -> str:
                return str(i)

        a = MyClassA()
        a.calc(i=42)

    def test_instance_method_int_is_not_float(self):
        class MyClassA:
            @pedantic
            def calc(self, i: float) -> str:
                return str(i)

        a = MyClassA()
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            a.calc(i=42)

    def test_instance_method_3_corrected(self):
        class MyClassA:
            @pedantic
            def calc(self, i: float) -> str:
                return str(i)

        a = MyClassA()
        a.calc(i=42.0)

    def test_instance_method_no_kwargs(self):
        class MyClassA:
            @pedantic
            def calc(self, i: int) -> str:
                return str(i)

        a = MyClassA()
        with self.assertRaises(expected_exception=PedanticCallWithArgsException):
            a.calc(42)

    def test_instance_method_5(self):
        """Problem here: instance methods is not called with kwargs"""
        class MyClassA:
            @pedantic
            def calc(self, i: int) -> str:
                return str(i)

        a = MyClassA()
        a.calc(i=42)

    def test_lambda_1(self):
        @pedantic
        def calc(i: float) -> Callable[[float], str]:
            return lambda x: str(x * i)

        calc(i=42.0)(10.0)

    def test_lambda_3(self):
        @pedantic
        def calc(i: float) -> Callable[[float], str]:
            def res(x: float) -> str:
                return str(x * i)
            return res

        calc(i=42.0)(10.0)

    def test_lambda_int_is_not_float(self):
        @pedantic
        def calc(i: float) -> Callable[[float], str]:
            def res(x: int) -> str:
                return str(x * i)
            return res

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(i=42.0)(x=10)

    def test_lambda_4_almost_corrected(self):
        """Problem here: float != str"""
        @pedantic
        def calc(i: float) -> Callable[[float], str]:
            @pedantic
            def res(x: int) -> str:
                return str(x * i)
            return res

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(i=42.0)(x=10)

    def test_lambda_4_almost_corrected_2(self):
        @pedantic
        def calc(i: float) -> Callable[[int], str]:
            @pedantic
            def res(x: int) -> str:
                return str(x * i)
            return res

        calc(i=42.0)(x=10)

    def test_lambda_5(self):
        """Problem here: float != int"""
        @pedantic
        def calc(i: float) -> Callable[[float], str]:
            @pedantic
            def res(x: float) -> str:
                return str(x * i)
            return res

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(i=42.0)(x=10)

    def test_lambda_corrected(self):
        @pedantic
        def calc(i: float) -> Callable[[float], str]:
            @pedantic
            def res(x: float) -> str:
                return str(x * i)

            return res

        calc(i=42.0)(x=10.0)

    def test_tuple_without_type_args(self):
        @pedantic
        def calc(i: Tuple) -> str:
            return str(i)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(i=(42.0, 43, 'hi'))

    def test_tuple_without_args_corrected(self):
        @pedantic
        def calc(i: Tuple[Any, ...]) -> str:
            return str(i)

        calc(i=(42.0, 43, 'hi'))

    def test_callable_without_type_args(self):
        @pedantic
        def calc(i: Callable) -> str:
            return str(i(' you'))

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(i=lambda x: (42.0, 43, 'hi', x))

    def test_callable_without_args_correct_with_lambdas(self):
        @pedantic
        def calc(i: Callable[[Any], Tuple[Any, ...]]) -> str:
            return str(i(x=' you'))

        calc(i=lambda x: (42.0, 43, 'hi', x))

    def test_callable_without_args_corrected(self):
        @pedantic
        def calc(i: Callable[[Any], Tuple[Any, ...]]) -> str:
            return str(i(x=' you'))

        @pedantic
        def arg(x: Any) -> Tuple[Any, ...]:
            return 42.0, 43, 'hi', x
        calc(i=arg)

    def test_list_without_args(self):
        @pedantic
        def calc(i: List) -> Any:
            return [i]

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(i=[42.0, 43, 'hi'])

    def test_list_without_args_corrected(self):
        @pedantic
        def calc(i: List[Any]) -> List[List[Any]]:
            return [i]

        calc(i=[42.0, 43, 'hi'])

    def test_ellipsis_in_callable_1(self):
        @pedantic
        def calc(i: Callable[..., int]) -> int:
            return i()

        @pedantic
        def call() -> int:
            return 42

        calc(i=call)

    def test_ellipsis_in_callable_2(self):
        @pedantic
        def calc(i: Callable[..., int]) -> int:
            return i(x=3.14, y=5)

        @pedantic
        def call(x: float, y: int) -> int:
            return 42

        calc(i=call)

    def test_ellipsis_in_callable_3(self):
        """Problem here: call to "call" misses one argument"""
        @pedantic
        def calc(i: Callable[..., int]) -> int:
            return i(x=3.14)

        @pedantic
        def call(x: float, y: int) -> int:
            return 42

        with self.assertRaises(expected_exception=PedanticException):
            calc(i=call)

    def test_optional_args_1(self):
        @pedantic
        def calc(a: int, b: int = 42) -> int:
            return a + b

        calc(a=2)

    def test_optional_args_2(self):
        @pedantic
        def calc(a: int = 3, b: int = 42, c: float = 5.0) -> float:
            return a + b + c

        calc()
        calc(a=1)
        calc(b=1)
        calc(c=1.0)
        calc(a=1, b=1)
        calc(a=1, c=1.0)
        calc(b=1, c=1.0)
        calc(a=1, b=1, c=1.0)

    def test_optional_args_3(self):
        """Problem here: optional argument c: 5 is not a float"""
        @pedantic
        def calc(a: int = 3, b: int = 42, c: float = 5) -> float:
            return a + b + c

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc()

    def test_optional_args_3_corrected(self):
        @pedantic
        def calc(a: int = 3, b: int = 42, c: float = 5.0) -> float:
            return a + b + c

        calc()

    def test_optional_args_4(self):
        class MyClass:
            @pedantic
            def foo(self, a: int, b: Optional[int] = 1) -> int:
                return a + b

        my_class = MyClass()
        my_class.foo(a=10)

    def test_optional_args_5(self):
        @pedantic
        def calc(d: Optional[Dict[int, int]] = None) -> Optional[int]:
            if d is None:
                return None
            return sum(d.keys())

        calc(d=None)
        calc()
        calc(d={42: 3})

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(d={42: 3.14})

    def test_optional_args_6(self):
        """"Problem here: str != int"""
        @pedantic
        def calc(d: int = 42) -> int:
            return int(d)

        calc(d=99999)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(d='999999')

    def test_enum_1(self):
        """Problem here: Type hint for 'a' should be MyEnum instead of MyEnum.GAMMA"""
        class MyEnum(Enum):
            ALPHA = 'startEvent'
            BETA = 'task'
            GAMMA = 'sequenceFlow'

        class MyClass:
            @pedantic
            def operation(self, a: MyEnum.GAMMA) -> None:
                print(a)

        m = MyClass()
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            m.operation(a=MyEnum.GAMMA)

    def test_enum_1_corrected(self):
        class MyEnum(Enum):
            ALPHA = 'startEvent'
            BETA = 'task'
            GAMMA = 'sequenceFlow'

        @pedantic
        def operation(a: MyEnum) -> None:
            print(a)

        operation(a=MyEnum.GAMMA)

    def test_sloppy_types_dict(self):
        @pedantic
        def operation(d: dict) -> int:
            return len(d.keys())

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d={1: 1, 2: 2})

    def test_sloppy_types_dict_almost_corrected_no_type_args(self):
        @pedantic
        def operation(d: Dict) -> int:
            return len(d.keys())

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d={1: 1, 2: 2})

    def test_sloppy_types_dict_corrected(self):
        @pedantic
        def operation(d: Dict[int, int]) -> int:
            return len(d.keys())

        operation(d={1: 1, 2: 2})

    def test_sloppy_types_list(self):
        @pedantic
        def operation(d: list) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d=[1, 2, 3, 4])

    def test_sloppy_types_list_almost_corrected_no_type_args(self):
        @pedantic
        def operation(d: List) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d=[1, 2, 3, 4])

    def test_sloppy_types_list_corrected(self):
        @pedantic
        def operation(d: List[int]) -> int:
            return len(d)

        operation(d=[1, 2, 3, 4])

    def test_sloppy_types_tuple(self):
        @pedantic
        def operation(d: tuple) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d=(1, 2, 3))

    def test_sloppy_types_tuple_almost_corrected_no_type_args(self):
        @pedantic
        def operation(d: Tuple) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d=(1, 2, 3))

    def test_sloppy_types_tuple_corrected(self):
        @pedantic
        def operation(d: Tuple[int, int, int]) -> int:
            return len(d)

        operation(d=(1, 2, 3))

    def test_sloppy_types_set(self):
        @pedantic
        def operation(d: set) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d={1, 2, 3})

    def test_sloppy_types_set_almost_corrected_to_type_args(self):
        @pedantic
        def operation(d: Set) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d={1, 2, 3})

    def test_sloppy_types_set_corrected(self):
        @pedantic
        def operation(d: Set[int]) -> int:
            return len(d)

        operation(d={1, 2, 3})

    def test_sloppy_types_frozenset(self):
        @pedantic
        def operation(d: frozenset) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d=frozenset({1, 2, 3}))

    def test_sloppy_types_frozenset_almost_corrected_no_type_args(self):
        @pedantic
        def operation(d: FrozenSet) -> int:
            return len(d)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            operation(d=frozenset({1, 2, 3}))

    def test_sloppy_types_frozenset_corrected(self):
        @pedantic
        def operation(d: FrozenSet[int]) -> int:
            return len(d)

        operation(d=frozenset({1, 2, 3}))

    def test_type_list_but_got_tuple(self):
        @pedantic
        def calc(ls: List[Any]) -> int:
            return len(ls)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            calc(ls=(1, 2, 3))

    def test_type_list_corrected(self):
        @pedantic
        def calc(ls: Tuple[Any, ...]) -> int:
            return len(ls)

        calc(ls=(1, 2, 3))

    def test_any(self):
        @pedantic
        def calc(ls: List[Any]) -> Dict[int, Any]:
            return {i: ls[i] for i in range(0, len(ls))}

        calc(ls=[1, 2, 3])
        calc(ls=[1.11, 2.0, 3.0])
        calc(ls=['1', '2', '3'])
        calc(ls=[10.5, '2', (3, 4, 5)])

    def test_aliases(self):
        Vector = List[float]

        @pedantic
        def scale(scalar: float, vector: Vector) -> Vector:
            return [scalar * num for num in vector]

        scale(scalar=2.0, vector=[1.0, -4.2, 5.4])

    def test_new_type(self):
        UserId = NewType('UserId', int)

        @pedantic
        def get_user_name(user_id: UserId) -> str:
            return str(user_id)

        some_id = UserId(524313)
        get_user_name(user_id=some_id)

        # the following would be desirable but impossible to check at runtime:
        # with self.assertRaises(expected_exception=AssertionError):
        #     get_user_name(user_id=-1)

    def test_list_of_new_type(self):
        UserId = NewType('UserId', int)

        @pedantic
        def get_user_name(user_ids: List[UserId]) -> str:
            return str(user_ids)

        get_user_name(user_ids=[UserId(524313), UserId(42)])
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            get_user_name(user_ids=[UserId(524313), UserId(42), 430.0])

    def test_callable_no_args(self):
        @pedantic
        def f(g: Callable[[], str]) -> str:
            return g()

        @pedantic
        def greetings() -> str:
            return 'hello world'

        f(g=greetings)

    def test_type_var(self):
        T = TypeVar('T')

        @pedantic
        def first(ls: List[T]) -> T:
            return ls[0]

        first(ls=[1, 2, 3])

    def test_type_var_wrong(self):
        T = TypeVar('T')

        @pedantic
        def first(ls: List[T]) -> T:
            return str(ls[0])

        with self.assertRaises(expected_exception=PedanticTypeVarMismatchException):
            first(ls=[1, 2, 3])

    def test_type_var_wrong_sequence(self):
        T = TypeVar('T')

        @pedantic
        def first(ls: Sequence[T]) -> T:
            return str(ls[0])

        with self.assertRaises(expected_exception=PedanticTypeVarMismatchException):
            first(ls=[1, 2, 3])

    def test_double_pedantic(self):
        @pedantic
        @pedantic
        def f(x: int, y: float) -> Tuple[float, str]:
            return float(x), str(y)

        f(x=5, y=3.14)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            f(x=5.0, y=3.14)
        with self.assertRaises(expected_exception=PedanticCallWithArgsException):
            f(5, 3.14)

    def test_args_kwargs(self):
        @pedantic
        def some_method(a: int = 0, b: float = 0.0) -> float:
            return a * b

        @pedantic
        def wrapper_method(*args: Union[int, float], **kwargs: Union[int, float]) -> float:
            return some_method(*args, **kwargs)

        some_method()
        with self.assertRaises(expected_exception=PedanticCallWithArgsException):
            some_method(3, 3.0)
        some_method(a=3, b=3.0)
        wrapper_method()
        with self.assertRaises(expected_exception=PedanticCallWithArgsException):
            wrapper_method(3, 3.0)
        wrapper_method(a=3, b=3.0)

    def test_args_kwargs_no_type_hint(self):
        @pedantic
        def method_no_type_hint(*args, **kwargs) -> None:
            print(args)
            print(kwargs)

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            method_no_type_hint(a=3, b=3.0)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            method_no_type_hint()

    def test_args_kwargs_wrong_type_hint(self):
        """See: https://www.python.org/dev/peps/pep-0484/#arbitrary-argument-lists-and-default-argument-values"""
        @pedantic
        def wrapper_method(*args: str, **kwargs: str) -> None:
            print(args)
            print(kwargs)

        wrapper_method()
        wrapper_method('hi', 'you', ':)')
        wrapper_method(a='hi', b='you', c=':)')
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            wrapper_method('hi', 'you', ':)', 7)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            wrapper_method(3, 3.0)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            wrapper_method(a=3, b=3.0)

    def test_additional_kwargs(self):
        @pedantic
        def some_method(a: int, b: float = 0.0, **kwargs: int) -> float:
            return sum([a, b])

        some_method(a=5)
        some_method(a=5, b=0.1)
        some_method(a=5, b=0.1, c=4)
        some_method(a=5, b=0.1, c=4, d=5, e=6)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            some_method(a=5, b=0.1, c=4, d=5.0, e=6)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            some_method(a=5.0, b=0.1, c=4, d=5, e=6)
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            some_method(a=5, b=0, c=4, d=5, e=6)

    def test_args_kwargs_different_types(self):
        @pedantic
        def foo(*args: str, **kwds: int) -> None:
            print(args)
            print(kwds)

        foo('a', 'b', 'c')
        foo(x=1, y=2)
        foo('', z=0)

    def test_pedantic_on_class(self):
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            @pedantic
            class MyClass:
                pass
            MyClass()

    def test_is_subtype_tuple(self):
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            @pedantic
            def foo() -> Callable[[Tuple[float, str]], Tuple[int]]:
                def bar(a: Tuple[float]) -> Tuple[int]:
                    return len(a[1]) + int(a[0]),
                return bar
            foo()

    def test_is_subtype_tuple_corrected(self):
        @pedantic
        def foo() -> Callable[[Tuple[float, str]], Tuple[int]]:
            def bar(a: Tuple[float, str]) -> Tuple[int]:
                return len(a[1]) + int(a[0]),
            return bar
        foo()

    def test_forward_ref(self):
        class Conversation:
            pass

        @pedantic
        def get_conversations() -> List['Conversation']:
            return [Conversation(), Conversation()]

        get_conversations()

    def test_alternative_list_type_hint(self):
        @pedantic
        def _is_digit_in_int(digit: [int], num: int) -> bool:
            num_str = str(num)
            for i in num_str:
                if int(i) == digit:
                    return True
            return False

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            _is_digit_in_int(digit=4, num=42)

    def test_callable_with_union_return(self):
        class MyClass:
            pass

        @pedantic
        def admin_required(func: Callable[..., Union[str, MyClass]]) -> Callable[..., Union[str, MyClass]]:
            @wraps(func)
            def decorated_function(*args, **kwargs):
                return func(*args, **kwargs)
            return decorated_function

        @admin_required
        @pedantic
        def get_server_info() -> str:
            return 'info'

        get_server_info()

    def test_pedantic(self):
        @pedantic
        def foo(a: int, b: str) -> str:
            return 'abc'

        self.assertEqual('abc', foo(a=4, b='abc'))

    def test_pedantic_always(self):
        @pedantic
        def foo(a: int, b: str) -> str:
            return 'abc'

        self.assertEqual('abc', foo(a=4, b='abc'))

    def test_pedantic_arguments_fail(self):
        @pedantic
        def foo(a: int, b: str) -> str:
            return 'abc'

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            foo(a=4, b=5)

    def test_pedantic_return_type_fail(self):
        @pedantic
        def foo(a: int, b: str) -> str:
            return 6

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            foo(a=4, b='abc')

    def test_return_type_none(self):
        @pedantic
        def foo() -> None:
            return 'a'
        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            foo()

    def test_marco(self):
        @pedantic_class
        class A:
            def __init__(self, val: int) -> None:
                self.val = val

            def __eq__(self, other: 'A') -> bool:  # other: A and all subclasses
                return self.val == other.val

        @pedantic_class
        class B(A):
            def __init__(self, val: int) -> None:
                super().__init__(val=val)

        @pedantic_class
        class C(A):
            def __init__(self, val: int) -> None:
                super().__init__(val=val)

        a = A(val=42)
        b = B(val=42)
        c = C(val=42)

        assert a == b  # works
        assert a == c  # works
        assert b == c  # error

    def test_date_datetime(self):
        @pedantic
        def foo(a: datetime, b: date) -> None:
            pass

        foo(a=datetime(1995, 2, 5), b=date(1987, 8, 7))
        foo(a=datetime(1995, 2, 5), b=datetime(1987, 8, 7))

        with self.assertRaises(expected_exception=PedanticTypeCheckException):
            foo(a=date(1995, 2, 5), b=date(1987, 8, 7))

    def test_any_type(self):
        @pedantic
        def foo(a: Any) -> None:
            pass

        foo(a='aa')

    def test_callable_exact_arg_count(self):
        @pedantic
        def foo(a: Callable[[int, str], int]) -> None:
            pass

        def some_callable(x: int, y: str) -> int:
            pass

        foo(a=some_callable)

    def test_callable_bad_type(self):
        @pedantic
        def foo(a: Callable[..., int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_callable_too_few_arguments(self):
        @pedantic
        def foo(a: Callable[[int, str], int]) -> None:
            pass

        def some_callable(x: int) -> int:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=some_callable)

    def test_callable_mandatory_kwonlyargs(self):
        @pedantic
        def foo(a: Callable[[int, str], int]) -> None:
            pass

        def some_callable(x: int, y: str, *, z: float, bar: str) -> int:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=some_callable)

    def test_callable_class(self):
        """
        Test that passing a class as a callable does not count the "self" argument "a"gainst the
        ones declared in the Callable specification.

        """
        @pedantic
        def foo(a: Callable[[int, str], Any]) -> None:
            pass

        class SomeClass:
            def __init__(self, x: int, y: str):
                pass

        foo(a=SomeClass)

    def test_callable_plain(self):
        @pedantic
        def foo(a: Callable[..., Any]) -> None:
            pass

        def callback(a):
            pass

        foo(a=callback)

    def test_callable_bound_method(self):
        @pedantic
        def foo(callback: Callable[[int], Any]) -> None:
            pass

        foo(callback=Child().method)

    def test_callable_defaults(self):
        """
        Test that a callable having "too many" arguments don't raise an error if the extra
        arguments have default values.

        """
        @pedantic
        def foo(callback: Callable[[int, str], Any]) -> None:
            pass

        def some_callable(x: int, y: str, z: float = 1.2) -> int:
            pass

        foo(callback=some_callable)

    def test_callable_builtin(self):
        @pedantic
        def foo(callback: types.BuiltinFunctionType) -> None:
            pass

        foo(callback=[].append)

    def test_dict_bad_type(self):
        @pedantic
        def foo(a: Dict[str, int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_dict_bad_key_type(self):
        @pedantic
        def foo(a: Dict[str, int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a={1: 2})

    def test_dict_bad_value_type(self):
        @pedantic
        def foo(a: Dict[str, int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a={'x': 'a'})

    def test_list_bad_type(self):
        @pedantic
        def foo(a: List[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_list_bad_element(self):
        @pedantic
        def foo(a: List[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=[1, 2, 'bb'])

    def test_sequence_bad_type(self):
        @pedantic
        def foo(a: Sequence[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_sequence_bad_element(self):
        @pedantic
        def foo(a: Sequence[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=[1, 2, 'bb'])

    def test_abstractset_custom_type(self):
        T = TypeVar('T')

        @pedantic_class
        class DummySet(AbstractSet[T]):
            def __contains__(self, x: object) -> bool:
                return x == 1

            def __len__(self) -> T:
                return 1

            def __iter__(self) -> Iterator[T]:
                yield 1

        @pedantic
        def foo(a: AbstractSet[int]) -> None:
            pass

        foo(a=DummySet[int]())

    def test_abstractset_bad_type(self):
        @pedantic
        def foo(a: AbstractSet[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_set_bad_type(self):
        @pedantic
        def foo(a: Set[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_abstractset_bad_element(self):
        @pedantic
        def foo(a: AbstractSet[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a={1, 2, 'bb'})

    def test_set_bad_element(self):
        @pedantic
        def foo(a: Set[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a={1, 2, 'bb'})

    def test_tuple_bad_type(self):
        @pedantic
        def foo(a: Tuple[int]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=5)

    def test_tuple_too_many_elements(self):
        @pedantic
        def foo(a: Tuple[int, str]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=(1, 'aa', 2))

    def test_tuple_too_few_elements(self):
        @pedantic
        def foo(a: Tuple[int, str]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=(1,))

    def test_tuple_bad_element(self):
        @pedantic
        def foo(a: Tuple[int, str]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=(1, 2))

    def test_tuple_ellipsis_bad_element(self):
        @pedantic
        def foo(a: Tuple[int, ...]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=(1, 2, 'blah'))

    def test_namedtuple(self):
        Employee = NamedTuple('Employee', [('name', str), ('id', int)])

        @pedantic
        def foo(bar: Employee) -> None:
            print(bar)

        foo(bar=Employee('bob', 1))

    def test_namedtuple_key_mismatch(self):
        Employee1 = NamedTuple('Employee', [('name', str), ('id', int)])
        Employee2 = NamedTuple('Employee', [('firstname', str), ('id', int)])

        @pedantic
        def foo(bar: Employee1) -> None:
            print(bar)

        with self.assertRaises(PedanticTypeCheckException):
            foo(bar=Employee2('bob', 1))

    def test_namedtuple_type_mismatch(self):
        Employee = NamedTuple('Employee', [('name', str), ('id', int)])

        @pedantic
        def foo(bar: Employee) -> None:
            print(bar)

        with self.assertRaises(PedanticTypeCheckException):
            foo(bar=('bob', 1))

    def test_namedtuple_huge_type_mismatch(self):
        Employee = NamedTuple('Employee', [('name', str), ('id', int)])

        @pedantic
        def foo(bar: int) -> None:
            print(bar)

        with self.assertRaises(PedanticTypeCheckException):
            foo(bar=foo(bar=Employee('bob', 1)))

    def test_namedtuple_wrong_field_type(self):
        Employee = NamedTuple('Employee', [('name', str), ('id', int)])

        @pedantic
        def foo(bar: Employee) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(bar=Employee(2, 1))

    def test_union(self):
        @pedantic
        def foo(a: Union[str, int]) -> None:
            pass

        for value in [6, 'xa']:
            foo(a=value)

    def test_union_new_syntax(self):
        @pedantic
        def foo(a: str | int) -> None:
            pass

        for value in [6, 'xa']:
            foo(a=value)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=1.7)

    def test_union_typing_type(self):
        @pedantic
        def foo(a: Union[str, Collection]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=1)

    def test_union_fail(self):
        @pedantic
        def foo(a: Union[str, int]) -> None:
            pass

        for value in [5.6, b'xa']:
            with self.assertRaises(PedanticTypeCheckException):
                foo(a=value)

    def test_type_var_constraints(self):
        T = TypeVar('T', int, str)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        for values in [
            {'a': 6, 'b': 7},
            {'a': 'aa', 'b': "bb"},
        ]:
            foo(**values)

    def test_type_var_constraints_fail_typing_type(self):
        T = TypeVar('T', int, Collection)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a='aa', b='bb')

    def test_typevar_constraints_fail(self):
        T = TypeVar('T', int, str)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=2.5, b='aa')

    def test_typevar_bound(self):
        T = TypeVar('T', bound=Parent)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        foo(a=Child(), b=Child())

    def test_type_var_bound_fail(self):
        T = TypeVar('T', bound=Child)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=Parent(), b=Parent())

    def test_type_var_invariant_fail(self):
        T = TypeVar('T', int, str)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=2, b=3.6)

    def test_type_var_covariant(self):
        T = TypeVar('T', covariant=True)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        foo(a=Parent(), b=Child())

    def test_type_var_covariant_fail(self):
        T = TypeVar('T', covariant=True)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        with self.assertRaises(PedanticTypeVarMismatchException):
            foo(a=Child(), b=Parent())

    def test_type_var_contravariant(self):
        T = TypeVar('T', contravariant=True)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        foo(a=Child(), b=Parent())

    def test_type_var_contravariant_fail(self):
        T = TypeVar('T', contravariant=True)

        @pedantic
        def foo(a: T, b: T) -> None:
            pass

        with self.assertRaises(PedanticTypeVarMismatchException):
            foo(a=Parent(), b=Child())

    def test_class_bad_subclass(self):
        @pedantic
        def foo(a: Type[Child]) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=Parent)

    def test_class_any(self):
        @pedantic
        def foo(a: Type[Any]) -> None:
            pass

        foo(a=str)

    def test_wrapped_function(self):
        def decorator(func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                return func(*args, **kwargs)
            return wrapper

        @pedantic
        @decorator
        def foo(a: 'Child') -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=Parent())

    def test_mismatching_default_type(self):
        @pedantic
        def foo(a: str = 1) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo()

    def test_implicit_default_none(self):
        """
        Test that if the default value is ``None``, a ``None`` argument can be passed.

        """
        @pedantic
        def foo(a: Optional[str] = None) -> None:
            pass

        foo()

    def test_generator_simple(self):
        """Test that argument type checking works in a generator function too."""
        @pedantic
        def generate(a: int) -> Generator[int, int, None]:
            yield a
            yield a + 1

        gen = generate(a=1)
        next(gen)

    def test_wrapped_generator_no_return_type_annotation(self):
        """Test that return type checking works in a generator function too."""
        @pedantic
        def generate(a: int) -> Generator[int, int, None]:
            yield a
            yield a + 1

        gen = generate(a=1)
        next(gen)

    def test_varargs(self):
        @pedantic
        def foo(*args: int) -> None:
            pass

        foo(1, 2)

    def test_varargs_fail(self):
        @pedantic
        def foo(*args: int) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(1, 'a')

    def test_kwargs(self):
        @pedantic
        def foo(**kwargs: int) -> None:
            pass

        foo(a=1, b=2)

    def test_kwargs_fail(self):
        @pedantic
        def foo(**kwargs: int) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=1, b='a')

    def test_generic(self):
        T_Foo = TypeVar('T_Foo')

        class FooGeneric(Generic[T_Foo]):
            pass

        @pedantic
        def foo(a: FooGeneric[str]) -> None:
            print(a)

        foo(a=FooGeneric[str]())

    def test_newtype(self):
        myint = NewType("myint", int)

        @pedantic
        def foo(a: myint) -> int:
            return 42

        assert foo(a=1) == 42

        with self.assertRaises(PedanticTypeCheckException):
            foo(a="a")

    def test_collection(self):
        @pedantic
        def foo(a: Collection) -> None:
            pass

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=True)

    def test_binary_io(self):
        @pedantic
        def foo(a: BinaryIO) -> None:
            print(a)

        foo(a=BytesIO())

    def test_text_io(self):
        @pedantic
        def foo(a: TextIO) -> None:
            print(a)

        foo(a=StringIO())

    def test_binary_io_fail(self):
        @pedantic
        def foo(a: TextIO) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=BytesIO())

    def test_text_io_fail(self):
        @pedantic
        def foo(a: BinaryIO) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=StringIO())

    def test_binary_io_real_file(self):
        @pedantic
        def foo(a: BinaryIO) -> None:
            print(a)

        with open(file=TEST_FILE, mode='wb') as f:
            foo(a=f)

    def test_text_io_real_file(self):
        @pedantic
        def foo(a: TextIO) -> None:
            print(a)

        with open(file=TEST_FILE, mode='w') as f:
            foo(a=f)

    def test_pedantic_return_type_var_fail(self):
        T = TypeVar('T', int, float)

        @pedantic
        def foo(a: T, b: T) -> T:
            return 'a'

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=4, b=2)

    def test_callable(self):
        @pedantic
        def foo_1(a: Callable[..., int]) -> None:
            print(a)

        @pedantic
        def foo_2(a: Callable) -> None:
            print(a)

        def some_callable() -> int:
            return 4

        foo_1(a=some_callable)

        with self.assertRaises(PedanticTypeCheckException):
            foo_2(a=some_callable)

    def test_list(self):
        @pedantic
        def foo_1(a: List[int]) -> None:
            print(a)

        @pedantic
        def foo_2(a: List) -> None:
            print(a)

        @pedantic
        def foo_3(a: list) -> None:
            print(a)

        @pedantic
        def foo_4(a: list[int]) -> None:
            print(a)

        foo_1(a=[1, 2])

        with self.assertRaises(PedanticTypeCheckException):
            foo_2(a=[1, 2])

        with self.assertRaises(PedanticTypeCheckException):
            foo_3(a=[1, 2])


        foo_4(a=[1, 2])

    def test_dict(self):
        @pedantic
        def foo_1(a: Dict[str, int]) -> None:
            print(a)

        @pedantic
        def foo_2(a: Dict) -> None:
            print(a)

        @pedantic
        def foo_3(a: dict) -> None:
            print(a)

        @pedantic
        def foo_4(a: dict[str, int]) -> None:
            print(a)

        foo_1(a={'x': 2})

        with self.assertRaises(PedanticTypeCheckException):
            foo_2(a={'x': 2})

        with self.assertRaises(PedanticTypeCheckException):
            foo_3(a={'x': 2})

        foo_4(a={'x': 2})

    def test_sequence(self):
        @pedantic
        def foo(a: Sequence[str]) -> None:
            print(a)

        for value in [('a', 'b'), ['a', 'b'], 'abc']:
            foo(a=value)

    def test_sequence_no_type_args(self):
        @pedantic
        def foo(a: Sequence) -> None:
            print(a)

        for value in [('a', 'b'), ['a', 'b'], 'abc']:
            with self.assertRaises(PedanticTypeCheckException):
                foo(a=value)

    def test_iterable(self):
        @pedantic
        def foo(a: Iterable[str]) -> None:
            print(a)

        for value in [('a', 'b'), ['a', 'b'], 'abc']:
            foo(a=value)

    def test_iterable_no_type_args(self):
        @pedantic
        def foo(a: Iterable) -> None:
            print(a)

        for value in [('a', 'b'), ['a', 'b'], 'abc']:
            with self.assertRaises(PedanticTypeCheckException):
                foo(a=value)

    def test_container(self):
        @pedantic
        def foo(a: Container[str]) -> None:
            print(a)

        for value in [('a', 'b'), ['a', 'b'], 'abc']:
            foo(a=value)

    def test_container_no_type_args(self):
        @pedantic
        def foo(a: Container) -> None:
            print(a)

        for value in [('a', 'b'), ['a', 'b'], 'abc']:
            with self.assertRaises(PedanticTypeCheckException):
                foo(a=value)

    def test_set(self):
        @pedantic
        def foo_1(a: AbstractSet[int]) -> None:
            print(a)

        @pedantic
        def foo_2(a: Set[int]) -> None:
            print(a)

        for value in [set(), {6}]:
            foo_1(a=value)
            foo_2(a=value)

    def test_set_no_type_args(self):
        @pedantic
        def foo_1(a: AbstractSet) -> None:
            print(a)

        @pedantic
        def foo_2(a: Set) -> None:
            print(a)

        @pedantic
        def foo_3(a: set) -> None:
            print(a)

        for value in [set(), {6}]:
            with self.assertRaises(PedanticTypeCheckException):
                foo_1(a=value)

            with self.assertRaises(PedanticTypeCheckException):
                foo_2(a=value)

            with self.assertRaises(PedanticTypeCheckException):
                foo_3(a=value)

    def test_tuple(self):
        @pedantic
        def foo_1(a: Tuple[int, int]) -> None:
            print(a)

        @pedantic
        def foo_2(a: Tuple[int, ...]) -> None:
            print(a)

        foo_1(a=(1, 2))
        foo_2(a=(1, 2))

    def test_tuple_no_type_args(self):
        @pedantic
        def foo_1(a: Tuple) -> None:
            print(a)

        @pedantic
        def foo_2(a: tuple) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo_1(a=(1, 2))

        with self.assertRaises(PedanticTypeCheckException):
            foo_2(a=(1, 2))

    def test_empty_tuple(self):
        @pedantic
        def foo(a: Tuple[()]) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=())

    def test_class(self):
        @pedantic
        def foo_1(a: Type[Parent]) -> None:
            print(a)

        @pedantic
        def foo_2(a: Type[TypeVar('UnboundType')]) -> None:
            print(a)

        @pedantic
        def foo_3(a: Type[TypeVar('BoundType', bound=Parent)]) -> None:
            print(a)

        foo_1(a=Child)
        foo_2(a=Child)
        foo_3(a=Child)

    def test_class_no_type_vars(self):
        @pedantic
        def foo_1(a: Type) -> None:
            print(a)

        @pedantic
        def foo_2(a: type) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo_1(a=Child)

        with self.assertRaises(PedanticTypeCheckException):
            foo_2(a=Child)

    def test_class_not_a_class(self):
        @pedantic
        def foo(a: Type[Parent]) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=1)

    def test_complex(self):
        @pedantic
        def foo(a: complex) -> None:
            print(a)

        foo(a=complex(1, 5))

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=1.0)

    def test_float(self):
        @pedantic
        def foo(a: float) -> None:
            print(a)

        foo(a=1.5)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=1)

    def test_coroutine_correct_return_type(self):
        @pedantic
        async def foo() -> str:
            return 'foo'

        coro = foo()

        with self.assertRaises(StopIteration):
            coro.send(None)

    def test_coroutine_wrong_return_type(self):
        @pedantic
        async def foo() -> str:
            return 1

        coro = foo()

        with self.assertRaises(PedanticTypeCheckException):
            coro.send(None)

    def test_bytearray_bytes(self):
        @pedantic
        def foo(x: bytearray) -> None:
            pass

        foo(x=bytearray([1]))

    def test_class_decorator(self):
        @pedantic_class
        class Foo:
            @staticmethod
            def staticmethod() -> int:
                return 'foo'

            @classmethod
            def classmethod(cls) -> int:
                return 'foo'

            def method(self) -> int:
                return 'foo'

        with self.assertRaises(PedanticTypeCheckException):
            Foo.staticmethod()

        with self.assertRaises(PedanticTypeCheckException):
            Foo.classmethod()

        with self.assertRaises(PedanticTypeCheckException):
            Foo().method()

    def test_generator(self):
        @pedantic
        def genfunc() -> Generator[int, str, List[str]]:
            val1 = yield 2
            val2 = yield 3
            val3 = yield 4
            return [val1, val2, val3]

        gen = genfunc()

        with self.assertRaises(StopIteration):
            value = next(gen)
            while True:
                value = gen.send(str(value))
                assert isinstance(value, int)

    def test_generator_no_type_args(self):
        @pedantic
        def genfunc() -> Generator:
            val1 = yield 2
            val2 = yield 3
            val3 = yield 4
            return [val1, val2, val3]

        with self.assertRaises(PedanticTypeCheckException):
            genfunc()

    def test_iterator(self):
        @pedantic
        def genfunc() -> Iterator[int]:
            val1 = yield 2
            val2 = yield 3
            val3 = yield 4
            return [val1, val2, val3]

        gen = genfunc()

        with self.assertRaises(PedanticTypeCheckException):
            value = next(gen)
            while True:
                value = gen.send(str(value))
                assert isinstance(value, int)

    def test_iterator_no_type_args(self):
        @pedantic
        def genfunc() -> Iterator:
            val1 = yield 2
            val2 = yield 3
            val3 = yield 4
            return [val1, val2, val3]

        with self.assertRaises(PedanticTypeCheckException):
            genfunc()

    def test_iterable_advanced(self):
        @pedantic
        def genfunc() -> Iterable[int]:
            val1 = yield 2
            val2 = yield 3
            val3 = yield 4
            return [val1, val2, val3]

        gen = genfunc()

        with self.assertRaises(PedanticTypeCheckException):
            value = next(gen)
            while True:
                value = gen.send(str(value))
                assert isinstance(value, int)

    def test_iterable_advanced_no_type_args(self):
        @pedantic
        def genfunc() -> Iterable:
            val1 = yield 2
            val2 = yield 3
            val3 = yield 4
            return [val1, val2, val3]

        with self.assertRaises(PedanticTypeCheckException):
            genfunc()

    def test_generator_bad_yield(self):
        @pedantic
        def genfunc_1() -> Generator[int, str, None]:
            yield 'foo'

        @pedantic
        def genfunc_2() -> Iterable[int]:
            yield 'foo'

        @pedantic
        def genfunc_3() -> Iterator[int]:
            yield 'foo'

        gen = genfunc_1()

        with self.assertRaises(PedanticTypeCheckException):
            next(gen)

        gen = genfunc_2()

        with self.assertRaises(PedanticTypeCheckException):
            next(gen)

        gen = genfunc_3()

        with self.assertRaises(PedanticTypeCheckException):
            next(gen)

    def test_generator_bad_send(self):
        @pedantic
        def genfunc() -> Generator[int, str, None]:
            yield 1
            yield 2

        gen = genfunc()
        next(gen)

        with self.assertRaises(PedanticTypeCheckException):
            gen.send(2)

    def test_generator_bad_return(self):
        @pedantic
        def genfunc() -> Generator[int, str, str]:
            yield 1
            return 6

        gen = genfunc()
        next(gen)

        with self.assertRaises(PedanticTypeCheckException):
            gen.send('foo')

    def test_return_generator(self):
        @pedantic
        def genfunc() -> Generator[int, None, None]:
            yield 1

        @pedantic
        def foo() -> Generator[int, None, None]:
            return genfunc()

        foo()

    def test_local_class(self):
        @pedantic_class
        class LocalClass:
            class Inner:
                pass

            def create_inner(self) -> 'Inner':
                return self.Inner()

        retval = LocalClass().create_inner()
        assert isinstance(retval, LocalClass.Inner)

    def test_local_class_async(self):
        @pedantic_class
        class LocalClass:
            class Inner:
                pass

            async def create_inner(self) -> 'Inner':
                return self.Inner()

        coro = LocalClass().create_inner()

        with self.assertRaises(StopIteration):
            coro.send(None)

    def test_callable_nonmember(self):
        class CallableClass:
            def __call__(self):
                pass

        @pedantic_class
        class LocalClass:
            some_callable = CallableClass()

    def test_inherited_class_method(self):
        @pedantic_class
        class Parent:
            @classmethod
            def foo(cls, x: str) -> str:
                return cls.__name__

        @pedantic_class
        class Child(Parent):
            pass

        self.assertEqual('Parent', Child.foo(x='bar'))

        with self.assertRaises(PedanticTypeCheckException):
            Child.foo(x=1)

    def test_type_var_forward_ref_bound(self):
        TBound = TypeVar('TBound', bound='Parent')

        @pedantic
        def func(x: TBound) -> None:
            pass

        func(x=Parent())

        with self.assertRaises(PedanticTypeCheckException):
            func(x='foo')

    def test_noreturn(self):
        @pedantic
        def foo() -> NoReturn:
            pass

        @pedantic
        def bar() -> NoReturn:
            raise ZeroDivisionError('bar')

        with self.assertRaises(expected_exception=ZeroDivisionError):
            bar()

        with self.assertRaises(PedanticTypeCheckException):
            foo()

    def test_literal(self):
        @pedantic
        def foo(a: Literal[1, True, 'x', b'y', 404]) -> None:
            print(a)

        foo(a=404)
        foo(a=True)
        foo(a='x')

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=4)

    def test_literal_union(self):
        @pedantic
        def foo(a: Union[str, Literal[1, 6, 8]]) -> None:
            print(a)

        foo(a=6)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=4)

    def test_literal_illegal_value(self):
        @pedantic
        def foo(a: Literal[1, 1.1]) -> None:
            print(a)

        with self.assertRaises(PedanticTypeCheckException):
            foo(a=4)

    def test_enum(self):
        with self.assertRaises(PedanticTypeCheckException):
            @pedantic_class
            class MyEnum(Enum):
                A = 'a'

    def test_enum_aggregate(self):
        T = TypeVar('T', bound=IntEnum)

        @pedantic_class
        class EnumAggregate(Generic[T]):
            enum: ClassVar[Type[T]]

            def __init__(self, value: Union[int, str, List[T]]) -> None:
                assert len(self.enum) < 10

                if value == '':
                    raise ValueError(f'Parameter "value" cannot be empty!')

                if isinstance(value, list):
                    self._value = ''.join([str(x.value) for x in value])
                else:
                    self._value = str(value)

                self._value = ''.join(sorted(self._value))  # sort characters in string
                self.to_list()  # check if is valid

            def __contains__(self, item: T) -> bool:
                return item in self.to_list()

            def __eq__(self, other: Union['EnumAggregate', str]) -> bool:
                if isinstance(other, str):
                    return self._value == other

                return self._value == other._value

            def __str__(self) -> str:
                return self._value

            def to_list(self) -> List[T]:
                return [self.enum(int(character)) for character in self._value]

            @property
            def value(self) -> str:
                return self._value

            @classmethod
            def all(cls) -> str:
                return ''.join([str(x.value) for x in cls.enum])

        class Gender(IntEnum):
            MALE = 1
            FEMALE = 2
            DIVERS = 3

        @pedantic_class
        class Genders(EnumAggregate[Gender]):
            enum = Gender

        Genders(value=12)

        with self.assertRaises(PedanticTypeCheckException):
            Genders(value=Child())

    def test_primitive_list_dict_tuple(self):
        @pedantic
        def f(x: list[dict[int, tuple[float, str]]]) -> list[Any]:
            return x

        f(x=[{3: (3.24, 'hi')}])

        for value in [
            [{3, (3, 'hi')}],
            [{3: (3, 'hi')}],
            [{3: (3.24, 3)}],
            [{3: (3.24, 'hi')}, {0}],
        ]:
            with self.assertRaises(PedanticTypeCheckException):
                f(x=value)

    def test_dataclass_protocol(self):
        class IsDataclass(typing.Protocol):
            __dataclass_fields__: ClassVar[Dict]

        @dataclass
        class Foo:
            v: int

        @pedantic
        def foo(x: IsDataclass) -> IsDataclass:
            return x

        foo(x=Foo(v=42))

    def test_dataclass_protocol_in_type(self):
        class IsDataclass(typing.Protocol):
            __dataclass_fields__: ClassVar[Dict]

        @dataclass
        class Foo:
            v: int

        @pedantic
        def foo(x: type[IsDataclass]) -> IsDataclass:
            return x

        assert foo(x=Foo) == Foo

    def test_dataclass_protocol_in_type_with_union(self):
        class IsDataclass(typing.Protocol):
            __dataclass_fields__: ClassVar[Dict]

        @dataclass
        class Foo:
            v: int

        @pedantic
        def foo(x: type[None | bool | IsDataclass]) -> IsDataclass:
            return x

        assert foo(x=Foo) == Foo

Ancestors

  • unittest.case.TestCase

Methods

def tearDown(self) ‑> None

Hook method for deconstructing the test fixture after testing it.

Expand source code
def tearDown(self) -> None:
    if os.path.isfile(TEST_FILE):
        os.remove(TEST_FILE)
def test_abstractset_bad_element(self)
Expand source code
def test_abstractset_bad_element(self):
    @pedantic
    def foo(a: AbstractSet[int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a={1, 2, 'bb'})
def test_abstractset_bad_type(self)
Expand source code
def test_abstractset_bad_type(self):
    @pedantic
    def foo(a: AbstractSet[int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=5)
def test_abstractset_custom_type(self)
Expand source code
def test_abstractset_custom_type(self):
    T = TypeVar('T')

    @pedantic_class
    class DummySet(AbstractSet[T]):
        def __contains__(self, x: object) -> bool:
            return x == 1

        def __len__(self) -> T:
            return 1

        def __iter__(self) -> Iterator[T]:
            yield 1

    @pedantic
    def foo(a: AbstractSet[int]) -> None:
        pass

    foo(a=DummySet[int]())
def test_additional_kwargs(self)
Expand source code
def test_additional_kwargs(self):
    @pedantic
    def some_method(a: int, b: float = 0.0, **kwargs: int) -> float:
        return sum([a, b])

    some_method(a=5)
    some_method(a=5, b=0.1)
    some_method(a=5, b=0.1, c=4)
    some_method(a=5, b=0.1, c=4, d=5, e=6)
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        some_method(a=5, b=0.1, c=4, d=5.0, e=6)
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        some_method(a=5.0, b=0.1, c=4, d=5, e=6)
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        some_method(a=5, b=0, c=4, d=5, e=6)
def test_aliases(self)
Expand source code
def test_aliases(self):
    Vector = List[float]

    @pedantic
    def scale(scalar: float, vector: Vector) -> Vector:
        return [scalar * num for num in vector]

    scale(scalar=2.0, vector=[1.0, -4.2, 5.4])
def test_all_ok_2(self)
Expand source code
def test_all_ok_2(self):
    @pedantic
    def calc(n: int, m: int, i: int) -> str:
        return str(n + m + i)

    calc(n=42, m=40, i=38)
def test_all_ok_3(self)
Expand source code
def test_all_ok_3(self):
    @pedantic
    def calc(n: int, m: int, i: int) -> None:
        str(n + m + i)

    calc(n=42, m=40, i=38)
def test_all_ok_4(self)
Expand source code
def test_all_ok_4(self):
    @pedantic
    def calc(n: int) -> List[List[int]]:
        return [[n]]

    calc(n=42)
def test_all_ok_5(self)
Expand source code
def test_all_ok_5(self):
    @pedantic
    def calc(n: int) -> List[Tuple[float, str]]:
        return [(float(n), str(n))]

    calc(n=42)
def test_all_ok_6(self)
Expand source code
def test_all_ok_6(self):
    @pedantic
    def calc(n: int) -> Callable[[int, float], Tuple[float, str]]:
        @pedantic
        def f(x: int, y: float) -> Tuple[float, str]:
            return n * float(x), str(y)
        return f

    calc(n=42)(x=72, y=3.14)
def test_all_ok_7(self)
Expand source code
def test_all_ok_7(self):
    @pedantic
    def calc(n: List[List[float]]) -> Any:
        return n[0][0]

    calc(n=[[42.0]])
def test_all_ok_8(self)
Expand source code
def test_all_ok_8(self):
    @pedantic
    def calc(n: int) -> Callable[[int, float], Tuple[float, str]]:
        @pedantic
        def f(x: int, y: float) -> Tuple[float, str]:
            return n * float(x), str(y)

        return f

    calc(n=42)(x=3, y=3.14)
def test_alternative_list_type_hint(self)
Expand source code
def test_alternative_list_type_hint(self):
    @pedantic
    def _is_digit_in_int(digit: [int], num: int) -> bool:
        num_str = str(num)
        for i in num_str:
            if int(i) == digit:
                return True
        return False

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        _is_digit_in_int(digit=4, num=42)
def test_any(self)
Expand source code
def test_any(self):
    @pedantic
    def calc(ls: List[Any]) -> Dict[int, Any]:
        return {i: ls[i] for i in range(0, len(ls))}

    calc(ls=[1, 2, 3])
    calc(ls=[1.11, 2.0, 3.0])
    calc(ls=['1', '2', '3'])
    calc(ls=[10.5, '2', (3, 4, 5)])
def test_any_type(self)
Expand source code
def test_any_type(self):
    @pedantic
    def foo(a: Any) -> None:
        pass

    foo(a='aa')
def test_args_kwargs(self)
Expand source code
def test_args_kwargs(self):
    @pedantic
    def some_method(a: int = 0, b: float = 0.0) -> float:
        return a * b

    @pedantic
    def wrapper_method(*args: Union[int, float], **kwargs: Union[int, float]) -> float:
        return some_method(*args, **kwargs)

    some_method()
    with self.assertRaises(expected_exception=PedanticCallWithArgsException):
        some_method(3, 3.0)
    some_method(a=3, b=3.0)
    wrapper_method()
    with self.assertRaises(expected_exception=PedanticCallWithArgsException):
        wrapper_method(3, 3.0)
    wrapper_method(a=3, b=3.0)
def test_args_kwargs_different_types(self)
Expand source code
def test_args_kwargs_different_types(self):
    @pedantic
    def foo(*args: str, **kwds: int) -> None:
        print(args)
        print(kwds)

    foo('a', 'b', 'c')
    foo(x=1, y=2)
    foo('', z=0)
def test_args_kwargs_no_type_hint(self)
Expand source code
def test_args_kwargs_no_type_hint(self):
    @pedantic
    def method_no_type_hint(*args, **kwargs) -> None:
        print(args)
        print(kwargs)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        method_no_type_hint(a=3, b=3.0)
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        method_no_type_hint()
def test_args_kwargs_wrong_type_hint(self)
Expand source code
def test_args_kwargs_wrong_type_hint(self):
    """See: https://www.python.org/dev/peps/pep-0484/#arbitrary-argument-lists-and-default-argument-values"""
    @pedantic
    def wrapper_method(*args: str, **kwargs: str) -> None:
        print(args)
        print(kwargs)

    wrapper_method()
    wrapper_method('hi', 'you', ':)')
    wrapper_method(a='hi', b='you', c=':)')
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        wrapper_method('hi', 'you', ':)', 7)
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        wrapper_method(3, 3.0)
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        wrapper_method(a=3, b=3.0)
def test_binary_io(self)
Expand source code
def test_binary_io(self):
    @pedantic
    def foo(a: BinaryIO) -> None:
        print(a)

    foo(a=BytesIO())
def test_binary_io_fail(self)
Expand source code
def test_binary_io_fail(self):
    @pedantic
    def foo(a: TextIO) -> None:
        print(a)

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=BytesIO())
def test_binary_io_real_file(self)
Expand source code
def test_binary_io_real_file(self):
    @pedantic
    def foo(a: BinaryIO) -> None:
        print(a)

    with open(file=TEST_FILE, mode='wb') as f:
        foo(a=f)
def test_bytearray_bytes(self)
Expand source code
def test_bytearray_bytes(self):
    @pedantic
    def foo(x: bytearray) -> None:
        pass

    foo(x=bytearray([1]))
def test_callable(self)
Expand source code
def test_callable(self):
    @pedantic
    def foo_1(a: Callable[..., int]) -> None:
        print(a)

    @pedantic
    def foo_2(a: Callable) -> None:
        print(a)

    def some_callable() -> int:
        return 4

    foo_1(a=some_callable)

    with self.assertRaises(PedanticTypeCheckException):
        foo_2(a=some_callable)
def test_callable_bad_type(self)
Expand source code
def test_callable_bad_type(self):
    @pedantic
    def foo(a: Callable[..., int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=5)
def test_callable_bound_method(self)
Expand source code
def test_callable_bound_method(self):
    @pedantic
    def foo(callback: Callable[[int], Any]) -> None:
        pass

    foo(callback=Child().method)
def test_callable_builtin(self)
Expand source code
def test_callable_builtin(self):
    @pedantic
    def foo(callback: types.BuiltinFunctionType) -> None:
        pass

    foo(callback=[].append)
def test_callable_class(self)

Test that passing a class as a callable does not count the "self" argument "a"gainst the ones declared in the Callable specification.

Expand source code
def test_callable_class(self):
    """
    Test that passing a class as a callable does not count the "self" argument "a"gainst the
    ones declared in the Callable specification.

    """
    @pedantic
    def foo(a: Callable[[int, str], Any]) -> None:
        pass

    class SomeClass:
        def __init__(self, x: int, y: str):
            pass

    foo(a=SomeClass)
def test_callable_defaults(self)

Test that a callable having "too many" arguments don't raise an error if the extra arguments have default values.

Expand source code
def test_callable_defaults(self):
    """
    Test that a callable having "too many" arguments don't raise an error if the extra
    arguments have default values.

    """
    @pedantic
    def foo(callback: Callable[[int, str], Any]) -> None:
        pass

    def some_callable(x: int, y: str, z: float = 1.2) -> int:
        pass

    foo(callback=some_callable)
def test_callable_exact_arg_count(self)
Expand source code
def test_callable_exact_arg_count(self):
    @pedantic
    def foo(a: Callable[[int, str], int]) -> None:
        pass

    def some_callable(x: int, y: str) -> int:
        pass

    foo(a=some_callable)
def test_callable_mandatory_kwonlyargs(self)
Expand source code
def test_callable_mandatory_kwonlyargs(self):
    @pedantic
    def foo(a: Callable[[int, str], int]) -> None:
        pass

    def some_callable(x: int, y: str, *, z: float, bar: str) -> int:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=some_callable)
def test_callable_no_args(self)
Expand source code
def test_callable_no_args(self):
    @pedantic
    def f(g: Callable[[], str]) -> str:
        return g()

    @pedantic
    def greetings() -> str:
        return 'hello world'

    f(g=greetings)
def test_callable_nonmember(self)
Expand source code
def test_callable_nonmember(self):
    class CallableClass:
        def __call__(self):
            pass

    @pedantic_class
    class LocalClass:
        some_callable = CallableClass()
def test_callable_plain(self)
Expand source code
def test_callable_plain(self):
    @pedantic
    def foo(a: Callable[..., Any]) -> None:
        pass

    def callback(a):
        pass

    foo(a=callback)
def test_callable_too_few_arguments(self)
Expand source code
def test_callable_too_few_arguments(self):
    @pedantic
    def foo(a: Callable[[int, str], int]) -> None:
        pass

    def some_callable(x: int) -> int:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=some_callable)
def test_callable_with_union_return(self)
Expand source code
def test_callable_with_union_return(self):
    class MyClass:
        pass

    @pedantic
    def admin_required(func: Callable[..., Union[str, MyClass]]) -> Callable[..., Union[str, MyClass]]:
        @wraps(func)
        def decorated_function(*args, **kwargs):
            return func(*args, **kwargs)
        return decorated_function

    @admin_required
    @pedantic
    def get_server_info() -> str:
        return 'info'

    get_server_info()
def test_callable_without_args_correct_with_lambdas(self)
Expand source code
def test_callable_without_args_correct_with_lambdas(self):
    @pedantic
    def calc(i: Callable[[Any], Tuple[Any, ...]]) -> str:
        return str(i(x=' you'))

    calc(i=lambda x: (42.0, 43, 'hi', x))
def test_callable_without_args_corrected(self)
Expand source code
def test_callable_without_args_corrected(self):
    @pedantic
    def calc(i: Callable[[Any], Tuple[Any, ...]]) -> str:
        return str(i(x=' you'))

    @pedantic
    def arg(x: Any) -> Tuple[Any, ...]:
        return 42.0, 43, 'hi', x
    calc(i=arg)
def test_callable_without_type_args(self)
Expand source code
def test_callable_without_type_args(self):
    @pedantic
    def calc(i: Callable) -> str:
        return str(i(' you'))

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(i=lambda x: (42.0, 43, 'hi', x))
def test_class(self)
Expand source code
def test_class(self):
    @pedantic
    def foo_1(a: Type[Parent]) -> None:
        print(a)

    @pedantic
    def foo_2(a: Type[TypeVar('UnboundType')]) -> None:
        print(a)

    @pedantic
    def foo_3(a: Type[TypeVar('BoundType', bound=Parent)]) -> None:
        print(a)

    foo_1(a=Child)
    foo_2(a=Child)
    foo_3(a=Child)
def test_class_any(self)
Expand source code
def test_class_any(self):
    @pedantic
    def foo(a: Type[Any]) -> None:
        pass

    foo(a=str)
def test_class_bad_subclass(self)
Expand source code
def test_class_bad_subclass(self):
    @pedantic
    def foo(a: Type[Child]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=Parent)
def test_class_decorator(self)
Expand source code
def test_class_decorator(self):
    @pedantic_class
    class Foo:
        @staticmethod
        def staticmethod() -> int:
            return 'foo'

        @classmethod
        def classmethod(cls) -> int:
            return 'foo'

        def method(self) -> int:
            return 'foo'

    with self.assertRaises(PedanticTypeCheckException):
        Foo.staticmethod()

    with self.assertRaises(PedanticTypeCheckException):
        Foo.classmethod()

    with self.assertRaises(PedanticTypeCheckException):
        Foo().method()
def test_class_no_type_vars(self)
Expand source code
def test_class_no_type_vars(self):
    @pedantic
    def foo_1(a: Type) -> None:
        print(a)

    @pedantic
    def foo_2(a: type) -> None:
        print(a)

    with self.assertRaises(PedanticTypeCheckException):
        foo_1(a=Child)

    with self.assertRaises(PedanticTypeCheckException):
        foo_2(a=Child)
def test_class_not_a_class(self)
Expand source code
def test_class_not_a_class(self):
    @pedantic
    def foo(a: Type[Parent]) -> None:
        print(a)

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=1)
def test_collection(self)
Expand source code
def test_collection(self):
    @pedantic
    def foo(a: Collection) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=True)
def test_complex(self)
Expand source code
def test_complex(self):
    @pedantic
    def foo(a: complex) -> None:
        print(a)

    foo(a=complex(1, 5))

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=1.0)
def test_container(self)
Expand source code
def test_container(self):
    @pedantic
    def foo(a: Container[str]) -> None:
        print(a)

    for value in [('a', 'b'), ['a', 'b'], 'abc']:
        foo(a=value)
def test_container_no_type_args(self)
Expand source code
def test_container_no_type_args(self):
    @pedantic
    def foo(a: Container) -> None:
        print(a)

    for value in [('a', 'b'), ['a', 'b'], 'abc']:
        with self.assertRaises(PedanticTypeCheckException):
            foo(a=value)
def test_coroutine_correct_return_type(self)
Expand source code
def test_coroutine_correct_return_type(self):
    @pedantic
    async def foo() -> str:
        return 'foo'

    coro = foo()

    with self.assertRaises(StopIteration):
        coro.send(None)
def test_coroutine_wrong_return_type(self)
Expand source code
def test_coroutine_wrong_return_type(self):
    @pedantic
    async def foo() -> str:
        return 1

    coro = foo()

    with self.assertRaises(PedanticTypeCheckException):
        coro.send(None)
def test_dataclass_protocol(self)
Expand source code
def test_dataclass_protocol(self):
    class IsDataclass(typing.Protocol):
        __dataclass_fields__: ClassVar[Dict]

    @dataclass
    class Foo:
        v: int

    @pedantic
    def foo(x: IsDataclass) -> IsDataclass:
        return x

    foo(x=Foo(v=42))
def test_dataclass_protocol_in_type(self)
Expand source code
def test_dataclass_protocol_in_type(self):
    class IsDataclass(typing.Protocol):
        __dataclass_fields__: ClassVar[Dict]

    @dataclass
    class Foo:
        v: int

    @pedantic
    def foo(x: type[IsDataclass]) -> IsDataclass:
        return x

    assert foo(x=Foo) == Foo
def test_dataclass_protocol_in_type_with_union(self)
Expand source code
def test_dataclass_protocol_in_type_with_union(self):
    class IsDataclass(typing.Protocol):
        __dataclass_fields__: ClassVar[Dict]

    @dataclass
    class Foo:
        v: int

    @pedantic
    def foo(x: type[None | bool | IsDataclass]) -> IsDataclass:
        return x

    assert foo(x=Foo) == Foo
def test_date_datetime(self)
Expand source code
def test_date_datetime(self):
    @pedantic
    def foo(a: datetime, b: date) -> None:
        pass

    foo(a=datetime(1995, 2, 5), b=date(1987, 8, 7))
    foo(a=datetime(1995, 2, 5), b=datetime(1987, 8, 7))

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        foo(a=date(1995, 2, 5), b=date(1987, 8, 7))
def test_dict(self)
Expand source code
def test_dict(self):
    @pedantic
    def foo_1(a: Dict[str, int]) -> None:
        print(a)

    @pedantic
    def foo_2(a: Dict) -> None:
        print(a)

    @pedantic
    def foo_3(a: dict) -> None:
        print(a)

    @pedantic
    def foo_4(a: dict[str, int]) -> None:
        print(a)

    foo_1(a={'x': 2})

    with self.assertRaises(PedanticTypeCheckException):
        foo_2(a={'x': 2})

    with self.assertRaises(PedanticTypeCheckException):
        foo_3(a={'x': 2})

    foo_4(a={'x': 2})
def test_dict_bad_key_type(self)
Expand source code
def test_dict_bad_key_type(self):
    @pedantic
    def foo(a: Dict[str, int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a={1: 2})
def test_dict_bad_type(self)
Expand source code
def test_dict_bad_type(self):
    @pedantic
    def foo(a: Dict[str, int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=5)
def test_dict_bad_value_type(self)
Expand source code
def test_dict_bad_value_type(self):
    @pedantic
    def foo(a: Dict[str, int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a={'x': 'a'})
def test_double_pedantic(self)
Expand source code
def test_double_pedantic(self):
    @pedantic
    @pedantic
    def f(x: int, y: float) -> Tuple[float, str]:
        return float(x), str(y)

    f(x=5, y=3.14)
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        f(x=5.0, y=3.14)
    with self.assertRaises(expected_exception=PedanticCallWithArgsException):
        f(5, 3.14)
def test_ellipsis_in_callable_1(self)
Expand source code
def test_ellipsis_in_callable_1(self):
    @pedantic
    def calc(i: Callable[..., int]) -> int:
        return i()

    @pedantic
    def call() -> int:
        return 42

    calc(i=call)
def test_ellipsis_in_callable_2(self)
Expand source code
def test_ellipsis_in_callable_2(self):
    @pedantic
    def calc(i: Callable[..., int]) -> int:
        return i(x=3.14, y=5)

    @pedantic
    def call(x: float, y: int) -> int:
        return 42

    calc(i=call)
def test_ellipsis_in_callable_3(self)

Problem here: call to "call" misses one argument

Expand source code
def test_ellipsis_in_callable_3(self):
    """Problem here: call to "call" misses one argument"""
    @pedantic
    def calc(i: Callable[..., int]) -> int:
        return i(x=3.14)

    @pedantic
    def call(x: float, y: int) -> int:
        return 42

    with self.assertRaises(expected_exception=PedanticException):
        calc(i=call)
def test_empty_tuple(self)
Expand source code
def test_empty_tuple(self):
    @pedantic
    def foo(a: Tuple[()]) -> None:
        print(a)

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=())
def test_enum(self)
Expand source code
def test_enum(self):
    with self.assertRaises(PedanticTypeCheckException):
        @pedantic_class
        class MyEnum(Enum):
            A = 'a'
def test_enum_1(self)

Problem here: Type hint for 'a' should be MyEnum instead of MyEnum.GAMMA

Expand source code
def test_enum_1(self):
    """Problem here: Type hint for 'a' should be MyEnum instead of MyEnum.GAMMA"""
    class MyEnum(Enum):
        ALPHA = 'startEvent'
        BETA = 'task'
        GAMMA = 'sequenceFlow'

    class MyClass:
        @pedantic
        def operation(self, a: MyEnum.GAMMA) -> None:
            print(a)

    m = MyClass()
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        m.operation(a=MyEnum.GAMMA)
def test_enum_1_corrected(self)
Expand source code
def test_enum_1_corrected(self):
    class MyEnum(Enum):
        ALPHA = 'startEvent'
        BETA = 'task'
        GAMMA = 'sequenceFlow'

    @pedantic
    def operation(a: MyEnum) -> None:
        print(a)

    operation(a=MyEnum.GAMMA)
def test_enum_aggregate(self)
Expand source code
def test_enum_aggregate(self):
    T = TypeVar('T', bound=IntEnum)

    @pedantic_class
    class EnumAggregate(Generic[T]):
        enum: ClassVar[Type[T]]

        def __init__(self, value: Union[int, str, List[T]]) -> None:
            assert len(self.enum) < 10

            if value == '':
                raise ValueError(f'Parameter "value" cannot be empty!')

            if isinstance(value, list):
                self._value = ''.join([str(x.value) for x in value])
            else:
                self._value = str(value)

            self._value = ''.join(sorted(self._value))  # sort characters in string
            self.to_list()  # check if is valid

        def __contains__(self, item: T) -> bool:
            return item in self.to_list()

        def __eq__(self, other: Union['EnumAggregate', str]) -> bool:
            if isinstance(other, str):
                return self._value == other

            return self._value == other._value

        def __str__(self) -> str:
            return self._value

        def to_list(self) -> List[T]:
            return [self.enum(int(character)) for character in self._value]

        @property
        def value(self) -> str:
            return self._value

        @classmethod
        def all(cls) -> str:
            return ''.join([str(x.value) for x in cls.enum])

    class Gender(IntEnum):
        MALE = 1
        FEMALE = 2
        DIVERS = 3

    @pedantic_class
    class Genders(EnumAggregate[Gender]):
        enum = Gender

    Genders(value=12)

    with self.assertRaises(PedanticTypeCheckException):
        Genders(value=Child())
def test_float(self)
Expand source code
def test_float(self):
    @pedantic
    def foo(a: float) -> None:
        print(a)

    foo(a=1.5)

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=1)
def test_forward_ref(self)
Expand source code
def test_forward_ref(self):
    class Conversation:
        pass

    @pedantic
    def get_conversations() -> List['Conversation']:
        return [Conversation(), Conversation()]

    get_conversations()
def test_generator(self)
Expand source code
def test_generator(self):
    @pedantic
    def genfunc() -> Generator[int, str, List[str]]:
        val1 = yield 2
        val2 = yield 3
        val3 = yield 4
        return [val1, val2, val3]

    gen = genfunc()

    with self.assertRaises(StopIteration):
        value = next(gen)
        while True:
            value = gen.send(str(value))
            assert isinstance(value, int)
def test_generator_bad_return(self)
Expand source code
def test_generator_bad_return(self):
    @pedantic
    def genfunc() -> Generator[int, str, str]:
        yield 1
        return 6

    gen = genfunc()
    next(gen)

    with self.assertRaises(PedanticTypeCheckException):
        gen.send('foo')
def test_generator_bad_send(self)
Expand source code
def test_generator_bad_send(self):
    @pedantic
    def genfunc() -> Generator[int, str, None]:
        yield 1
        yield 2

    gen = genfunc()
    next(gen)

    with self.assertRaises(PedanticTypeCheckException):
        gen.send(2)
def test_generator_bad_yield(self)
Expand source code
def test_generator_bad_yield(self):
    @pedantic
    def genfunc_1() -> Generator[int, str, None]:
        yield 'foo'

    @pedantic
    def genfunc_2() -> Iterable[int]:
        yield 'foo'

    @pedantic
    def genfunc_3() -> Iterator[int]:
        yield 'foo'

    gen = genfunc_1()

    with self.assertRaises(PedanticTypeCheckException):
        next(gen)

    gen = genfunc_2()

    with self.assertRaises(PedanticTypeCheckException):
        next(gen)

    gen = genfunc_3()

    with self.assertRaises(PedanticTypeCheckException):
        next(gen)
def test_generator_no_type_args(self)
Expand source code
def test_generator_no_type_args(self):
    @pedantic
    def genfunc() -> Generator:
        val1 = yield 2
        val2 = yield 3
        val3 = yield 4
        return [val1, val2, val3]

    with self.assertRaises(PedanticTypeCheckException):
        genfunc()
def test_generator_simple(self)

Test that argument type checking works in a generator function too.

Expand source code
def test_generator_simple(self):
    """Test that argument type checking works in a generator function too."""
    @pedantic
    def generate(a: int) -> Generator[int, int, None]:
        yield a
        yield a + 1

    gen = generate(a=1)
    next(gen)
def test_generic(self)
Expand source code
def test_generic(self):
    T_Foo = TypeVar('T_Foo')

    class FooGeneric(Generic[T_Foo]):
        pass

    @pedantic
    def foo(a: FooGeneric[str]) -> None:
        print(a)

    foo(a=FooGeneric[str]())
def test_implicit_default_none(self)

Test that if the default value is None, a None argument can be passed.

Expand source code
def test_implicit_default_none(self):
    """
    Test that if the default value is ``None``, a ``None`` argument can be passed.

    """
    @pedantic
    def foo(a: Optional[str] = None) -> None:
        pass

    foo()
def test_inheritance_1(self)
Expand source code
def test_inheritance_1(self):
    class MyClassA:
        pass

    class MyClassB(MyClassA):
        pass

    @pedantic
    def calc(a: MyClassA) -> str:
        return str(a)

    calc(a=MyClassA())
    calc(a=MyClassB())
def test_inheritance_2(self)

Problem here: A is not a subtype of B

Expand source code
def test_inheritance_2(self):
    """Problem here: A is not a subtype of B"""
    class MyClassA:
        pass

    class MyClassB(MyClassA):
        pass

    @pedantic
    def calc(a: MyClassB) -> str:
        return str(a)

    calc(a=MyClassB())
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(a=MyClassA())
def test_inherited_class_method(self)
Expand source code
def test_inherited_class_method(self):
    @pedantic_class
    class Parent:
        @classmethod
        def foo(cls, x: str) -> str:
            return cls.__name__

    @pedantic_class
    class Child(Parent):
        pass

    self.assertEqual('Parent', Child.foo(x='bar'))

    with self.assertRaises(PedanticTypeCheckException):
        Child.foo(x=1)
def test_instance_method_1(self)
Expand source code
def test_instance_method_1(self):
    class MyClassA:
        @pedantic
        def calc(self, i: int) -> str:
            return str(i)

    a = MyClassA()
    a.calc(i=42)
def test_instance_method_2(self)

Problem here: 'i' has no type annotation

Expand source code
def test_instance_method_2(self):
    """Problem here: 'i' has no type annotation"""
    class MyClassA:
        @pedantic
        def calc(self, i) -> str:
            return str(i)

    a = MyClassA()
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        a.calc(i=42)
def test_instance_method_2_corrected(self)
Expand source code
def test_instance_method_2_corrected(self):
    class MyClassA:
        @pedantic
        def calc(self, i: int) -> str:
            return str(i)

    a = MyClassA()
    a.calc(i=42)
def test_instance_method_3_corrected(self)
Expand source code
def test_instance_method_3_corrected(self):
    class MyClassA:
        @pedantic
        def calc(self, i: float) -> str:
            return str(i)

    a = MyClassA()
    a.calc(i=42.0)
def test_instance_method_5(self)

Problem here: instance methods is not called with kwargs

Expand source code
def test_instance_method_5(self):
    """Problem here: instance methods is not called with kwargs"""
    class MyClassA:
        @pedantic
        def calc(self, i: int) -> str:
            return str(i)

    a = MyClassA()
    a.calc(i=42)
def test_instance_method_int_is_not_float(self)
Expand source code
def test_instance_method_int_is_not_float(self):
    class MyClassA:
        @pedantic
        def calc(self, i: float) -> str:
            return str(i)

    a = MyClassA()
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        a.calc(i=42)
def test_instance_method_no_kwargs(self)
Expand source code
def test_instance_method_no_kwargs(self):
    class MyClassA:
        @pedantic
        def calc(self, i: int) -> str:
            return str(i)

    a = MyClassA()
    with self.assertRaises(expected_exception=PedanticCallWithArgsException):
        a.calc(42)
def test_is_subtype_tuple(self)
Expand source code
def test_is_subtype_tuple(self):
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        @pedantic
        def foo() -> Callable[[Tuple[float, str]], Tuple[int]]:
            def bar(a: Tuple[float]) -> Tuple[int]:
                return len(a[1]) + int(a[0]),
            return bar
        foo()
def test_is_subtype_tuple_corrected(self)
Expand source code
def test_is_subtype_tuple_corrected(self):
    @pedantic
    def foo() -> Callable[[Tuple[float, str]], Tuple[int]]:
        def bar(a: Tuple[float, str]) -> Tuple[int]:
            return len(a[1]) + int(a[0]),
        return bar
    foo()
def test_iterable(self)
Expand source code
def test_iterable(self):
    @pedantic
    def foo(a: Iterable[str]) -> None:
        print(a)

    for value in [('a', 'b'), ['a', 'b'], 'abc']:
        foo(a=value)
def test_iterable_advanced(self)
Expand source code
def test_iterable_advanced(self):
    @pedantic
    def genfunc() -> Iterable[int]:
        val1 = yield 2
        val2 = yield 3
        val3 = yield 4
        return [val1, val2, val3]

    gen = genfunc()

    with self.assertRaises(PedanticTypeCheckException):
        value = next(gen)
        while True:
            value = gen.send(str(value))
            assert isinstance(value, int)
def test_iterable_advanced_no_type_args(self)
Expand source code
def test_iterable_advanced_no_type_args(self):
    @pedantic
    def genfunc() -> Iterable:
        val1 = yield 2
        val2 = yield 3
        val3 = yield 4
        return [val1, val2, val3]

    with self.assertRaises(PedanticTypeCheckException):
        genfunc()
def test_iterable_no_type_args(self)
Expand source code
def test_iterable_no_type_args(self):
    @pedantic
    def foo(a: Iterable) -> None:
        print(a)

    for value in [('a', 'b'), ['a', 'b'], 'abc']:
        with self.assertRaises(PedanticTypeCheckException):
            foo(a=value)
def test_iterator(self)
Expand source code
def test_iterator(self):
    @pedantic
    def genfunc() -> Iterator[int]:
        val1 = yield 2
        val2 = yield 3
        val3 = yield 4
        return [val1, val2, val3]

    gen = genfunc()

    with self.assertRaises(PedanticTypeCheckException):
        value = next(gen)
        while True:
            value = gen.send(str(value))
            assert isinstance(value, int)
def test_iterator_no_type_args(self)
Expand source code
def test_iterator_no_type_args(self):
    @pedantic
    def genfunc() -> Iterator:
        val1 = yield 2
        val2 = yield 3
        val3 = yield 4
        return [val1, val2, val3]

    with self.assertRaises(PedanticTypeCheckException):
        genfunc()
def test_kwargs(self)
Expand source code
def test_kwargs(self):
    @pedantic
    def foo(**kwargs: int) -> None:
        pass

    foo(a=1, b=2)
def test_kwargs_fail(self)
Expand source code
def test_kwargs_fail(self):
    @pedantic
    def foo(**kwargs: int) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=1, b='a')
def test_lambda_1(self)
Expand source code
def test_lambda_1(self):
    @pedantic
    def calc(i: float) -> Callable[[float], str]:
        return lambda x: str(x * i)

    calc(i=42.0)(10.0)
def test_lambda_3(self)
Expand source code
def test_lambda_3(self):
    @pedantic
    def calc(i: float) -> Callable[[float], str]:
        def res(x: float) -> str:
            return str(x * i)
        return res

    calc(i=42.0)(10.0)
def test_lambda_4_almost_corrected(self)

Problem here: float != str

Expand source code
def test_lambda_4_almost_corrected(self):
    """Problem here: float != str"""
    @pedantic
    def calc(i: float) -> Callable[[float], str]:
        @pedantic
        def res(x: int) -> str:
            return str(x * i)
        return res

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(i=42.0)(x=10)
def test_lambda_4_almost_corrected_2(self)
Expand source code
def test_lambda_4_almost_corrected_2(self):
    @pedantic
    def calc(i: float) -> Callable[[int], str]:
        @pedantic
        def res(x: int) -> str:
            return str(x * i)
        return res

    calc(i=42.0)(x=10)
def test_lambda_5(self)

Problem here: float != int

Expand source code
def test_lambda_5(self):
    """Problem here: float != int"""
    @pedantic
    def calc(i: float) -> Callable[[float], str]:
        @pedantic
        def res(x: float) -> str:
            return str(x * i)
        return res

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(i=42.0)(x=10)
def test_lambda_corrected(self)
Expand source code
def test_lambda_corrected(self):
    @pedantic
    def calc(i: float) -> Callable[[float], str]:
        @pedantic
        def res(x: float) -> str:
            return str(x * i)

        return res

    calc(i=42.0)(x=10.0)
def test_lambda_int_is_not_float(self)
Expand source code
def test_lambda_int_is_not_float(self):
    @pedantic
    def calc(i: float) -> Callable[[float], str]:
        def res(x: int) -> str:
            return str(x * i)
        return res

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(i=42.0)(x=10)
def test_list(self)
Expand source code
def test_list(self):
    @pedantic
    def foo_1(a: List[int]) -> None:
        print(a)

    @pedantic
    def foo_2(a: List) -> None:
        print(a)

    @pedantic
    def foo_3(a: list) -> None:
        print(a)

    @pedantic
    def foo_4(a: list[int]) -> None:
        print(a)

    foo_1(a=[1, 2])

    with self.assertRaises(PedanticTypeCheckException):
        foo_2(a=[1, 2])

    with self.assertRaises(PedanticTypeCheckException):
        foo_3(a=[1, 2])


    foo_4(a=[1, 2])
def test_list_bad_element(self)
Expand source code
def test_list_bad_element(self):
    @pedantic
    def foo(a: List[int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=[1, 2, 'bb'])
def test_list_bad_type(self)
Expand source code
def test_list_bad_type(self):
    @pedantic
    def foo(a: List[int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=5)
def test_list_of_new_type(self)
Expand source code
def test_list_of_new_type(self):
    UserId = NewType('UserId', int)

    @pedantic
    def get_user_name(user_ids: List[UserId]) -> str:
        return str(user_ids)

    get_user_name(user_ids=[UserId(524313), UserId(42)])
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        get_user_name(user_ids=[UserId(524313), UserId(42), 430.0])
def test_list_without_args(self)
Expand source code
def test_list_without_args(self):
    @pedantic
    def calc(i: List) -> Any:
        return [i]

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(i=[42.0, 43, 'hi'])
def test_list_without_args_corrected(self)
Expand source code
def test_list_without_args_corrected(self):
    @pedantic
    def calc(i: List[Any]) -> List[List[Any]]:
        return [i]

    calc(i=[42.0, 43, 'hi'])
def test_literal(self)
Expand source code
def test_literal(self):
    @pedantic
    def foo(a: Literal[1, True, 'x', b'y', 404]) -> None:
        print(a)

    foo(a=404)
    foo(a=True)
    foo(a='x')

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=4)
def test_literal_illegal_value(self)
Expand source code
def test_literal_illegal_value(self):
    @pedantic
    def foo(a: Literal[1, 1.1]) -> None:
        print(a)

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=4)
def test_literal_union(self)
Expand source code
def test_literal_union(self):
    @pedantic
    def foo(a: Union[str, Literal[1, 6, 8]]) -> None:
        print(a)

    foo(a=6)

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=4)
def test_local_class(self)
Expand source code
def test_local_class(self):
    @pedantic_class
    class LocalClass:
        class Inner:
            pass

        def create_inner(self) -> 'Inner':
            return self.Inner()

    retval = LocalClass().create_inner()
    assert isinstance(retval, LocalClass.Inner)
def test_local_class_async(self)
Expand source code
def test_local_class_async(self):
    @pedantic_class
    class LocalClass:
        class Inner:
            pass

        async def create_inner(self) -> 'Inner':
            return self.Inner()

    coro = LocalClass().create_inner()

    with self.assertRaises(StopIteration):
        coro.send(None)
def test_marco(self)
Expand source code
def test_marco(self):
    @pedantic_class
    class A:
        def __init__(self, val: int) -> None:
            self.val = val

        def __eq__(self, other: 'A') -> bool:  # other: A and all subclasses
            return self.val == other.val

    @pedantic_class
    class B(A):
        def __init__(self, val: int) -> None:
            super().__init__(val=val)

    @pedantic_class
    class C(A):
        def __init__(self, val: int) -> None:
            super().__init__(val=val)

    a = A(val=42)
    b = B(val=42)
    c = C(val=42)

    assert a == b  # works
    assert a == c  # works
    assert b == c  # error
def test_mismatching_default_type(self)
Expand source code
def test_mismatching_default_type(self):
    @pedantic
    def foo(a: str = 1) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo()
def test_missing_type_hint_1(self)

Problem here: type hint for n missed

Expand source code
def test_missing_type_hint_1(self):
    """Problem here: type hint for n missed"""
    @pedantic
    def calc(n) -> float:
        return 42.0 * n

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42)
def test_missing_type_hint_1_corrected(self)
Expand source code
def test_missing_type_hint_1_corrected(self):
    @pedantic
    def calc(n: int) -> float:
        return 42.0 * n

    calc(n=42)
def test_missing_type_hint_2(self)

Problem here: Return type annotation missed

Expand source code
def test_missing_type_hint_2(self):
    """Problem here: Return type annotation missed"""
    @pedantic
    def calc(n: int):
        return 'Hi' + str(n)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42)
def test_missing_type_hint_2_corrected(self)
Expand source code
def test_missing_type_hint_2_corrected(self):
    @pedantic
    def calc(n: int) -> str:
        return 'Hi' + str(n)

    calc(n=42)
def test_missing_type_hint_3(self)

Problem here: type hint for i missed

Expand source code
def test_missing_type_hint_3(self):
    """Problem here: type hint for i missed"""
    @pedantic
    def calc(n: int, m: int, i) -> int:
        return n + m + i

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42, m=40, i=38)
def test_missing_type_hint_3_corrected(self)
Expand source code
def test_missing_type_hint_3_corrected(self):
    @pedantic
    def calc(n: int, m: int, i: int) -> int:
        return n + m + i

    calc(n=42, m=40, i=38)
def test_namedtuple(self)
Expand source code
def test_namedtuple(self):
    Employee = NamedTuple('Employee', [('name', str), ('id', int)])

    @pedantic
    def foo(bar: Employee) -> None:
        print(bar)

    foo(bar=Employee('bob', 1))
def test_namedtuple_huge_type_mismatch(self)
Expand source code
def test_namedtuple_huge_type_mismatch(self):
    Employee = NamedTuple('Employee', [('name', str), ('id', int)])

    @pedantic
    def foo(bar: int) -> None:
        print(bar)

    with self.assertRaises(PedanticTypeCheckException):
        foo(bar=foo(bar=Employee('bob', 1)))
def test_namedtuple_key_mismatch(self)
Expand source code
def test_namedtuple_key_mismatch(self):
    Employee1 = NamedTuple('Employee', [('name', str), ('id', int)])
    Employee2 = NamedTuple('Employee', [('firstname', str), ('id', int)])

    @pedantic
    def foo(bar: Employee1) -> None:
        print(bar)

    with self.assertRaises(PedanticTypeCheckException):
        foo(bar=Employee2('bob', 1))
def test_namedtuple_type_mismatch(self)
Expand source code
def test_namedtuple_type_mismatch(self):
    Employee = NamedTuple('Employee', [('name', str), ('id', int)])

    @pedantic
    def foo(bar: Employee) -> None:
        print(bar)

    with self.assertRaises(PedanticTypeCheckException):
        foo(bar=('bob', 1))
def test_namedtuple_wrong_field_type(self)
Expand source code
def test_namedtuple_wrong_field_type(self):
    Employee = NamedTuple('Employee', [('name', str), ('id', int)])

    @pedantic
    def foo(bar: Employee) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(bar=Employee(2, 1))
def test_nested_type_hints_1(self)
Expand source code
def test_nested_type_hints_1(self):
    @pedantic
    def calc(n: int) -> List[List[float]]:
        return [0.0 * n]

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42)
def test_nested_type_hints_1_corrected(self)
Expand source code
def test_nested_type_hints_1_corrected(self):
    @pedantic
    def calc(n: int) -> List[List[float]]:
        return [[0.0 * n]]

    calc(n=42)
def test_nested_type_hints_2(self)

Problem here: int != float

Expand source code
def test_nested_type_hints_2(self):
    """Problem here: int != float"""
    @pedantic
    def calc(n: int) -> List[Tuple[float, str]]:
        return [(n, str(n))]

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42)
def test_nested_type_hints_2_corrected(self)
Expand source code
def test_nested_type_hints_2_corrected(self):
    @pedantic
    def calc(n: int) -> List[Tuple[int, str]]:
        return [(n, str(n))]

    @pedantic
    def calc_2(n: float) -> List[Tuple[float, str]]:
        return [(n, str(n))]

    calc(n=42)
    calc_2(n=42.0)
def test_nested_type_hints_3(self)

Problem here: inner function actually returns Tuple[int, str]

Expand source code
def test_nested_type_hints_3(self):
    """Problem here: inner function actually returns Tuple[int, str]"""
    @pedantic
    def calc(n: int) -> Callable[[int, float], Tuple[float, str]]:
        @pedantic
        def f(x: int, y: float) -> Tuple[float, str]:
            return n * x, str(y)
        return f

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42)(x=3, y=3.14)
def test_nested_type_hints_3_corrected(self)
Expand source code
def test_nested_type_hints_3_corrected(self):
    @pedantic
    def calc(n: int) -> Callable[[int, float], Tuple[int, str]]:
        @pedantic
        def f(x: int, y: float) -> Tuple[int, str]:
            return n * x, str(y)

        return f

    calc(n=42)(x=3, y=3.14)
def test_nested_type_hints_4(self)

Problem here: return type is actually float

Expand source code
def test_nested_type_hints_4(self):
    """Problem here: return type is actually float"""
    @pedantic
    def calc(n: List[List[float]]) -> int:
        return n[0][0]

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=[[42.0]])
def test_nested_type_hints_5(self)

Problem here: Tuple[float, str] != Tuple[float, float]

Expand source code
def test_nested_type_hints_5(self):
    """Problem here: Tuple[float, str] != Tuple[float, float]"""

    @pedantic
    def calc(n: int) -> Callable[[int, float], Tuple[float, str]]:
        @pedantic
        def f(x: int, y: float) -> Tuple[float, float]:
            return n * float(x), y
        return f

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42)
def test_nested_type_hints_5_corrected(self)
Expand source code
def test_nested_type_hints_5_corrected(self):
    @pedantic
    def calc(n: int) -> Callable[[int, float], Tuple[float, float]]:
        @pedantic
        def f(x: int, y: float) -> Tuple[float, float]:
            return n * float(x), y
        return f

    calc(n=42)
def test_nested_type_hints_corrected(self)
Expand source code
def test_nested_type_hints_corrected(self):
    @pedantic
    def calc(n: List[List[float]]) -> int:
        return int(n[0][0])

    calc(n=[[42.0]])
def test_new_type(self)
Expand source code
def test_new_type(self):
    UserId = NewType('UserId', int)

    @pedantic
    def get_user_name(user_id: UserId) -> str:
        return str(user_id)

    some_id = UserId(524313)
    get_user_name(user_id=some_id)

    # the following would be desirable but impossible to check at runtime:
    # with self.assertRaises(expected_exception=AssertionError):
    #     get_user_name(user_id=-1)
def test_newtype(self)
Expand source code
def test_newtype(self):
    myint = NewType("myint", int)

    @pedantic
    def foo(a: myint) -> int:
        return 42

    assert foo(a=1) == 42

    with self.assertRaises(PedanticTypeCheckException):
        foo(a="a")
def test_no_kwargs(self)
Expand source code
def test_no_kwargs(self):
    @pedantic
    def calc(n: int, m: int, i: int) -> int:
        return n + m + i

    with self.assertRaises(expected_exception=PedanticCallWithArgsException):
        calc(42, 40, 38)
    with self.assertRaises(expected_exception=PedanticCallWithArgsException):
        calc(42, m=40, i=38)
    calc(n=42, m=40, i=38)
def test_none_1(self)

Problem here: None is not accepted

Expand source code
def test_none_1(self):
    """Problem here: None is not accepted"""
    @pedantic
    def calc(n: int, m: int, i: int) -> int:
        return n + m + i

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42, m=40, i=None)
def test_none_2(self)
Expand source code
def test_none_2(self):
    @pedantic
    def calc(n: int, m: int, i: Optional[int]) -> int:
        return n + m + i if i is not None else n + m

    calc(n=42, m=40, i=None)
def test_none_3(self)
Expand source code
def test_none_3(self):
    @pedantic
    def calc(n: int, m: int, i: Union[int, None]) -> int:
        return n + m + i if i is not None else n + m

    calc(n=42, m=40, i=None)
def test_none_4(self)

Problem here: function may return None

Expand source code
def test_none_4(self):
    """Problem here: function may return None"""
    @pedantic
    def calc(n: int, m: int, i: Union[int, None]) -> int:
        return n + m + i if i is not None else None

    calc(n=42, m=40, i=42)
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42, m=40, i=None)
def test_none_5(self)
Expand source code
def test_none_5(self):
    @pedantic
    def calc(n: int, m: int, i: Union[int, None]) -> Optional[int]:
        return n + m + i if i is not None else None

    calc(n=42, m=40, i=None)
def test_noreturn(self)
Expand source code
def test_noreturn(self):
    @pedantic
    def foo() -> NoReturn:
        pass

    @pedantic
    def bar() -> NoReturn:
        raise ZeroDivisionError('bar')

    with self.assertRaises(expected_exception=ZeroDivisionError):
        bar()

    with self.assertRaises(PedanticTypeCheckException):
        foo()
def test_optional_args_1(self)
Expand source code
def test_optional_args_1(self):
    @pedantic
    def calc(a: int, b: int = 42) -> int:
        return a + b

    calc(a=2)
def test_optional_args_2(self)
Expand source code
def test_optional_args_2(self):
    @pedantic
    def calc(a: int = 3, b: int = 42, c: float = 5.0) -> float:
        return a + b + c

    calc()
    calc(a=1)
    calc(b=1)
    calc(c=1.0)
    calc(a=1, b=1)
    calc(a=1, c=1.0)
    calc(b=1, c=1.0)
    calc(a=1, b=1, c=1.0)
def test_optional_args_3(self)

Problem here: optional argument c: 5 is not a float

Expand source code
def test_optional_args_3(self):
    """Problem here: optional argument c: 5 is not a float"""
    @pedantic
    def calc(a: int = 3, b: int = 42, c: float = 5) -> float:
        return a + b + c

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc()
def test_optional_args_3_corrected(self)
Expand source code
def test_optional_args_3_corrected(self):
    @pedantic
    def calc(a: int = 3, b: int = 42, c: float = 5.0) -> float:
        return a + b + c

    calc()
def test_optional_args_4(self)
Expand source code
def test_optional_args_4(self):
    class MyClass:
        @pedantic
        def foo(self, a: int, b: Optional[int] = 1) -> int:
            return a + b

    my_class = MyClass()
    my_class.foo(a=10)
def test_optional_args_5(self)
Expand source code
def test_optional_args_5(self):
    @pedantic
    def calc(d: Optional[Dict[int, int]] = None) -> Optional[int]:
        if d is None:
            return None
        return sum(d.keys())

    calc(d=None)
    calc()
    calc(d={42: 3})

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(d={42: 3.14})
def test_optional_args_6(self)

"Problem here: str != int

Expand source code
def test_optional_args_6(self):
    """"Problem here: str != int"""
    @pedantic
    def calc(d: int = 42) -> int:
        return int(d)

    calc(d=99999)
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(d='999999')
def test_pedantic(self)
Expand source code
def test_pedantic(self):
    @pedantic
    def foo(a: int, b: str) -> str:
        return 'abc'

    self.assertEqual('abc', foo(a=4, b='abc'))
def test_pedantic_always(self)
Expand source code
def test_pedantic_always(self):
    @pedantic
    def foo(a: int, b: str) -> str:
        return 'abc'

    self.assertEqual('abc', foo(a=4, b='abc'))
def test_pedantic_arguments_fail(self)
Expand source code
def test_pedantic_arguments_fail(self):
    @pedantic
    def foo(a: int, b: str) -> str:
        return 'abc'

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        foo(a=4, b=5)
def test_pedantic_on_class(self)
Expand source code
def test_pedantic_on_class(self):
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        @pedantic
        class MyClass:
            pass
        MyClass()
def test_pedantic_return_type_fail(self)
Expand source code
def test_pedantic_return_type_fail(self):
    @pedantic
    def foo(a: int, b: str) -> str:
        return 6

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        foo(a=4, b='abc')
def test_pedantic_return_type_var_fail(self)
Expand source code
def test_pedantic_return_type_var_fail(self):
    T = TypeVar('T', int, float)

    @pedantic
    def foo(a: T, b: T) -> T:
        return 'a'

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=4, b=2)
def test_primitive_list_dict_tuple(self)
Expand source code
def test_primitive_list_dict_tuple(self):
    @pedantic
    def f(x: list[dict[int, tuple[float, str]]]) -> list[Any]:
        return x

    f(x=[{3: (3.24, 'hi')}])

    for value in [
        [{3, (3, 'hi')}],
        [{3: (3, 'hi')}],
        [{3: (3.24, 3)}],
        [{3: (3.24, 'hi')}, {0}],
    ]:
        with self.assertRaises(PedanticTypeCheckException):
            f(x=value)
def test_return_generator(self)
Expand source code
def test_return_generator(self):
    @pedantic
    def genfunc() -> Generator[int, None, None]:
        yield 1

    @pedantic
    def foo() -> Generator[int, None, None]:
        return genfunc()

    foo()
def test_return_type_none(self)
Expand source code
def test_return_type_none(self):
    @pedantic
    def foo() -> None:
        return 'a'
    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        foo()
def test_sequence(self)
Expand source code
def test_sequence(self):
    @pedantic
    def foo(a: Sequence[str]) -> None:
        print(a)

    for value in [('a', 'b'), ['a', 'b'], 'abc']:
        foo(a=value)
def test_sequence_bad_element(self)
Expand source code
def test_sequence_bad_element(self):
    @pedantic
    def foo(a: Sequence[int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=[1, 2, 'bb'])
def test_sequence_bad_type(self)
Expand source code
def test_sequence_bad_type(self):
    @pedantic
    def foo(a: Sequence[int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=5)
def test_sequence_no_type_args(self)
Expand source code
def test_sequence_no_type_args(self):
    @pedantic
    def foo(a: Sequence) -> None:
        print(a)

    for value in [('a', 'b'), ['a', 'b'], 'abc']:
        with self.assertRaises(PedanticTypeCheckException):
            foo(a=value)
def test_set(self)
Expand source code
def test_set(self):
    @pedantic
    def foo_1(a: AbstractSet[int]) -> None:
        print(a)

    @pedantic
    def foo_2(a: Set[int]) -> None:
        print(a)

    for value in [set(), {6}]:
        foo_1(a=value)
        foo_2(a=value)
def test_set_bad_element(self)
Expand source code
def test_set_bad_element(self):
    @pedantic
    def foo(a: Set[int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a={1, 2, 'bb'})
def test_set_bad_type(self)
Expand source code
def test_set_bad_type(self):
    @pedantic
    def foo(a: Set[int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=5)
def test_set_no_type_args(self)
Expand source code
def test_set_no_type_args(self):
    @pedantic
    def foo_1(a: AbstractSet) -> None:
        print(a)

    @pedantic
    def foo_2(a: Set) -> None:
        print(a)

    @pedantic
    def foo_3(a: set) -> None:
        print(a)

    for value in [set(), {6}]:
        with self.assertRaises(PedanticTypeCheckException):
            foo_1(a=value)

        with self.assertRaises(PedanticTypeCheckException):
            foo_2(a=value)

        with self.assertRaises(PedanticTypeCheckException):
            foo_3(a=value)
def test_sloppy_types_dict(self)
Expand source code
def test_sloppy_types_dict(self):
    @pedantic
    def operation(d: dict) -> int:
        return len(d.keys())

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        operation(d={1: 1, 2: 2})
def test_sloppy_types_dict_almost_corrected_no_type_args(self)
Expand source code
def test_sloppy_types_dict_almost_corrected_no_type_args(self):
    @pedantic
    def operation(d: Dict) -> int:
        return len(d.keys())

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        operation(d={1: 1, 2: 2})
def test_sloppy_types_dict_corrected(self)
Expand source code
def test_sloppy_types_dict_corrected(self):
    @pedantic
    def operation(d: Dict[int, int]) -> int:
        return len(d.keys())

    operation(d={1: 1, 2: 2})
def test_sloppy_types_frozenset(self)
Expand source code
def test_sloppy_types_frozenset(self):
    @pedantic
    def operation(d: frozenset) -> int:
        return len(d)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        operation(d=frozenset({1, 2, 3}))
def test_sloppy_types_frozenset_almost_corrected_no_type_args(self)
Expand source code
def test_sloppy_types_frozenset_almost_corrected_no_type_args(self):
    @pedantic
    def operation(d: FrozenSet) -> int:
        return len(d)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        operation(d=frozenset({1, 2, 3}))
def test_sloppy_types_frozenset_corrected(self)
Expand source code
def test_sloppy_types_frozenset_corrected(self):
    @pedantic
    def operation(d: FrozenSet[int]) -> int:
        return len(d)

    operation(d=frozenset({1, 2, 3}))
def test_sloppy_types_list(self)
Expand source code
def test_sloppy_types_list(self):
    @pedantic
    def operation(d: list) -> int:
        return len(d)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        operation(d=[1, 2, 3, 4])
def test_sloppy_types_list_almost_corrected_no_type_args(self)
Expand source code
def test_sloppy_types_list_almost_corrected_no_type_args(self):
    @pedantic
    def operation(d: List) -> int:
        return len(d)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        operation(d=[1, 2, 3, 4])
def test_sloppy_types_list_corrected(self)
Expand source code
def test_sloppy_types_list_corrected(self):
    @pedantic
    def operation(d: List[int]) -> int:
        return len(d)

    operation(d=[1, 2, 3, 4])
def test_sloppy_types_set(self)
Expand source code
def test_sloppy_types_set(self):
    @pedantic
    def operation(d: set) -> int:
        return len(d)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        operation(d={1, 2, 3})
def test_sloppy_types_set_almost_corrected_to_type_args(self)
Expand source code
def test_sloppy_types_set_almost_corrected_to_type_args(self):
    @pedantic
    def operation(d: Set) -> int:
        return len(d)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        operation(d={1, 2, 3})
def test_sloppy_types_set_corrected(self)
Expand source code
def test_sloppy_types_set_corrected(self):
    @pedantic
    def operation(d: Set[int]) -> int:
        return len(d)

    operation(d={1, 2, 3})
def test_sloppy_types_tuple(self)
Expand source code
def test_sloppy_types_tuple(self):
    @pedantic
    def operation(d: tuple) -> int:
        return len(d)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        operation(d=(1, 2, 3))
def test_sloppy_types_tuple_almost_corrected_no_type_args(self)
Expand source code
def test_sloppy_types_tuple_almost_corrected_no_type_args(self):
    @pedantic
    def operation(d: Tuple) -> int:
        return len(d)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        operation(d=(1, 2, 3))
def test_sloppy_types_tuple_corrected(self)
Expand source code
def test_sloppy_types_tuple_corrected(self):
    @pedantic
    def operation(d: Tuple[int, int, int]) -> int:
        return len(d)

    operation(d=(1, 2, 3))
def test_text_io(self)
Expand source code
def test_text_io(self):
    @pedantic
    def foo(a: TextIO) -> None:
        print(a)

    foo(a=StringIO())
def test_text_io_fail(self)
Expand source code
def test_text_io_fail(self):
    @pedantic
    def foo(a: BinaryIO) -> None:
        print(a)

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=StringIO())
def test_text_io_real_file(self)
Expand source code
def test_text_io_real_file(self):
    @pedantic
    def foo(a: TextIO) -> None:
        print(a)

    with open(file=TEST_FILE, mode='w') as f:
        foo(a=f)
def test_tuple(self)
Expand source code
def test_tuple(self):
    @pedantic
    def foo_1(a: Tuple[int, int]) -> None:
        print(a)

    @pedantic
    def foo_2(a: Tuple[int, ...]) -> None:
        print(a)

    foo_1(a=(1, 2))
    foo_2(a=(1, 2))
def test_tuple_bad_element(self)
Expand source code
def test_tuple_bad_element(self):
    @pedantic
    def foo(a: Tuple[int, str]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=(1, 2))
def test_tuple_bad_type(self)
Expand source code
def test_tuple_bad_type(self):
    @pedantic
    def foo(a: Tuple[int]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=5)
def test_tuple_ellipsis_bad_element(self)
Expand source code
def test_tuple_ellipsis_bad_element(self):
    @pedantic
    def foo(a: Tuple[int, ...]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=(1, 2, 'blah'))
def test_tuple_no_type_args(self)
Expand source code
def test_tuple_no_type_args(self):
    @pedantic
    def foo_1(a: Tuple) -> None:
        print(a)

    @pedantic
    def foo_2(a: tuple) -> None:
        print(a)

    with self.assertRaises(PedanticTypeCheckException):
        foo_1(a=(1, 2))

    with self.assertRaises(PedanticTypeCheckException):
        foo_2(a=(1, 2))
def test_tuple_too_few_elements(self)
Expand source code
def test_tuple_too_few_elements(self):
    @pedantic
    def foo(a: Tuple[int, str]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=(1,))
def test_tuple_too_many_elements(self)
Expand source code
def test_tuple_too_many_elements(self):
    @pedantic
    def foo(a: Tuple[int, str]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=(1, 'aa', 2))
def test_tuple_without_args_corrected(self)
Expand source code
def test_tuple_without_args_corrected(self):
    @pedantic
    def calc(i: Tuple[Any, ...]) -> str:
        return str(i)

    calc(i=(42.0, 43, 'hi'))
def test_tuple_without_type_args(self)
Expand source code
def test_tuple_without_type_args(self):
    @pedantic
    def calc(i: Tuple) -> str:
        return str(i)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(i=(42.0, 43, 'hi'))
def test_type_list_but_got_tuple(self)
Expand source code
def test_type_list_but_got_tuple(self):
    @pedantic
    def calc(ls: List[Any]) -> int:
        return len(ls)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(ls=(1, 2, 3))
def test_type_list_corrected(self)
Expand source code
def test_type_list_corrected(self):
    @pedantic
    def calc(ls: Tuple[Any, ...]) -> int:
        return len(ls)

    calc(ls=(1, 2, 3))
def test_type_var(self)
Expand source code
def test_type_var(self):
    T = TypeVar('T')

    @pedantic
    def first(ls: List[T]) -> T:
        return ls[0]

    first(ls=[1, 2, 3])
def test_type_var_bound_fail(self)
Expand source code
def test_type_var_bound_fail(self):
    T = TypeVar('T', bound=Child)

    @pedantic
    def foo(a: T, b: T) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=Parent(), b=Parent())
def test_type_var_constraints(self)
Expand source code
def test_type_var_constraints(self):
    T = TypeVar('T', int, str)

    @pedantic
    def foo(a: T, b: T) -> None:
        pass

    for values in [
        {'a': 6, 'b': 7},
        {'a': 'aa', 'b': "bb"},
    ]:
        foo(**values)
def test_type_var_constraints_fail_typing_type(self)
Expand source code
def test_type_var_constraints_fail_typing_type(self):
    T = TypeVar('T', int, Collection)

    @pedantic
    def foo(a: T, b: T) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a='aa', b='bb')
def test_type_var_contravariant(self)
Expand source code
def test_type_var_contravariant(self):
    T = TypeVar('T', contravariant=True)

    @pedantic
    def foo(a: T, b: T) -> None:
        pass

    foo(a=Child(), b=Parent())
def test_type_var_contravariant_fail(self)
Expand source code
def test_type_var_contravariant_fail(self):
    T = TypeVar('T', contravariant=True)

    @pedantic
    def foo(a: T, b: T) -> None:
        pass

    with self.assertRaises(PedanticTypeVarMismatchException):
        foo(a=Parent(), b=Child())
def test_type_var_covariant(self)
Expand source code
def test_type_var_covariant(self):
    T = TypeVar('T', covariant=True)

    @pedantic
    def foo(a: T, b: T) -> None:
        pass

    foo(a=Parent(), b=Child())
def test_type_var_covariant_fail(self)
Expand source code
def test_type_var_covariant_fail(self):
    T = TypeVar('T', covariant=True)

    @pedantic
    def foo(a: T, b: T) -> None:
        pass

    with self.assertRaises(PedanticTypeVarMismatchException):
        foo(a=Child(), b=Parent())
def test_type_var_forward_ref_bound(self)
Expand source code
def test_type_var_forward_ref_bound(self):
    TBound = TypeVar('TBound', bound='Parent')

    @pedantic
    def func(x: TBound) -> None:
        pass

    func(x=Parent())

    with self.assertRaises(PedanticTypeCheckException):
        func(x='foo')
def test_type_var_invariant_fail(self)
Expand source code
def test_type_var_invariant_fail(self):
    T = TypeVar('T', int, str)

    @pedantic
    def foo(a: T, b: T) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=2, b=3.6)
def test_type_var_wrong(self)
Expand source code
def test_type_var_wrong(self):
    T = TypeVar('T')

    @pedantic
    def first(ls: List[T]) -> T:
        return str(ls[0])

    with self.assertRaises(expected_exception=PedanticTypeVarMismatchException):
        first(ls=[1, 2, 3])
def test_type_var_wrong_sequence(self)
Expand source code
def test_type_var_wrong_sequence(self):
    T = TypeVar('T')

    @pedantic
    def first(ls: Sequence[T]) -> T:
        return str(ls[0])

    with self.assertRaises(expected_exception=PedanticTypeVarMismatchException):
        first(ls=[1, 2, 3])
def test_typevar_bound(self)
Expand source code
def test_typevar_bound(self):
    T = TypeVar('T', bound=Parent)

    @pedantic
    def foo(a: T, b: T) -> None:
        pass

    foo(a=Child(), b=Child())
def test_typevar_constraints_fail(self)
Expand source code
def test_typevar_constraints_fail(self):
    T = TypeVar('T', int, str)

    @pedantic
    def foo(a: T, b: T) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=2.5, b='aa')
def test_union(self)
Expand source code
def test_union(self):
    @pedantic
    def foo(a: Union[str, int]) -> None:
        pass

    for value in [6, 'xa']:
        foo(a=value)
def test_union_fail(self)
Expand source code
def test_union_fail(self):
    @pedantic
    def foo(a: Union[str, int]) -> None:
        pass

    for value in [5.6, b'xa']:
        with self.assertRaises(PedanticTypeCheckException):
            foo(a=value)
def test_union_new_syntax(self)
Expand source code
def test_union_new_syntax(self):
    @pedantic
    def foo(a: str | int) -> None:
        pass

    for value in [6, 'xa']:
        foo(a=value)

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=1.7)
def test_union_typing_type(self)
Expand source code
def test_union_typing_type(self):
    @pedantic
    def foo(a: Union[str, Collection]) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=1)
def test_varargs(self)
Expand source code
def test_varargs(self):
    @pedantic
    def foo(*args: int) -> None:
        pass

    foo(1, 2)
def test_varargs_fail(self)
Expand source code
def test_varargs_fail(self):
    @pedantic
    def foo(*args: int) -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(1, 'a')
def test_wrapped_function(self)
Expand source code
def test_wrapped_function(self):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper

    @pedantic
    @decorator
    def foo(a: 'Child') -> None:
        pass

    with self.assertRaises(PedanticTypeCheckException):
        foo(a=Parent())
def test_wrapped_generator_no_return_type_annotation(self)

Test that return type checking works in a generator function too.

Expand source code
def test_wrapped_generator_no_return_type_annotation(self):
    """Test that return type checking works in a generator function too."""
    @pedantic
    def generate(a: int) -> Generator[int, int, None]:
        yield a
        yield a + 1

    gen = generate(a=1)
    next(gen)
def test_wrong_type_hint_1(self)

Problem here: str != int

Expand source code
def test_wrong_type_hint_1(self):
    """Problem here: str != int"""
    @pedantic
    def calc(n: int, m: int, i: int) -> str:
        return n + m + i

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42, m=40, i=38)
def test_wrong_type_hint_1_corrected(self)
Expand source code
def test_wrong_type_hint_1_corrected(self):
    @pedantic
    def calc(n: int, m: int, i: int) -> str:
        return str(n + m + i)

    calc(n=42, m=40, i=38)
def test_wrong_type_hint_2(self)

Problem here: str != int

Expand source code
def test_wrong_type_hint_2(self):
    """Problem here: str != int"""
    @pedantic
    def calc(n: int, m: int, i: str) -> int:
        return n + m + i

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42, m=40, i=38)
def test_wrong_type_hint_2_corrected(self)
Expand source code
def test_wrong_type_hint_2_corrected(self):
    @pedantic
    def calc(n: int, m: int, i: str) -> int:
        return n + m + int(i)

    calc(n=42, m=40, i='38')
def test_wrong_type_hint_3(self)

Problem here: None != int

Expand source code
def test_wrong_type_hint_3(self):
    """Problem here: None != int"""
    @pedantic
    def calc(n: int, m: int, i: int) -> None:
        return n + m + i

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42, m=40, i=38)
def test_wrong_type_hint_4(self)

Problem here: None != int

Expand source code
def test_wrong_type_hint_4(self):
    """Problem here: None != int"""
    @pedantic
    def calc(n: int, m: int, i: int) -> int:
        print(n + m + i)

    with self.assertRaises(expected_exception=PedanticTypeCheckException):
        calc(n=42, m=40, i=38)
def test_wrong_type_hint_4_corrected(self)
Expand source code
def test_wrong_type_hint_4_corrected(self):
    @pedantic
    def calc(n: int, m: int, i: int) -> int:
        return n + m + i

    calc(n=42, m=40, i=38)
def test_wrong_type_hint_corrected(self)
Expand source code
def test_wrong_type_hint_corrected(self):
    @pedantic
    def calc(n: int, m: int, i: int) -> None:
        print(n + m + i)

    calc(n=42, m=40, i=38)