Skip to content

Instantly share code, notes, and snippets.

@dpwrussell
Last active December 28, 2015 22:39
Show Gist options
  • Save dpwrussell/7573003 to your computer and use it in GitHub Desktop.
Save dpwrussell/7573003 to your computer and use it in GitHub Desktop.
Demonstration of method_decorator failure
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)
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)
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