﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
21513	method_decorator doesn't work with argumented Class-Based Decorator	dpwrussell	nobody	"https://code.djangoproject.com/ticket/13093 was a similar example of this. decorator_from_middleware has been adapted to use available_attrs. The same treatment is needed in method_decorator:

method_decorator blindly attempts to access attributes which are not present on an object, only on a class. This causes method_decorator to fail completely if it is used on any decorator used like this (with or without actual arguments):

{{{
class MyDecorator(object):
    def __init__(self, extra):
        self.extra = extra

    def __call__(self, f):

        def wrapped(a,b):
            return f(a,b) + self.extra
        return update_wrapper(wrapped, f)

class C(object):
    def __init__(self):
    @method_decorator(MyDecorator(5))
    def addStuff(self, a, b):
        return a+b

c = C()
print(c.addStuff(1,2)) # Should return 1+2+5=8
}}}

Something like this would be a fix, although I'm not really familiar enough with the nuances to know for 100% if this will work in all cases. Seems to be ok though:


{{{
from functools import update_wrapper
from django.utils.decorators import available_attrs, WRAPPER_ASSIGNMENTS


def method_decorator(decorator):
    """"""
    Converts a function decorator into a method decorator
    """"""
    # 'func' is a function at the time it is passed to _dec, but will eventually
    # be a method of the class it is defined it.
    def _dec(func):
        def _wrapper(self, *args, **kwargs):
            @decorator
            def bound_func(*args2, **kwargs2):
                return func(self, *args2, **kwargs2)
            # bound_func has the signature that 'decorator' expects i.e.  no
            # 'self' argument, but it is a closure over self so it can call
            # 'func' correctly.
            return bound_func(*args, **kwargs)
        # In case 'decorator' adds attributes to the function it decorates, we
        # want to copy those. We don't have access to bound_func in this scope,
        # but we can cheat by using it on a dummy function.

        @decorator
        def dummy(*args, **kwargs):
            pass
        update_wrapper(_wrapper, dummy)

        # Need to preserve any existing attributes of 'func', including the name.

        update_wrapper(_wrapper, func)

        return _wrapper

    update_wrapper(_dec, decorator, assigned=available_attrs(decorator))
    # Change the name to aid debugging.
    if hasattr(decorator, '__name__'):
        _dec.__name__ = 'method_decorator(%s)' % decorator.__name__
    else:
        _dec.__name__ = 'method_decorator(%s)' % decorator.__class__.__name__
    return _dec
}}}"	Uncategorized	closed	Utilities	1.6	Normal	fixed	decorator, functools, CBV, method_decorator		Unreviewed	0	0	0	0	0	0
