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