I'm writing several functions which accept an argument called policy, which is allowed only to have certain values (namely, 'allow' or 'deny'). If it doesn't, I would like a ValueError to be raised.
For brevity, I would like to define a decorator for this. So far, I have come up with the following:
def validate_policy(function):
'''Wrapper which ensures that if the function accepts a 'policy' argument, that argument is either 'allow' or 'deny'.'''
def wrapped_function(policy, *args, **kwargs):
if policy not in ['allow', 'deny']:
raise ValueError("The policy must be either 'allow' or 'deny'.")
return function(policy, *args, **kwargs)
return wrapped_function
The problem is that this only works if policy is the first positional argument of the function. However, I would like to allow for policy to appear at any position.
To be specific, here are some (dummy) functions called make_decision and make_informed_decision which accept an argument policy at different positions, and some test cases to go with them:
import pytest
@validate_policy
def make_decision(policy): # The 'policy' might be the first positional argument
if policy == 'allow':
print "Allowed."
elif policy == 'deny':
print "Denied."
@validate_policy
def make_informed_decision(data, policy): # It also might be the second one
if policy == 'allow':
print "Based on the data {data} it is allowed.".format(data=data)
elif policy == 'deny':
print "Based on the data {data} it is denied.".format(data=data)
'''Tests'''
def test_make_decision_with_invalid_policy_as_positional_argument():
with pytest.raises(ValueError):
make_decision('foobar')
def test_make_decision_with_invalid_policy_as_keyword_argument():
with pytest.raises(ValueError):
make_decision(policy='foobar')
def test_make_informed_decision_with_invalid_policy_as_positional_argument():
with pytest.raises(ValueError):
make_informed_decision("allow", "foobar")
def test_make_informed_decision_with_invalid_policy_as_keyword_argument():
with pytest.raises(ValueError):
make_informed_decision(data="allow", policy="foobar")
if __name__ == "__main__":
pytest.main([__file__])
Currently all the tests pass except the third one, because the first positional argument 'allow' is interpreted as the policy rather than as the data as it should be.
How can I adapt the validate_policy decorator such that all the tests pass?
policya required keyword argument?