1
# Copyright (C) 2006-2010 Canonical Ltd
1
# Copyright (C) 2005 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from __future__ import absolute_import
19
18
__all__ = ['needs_read_lock',
20
19
'needs_write_lock',
28
from bzrlib import trace
31
28
def _get_parameters(func):
32
29
"""Recreate the parameters for a function using introspection.
34
:return: (function_params, calling_params, default_values)
31
:return: (function_params, calling_params)
35
32
function_params: is a string representing the parameters of the
36
33
function. (such as "a, b, c=None, d=1")
37
34
This is used in the function declaration.
38
35
calling_params: is another string representing how you would call the
39
36
function with the correct parameters. (such as "a, b, c=c, d=d")
40
Assuming you used function_params in the function declaration, this
37
Assuming you sued function_params in the function declaration, this
41
38
is the parameters to put in the function call.
42
default_values_block: a dict with the default values to be passed as
43
the scope for the 'exec' statement.
54
49
args, varargs, varkw, defaults = inspect.getargspec(func)
56
def formatvalue(value):
57
default_name = '__default_%d' % len(defaults_dict)
58
defaults_dict[default_name] = value
59
return '=' + default_name
60
50
formatted = inspect.formatargspec(args, varargs=varargs,
63
formatvalue=formatvalue)
64
53
if defaults is None:
74
63
args_passed.append('**' + varkw)
75
64
args_passed = ', '.join(args_passed)
77
return formatted[1:-1], args_passed, defaults_dict
66
return formatted[1:-1], args_passed
80
69
def _pretty_needs_read_lock(unbound):
114
raise exc_info[0], exc_info[1], exc_info[2]
102
raise exc_info[0], exc_info[1], exc_info[2]
120
106
read_locked = %(name)s_read_locked
122
params, passed_params, defaults_dict = _get_parameters(unbound)
108
params, passed_params = _get_parameters(unbound)
123
109
variables = {'name':unbound.__name__,
125
111
'passed_params':passed_params,
127
113
func_def = template % variables
129
scope = dict(defaults_dict)
130
scope['unbound'] = unbound
131
exec func_def in scope
132
read_locked = scope['read_locked']
115
exec func_def in locals()
134
117
read_locked.__doc__ = unbound.__doc__
135
118
read_locked.__name__ = unbound.__name__
188
raise exc_info[0], exc_info[1], exc_info[2]
167
raise exc_info[0], exc_info[1], exc_info[2]
194
171
write_locked = %(name)s_write_locked
196
params, passed_params, defaults_dict = _get_parameters(unbound)
173
params, passed_params = _get_parameters(unbound)
197
174
variables = {'name':unbound.__name__,
199
176
'passed_params':passed_params,
201
178
func_def = template % variables
203
scope = dict(defaults_dict)
204
scope['unbound'] = unbound
205
exec func_def in scope
206
write_locked = scope['write_locked']
180
exec func_def in locals()
208
182
write_locked.__doc__ = unbound.__doc__
209
183
write_locked.__name__ = unbound.__name__
233
204
return write_locked
236
def only_raises(*errors):
237
"""Make a decorator that will only allow the given error classes to be
238
raised. All other errors will be logged and then discarded.
240
Typical use is something like::
242
@only_raises(LockNotHeld, LockBroken)
246
def decorator(unbound):
247
def wrapped(*args, **kwargs):
249
return unbound(*args, **kwargs)
253
trace.mutter('Error suppressed by only_raises:')
254
trace.log_exception_quietly()
255
wrapped.__doc__ = unbound.__doc__
256
wrapped.__name__ = unbound.__name__
261
207
# Default is more functionality, 'bzr' the commandline will request fast
263
209
needs_read_lock = _pretty_needs_read_lock
280
226
global needs_read_lock, needs_write_lock
281
227
needs_read_lock = _pretty_needs_read_lock
282
228
needs_write_lock = _pretty_needs_write_lock
285
# This implementation of cachedproperty is copied from Launchpad's
286
# canonical.launchpad.cachedproperty module (with permission from flacoste)
287
# -- spiv & vila 100120
288
def cachedproperty(attrname_or_fn):
289
"""A decorator for methods that makes them properties with their return
292
The value is cached on the instance, using the attribute name provided.
294
If you don't provide a name, the mangled name of the property is used.
296
>>> class CachedPropertyTest(object):
298
... @cachedproperty('_foo_cache')
300
... print 'foo computed'
305
... print 'bar computed'
308
>>> cpt = CachedPropertyTest()
309
>>> getattr(cpt, '_foo_cache', None) is None
321
>>> cpt._bar_cached_value
325
if isinstance(attrname_or_fn, basestring):
326
attrname = attrname_or_fn
327
return _CachedPropertyForAttr(attrname)
330
attrname = '_%s_cached_value' % fn.__name__
331
return _CachedProperty(attrname, fn)
334
class _CachedPropertyForAttr(object):
336
def __init__(self, attrname):
337
self.attrname = attrname
339
def __call__(self, fn):
340
return _CachedProperty(self.attrname, fn)
343
class _CachedProperty(object):
345
def __init__(self, attrname, fn):
347
self.attrname = attrname
348
self.marker = object()
350
def __get__(self, inst, cls=None):
353
cachedresult = getattr(inst, self.attrname, self.marker)
354
if cachedresult is self.marker:
355
result = self.fn(inst)
356
setattr(inst, self.attrname, result)