Last active
December 28, 2015 22:39
-
-
Save dpwrussell/7573003 to your computer and use it in GitHub Desktop.
Demonstration of method_decorator failure
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from functools import update_wrapper | |
from django.utils.decorators import method_decorator | |
class MyDecorator(object): | |
def __init__(self): | |
print 'MyDecorator init' | |
def __call__(self, f): | |
print 'MyDecorator call' | |
def wrapped(a,b): | |
print('wrapper called') | |
f(a,b) | |
return update_wrapper(wrapped, f) | |
class C(object): | |
def __init__(self): | |
print('C init') | |
@method_decorator(MyDecorator()) | |
def doStuff(self, a, b): | |
print('doStuff') | |
print('declare c') | |
c = C() | |
print('call doStuff') | |
c.doStuff(1,2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from functools import update_wrapper | |
from django.utils.decorators import method_decorator as method_decorator_orig | |
def method_decorator(decorator): | |
print('method_decoration') | |
return method_decorator_orig(decorator) | |
class MyDecorator(object): | |
__name__ = "hello" | |
def __init__(self): | |
print 'MyDecorator init' | |
def __call__(self, f): | |
print 'MyDecorator call' | |
def wrapped(a,b): | |
print('wrapped called') | |
f(a,b) | |
return update_wrapper(wrapped, f) | |
class C(object): | |
def __init__(self): | |
print('C init') | |
@method_decorator(MyDecorator()) | |
def doStuff(self, a, b): | |
print('doStuff') | |
@method_decorator(MyDecorator()) | |
def doStuff2(self, a, b): | |
print('doStuff2') | |
print('declare c') | |
c = C() | |
c2 = C() | |
print('call doStuff') | |
c.doStuff(1,2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
class MyDecorator(object): | |
#__name__ = "hello" | |
def __init__(self, extra): | |
print 'MyDecorator init' | |
self.extra = extra | |
def __call__(self, f): | |
print 'MyDecorator call' | |
def wrapped(a,b): | |
print('wrapped called') | |
return f(a,b) + self.extra | |
return update_wrapper(wrapped, f) | |
# Make a classic function decorator | |
def funcDeco(f): | |
def new_func(*args, **kwargs): | |
print('funcDeco') | |
return f(*args, **kwargs) | |
return new_func | |
# Test it on a function | |
@funcDeco | |
def testIt(x, y): | |
return x+y | |
print testIt(1, 2) | |
# Test new method_decorator on both types of decorator | |
class C(object): | |
def __init__(self): | |
print('C init') | |
@method_decorator(MyDecorator(5)) | |
def doStuff(self, a, b): | |
print('doStuff') | |
return a+b | |
@method_decorator(funcDeco) | |
def doStuffClassic(self, a, b): | |
print('doStuffClassic') | |
return a*b | |
c = C() | |
print(c.doStuff(1,2)) | |
print(c.doStuffClassic(5,10)) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment