Coverage for src\pqlattice\typing\_types_validator.py: 100%
36 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-01-11 23:45 +0100
« prev ^ index » next coverage.py v7.11.0, created at 2026-01-11 23:45 +0100
1import logging
2from collections.abc import Callable
3from inspect import signature
4from typing import Any, TypeAliasType, TypeGuard
6import numpy as np
7from numpy.typing import NDArray
9from ._types import Matrix, SquareMatrix, Vector
11logger = logging.getLogger(__name__)
14def _is_nparray(obj: Any) -> TypeGuard[NDArray[Any]]:
15 return isinstance(obj, np.ndarray)
18def is_Vector(obj: Any) -> TypeGuard[Vector]:
19 return _is_nparray(obj) and len(obj.shape) == 1
22def is_Matrix(obj: Any) -> TypeGuard[Matrix]:
23 return _is_nparray(obj) and len(obj.shape) == 2
26def is_SquareMatrix(obj: Any) -> TypeGuard[SquareMatrix]:
27 return is_Matrix(obj) and obj.shape[0] == obj.shape[1]
30def _get_predicate_for_alias[T: TypeAliasType](type_name: T) -> Callable[[T], bool] | None:
31 # Bare
32 if type_name == Vector:
33 return is_Vector
35 if type_name == Matrix:
36 return is_Matrix
38 if type_name == SquareMatrix:
39 return is_SquareMatrix
41 return None
44def validate_aliases[**P, T](func: Callable[P, T]) -> Callable[P, T]:
45 def wrapper(*args: P.args, **kwds: P.kwargs) -> T:
46 sig = signature(func)
47 bounded_args = sig.bind(*args, **kwds)
48 bounded_args.apply_defaults()
49 for arg_name, arg_value in bounded_args.arguments.items():
50 if expected_type := func.__annotations__.get(arg_name): # There is a type annotation for the argument
51 pred = _get_predicate_for_alias(expected_type)
52 if pred is not None and not pred(arg_value): # type annotations has a predicate to be checked and predicate is not fullfilled
53 raise TypeError(f"func <{func.__name__}>, arg <{arg_name}> val <{arg_value}> arg's type <{type(arg_value)}> predicate for <{expected_type}> failed")
55 return func(*args, **kwds)
57 return wrapper