I have a decorator that validates some parameters and passes an validated key to various functions:
from functools import wraps
ref validate(f):
#wraps(f) # This is to ensure docstrings are passed through the decorated function
def redirect_if_invalid(request, *args, **kwargs):
if request.valid == False:
return HttpResponseRedirect('/login')
else:
newkwargs = { 'key': request.key }
return f(request, *args, **newkwargs)
return redirect_if_invalid
This is used by some other functions:
#validate
def genericHandler(request, key)
pass
I'd call the function like this:
genericHandler(request)
And the decorator generates the 'key' kwarg. However, I'd like to optionally pass in the key at some other point, ie call:
genericHandler(request, 'keydata')
Currently this gives me an error:
TypeError: genericHandler() got multiple values for keyword argument 'key'
How can I get around this? To reiterate, the main goal is to be able to call genericHandler() with or without the optional parameter, and have the decorator generate the parameter only if it's missing.
So far inside the decorator, I can't figure out how to determine whether the 'key' parameter was passed in or not because functools.wraps() seems to hide it.
There's not any reasonable way to do this if you want your wrapper's signature to still be (request, *args, **kwargs). On the other hand, it looks like your decorator already assumes that the wrapped function takes a key parameter, so why not rewrite the wrapper to take one as well? In that case it becomes trivial to check if it's been passed or not.
def validate(f):
#wraps(f)
def redirect_if_invalid(request, key=None):
# do the validation
if key is None:
key = request.key
return f(request, key)
return redirect_if_invalid
You can add the *args and **kwargs parameters back if you like, of course.
So the best way for me to do this was to explicitly pass kwargs as kwargs. So decorated functions should actually be:
#validate
def genericHandler(request, **kwargs)
key = kwargs.get('key')
pass
This way, I can call the function either with or without args:
genericHandler(request)
or
genericHandler(request, **{ 'key' : key })
And the actual decorated looks like:
def validate(f):
#wraps(f) # This is to ensure docstrings are passed through the decorated function
def redirect_if_invalid(request, *args, **kwargs):
key = kwargs.get('key')
if not key:
kwargs.set('key', request.key)
return f(request, *args, **kwargs)
return redirect_if_invalid
Related
# This version only accepts one argument
# def shout(fn):
# def wrapper(name):
# return fn(name).upper()
# return wrapper
# This version works with any number of args
def shout(fn):
def wrapper(*args, **kwargs):
return fn(*args, **kwargs).upper()
return wrapper
#shout
def greet(name):
return f"Hi, I'm {name}."
#shout
def order(main, side):
return f"Hi, I'd like the {main}, with a side of {side}, please."
#shout
def lol():
return "lol"
print(greet("todd"))
print(order(side="burger", main="fries"))
print(lol())
In the above code,
def wrapper(*args, **kwargs):
print(f"abc is {fn.__name__}")
return fn(*args, **kwargs).upper()
return wrapper
When wrapper functions executed, how does it knows the value of arguments which are to be assigned to , * args and **kwargs. We have not defined the values of arguments here, but instead func is given the parameters.
side="burger", main="fries".
**kwargs open up the dictionary but when did we defined such dictionary?
How does side="burger", main="fries" are set as arguments of wrapper function and Why are they being assigned to args and kwargs ?
Why are arguments given to fync being assigned to parameters of wrapper function?
They are set when you call the function. When you call the wrapper, all positional arguments are packed together into args and all keyword arguments are put into kwargs, which are then unpacked and passed to the wrapped function.
When you call:
order(side="burger", main="fries")
You are actually calling wrapper() with those parameters.
This is the time at which **kwargs is assigned.
wrapper() then goes on to call your actual function order() with *args and **kwargs.
I'm writing several functions which accept an argument called policy, which is allowed only to have certain values (namely, 'allow' or 'deny'). If it doesn't, I would like a ValueError to be raised.
For brevity, I would like to define a decorator for this. So far, I have come up with the following:
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
The problem is that this only works if policy is the first positional argument of the function. However, I would like to allow for policy to appear at any position.
To be specific, here are some (dummy) functions called make_decision and make_informed_decision which accept an argument policy at different positions, and some test cases to go with them:
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__])
Currently all the tests pass except the third one, because the first positional argument 'allow' is interpreted as the policy rather than as the data as it should be.
How can I adapt the validate_policy decorator such that all the tests pass?
You can use the inspect module's Signature.bind function:
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
Here is another solution using inspect.getcallargs:
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
It makes all the tests pass.
I want to give user API for my library with easier way to distinguish different types of parameters which I pass to function. All groups of arguments are defined earlier (for now I have 3 groups), but attributes of them need to be constructed on run. I can do this in Django ORM style, where double underscore separates 2 parts of parameter. But it is very unreadable. Example:
def api_function(**kwargs):
""" Separate passed arguments """
api_function(post__arg1='foo', api__arg1='bar', post_arg2='foo2')
Better way do this SQLAlchemy, but only to compare attributes and all args are defined earlier. Example:
class API(object):
arg1 = Arg()
arg2 = Arg()
class Post(object): #...
def api_function(*args):
""" Separate passed arguments """
api_function(POST.arg1=='foo', API.arg1=='bar', POST.arg2=='foo2')
What I would like to achive is behaviour like this:
class API(object): # Magic
class POST(object): # Magic
def api_function(*args):
""" Separate passed arguments """
api_function(POST.arg1='foo', API.arg1='bar', POST.arg2='foo2')
What have I tried:
declare metamodel with defined __setattr__, but it rise on evaluation SyntaxError: keyword can't be an expression
declare __set__, but it is designed for known attributes
My questions are:
Is it even possible in Python to work like in third snippet?
If not, is there any really close solution to look like in third snippet? The best way should use assignment operator API.arg1='foo', the worst API(arg1='foo')
Requirements -- should work at least at Python 2.7. Good to work on Python 3.2.
EDIT1
My first test, which is using equality operator (but it NEVER should be use in this way):
class APIMeta(type):
def __getattr__(cls, item):
return ApiData(item, None)
class API(object):
__metaclass__ = APIMeta
def __init__(self, key, value):
self.key = key
self.value = value
def __str__(self):
return "{0}={1}".format(self.key, self.value)
def __eq__(self, other):
self.value = other
return self
def print_api(*api_data):
for a in api_data:
print(str(a))
print_api(API.page=='3', API=='bar')
It is working right, but using == is suggesting that I want to compare something and I want to assign value.
NOTE: I don't know how much I like this schema you want. But I know one annoying thing will be all the imports to call api_function. E.G. from api import POST, API, api_function
As I said in the comments, the first way is not possible. This is because assignment (=) is a statement not an expression, so it can't return a value. Source
But the other way you asked for certainly is:
class POST(object):
def __init__(self, **kwargs):
self.args = kwargs
# You'll also probably want to make this function a little safer.
def __getattr__(self, name):
return self.args[name]
def api_function(*args):
# Update this to how complicated the handling needs to be
# but you get the general idea...
post_data = None
for a in args:
if isinstance(a, POST):
post_data = a.args
if post_data is None:
raise Exception('This function needs a POST object passed.')
print post_data
Using it:
>>> api_function('foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in api_function
Exception: This function needs a POST object passed.
>>> api_function(POST(arg1='foo'))
{'arg1': 'foo'}
>>> api_function(POST(arg1='foo',
... arg2='bar'
... )
... )
{'arg1': 'foo', 'arg2': 'bar'}
Here's my solution. It's not the best in design, as the structure of the argument grouper is nested quite deep, so I'd appreciate feedback on it:
class ArgumentGrouper(object):
"""Transforms a function so that you can apply arguments in named groups.
This system isn't tested as thoroughly as something with so many moving
parts should be. Use at own risk.
Usage:
#ArgumentGrouper("foo", "bar")
def method(regular_arg, foo__arg1, bar__arg2):
print(regular_arg + foo__arg1 + bar__arg2)
method.foo(", ").bar("world!")("Hello")() # Prints "Hello, world!"
"""
def __call__(self, func):
"""Decorate the function."""
return self.Wrapper(func, self.argument_values)
def __init__(self, *argument_groups):
"""Constructor.
argument_groups -- The names of argument groups in the function.
"""
self.argument_values = {i: {} for i in argument_groups}
class Wrapper(object):
"""This is the result of decorating the function. You can call group
names as function to supply their keyword arguments.
"""
def __call__(self, *args):
"""Execute the decorated function by passing any given arguments
and predefined group arguments.
"""
kwargs = {}
for group, values in self.argument_values.items():
for name, value in values.items():
# Add a new argument in the form foo__arg1 to kwargs, as
# per the supplied arguments.
new_name = "{}__{}".format(
group,
name
)
kwargs[new_name] = value
# Invoke the function with the determined arguments.
return self.func(*args, **kwargs)
def __init__(self, func, argument_values):
"""Constructor.
func -- The decorated function.
argument_values -- A dict with the current values for group
arguments. Must be a reference to the actual dict, since each
WrappedMethod uses it.
"""
self.func = func
self.argument_values = argument_values
def __getattr__(self, name):
"""When trying to call `func.foo(arg1="bar")`, provide `foo`. TODO:
This would be better handled at initialization time.
"""
if name in self.argument_values:
return self.WrappedMethod(name, self, self.argument_values)
else:
return self.__dict__[name]
class WrappedMethod(object):
"""For `func.foo(arg1="bar")`, this is `foo`. Pretends to be a
function that takes the keyword arguments to be supplied to the
decorated function.
"""
def __call__(self, **kwargs):
"""`foo` has been called, record the arguments passed."""
for k, v in kwargs.items():
self.argument_values[self.name][k] = v
return self.wrapper
def __init__(self, name, wrapper, argument_values):
"""Constructor.
name -- The name of the argument group. (This is the string
"foo".)
wrapper -- The decorator. We need this so that we can return it
to chain calls.
argument_values -- A dict with the current values for group
arguments. Must be a reference to the actual dict, since
each WrappedMethod uses it.
"""
self.name = name
self.wrapper = wrapper
self.argument_values = argument_values
# Usage:
#ArgumentGrouper("post", "api")
def api_function(regular_arg, post__arg1, post__arg2, api__arg3):
print("Got regular args {}".format(regular_arg))
print("Got API args {}, {}, {}".format(post__arg1, post__arg2, api__arg3))
api_function.post(
arg1="foo", arg2="bar"
).api(
arg3="baz"
)
api_function("foo")
Then, usage:
#ArgumentGrouper("post", "api")
def api_function(regular_arg, post__arg1, post__arg2, api__arg3):
print("Got regular args {}".format(regular_arg))
print("Got API args {}, {}, {}".format(post__arg1, post__arg2, api__arg3))
api_function.post(
arg1="foo", arg2="bar"
).api(
arg3="baz"
)
api_function("foo")
Output:
Got regular args foo
Got API args foo, bar, baz
It should be simple to scrape argument group names by introspection.
You'll notice the argument naming convention is hardcoded into the WrappedMethod, so you'll have to make sure you're okay with that.
You can also invoke it in one statement:
api_function.post(
arg1="foo", arg2="bar"
).api(
arg3="baz"
)("foo")
Or you could add a dedicated run method which would invoke it, which would just take the place of Wrapper.__call__.
Python don't allow to use assignment operator inside any other code, so:
(a=1)
func((a=1))
will rise SyntaxError. This means that it is not possible to use it in this way. Moreover:
func(API.arg1=3)
Will be treated that left side of assignment is argument API.arg1 which is not valid name in Python for variables. Only solution is to make this in SQLAlchemy style:
func({
API.arg1: 'foo',
API.arg2: 'bar',
DATA.arg1: 'foo1',
})
or
func(**{
API.arg1: 'foo',
API.arg2: 'bar',
DATA.arg1: 'foo1',
})
or just only:
func( API(arg1='foo', arg2='bar'), POST(arg1='foo1'), POST(arg2='bar1'))
Thank you for your interest and answers.
I am trying to write a "login_required" decorator for the views in a WSGI+Werkzeug application.
In order to do this, I need to get at the user's session, which is accessible via the Request object that is passed into the view methods.
I can't figure out how to get at that instance of Request in the decorator, though. I looked at PEP318, specifically the fourth example, but I'm not quite getting it.
Here's what I'm trying:
def login_required(*args, **kw):
def goto_login(**kw):
return redirect(url_for('login'))
def decorate(f):
# args[0] should be request
args[0].client_session['test'] = True
logged_in = 0
if logged_in:
return f
else:
return goto_login
return decorate
#login_required()
#expose('/hello/<string:name>')
def hello(request, name):
return render_template('say_hello.html', name=name)
but I get an index out of bounds error trying to call args[0].
Is there any way I can get access to the request argument passed into the "hello" function in the "login_required" decorator?
The decorator login_required is passed the function (hello in this case).
So what you want to do is:
def login_required(f):
# This function is what we "replace" hello with
def wrapper(*args, **kw):
args[0].client_session['test'] = True
logged_in = 0
if logged_in:
return f(*args, **kw) # Call hello
else:
return redirect(url_for('login'))
return wrapper
kwargs is a dictionary containing argument as keys and values as values.
So all you need to do is check:
some_var = kw['my_property']
def validate(request, *args, **kwargs):
form_class = kwargs.pop('form_class')
extra_args_func = kwargs.pop('callback', lambda request, *args, **kwargs: {})
thanks
a={'a':'aaa','b':'bbb'}
b=a.pop('a',lambda x,y:x)
print a
i know dict.pop('a'),but i don't know dict.pop('a',func)
what is the use of 'func‘ in here
The expression:
lambda request, *args, **kwargs: {}
builds an anonymous function which must be called with at least one argument (which, if named, must be named request) and can be called with any number of positional and named arguments: when called, it ignores all the arguments and returns a new empty dictionary.
The code snippet:
a={'a':'aaa','b':'bbb'}
b=a.pop('a',lambda x,y:x)
print a
prints {'b': 'bbb'} (which is also the value a stays bound this after the snippet executes) and bings the string 'aaa' to name b. The second argument to the .pop method plays no role in this case: it's only used when the first argument is not found as a key in the dictionary on which the method is called (in which case, .pop's second argument would be the "default value" returned by the call to .pop, without any alteration to the dictionary). In this case, 'a' is indeed found at that time as a key in dictionary a, and therefore it's removed from that dictionary, and the corresponding value, string 'aaa', is returned by the call to .pop (whence it gets then bound to name b).
That is a "lambda function". It's a short way of expressing a function that's declared inline. It looks like this:
lambda arg1,arg2,...: expression
and is the equivalent of a nameless function that would look like this:
def some_nameless_function(arg1,arg2,...):
return expression
So, the code you have there,
def validate(request, *args, **kwargs):
form_class = kwargs.pop('form_class')
extra_args_func = kwargs.pop('callback', lambda request, *args, **kwargs: {})
is equivalent to a function that looks like this:
def nameless_function(request, *args, **kwargs):
return {}
def validate(request, *args, **kwargs):
form_class = kwargs.pop('form_class')
extra_args_func = kwargs.pop('callback', nameless_function)
What it's doing is pop off a callback function from the kwargs dict, or if that isn't set, it creates a lambda function that does nothing (returns an empty dict). The request, *args, **kwargs part is presumably the "signature" of the callback function.