简体   繁体   中英

decorator inside class not getting values

I have a decorator that validates a json response that I obtain using requests. I also wrapped some requests logic into a class which accepts a schema that the decorator needs to validate. I tried to use the decorator on the get function of the class but i get a type error each way I try to do it:

TypeError: get() takes exactly 1 argument (0 given)

I know that if you use a decorator on a class method you need to set self=None in the decorator.

Here is the code:

schema = {'pid': int,
          'description': str}

def validator(schema):
    def _validator(f):
        @wraps(f)
        def wrapped(self=None):
            print f
            check = all([isinstance(v, schema[k]) for k,v in f().iteritems() if v])
            return f() if check else None
        return wrapped
    return _validator


class Data(object):
    ''' Base request wrapper to use for all apis
    accepts a validator schema to check the type of response values. '''

    def __init__(self, base_url, schema=None, debug=False):
        self.base_url = base_url
        self.schema = schema

    def __getattr__(self,key):
        new_base = self.append_to_url(self.base_url, key)
        return self.__class__(base_url=new_base)

    def __getitem__(self,key):
        return self.__getattr__(key)

    # def __call__(self, **kwargs):
    #     self.base_url = self.base_url[:-1]
    #     return self.get(self.base_url, **kwargs)

    def append_to_url(self, base_url, param):
        return '{}{}/'.format(base_url, param)

    @validator(schema)
    def get(self, **kwargs):
        try:
            r = requests.get(self.base_url[:-1], **kwargs)
            r.raise_for_status()
            return r.json()

        except requests.exceptions.ConnectionError as e:
            raise errors.ApiError(e)

        except requests.exceptions.HTTPError as e:
            raise errors.ApiError(e)

product_details = Data('my_api_url', schema).shoes['10'].get()

I think this happens because in my validator I initialize f() which is the get function is expecting self. I tried to pass self to f() in the decorator but that also yields the same error. I even tried to have the get function initialize on the __call__ method but that yields the get function is expecting 1 arg and 2 were given.

There must be a more efficient or pythonic way to achieve this. I know there is a library called voluptuous that does tons of validations but I wanted to try something simple as a learning exercise.

It's not the wrapped function call that's failing, it's the get call. Everywhere you call the get you call it like f() , ie without any arguments.

A working example, properly passing self argument to the wrapped function:

import functools

def validator(schema):
    def _validator(f):
        @functools.wraps(f)
        def wrapper(self):
            # pass self to the wrapped function
            result = f(self)
            return 'wrapped {} {}'.format(schema, result)
        return wrapper
    return _validator

class Data(object):
    @validator('test')
    def get(self):
        return 'data'

print(Data().get())
# wrapped test data

While you can use self or other such specific arguments directly, using *args and **kwargs instead makes the decorator more flexible, eg enables it to be used for non-bound functions too:

import functools

def validator(schema):
    def _validator(f):
        @functools.wraps(f)
        def wrapped(*args, **kwargs):
            # pass aribitrary args to the wrapped function
            # for greater flexibility
            result = f(*args, **kwargs)
            return 'wrapped {} {}'.format(schema, result)
        return wrapped
    return _validator

class Data(object):
    @validator('test')
    def get(self):
        return 'data'

@validator('test2')
def get():
    return 'data2'

print(Data().get())
# wrapped test data
print(get())
# wrapped test2 data2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM