Validators

Collection of functions to specifies wich validations you want


require_params()

Signature

def require_params(*expected_params: str |  tuple[str, *tuple[type, ...]]) -> Callable[[Callable[..., Any], Any], Callable[..., Any]]

Description

Ensure that the decorated function has the required parameters. Take parameter names as strings or tuples composed of the parameter names and the expected types. Type requirement is done by parsing the type hint, so it's not type-safe.

Parameters

  • *expected_params: Parameter names as strings or tuples composed of the parameter names and the expected types

Raises

  • TypeError if arguments are not str or not (str, type, ..., type)
  • DecoratorUsageValidationError if the decorated decorator is not used correctly.

Example use

@validate_decorated(require_params("x", "y"))
def my_decorator(func): return func

@my_decorator
def correct_usage(x, y): pass

@my_decorator
def bad_usage(x): pass # Will raise an DecoratorUsageValidationError

###

@validate_decorated(require_params(("x", int, float), ("y", str)))
def deco_factory(param):
    def inner(f):
        return f
    return inner

@deco_factory("test")
def correct_usage(x: int | float, y: str, z: float): pass # Has all of the required params

@deco_factory("test")
def bad_usage(x: int, y: str, z: float): pass # Will raise an DecoratorUsageValidationError

require_at_least_n_params()

Signature

def require_at_least_n_params(n: int, *expected_params: str | tuple[str, *tuple[type, ...]]) -> Callable[[Callable[..., Any], Any], Callable[..., Any]]

Description

Ensure that the decorated function has at least n required parameters. Take parameter names as strings or tuples composed of the parameter names and the expected types. Type requirement is done by parsing the type hint, so it's not type-safe.

Parameters

  • n: Minimum number of required parameters that must be present
  • *expected_params: Parameter names as strings or tuples composed of the parameter names and the expected types

Raises

  • TypeError if n is not a positive integer or if arguments are not str or not (str, type, ..., type)
  • DecoratorUsageValidationError if the decorated decorator is not used correctly.

Example use

@validate_decorated(require_at_least_n_params(2, "x", "y", "z"))
def my_decorator(func): return func

@my_decorator
def correct_usage(x, y): pass  # Has 2 of the required params

@my_decorator
def also_correct(x, y, z): pass  # Has all 3 params

@my_decorator
def bad_usage(x): pass # Will raise an DecoratorUsageValidationError (only 1 param)

###

@validate_decorated(require_at_least_n_params(1, ("x", int, float), ("y", str), ("z", bool)))
def deco_factory(param):
    def inner(f):
        return f
    return inner

@deco_factory("test")
def correct_usage(x: int | float, w: str): pass  # Has x (int) which satisfies the requirement

@deco_factory("test")
def bad_usage(w: str): pass # Will raise an DecoratorUsageValidationError (no matching params)

no_params()

Signature

def no_params() -> Callable[[Callable[..., Any], Any], Callable[..., Any]]

Description

Ensure that the decorated function takes no parameters (except self for methods). This validator checks that the function signature has no required or optional parameters. The self parameter is automatically ignored to allow usage with methods.

Raises

  • DecoratorUsageValidationError if the decorated function has any parameters (other than self).

Example use

@validate_decorated(no_params())
def my_decorator(func): return func

@my_decorator
def correct_usage(): pass  # No parameters - OK

@my_decorator
def method(self): pass  # Only 'self' parameter - OK for methods

@my_decorator
def bad_usage(x): pass # Will raise a DecoratorUsageValidationError

###

@validate_decorated(no_params())
def deco_factory(param):
    def inner(f):
        return f
    return inner

@deco_factory("test")
def correct_usage(): pass  # No parameters - OK

@deco_factory("test")
def bad_usage(x, y): pass # Will raise a DecoratorUsageValidationError

no_optional_params()

Signature

def no_optional_params() -> Callable[[Callable[..., Any], Any], Callable[..., Any]]

Description

Ensure that the decorated function has no optional parameters (except self for methods). This validator checks that all function parameters are required (no default values). The self parameter is automatically ignored to allow usage with methods.

Raises

  • DecoratorUsageValidationError if the decorated function has any optional parameters (parameters with default values).

Example use

@validate_decorated(no_optional_params())
def my_decorator(func): return func

@my_decorator
def correct_usage(x, y): pass  # All parameters are required - OK

@my_decorator
def method(self, x, y): pass  # Only 'self' parameter - OK for methods

@my_decorator
def also_correct(): pass  # No parameters at all - OK

@my_decorator
def bad_usage(x, y=10): pass # Will raise a DecoratorUsageValidationError

###

@validate_decorated(no_optional_params())
def deco_factory(param):
    def inner(f):
        return f
    return inner

@deco_factory("test")
def correct_usage(x, y, z): pass  # All parameters are required - OK

@deco_factory("test")
def bad_usage(x, y=10, z="hello"): pass # Will raise a DecoratorUsageValidationError

has_return()

Signature

def has_return() -> Callable[[Callable[..., Any], Any], Callable[..., Any]]

Description

Ensure that the decorated function has a return type annotation and contains at least one return statement that is not 'return None'. This validator checks that the function signature includes a return type annotation and that the function body contains meaningful return statements.

Raises

  • DecoratorUsageValidationError if the decorated function does not have a return type annotation or does not contain at least one meaningful return statement (not 'return None' or empty return).

Example use

@validate_decorated(has_return())
def my_decorator(func): return func

@my_decorator
def correct_usage() -> int: return 42  # Has return annotation - OK

@my_decorator
def bad_usage():  # Will raise a DecoratorUsageValidationError
    return "no annotation"

@my_decorator
def also_bad() -> int:  # Will raise a DecoratorUsageValidationError
    return None

@my_decorator
def another_bad() -> str:  # Will raise a DecoratorUsageValidationError
    if True:
        return  # Empty return
    else:
        return None

###

@validate_decorated(has_return())
def deco_factory(param):
    def inner(f):
        return f
    return inner

@deco_factory("test")
def correct_usage() -> str: return "hello"  # Has return annotation - OK

@deco_factory("test")
def bad_usage():  # Will raise a DecoratorUsageValidationError
    pass

@deco_factory("test")
def also_bad() -> int:  # Will raise a DecoratorUsageValidationError
    return None

no_return()

Signature

def no_return() -> Callable[[Callable[..., Any], Any], Callable[..., Any]]

Description

Ensure that the decorated function has no return type annotation and contains no meaningful return statements. This validator checks that the function signature has no return type annotation and that the function body contains no meaningful return statements.

Raises

  • DecoratorUsageValidationError if the decorated function has a return type annotation or contains at least one meaningful return statement.

Example use

@validate_decorated(no_return())
def my_decorator(func): return func

@my_decorator
def correct_usage(): pass  # No return annotation - OK

@my_decorator
def also_correct():  # OK - returns None
    return None

@my_decorator
def another_correct():  # OK - empty return
    if True:
        return
    print("hello")

@my_decorator
def bad_usage() -> int:  # Will raise a DecoratorUsageValidationError
    pass

@my_decorator
def also_bad():  # Will raise a DecoratorUsageValidationError
    return 42

###

@validate_decorated(no_return())
def deco_factory(param):
    def inner(f):
        return f
    return inner

@deco_factory("test")
def correct_usage(): pass  # No return annotation - OK

@deco_factory("test")
def bad_usage() -> str:  # Will raise a DecoratorUsageValidationError
    return "hello"

@deco_factory("test")
def also_bad():  # Will raise a DecoratorUsageValidationError
    return 42

custom_validator()

Signature

def custom_validator(validation_func: Callable[[Callable[..., Any]], bool], error_message: str = "does not meet custom validation requirements") -> Callable[[Callable[..., Any], Any], Callable[..., Any]]

Description

Allow users to define their own validation logic using a custom function. The validation function should take the decorated function as input and return True if valid, False otherwise.

Parameters

  • validation_func: A function that takes the decorated function and returns True if valid, False if invalid
  • error_message: Custom error message to display when validation fails (optional)

Raises

  • TypeError if validation_func is not callable or error_message is not a string
  • DecoratorUsageValidationError if the custom validation fails or if the validation function raises an exception

Example use

def check_name_starts_with_test(func):
    return func.__name__.startswith("test_")

@validate_decorated(custom_validator(check_name_starts_with_test))
def my_decorator(func): return func

@my_decorator
def test_correct_usage(): pass  # OK - name starts with 'test_'

@my_decorator
def bad_usage(): pass  # Will raise a DecoratorUsageValidationError

###

def check_has_docstring(func):
    return func.__doc__ is not None

@validate_decorated(custom_validator(check_has_docstring, "function must have a docstring"))
def my_decorator(func): return func

@my_decorator
def correct_usage():
    """This function has a docstring"""
    pass

@my_decorator
def bad_usage(): pass  # Will raise: "function must have a docstring"

###

def check_param_count_at_least(min_count):
    def validator_func(func):
        from inspect import signature
        return len(signature(func).parameters) >= min_count
    return validator_func

@validate_decorated(custom_validator(check_param_count_at_least(2), "function must have at least 2 parameters"))
def deco_factory(param):
    def inner(f):
        return f
    return inner

@deco_factory("test")
def correct_usage(x, y): pass  # OK - has 2 parameters

@deco_factory("test")
def bad_usage(x): pass  # Will raise a DecoratorUsageValidationError