17
17
from __future__ import absolute_import
19
__all__ = ['needs_read_lock',
21
'use_fast_decorators',
22
'use_pretty_decorators',
26
19
from . import trace
29
def _get_parameters(func):
30
"""Recreate the parameters for a function using introspection.
32
:return: (function_params, calling_params, default_values)
33
function_params: is a string representing the parameters of the
34
function. (such as "a, b, c=None, d=1")
35
This is used in the function declaration.
36
calling_params: is another string representing how you would call the
37
function with the correct parameters. (such as "a, b, c=c, d=d")
38
Assuming you used function_params in the function declaration, this
39
is the parameters to put in the function call.
40
default_values_block: a dict with the default values to be passed as
41
the scope for the 'exec' statement.
45
def wrapper(%(function_params)s):
46
return original(%(calling_params)s)
48
# "import inspect" should stay in local scope. 'inspect' takes a long time
49
# to import the first time. And since we don't always need it, don't import
52
args, varargs, varkw, defaults = inspect.getargspec(func)
54
def formatvalue(value):
55
default_name = '__default_%d' % len(defaults_dict)
56
defaults_dict[default_name] = value
57
return '=' + default_name
58
formatted = inspect.formatargspec(args, varargs=varargs,
61
formatvalue=formatvalue)
65
first_default = len(args) - len(defaults)
66
args_passed = args[:first_default]
67
for arg in args[first_default:]:
68
args_passed.append("%s=%s" % (arg, arg))
69
if varargs is not None:
70
args_passed.append('*' + varargs)
72
args_passed.append('**' + varkw)
73
args_passed = ', '.join(args_passed)
75
return formatted[1:-1], args_passed, defaults_dict
78
def _pretty_needs_read_lock(unbound):
79
"""Decorate unbound to take out and release a read lock.
81
This decorator can be applied to methods of any class with lock_read() and
88
def branch_method(self, ...):
91
# This compiles a function with a similar name, but wrapped with
92
# lock_read/unlock calls. We use dynamic creation, because we need the
93
# internal name of the function to be modified so that --lsprof will see
95
# TODO: jam 20070111 Modify this template so that the generated function
96
# has the same argument signature as the original function, which
97
# will help commands like epydoc.
98
# This seems possible by introspecting foo.func_defaults, and
99
# foo.func_code.co_argcount and foo.func_code.co_varnames
101
def %(name)s_read_locked(%(params)s):
104
result = unbound(%(passed_params)s)
113
read_locked = %(name)s_read_locked
115
params, passed_params, defaults_dict = _get_parameters(unbound)
116
variables = {'name':unbound.__name__,
118
'passed_params':passed_params,
120
func_def = template % variables
122
scope = dict(defaults_dict)
123
scope['unbound'] = unbound
124
exec(func_def, scope)
125
read_locked = scope['read_locked']
127
read_locked.__doc__ = unbound.__doc__
128
read_locked.__name__ = unbound.__name__
132
def _fast_needs_read_lock(unbound):
133
"""Decorate unbound to take out and release a read lock.
135
This decorator can be applied to methods of any class with lock_read() and
142
def branch_method(self, ...):
145
def read_locked(self, *args, **kwargs):
148
result = unbound(self, *args, **kwargs)
157
read_locked.__doc__ = unbound.__doc__
158
read_locked.__name__ = unbound.__name__
162
def _pretty_needs_write_lock(unbound):
163
"""Decorate unbound to take out and release a write lock."""
165
def %(name)s_write_locked(%(params)s):
168
result = unbound(%(passed_params)s)
177
write_locked = %(name)s_write_locked
179
params, passed_params, defaults_dict = _get_parameters(unbound)
180
variables = {'name':unbound.__name__,
182
'passed_params':passed_params,
184
func_def = template % variables
186
scope = dict(defaults_dict)
187
scope['unbound'] = unbound
188
exec(func_def, scope)
189
write_locked = scope['write_locked']
191
write_locked.__doc__ = unbound.__doc__
192
write_locked.__name__ = unbound.__name__
196
def _fast_needs_write_lock(unbound):
197
"""Decorate unbound to take out and release a write lock."""
198
def write_locked(self, *args, **kwargs):
201
result = unbound(self, *args, **kwargs)
210
write_locked.__doc__ = unbound.__doc__
211
write_locked.__name__ = unbound.__name__
215
22
def only_raises(*errors):
216
23
"""Make a decorator that will only allow the given error classes to be
217
24
raised. All other errors will be logged and then discarded.
240
# Default is more functionality, 'bzr' the commandline will request fast
242
needs_read_lock = _pretty_needs_read_lock
243
needs_write_lock = _pretty_needs_write_lock
246
def use_fast_decorators():
247
"""Change the default decorators to be fast loading ones.
249
The alternative is to have decorators that do more work to produce
250
nice-looking decorated functions, but this slows startup time.
252
global needs_read_lock, needs_write_lock
253
needs_read_lock = _fast_needs_read_lock
254
needs_write_lock = _fast_needs_write_lock
257
def use_pretty_decorators():
258
"""Change the default decorators to be pretty ones."""
259
global needs_read_lock, needs_write_lock
260
needs_read_lock = _pretty_needs_read_lock
261
needs_write_lock = _pretty_needs_write_lock
264
47
# This implementation of cachedproperty is copied from Launchpad's
265
48
# canonical.launchpad.cachedproperty module (with permission from flacoste)
266
49
# -- spiv & vila 100120