1def type_check(io):
 2    def decorator(*valid_types):
 3        def wrapper(func):
 4            def type_decorator(*args, **kwargs):
 5                types = {type(arg) for arg in args}
 6                types.update({type(value) for key, value in kwargs.items()})
 7
 8                for current_type in types:
 9                    if current_type not in valid_types:
10                        if io == "in":
11                            print(f"Invalid input arguments, expected {", ".join(map(str, valid_types))}!")
12                        else:
13                            print(f"Invalid output value, expected {", ".join(map(str, valid_types))}!")
14                        break
15                
16                return func(*args, **kwargs)
17            return type_decorator
18        return wrapper
19    return decorator
FF.F
======================================================================
FAIL: test_check_both (test.TestTypeCheck.test_check_both)
The decorator should report invalid "in" and "out" together.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3.12/unittest/mock.py", line 1390, in patched
    return func(*newargs, **newkeywargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test.py", line 98, in test_check_both
    mock_print.assert_has_calls(
  File "/usr/lib/python3.12/unittest/mock.py", line 981, in assert_has_calls
    raise AssertionError(
AssertionError: Calls not found.
Expected: [call("Invalid input arguments, expected <class 'float'>!"),
 call("Invalid output value, expected <class 'int'>!")]
  Actual: [call("Invalid output value, expected <class 'int'>!"),
 call("Invalid input arguments, expected <class 'float'>!")]
======================================================================
FAIL: test_check_decorated_exception (test.TestTypeCheck.test_check_decorated_exception)
The decorator should not supress any exceptions raised.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3.12/unittest/mock.py", line 1390, in patched
    return func(*newargs, **newkeywargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test.py", line 112, in test_check_decorated_exception
    self.assert_in_permutations(mock_print.mock_calls[0], types, self.BASE_STRING_IN)
  File "/tmp/test.py", line 35, in assert_in_permutations
    self.assertIn(call, call_permutations)
AssertionError: call("Invalid output value, expected <class 'list'>, <class 'tuple'>!") not found in [call("Invalid input arguments, expected <class 'list'>, <class 'tuple'>!"), call("Invalid input arguments, expected <class 'tuple'>, <class 'list'>!")]
======================================================================
FAIL: test_check_out (test.TestTypeCheck.test_check_out)
The decorator should report an invalid "out" value.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3.12/unittest/mock.py", line 1390, in patched
    return func(*newargs, **newkeywargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/test.py", line 72, in test_check_out
    mock_print.assert_has_calls([call(self.BASE_STRING_OUT.format("my type"))])
  File "/usr/lib/python3.12/unittest/mock.py", line 981, in assert_has_calls
    raise AssertionError(
AssertionError: Calls not found.
Expected: [call('Invalid output value, expected my type!')]
----------------------------------------------------------------------
Ran 4 tests in 0.004s
FAILED (failures=3)