Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python decorator to ensure that kwargs are correct

Tags:

python

I have done a decorator that I used to ensure that the keyword arguments passed to a constructor are the correct/expected ones. The code is the following:

from functools import wraps

def keyargs_check(keywords):
"""
This decorator ensures that the keys passed in kwargs are the onces that
are specified in the passed tuple. When applied this decorate will
check the keywords and will throw an exception if the developer used
one that is not recognized.

@type keywords: tuple
@param keywords: A tuple with all the keywords recognized by the function.
"""

def wrap(f):
    @wraps(f)
    def newFunction(*args, **kw):
        # we are going to add an extra check in kw
        for current_key in kw.keys():
            if not current_key in keywords:
                raise ValueError(
                    "The key {0} is a not recognized parameters by {1}.".format(
                        current_key, f.__name__))
        return f(*args, **kw)
    return newFunction
return wrap

An example use of this decorator would be the following:

class Person(object):

@keyargs_check(("name", "surname", "age"))
def __init__(self, **kwargs):
    # perform init according to args

Using the above code if the developer passes a key args like "blah" it will throw an exception. Unfortunately my implementation has a major problem with inheritance, if I define the following:

class PersonTest(Person):

@keyargs_check(("test"))
def __init__(self, **kwargs):
    Person.__init__(self,**kwargs) 

Because I'm passing kwargs to the super class init method, I'm going to get an exception because "test" is not in the tuple passed to the decorator of the super class. Is there a way to let the decorator used in the super class to know about the extra keywords? or event better, is there a standard way to achieve what I want?

Update: I am more interested in automate the way I throw an exception when a developer passes the wrong kwarg rather than on the fact that I use kwargs instead of args. What I mean is, I do not want have to write the code that check the args passed to the method in every class.

like image 364
mandel Avatar asked Apr 28 '26 16:04

mandel


1 Answers

Your decorator is not necessary. The only thing the decorator does that can't be done with the standard syntax is prevent keyword args from absorbing positional arguments. Thus

class Base(object):
    def __init__(name=None,surname=None,age=None):
        #some code

class Child(Base):
    def __init__(test=None,**kwargs):
        Base.__init__(self,**kwargs)

The advantage of this is that kwargs in Child will not contain test. The problem is that you can muck it up with a call like c = Child('red herring'). This is fixed in python 3.0.

The problem with your approach is that you're trying to use a decorator to do a macro's job, which is unpythonic. The only thing that will get you what you want is something that modifies the locals of the innermost function (f in your code, specifically the kwargs variable). How should your decorator know the wrapper's insides, how would it know that it calls a superclass?

like image 126
David Berger Avatar answered Apr 30 '26 06:04

David Berger



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!