Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Using *args, **kwargs in wrapper functions

I'm writing a wrapper function for Django's render_to_response() to add a CSRF processing.

The logic is:

def some_view (request)
    dictionary = {'context_param': some_param}
    dictionary.update(csrf(request))
    # ... view code here
    return render_to_response("a_template.html", dictionary)

render_to_response() has the following signature:

def render_to_response(*args, **kwargs)

I want to write transparent wrapper - just add some functionality (mentioned before) and leave other things as they are. I guess I should write someting like this:

def csrf_response (request, *args, **kwargs):

    # Here I need to somehow extract dictionary from args or kwargs

    if dictionary is None:
        dictionary = {}
    dictionary.update(csrf(request))

    # And here I somehow need to pass dictionary into render_to_response() fot the furher processing
    return render_to_response(*args, **kwargs)

So question is - what is the best practice for extracting needed parameter from args/kwargs, (then changing it) and passing it further?

BTW code of render_to_response() seemed me a bit strange. Here it is:

def render_to_response(*args, **kwargs):
    """
    Returns a HttpResponse whose content is filled with the result of calling
    django.template.loader.render_to_string() with the passed arguments.
    """
    httpresponse_kwargs = {'mimetype': kwargs.pop('mimetype', None)}
    return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs)

What if someone call it with all positional arguments, so kwargs will be empty, but mimetype parameter will be specified as a last positional argument? Looks like in that case its behavior would be wrong.

like image 485
Alexander Avatar asked May 09 '11 18:05

Alexander


1 Answers

Because of how render_to_response is implemented, the only way to specify mimetype is by using a named argument. Anything passed as a positional argument will be passed on to loader.render_to_string.

The methodology for how to go about extracting certain arguments and passing on others really depends on what you're doing. There's no one "correct" way to always do it. Naturally, different authors have their own preferred conventions.

In the place of your comment # Here I need to somehow extract dictionary from args or kwargs you can use kwargs directly as a dictionary, and args as a tuple, because that's exactly what they are. For the args, you have no choice but to assert (i.e. assume) the meaning of the value in each position.

I personally prefer that if you are writing code that knows the values of certain arguments, they should be declared in the signature, like so:

def spam(session, name, *args, clear=True, **kwargs):
  # do something with session, name, clear
  return eggs(name, *args, **kwargs)  # if eggs requires name
like image 130
wberry Avatar answered Oct 02 '22 23:10

wberry