I just started playing with decorators in Python. I am currently having trouble figuring out the best possible way to handle this use cases. Since a decorated function cannot access local variables from the decorator.
schema = {
'lang': {'type': 'string', 'required':True}
}
def validate(schema):
def my_decorator(func):
def wrapper(*args, **kwargs):
# Read from request body args[1]
# Validate the json is in fact correct
valid_json = json.loads(body.decode('utf-8'))
# Compare valid_json to the schema using "Cerberus"
return args, kwargs
return wrapper
return my_decorator
@validate(schema)
def main_function(self, req, resp):
# call database with valid_json
My question is: How do I access valid_json from my decorated function to be able to insert into my database afterwards. What is the best practice for this situation?
Edit: I am running pypy 2.4
IIUC what you asked, the following could do so. I can't say that I'm crazy about this solution (or about what you're asking, for that matter) - it's too much "magic" for my taste.
For simplicity, let's assume that schemas and everything are integers, and validating something by a schema simply means adding the schema integer to it (this is just for illustration). So we have:
def validate_schema(schema, arg):
return arg + schema
Now we can write the following decorator:
def validate(schema, arg_pos):
"""
Decorates a function so that the arg_pos argument will
be modified to be validated by schema.
"""
def decorator(method):
@functools.wraps(method)
def f(*args, **kwargs):
# Manipulate args to modify the validated argument:
args = args[: arg_pos] + (validate_schema(schema, args[arg_pos]), ) + args[arg_pos + 1: ]
# Call the method with the modified arguments
return method(*args, **kwargs)
return f
return decorator
Now we can use it as so:
# Validate arg #0 with schema 12
@validate(12, 0)
def bar(inp_):
print inp_
>>> bar(1)
13
Of course, you can elaborate this further, to take pairs of schema+arguments, handle keyword arguments, and so forth. That is the principle of this answer, though. I think it's somewhat amusing, but I wouldn't use it.
You can't do this directly. The scope of the function being decorated is set before it's wrapped by the decorator, and it has no connection to the scopes established by the decorator added wrapper.
The best you can do is have the function accept additional arguments which the decorator passes, eg:
@validate(schema)
def main_function(self, req, resp, valid_json=None):
# call database with valid_json
with the decorator explicitly adding valid_json=valid_json
when it calls the wrapped function, eg return func(*args, **kwargs, valid_json=valid_json)
.
I've also been playing with decorators lately and I am under the impression that there's not really a good way to do this. You can't access the local scope of the function from outside the function.
A potential solution, albeit not exactly what you want, using partial function application and function member data might be:
from functools import wraps
schema = { 'lang': {'type': 'string', 'required':True} }
def compare(schema, json):
return schema == json
def validate(schema):
def outer(function):
@wraps(function)
def inner(*args, **kwargs):
return function(*args, **kwargs)
inner.validate = lambda json: compare(schema, json)
return inner
return outer
@validate(schema)
def main_function():
print main_function.validate({ 'lang': {'type': 'string', 'required':True} })
print main_function.validate({})
main_function()
# Output:
# True
# False
The compare
function can obviously be customized to do something useful, but you still need to validate the json from within main_function
. I don't know if there is a way around this without making a inner function within main_function
.
It is also possible to "inject" a variable into a function's global scope and use a decorator to do the necessary book-keeping:
from functools import wraps
schema = {
'lang': {'type': 'string', 'required':True}
}
def validate(schema):
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
_backup, _check = None, False
if 'valid_json' in func.func_globals:
_backup, _check = func.func_globals['valid_json'], True
valid_json = {'hi': 'mom'} # or whatever
func.func_globals['valid_json'] = valid_json
output = func(*args, **kwargs)
if _check:
func.func_globals['valid_json'] = _backup
else:
del func.func_globals['valid_json']
return output
return wrapper
return my_decorator
@validate(schema)
def main_function():
print valid_json
main_function()
# Output:
# {'hi': 'mom'}
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.