2017-03-14 2 views
3

Ich schreibe mehrere Funktionen, die ein Argument namens policy akzeptieren, das nur bestimmte Werte haben darf (nämlich 'allow' oder 'deny'). Wenn nicht, möchte ich eine ValueError ausgelöst werden.Wie definiere ich in Python einen Funktions-Wrapper, der ein Argument mit einem bestimmten Namen validiert?

Der Kürze halber möchte ich einen Dekorateur dafür definieren. Bisher habe ich mit folgendem kommen:

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 

Das Problem ist, dass dies nur funktioniert, wenn policy das erste Positions Argument der Funktion ist. Ich möchte jedoch zulassen, dass policy an jeder Position erscheint.

Um genau zu sein, sind hier einige (Dummy) Funktionen make_decision und make_informed_decision genannt, die ein Argument policy an verschiedenen Positionen annehmen, und einige Testfälle mit ihnen zu gehen:

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__]) 

Derzeit werden alle Tests bestanden außer der dritte, weil das erste Positionsargument 'allow' eher als policy interpretiert wird als als data, wie es sein sollte.

Wie kann ich den validate_policy Dekorateur so anpassen, dass alle Tests bestanden werden?

+1

Machen Sie die Richtlinie zu einem erforderlichen Schlüsselwortargument. Explizit ist besser als implizit. –

+0

[Rick Teachey] (http://stackoverflow.com/users/2437514/rick-teachey), das scheint eine gute Idee zu sein, außer dass meine Code-Basis in Python 2 ist, also [PEP 3102] (http: // legacy .python.org/dev/peps/pep-3102 /) wurde nicht implementiert. Ich nehme an, dass Sie damit gemeint sind, indem Sie "Richtlinie" zu einem erforderlichen Keyword-Argument machen? –

+0

Bummer ......... –

Antwort

2

können Sie die inspect Moduls verwenden Signature.bind Funktion:

import inspect 

def validate_policy(function): 
    '''Wrapper which ensures that if the function accepts a 'policy' argument, that argument is either 'allow' or 'deny'.''' 
    signature= inspect.signature(function) 
    def wrapped_function(*args, **kwargs): 
     bound_args= signature.bind(*args, **kwargs) 
     bound_args.apply_defaults() 
     if bound_args.arguments.get('policy') not in ['allow', 'deny']: 
      raise ValueError("The policy must be either 'allow' or 'deny'.") 
     return function(*args, **kwargs) 
    return wrapped_function 
1

Hier ist eine andere Lösung inspect.getcallargs mit:

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(*args, **kwargs): 
     call_args = inspect.getcallargs(function, *args, **kwargs) 
     if 'policy' in call_args: 
      if call_args['policy'] not in ['allow', 'deny']: 
       raise ValueError("The policy must be either 'allow' or 'deny'.") 
     return function(*args, **kwargs) 
    return wrapped_function 

Es macht alle Tests bestanden.

Verwandte Themen