Source code for slickml.utils._validation

from typing import Any, Optional, Tuple, Union


# TODO(amir): figure out a way to extend validations (i.e. when we are validating list[values])
# currently, the values cannot be done for iterable i.e. tuple, list, and dict
# the easy way to pull it off is a for loop
# TODO(amir): we might wanna add `NotImplementedError` exception as well somehow!
[docs] def check_var( var: Any, *, var_name: str, dtypes: Union[Any, Tuple[Any]], values: Optional[Union[Any, Tuple[Any]]] = None, ) -> None: """Validates the variable's dtype and possible value. Parameters ---------- var : Any Variable var_name : str Variable name dtypes : Union[type, Tuple[type]] Data type classes values : Union[Any, Tuple[Any]], optional Possible values, by default None Returns ------- None Raises ------ TypeError If dtypes are invalid ValueError If values are invalid Notes ----- This is the main function that is being used across the API as the variable checker before any class/function being instantiated. This is our solution instead of using ``pydantic`` validator and root_validator due to a lot of issues (i.e. data type casting/truncation in a silence mode) that we have seen in our investigation. Hopefully, when ``pydantic`` version 2.0 is released, we can use it. Examples -------- >>> from dataclasses import dataclass >>> from slickml.utils import check_var >>> @dataclass ... class Foo: ... var_str: str ... var_float: float = 42.0 ... var_int: int = 1367 ... def __post_init__(self): ... check_var(self.var_str, var_name="var_str", dtypes=str) ... check_var(self.var_float, var_name="var_float", dtypes=float, values=(41, 42)) ... check_var(self.var_int, var_name="var_int", dtypes=str, values=(1367, 1400)) """ def _check_dtypes( var: Any, var_name: str, dtypes: Union[type, Tuple[type]], ) -> None: """Validates the variable's dtype. Parameters ---------- var : Any Variable var_name : str Variable name dtypes : Union[type, Tuple[type]] Data type classes Returns ------- None Raises ------ TypeError If dtypes are invalid """ if not isinstance(dtypes, tuple): dtypes = (dtypes,) _sum = sum([isinstance(d, type) for d in dtypes]) if _sum != len(dtypes): raise TypeError("The input dtypes must have one of [type, Tuple(type)] dtypes.") dtype_names = [s.__name__ for s in dtypes] if len(dtype_names) == 1: type_error_msg = f"The input {var_name} must have {dtype_names[0]} dtype." else: type_error_msg = f"The input {var_name} must have one of {dtype_names} dtypes." if not isinstance(var, dtypes): raise TypeError(type_error_msg) def _check_values( var: Any, var_name: str, values: Union[Any, Tuple[Any]], ) -> None: """Validates variable's possible value. Parameters ---------- var : Any Variable var_name : str Variable name values : Union[Any, Tuple[Any]] Possible values Returns ------- None Raises ------ ValueError If values are invalid """ if not isinstance(values, tuple): values = (values,) if len(values) == 1: value_error_msg = f"The input {var_name} must have a value of {values[0]}." else: value_error_msg = f"The input {var_name} must have one of {values} values." if var not in values: raise ValueError(value_error_msg) _check_dtypes( var=var, var_name=var_name, dtypes=dtypes, ) if values: _check_values( var=var, var_name=var_name, values=values, ) return None