src.pydasa.validations.decorators#

Decorator-based validation system for PyDASA property setters, reducing boilerplate code with reusable validation logic.

Functions:
  • validate_type: Validates value against expected type(s).

  • validate_emptiness: Ensures string values are non-empty.

  • validate_choices: Validates value is in allowed set of choices.

  • validate_range: Validates numeric value is within specified range.

  • validate_index: Validates integer values with negativity control.

  • validate_pattern: Validates string matches regex pattern(s) or is alphanumeric.

  • validate_custom: Custom validation logic.

Functions#

validate_type(*expected_types[, allow_none, allow_nan])

Decorator to validate argument type against expected type(s).

validate_emptiness([strip])

Decorator to ensure values are non-empty.

validate_choices(choices[, allow_none, case_sensitive])

Decorator to validate value is in allowed set of choices.

validate_index([allow_zero, allow_negative])

Decorator to validate integer values with negativity and zero control.

validate_range([min_value, max_value, min_inclusive, ...])

Decorator to validate numeric value is within specified range.

validate_pattern([pattern, allow_alnum, error_msg, ...])

Decorator to validate string matches regex pattern(s) or is alphanumeric.

validate_list_types(*elm_types)

Decorator to validate list contains only specified element types. It asumes the list exists.

validate_dict_types(key_type, val_types)

Decorator to validate dict has correct key and value types. It asumes the dict exists.

validate_custom(validator_func)

Decorator for custom validation logic. Allows implementing custom validation logic by providing a validator function.

Module Contents#

src.pydasa.validations.decorators.validate_type(*expected_types, allow_none=True, allow_nan=False)#

Decorator to validate argument type against expected type(s).

Parameters:
  • *expected_types (type) – One or more expected types for validation.

  • allow_none (bool, optional) – Whether None values are allowed. Defaults to True.

  • allow_nan (bool, optional) – Whether np.nan values are allowed. Defaults to False.

Raises:
  • ValueError – If value is None when allow_none is False.

  • ValueError – If value is np.nan when allow_nan is False.

  • ValueError – If value type does not match any of the expected types.

Returns:

Decorated function with type validation.

Return type:

Callable

Example:

@property
def unit(self) -> str:
    return self._unit

@unit.setter
@validate_type(str)
def unit(self, val: str) -> None:
    self._unit = val

# Multiple types
@value.setter
@validate_type(int, float)
def value(self, val: Union[int, float]) -> None:
    self._value = val

# Allow np.nan
@mean.setter
@validate_type(int, float, allow_nan=True)
def mean(self, val: Optional[float]) -> None:
    self._mean = val
src.pydasa.validations.decorators.validate_emptiness(strip=True)#

Decorator to ensure values are non-empty.

Handles strings, dictionaries, lists, tuples, and other collections. For strings, optionally strips whitespace before checking.

Parameters:

strip (bool, optional) – Whether to strip whitespace before checking strings. Defaults to True.

Raises:

ValueError – If string is empty/whitespace-only, or if collection has no elements.

Returns:

Decorated function with non-empty validation.

Return type:

Callable

Example:

@unit.setter
@validate_type(str)
@validate_emptiness()
def unit(self, val: str) -> None:
    self._unit = val

@variables.setter
@validate_type(dict)
@validate_emptiness()
def variables(self, val: dict) -> None:
    self._variables = val
src.pydasa.validations.decorators.validate_choices(choices, allow_none=False, case_sensitive=False)#

Decorator to validate value is in allowed set of choices.

Parameters:
  • choices (Union[dict, set, list, tuple, Type[Enum]]) – Dictionary, set, list, tuple, or Enum type of allowed values.

  • allow_none (bool, optional) – Whether None values are allowed. Defaults to False.

  • case_sensitive (bool, optional) – Whether string comparison is case-sensitive. Defaults to False.

Raises:

ValueError – If value is not in the allowed choices.

Returns:

Decorated function with choice validation.

Return type:

Callable

Example:

from pydasa.core.setup import Frameworks

@fwk.setter
@validate_type(str)
@validate_choices(Frameworks.values())
def fwk(self, val: str) -> None:
    self._fwk = val.upper()

# Case-sensitive choices
@status.setter
@validate_choices(["Active", "Inactive"], case_sensitive=True)
def status(self, val: str) -> None:
    self._status = val
src.pydasa.validations.decorators.validate_index(allow_zero=True, allow_negative=False)#

Decorator to validate integer values with negativity and zero control.

Parameters:
  • allow_zero (bool, optional) – Whether zero is allowed. Defaults to True.

  • allow_negative (bool, optional) – Whether negative integers are allowed. Defaults to False.

Raises:
  • ValueError – If value is not an integer.

  • ValueError – If negative value when allow_negative is False.

  • ValueError – If zero value when allow_zero is False.

Returns:

Decorated function with integer validation.

Return type:

Callable

Example:

# Non-negative integers only
@idx.setter
@validate_index(allow_negative=False)
def idx(self, val: int) -> None:
    self._idx = val

# Positive integers only (no zero)
@count.setter
@validate_index(allow_negative=False, allow_zero=False)
def count(self, val: int) -> None:
    self._count = val
src.pydasa.validations.decorators.validate_range(min_value=None, max_value=None, min_inclusive=True, max_inclusive=True, min_attr=None, max_attr=None)#

Decorator to validate numeric value is within specified range.

Parameters:
  • min_value (Optional[float], optional) – Static minimum value. Defaults to None.

  • max_value (Optional[float], optional) – Static maximum value. Defaults to None.

  • min_inclusive (bool, optional) – Whether minimum is inclusive (>=) or exclusive (>). Defaults to True.

  • max_inclusive (bool, optional) – Whether maximum is inclusive (<=) or exclusive (<). Defaults to True.

  • min_attr (Optional[str], optional) – Attribute name for dynamic minimum (e.g., ‘_min’). Defaults to None.

  • max_attr (Optional[str], optional) – Attribute name for dynamic maximum (e.g., ‘_max’). Defaults to None.

Raises:

ValueError – If value is outside the specified range.

Returns:

Decorated function with range validation.

Return type:

Callable

Example:

# Static range
@age.setter
@validate_type(int)
@validate_range(min_value=0, max_value=150)
def age(self, val: int) -> None:
    self._age = val

# Dynamic range based on other attributes
@mean.setter
@validate_type(int, float)
@validate_range(min_attr='_min', max_attr='_max')
def mean(self, val: float) -> None:
    self._mean = val
src.pydasa.validations.decorators.validate_pattern(pattern=None, allow_alnum=False, error_msg=None, examples=None)#

Decorator to validate string matches regex pattern(s) or is alphanumeric.

This unified decorator handles: - Single pattern matching - Multiple pattern matching (OR logic - matches any pattern) - Optional alphanumeric validation - Scientific/mathematical symbols (alphanumeric OR LaTeX)

Parameters:
  • pattern (Union[str, list, tuple]) – Single regex pattern string, or list/tuple of patterns to match (OR logic).

  • allow_alnum (bool, optional) – Whether to accept alphanumeric strings. Defaults to False.

  • error_msg (Optional[str], optional) – Custom error message (overrides default). Defaults to None.

  • examples (Optional[str], optional) – Example strings to show in error messages. Defaults to None.

Raises:

ValueError – If value does not match any pattern and is not alphanumeric (when allowed).

Returns:

Decorated function with pattern validation.

Return type:

Callable

Examples:

# Simple pattern matching
@code.setter
@validate_pattern(r'^[A-Z]\d{3}$')
def code(self, val: str) -> None:
    self._code = val

# Symbol validation (alphanumeric OR LaTeX)
from pydasa.validations.patterns import LATEX_RE

@sym.setter
@validate_type(str)
@validate_emptiness()
@validate_pattern(LATEX_RE, allow_alnum=True)
def sym(self, val: str) -> None:
    self._sym = val

# Multiple patterns (match any)
@validate_pattern([r'^\\[a-z]+$', r'^\d+$'])
def value(self, val: str) -> None:
    self._value = val
src.pydasa.validations.decorators.validate_list_types(*elm_types)#

Decorator to validate list contains only specified element types. It asumes the list exists.

Parameters:

*elm_types (type) – One or more expected types for list elements.

Raises:
Returns:

Decorated function with list type validation.

Return type:

Callable

Example:

@dim_col.setter
@validate_type(list, allow_none=False)
@validate_emptiness()
@validate_list_types(int, float)
def dim_col(self, val: List[int]) -> None:
    self._dim_col = [int(x) for x in val]
src.pydasa.validations.decorators.validate_dict_types(key_type, val_types)#

Decorator to validate dict has correct key and value types. It asumes the dict exists.

Parameters:
  • key_type (type) – Expected type for dictionary keys.

  • val_types (type | Tuple[type, ...]) – Expected types for dictionary values. At least one type must be provided.

Raises:

ValueError – If dict keys or values have wrong types.

Returns:

Decorated function with dict type validation.

Return type:

Callable

Example:

@variables.setter
@validate_type(dict, allow_none=False)
@validate_emptiness()
@validate_dict_types(str, (Variable, dict))
def variables(self, val: Dict[str, Variable]) -> None:
    self._variables = val
src.pydasa.validations.decorators.validate_custom(validator_func)#

Decorator for custom validation logic. Allows implementing custom validation logic by providing a validator function.

The validator function should raise ValueError if validation fails. NOTE: this is too abstract and should be used sparingly.

Parameters:

validator_func (Callable[[Any, Any], None]) – Function(self, value) that raises ValueError if invalid.

Raises:

ValueError – If custom validator function raises ValueError.

Returns:

Decorated function with custom validation.

Return type:

Callable

Example:

def check_range_consistency(self, value):
    '''Ensure minimum does not exceed maximum.'''
    if value is not None and self._max is not None and value > self._max:
        raise ValueError(f"min {value} > max {self._max}")

@min.setter
@validate_type(int, float)
@validate_custom(check_range_consistency)
def min(self, val: float) -> None:
    self._min = val