Reputation: 1505
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.
Upvotes: 4
Views: 189
Reputation: 14126
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()
, i.e. 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, e.g. 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
Upvotes: 1