Module pedantic.models.function_call
Classes
class FunctionCall (func: DecoratedFunction,
args: Tuple[Any, ...],
kwargs: Dict[str, Any],
context: Dict[str, Type])-
Expand source code
class FunctionCall: def __init__(self, func: DecoratedFunction, args: Tuple[Any, ...], kwargs: Dict[str, Any], context: Dict[str, Type]) -> None: self._func = func self._args = args self._kwargs = kwargs self._context = context self._instance = self.args[0] if self.func.is_instance_method else None self._type_vars = dict() self._params_without_self = {k: v for k, v in self.func.signature.parameters.items() if v.name != 'self'} self._already_checked_kwargs = [] self._get_type_vars = lambda: self._type_vars @property def func(self) -> DecoratedFunction: return self._func @property def args(self) -> Tuple[Any, ...]: return self._args @property def kwargs(self) -> Dict[str, Any]: return self._kwargs @property def not_yet_check_kwargs(self) -> Dict[str, Any]: return {k: v for k, v in self._kwargs.items() if k not in self._already_checked_kwargs} @property def type_vars(self) -> Dict[TypeVar, Any]: if hasattr(self._instance, TYPE_VAR_METHOD_NAME): self._get_type_vars = getattr(self._instance, TYPE_VAR_METHOD_NAME) res = self._get_type_vars() if TYPE_VAR_SELF not in res: res[TYPE_VAR_SELF] = self.clazz return res @property def clazz(self) -> Optional[Type]: """ Return the enclosing class of the called function if there is one. """ if self._instance is not None: # case instance method return type(self._instance) elif self.func.is_class_method: return self.func.func.__self__ elif self.func.is_static_method: if not self.args: # static method was called on class class_name = self.func.full_name.split('.')[-2] return ForwardRef(class_name) # this ForwardRef is resolved later # static method was called on instance return type(self.args[0]) @property def params_without_self(self) -> Dict[str, inspect.Parameter]: return self._params_without_self @property def args_without_self(self) -> Tuple[Any, ...]: max_allowed = 0 if not self.func.is_pedantic else 1 uses_multiple_decorators = self.func.num_of_decorators > max_allowed if self.func.is_instance_method or self.func.is_static_method or uses_multiple_decorators: return self.args[1:] # self is always the first argument if present return self.args def assert_uses_kwargs(self) -> None: if self.func.should_have_kwargs and self.args_without_self: raise PedanticCallWithArgsException( f'{self.func.err}Use kwargs when you call function {self.func.name}. Args: {self.args_without_self}') def check_types(self) -> ReturnType: self._check_types_of_arguments() return self._check_types_return(result=self._get_return_value()) async def async_check_types(self) -> ReturnType: self._check_types_of_arguments() return self._check_types_return(result=await self._async_get_return_value()) def _check_types_of_arguments(self) -> None: d = self.params_without_self.items() self._check_type_param(params={k: v for k, v in d if not str(v).startswith('*')}) self._check_types_args(params={k: v for k, v in d if str(v).startswith('*') and not str(v).startswith('**')}) self._check_types_kwargs(params={k: v for k, v in d if str(v).startswith('**')}) def _check_type_param(self, params: Dict[str, inspect.Parameter]) -> None: arg_index = 1 if self.func.is_instance_method else 0 for key, param in params.items(): self._already_checked_kwargs.append(key) self._assert_param_has_type_annotation(param=param) if param.default is inspect.Signature.empty: if self.func.should_have_kwargs: if key not in self.kwargs: raise PedanticTypeCheckException(f'{self.func.err}Parameter "{key}" is unfilled.') actual_value = self.kwargs[key] else: actual_value = self.args[arg_index] arg_index += 1 else: if key in self.kwargs: actual_value = self.kwargs[key] else: actual_value = param.default assert_value_matches_type( value=actual_value, type_=param.annotation, err=self.func.err, type_vars=self.type_vars, key=key, context=self._context, ) def _check_types_args(self, params: Dict[str, inspect.Parameter]) -> None: if not params: return expected = list(params.values())[0].annotation # it's not possible to have more than 1 for arg in self.args: assert_value_matches_type( value=arg, type_=expected, err=self.func.err, type_vars=self.type_vars, context=self._context, ) def _check_types_kwargs(self, params: Dict[str, inspect.Parameter]) -> None: if not params: return param = list(params.values())[0] # it's not possible to have more than 1 self._assert_param_has_type_annotation(param=param) for kwarg in self.not_yet_check_kwargs: actual_value = self.kwargs[kwarg] assert_value_matches_type( value=actual_value, type_=param.annotation, err=self.func.err, type_vars=self.type_vars, key=kwarg, context=self._context, ) def _check_types_return(self, result: Any) -> Union[None, GeneratorWrapper]: if self.func.signature.return_annotation is inspect.Signature.empty: raise PedanticTypeCheckException( f'{self.func.err}There should be a type hint for the return type (e.g. None if nothing is returned).') expected_result_type = self.func.annotations['return'] if self.func.is_generator: return GeneratorWrapper( wrapped=result, expected_type=expected_result_type, err_msg=self.func.err, type_vars=self.type_vars) msg = f'{self.func.err}Type hint of return value is incorrect: Expected type {expected_result_type} ' \ f'but {result} of type {type(result)} was the return value which does not match.' assert_value_matches_type( value=result, type_=expected_result_type, err=self.func.err, type_vars=self.type_vars, msg=msg, context=self._context, ) return result def _assert_param_has_type_annotation(self, param: inspect.Parameter): if param.annotation == inspect.Parameter.empty: raise PedanticTypeCheckException(f'{self.func.err}Parameter "{param.name}" should have a type hint.') def _get_return_value(self) -> Any: if self.func.is_static_method or self.func.is_class_method: return self.func.func(**self.kwargs) else: return self.func.func(*self.args, **self.kwargs) async def _async_get_return_value(self) -> Any: if self.func.is_static_method or self.func.is_class_method: return await self.func.func(**self.kwargs) else: return await self.func.func(*self.args, **self.kwargs)Instance variables
prop args : Tuple[Any, ...]-
Expand source code
@property def args(self) -> Tuple[Any, ...]: return self._args prop args_without_self : Tuple[Any, ...]-
Expand source code
@property def args_without_self(self) -> Tuple[Any, ...]: max_allowed = 0 if not self.func.is_pedantic else 1 uses_multiple_decorators = self.func.num_of_decorators > max_allowed if self.func.is_instance_method or self.func.is_static_method or uses_multiple_decorators: return self.args[1:] # self is always the first argument if present return self.args prop clazz : Type | None-
Expand source code
@property def clazz(self) -> Optional[Type]: """ Return the enclosing class of the called function if there is one. """ if self._instance is not None: # case instance method return type(self._instance) elif self.func.is_class_method: return self.func.func.__self__ elif self.func.is_static_method: if not self.args: # static method was called on class class_name = self.func.full_name.split('.')[-2] return ForwardRef(class_name) # this ForwardRef is resolved later # static method was called on instance return type(self.args[0])Return the enclosing class of the called function if there is one.
prop func : DecoratedFunction-
Expand source code
@property def func(self) -> DecoratedFunction: return self._func prop kwargs : Dict[str, Any]-
Expand source code
@property def kwargs(self) -> Dict[str, Any]: return self._kwargs prop not_yet_check_kwargs : Dict[str, Any]-
Expand source code
@property def not_yet_check_kwargs(self) -> Dict[str, Any]: return {k: v for k, v in self._kwargs.items() if k not in self._already_checked_kwargs} prop params_without_self : Dict[str, inspect.Parameter]-
Expand source code
@property def params_without_self(self) -> Dict[str, inspect.Parameter]: return self._params_without_self prop type_vars : Dict[TypeVar, Any]-
Expand source code
@property def type_vars(self) -> Dict[TypeVar, Any]: if hasattr(self._instance, TYPE_VAR_METHOD_NAME): self._get_type_vars = getattr(self._instance, TYPE_VAR_METHOD_NAME) res = self._get_type_vars() if TYPE_VAR_SELF not in res: res[TYPE_VAR_SELF] = self.clazz return res
Methods
def assert_uses_kwargs(self) ‑> None-
Expand source code
def assert_uses_kwargs(self) -> None: if self.func.should_have_kwargs and self.args_without_self: raise PedanticCallWithArgsException( f'{self.func.err}Use kwargs when you call function {self.func.name}. Args: {self.args_without_self}') async def async_check_types(self) ‑> ~ReturnType-
Expand source code
async def async_check_types(self) -> ReturnType: self._check_types_of_arguments() return self._check_types_return(result=await self._async_get_return_value()) def check_types(self) ‑> ~ReturnType-
Expand source code
def check_types(self) -> ReturnType: self._check_types_of_arguments() return self._check_types_return(result=self._get_return_value())