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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With