I have a list of functions whose parameters in the signature should be validated with the same criteria each time.
def sum_first_positive(a=1,b=1,c=1):
assert a > 0
return a + b + c
def sum_second_positive(a=1,b=1,c=1):
assert b > 0
return a + b + c
def sum_third_positive(a=1,b=1,c=1):
assert c > 0
return a + b + c
def sum_all_positives(a=1,b=1,c=1):
assert a > 0
assert b > 0
assert c > 0
return a + b + c
Rather than copy pasting the same validator inside each function, I want to use a decorator instead.
Here is my (working) attempt:
def decorator(*decorator_arguments):
def inner_decorator(function):
def wrapper(**kwargs):
for val in decorator_arguments:
if kwargs[val] <= 0:
raise ValueError(f"Value '{val}' should be strictly positive. You passed {kwargs[val]}")
return function(**kwargs)
return wrapper
return inner_decorator
@decorator("c")
def sum_third_positive(a=0, b=0, c=1):
return a + b + c
@decorator("a", "b", "c")
def sum_all_positives(a=0, b=0, c=0):
return a + b + c
# Fails
sum_all_positives(a=-1,b=1,c=1)
# OK
sum_third_positive(a=-1,b=1,c=10)
# Fails
sum_third_positive(a=-1,b=1,c=-10)
The problem is, the user will have to pass all the arguments by name as I can not pass positional arguments to the wrapper.
I can define the function as:
@decorator("c")
def sum_third_positive(*, a=0, b=0, c=1):
return a + b + c
to not allow for positional arguments, but this is not ideal.
Is there a solution to pass positional arguments, by position to the decorator arguments and check on their values?