Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django 2.1 - 'functools.partial' object has no attribute '__name__'

Tags:

python

django

I recently upgraded Django from 2.0.7 to 2.1.1, a new error occurs in which I get this error 'functools.partial' object has no attribute '__name__'.

I'd like to understand if my fix is right and what caused this new error to happen, I couldn't find anything on the django release notes related to this issue, maybe I missed it.

decorators.py

def auth0_login_required(function):
    def wrap(request, *args, **kwargs):

        if request.isAuthenticated or request.user.is_staff:
            pass
        else:
            raise Http404()
            
        return function(request, *args, **kwargs)
    wrap.__doc__ = function.__doc__
    wrap.__name__ = function.__name__ # ERROR HERE
    return wrap

How it is used, views.py:

@method_decorator(auth0_login_required, name='dispatch')
class Dashboard(View):
    ...

For the fix I just removed wrap.__name__ = function.__name__, but I'm not sure if it'll break something else.

like image 612
Hiroyuki Nuri Avatar asked Sep 11 '18 08:09

Hiroyuki Nuri


1 Answers

Rather than manually copy things across, use the @functools.wraps() decorator to handle this for you:

from functools import wraps

def auth0_login_required(function):
    @wraps(function)
    def wrap(request, *args, **kwargs):

        if request.isAuthenticated or request.user.is_staff:
            pass
        else:
            raise Http404()

        return function(request, *args, **kwargs)

    return wrap

The @wraps() decorator (via the functools.update_wrapper() function it calls knows how to handle functools.partial objects correctly (or rather, it can handle the fact that functools.partial objects have no __name__ attribute).

It's fine that the wrapped functools.partial() object found on the View class doesn't have a __name__ attribute, what's not fine is that you then don't copy that attribute at all even when you are decorating functions that do have the attribute. If you don't want to use @wraps() you'd have to manually copy the attribute across and handle the exception yourself:

try:
    wrap.__name__ = function.__name__
except AttributeError:
    pass
try:
    wrap.__doc__ = function.__doc__
except AttributeError:
    pass

but take into account that this doesn't copy the __qualname__, __module__ and __annotations__ attributes, doesn't handle any custom attributes set on function (which other decorators might rely on). @functools.wraps() does take care of all of those, plus it sets the __wrapped__ attribute on the decorator wrapper function that would let you unwrap the decorator again.

like image 162
Martijn Pieters Avatar answered Sep 21 '22 18:09

Martijn Pieters