1 | from functools import wraps, update_wrapper, WRAPPER_ASSIGNMENTS
|
---|
2 |
|
---|
3 | def method_decorator(decorator):
|
---|
4 | """
|
---|
5 | Converts a function decorator into a method decorator
|
---|
6 | """
|
---|
7 | # 'func' is a function at the time it is passed to _dec, but will eventually
|
---|
8 | # be a method of the class it is defined it.
|
---|
9 | def _dec(func):
|
---|
10 | def _wrapper(self, *args, **kwargs):
|
---|
11 | @decorator
|
---|
12 | def bound_func(*args2, **kwargs2):
|
---|
13 | #return func(self, *args2, **kwargs2)
|
---|
14 | return func.__get__(self, type(self))(*args2, **kwargs2)
|
---|
15 | # bound_func has the signature that 'decorator' expects i.e. no
|
---|
16 | # 'self' argument, but it is a closure over self so it can call
|
---|
17 | # 'func' correctly.
|
---|
18 | return bound_func(*args, **kwargs)
|
---|
19 | # In case 'decorator' adds attributes to the function it decorates, we
|
---|
20 | # want to copy those. We don't have access to bound_func in this scope,
|
---|
21 | # but we can cheat by using it on a dummy function.
|
---|
22 | @decorator
|
---|
23 | def dummy(*args, **kwargs):
|
---|
24 | pass
|
---|
25 | update_wrapper(_wrapper, dummy)
|
---|
26 | # Need to preserve any existing attributes of 'func', including the name.
|
---|
27 | update_wrapper(_wrapper, func)
|
---|
28 |
|
---|
29 | return _wrapper
|
---|
30 | update_wrapper(_dec, decorator)
|
---|
31 | # Change the name to aid debugging.
|
---|
32 | _dec.__name__ = 'method_decorator(%s)' % decorator.__name__
|
---|
33 | return _dec
|
---|
34 |
|
---|
35 | def my_wrapper_1(wrapped):
|
---|
36 | def _wrapper(arg1, arg2):
|
---|
37 | print('_wrapper', arg1, arg2)
|
---|
38 | return wrapped(arg1, arg2)
|
---|
39 | return _wrapper
|
---|
40 |
|
---|
41 | @my_wrapper_1
|
---|
42 | def function(arg1, arg2):
|
---|
43 | print('function', arg1, arg2)
|
---|
44 | return arg1, arg2
|
---|
45 |
|
---|
46 | function(1, 2)
|
---|
47 |
|
---|
48 | my_method_wrapper_1 = method_decorator(my_wrapper_1)
|
---|
49 |
|
---|
50 | class my_bound_wrapper_2(object):
|
---|
51 | def __init__(self, wrapped):
|
---|
52 | self.wrapped = wrapped
|
---|
53 | self.__name__ = wrapped.__name__
|
---|
54 | def __call__(self, arg1, arg2):
|
---|
55 | print('my_bound_wrapper_2.__call__', arg1, arg2)
|
---|
56 | return self.wrapped(arg1, arg2)
|
---|
57 | def __get__(self, instance, owner):
|
---|
58 | print('my_bound_wrapper_2.__get__', instance, owner)
|
---|
59 | return self
|
---|
60 |
|
---|
61 | class my_desc_wrapper_2(object):
|
---|
62 | def __init__(self, wrapped):
|
---|
63 | self.wrapped = wrapped
|
---|
64 | self.__name__ = wrapped.__name__
|
---|
65 | def __get__(self, instance, owner):
|
---|
66 | print('my_desc_wrapper_2.__get__', instance, owner)
|
---|
67 | return my_bound_wrapper_2(self.wrapped.__get__(instance, owner))
|
---|
68 |
|
---|
69 | class Class(object):
|
---|
70 |
|
---|
71 | @my_method_wrapper_1
|
---|
72 | def method_1(self, arg1, arg2):
|
---|
73 | print('Class.method_1', self, arg1, arg2)
|
---|
74 | return arg1, arg2
|
---|
75 |
|
---|
76 | @my_desc_wrapper_2
|
---|
77 | def method_2(self, arg1, arg2):
|
---|
78 | print('Class.method_2', arg1, arg2)
|
---|
79 | return arg1, arg2
|
---|
80 |
|
---|
81 | @my_method_wrapper_1
|
---|
82 | @my_desc_wrapper_2
|
---|
83 | def method_3(self, arg1, arg2):
|
---|
84 | print('Class.method_3', arg1, arg2)
|
---|
85 | return arg1, arg2
|
---|
86 |
|
---|
87 | @my_method_wrapper_1
|
---|
88 | @my_method_wrapper_1
|
---|
89 | def method_4(self, arg1, arg2):
|
---|
90 | print('Class.method_4', self, arg1, arg2)
|
---|
91 | return arg1, arg2
|
---|
92 |
|
---|
93 | c = Class()
|
---|
94 |
|
---|
95 | c.method_1(1, 2)
|
---|
96 | c.method_2(1, 2)
|
---|
97 | c.method_3(1, 2)
|
---|
98 | c.method_4(1, 2)
|
---|