~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/decorators.py

  • Committer: John Arbash Meinel
  • Author(s): Mark Hammond
  • Date: 2008-09-09 17:02:21 UTC
  • mto: This revision was merged to the branch mainline in revision 3697.
  • Revision ID: john@arbash-meinel.com-20080909170221-svim3jw2mrz0amp3
An updated transparent icon for bzr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2005 Canonical Ltd
2
2
#
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
12
12
#
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
16
16
 
17
 
from __future__ import absolute_import
18
17
 
19
18
__all__ = ['needs_read_lock',
20
19
           'needs_write_lock',
25
24
 
26
25
import sys
27
26
 
28
 
from bzrlib import trace
29
 
 
30
27
 
31
28
def _get_parameters(func):
32
29
    """Recreate the parameters for a function using introspection.
33
30
 
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.
44
39
 
45
40
        For example:
46
41
 
52
47
    # it globally.
53
48
    import inspect
54
49
    args, varargs, varkw, defaults = inspect.getargspec(func)
55
 
    defaults_dict = {}
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,
61
51
                                      varkw=varkw,
62
 
                                      defaults=defaults,
63
 
                                      formatvalue=formatvalue)
 
52
                                      defaults=defaults)
64
53
    if defaults is None:
65
54
        args_passed = args
66
55
    else:
74
63
        args_passed.append('**' + varkw)
75
64
    args_passed = ', '.join(args_passed)
76
65
 
77
 
    return formatted[1:-1], args_passed, defaults_dict
 
66
    return formatted[1:-1], args_passed
78
67
 
79
68
 
80
69
def _pretty_needs_read_lock(unbound):
82
71
 
83
72
    This decorator can be applied to methods of any class with lock_read() and
84
73
    unlock() methods.
85
 
 
 
74
    
86
75
    Typical usage:
87
 
 
 
76
        
88
77
    class Branch(...):
89
78
        @needs_read_lock
90
79
        def branch_method(self, ...):
110
99
        try:
111
100
            self.unlock()
112
101
        finally:
113
 
            try:
114
 
                raise exc_info[0], exc_info[1], exc_info[2]
115
 
            finally:
116
 
                del exc_info
 
102
            raise exc_info[0], exc_info[1], exc_info[2]
117
103
    else:
118
104
        self.unlock()
119
105
        return result
120
106
read_locked = %(name)s_read_locked
121
107
"""
122
 
    params, passed_params, defaults_dict = _get_parameters(unbound)
 
108
    params, passed_params = _get_parameters(unbound)
123
109
    variables = {'name':unbound.__name__,
124
110
                 'params':params,
125
111
                 'passed_params':passed_params,
126
112
                }
127
113
    func_def = template % variables
128
114
 
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()
133
116
 
134
117
    read_locked.__doc__ = unbound.__doc__
135
118
    read_locked.__name__ = unbound.__name__
141
124
 
142
125
    This decorator can be applied to methods of any class with lock_read() and
143
126
    unlock() methods.
144
 
 
 
127
    
145
128
    Typical usage:
146
 
 
 
129
        
147
130
    class Branch(...):
148
131
        @needs_read_lock
149
132
        def branch_method(self, ...):
159
142
            try:
160
143
                self.unlock()
161
144
            finally:
162
 
                try:
163
 
                    raise exc_info[0], exc_info[1], exc_info[2]
164
 
                finally:
165
 
                    del exc_info
 
145
                raise exc_info[0], exc_info[1], exc_info[2]
166
146
        else:
167
147
            self.unlock()
168
148
            return result
184
164
        try:
185
165
            self.unlock()
186
166
        finally:
187
 
            try:
188
 
                raise exc_info[0], exc_info[1], exc_info[2]
189
 
            finally:
190
 
                del exc_info
 
167
            raise exc_info[0], exc_info[1], exc_info[2]
191
168
    else:
192
169
        self.unlock()
193
170
        return result
194
171
write_locked = %(name)s_write_locked
195
172
"""
196
 
    params, passed_params, defaults_dict = _get_parameters(unbound)
 
173
    params, passed_params = _get_parameters(unbound)
197
174
    variables = {'name':unbound.__name__,
198
175
                 'params':params,
199
176
                 'passed_params':passed_params,
200
177
                }
201
178
    func_def = template % variables
202
179
 
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()
207
181
 
208
182
    write_locked.__doc__ = unbound.__doc__
209
183
    write_locked.__name__ = unbound.__name__
221
195
            try:
222
196
                self.unlock()
223
197
            finally:
224
 
                try:
225
 
                    raise exc_info[0], exc_info[1], exc_info[2]
226
 
                finally:
227
 
                    del exc_info
 
198
                raise exc_info[0], exc_info[1], exc_info[2]
228
199
        else:
229
200
            self.unlock()
230
201
            return result
233
204
    return write_locked
234
205
 
235
206
 
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.
239
 
 
240
 
    Typical use is something like::
241
 
 
242
 
        @only_raises(LockNotHeld, LockBroken)
243
 
        def unlock(self):
244
 
            # etc
245
 
    """
246
 
    def decorator(unbound):
247
 
        def wrapped(*args, **kwargs):
248
 
            try:
249
 
                return unbound(*args, **kwargs)
250
 
            except errors:
251
 
                raise
252
 
            except:
253
 
                trace.mutter('Error suppressed by only_raises:')
254
 
                trace.log_exception_quietly()
255
 
        wrapped.__doc__ = unbound.__doc__
256
 
        wrapped.__name__ = unbound.__name__
257
 
        return wrapped
258
 
    return decorator
259
 
 
260
 
 
261
207
# Default is more functionality, 'bzr' the commandline will request fast
262
208
# versions.
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
283
 
 
284
 
 
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
290
 
    value cached.
291
 
 
292
 
    The value is cached on the instance, using the attribute name provided.
293
 
 
294
 
    If you don't provide a name, the mangled name of the property is used.
295
 
 
296
 
    >>> class CachedPropertyTest(object):
297
 
    ...
298
 
    ...     @cachedproperty('_foo_cache')
299
 
    ...     def foo(self):
300
 
    ...         print 'foo computed'
301
 
    ...         return 23
302
 
    ...
303
 
    ...     @cachedproperty
304
 
    ...     def bar(self):
305
 
    ...         print 'bar computed'
306
 
    ...         return 69
307
 
 
308
 
    >>> cpt = CachedPropertyTest()
309
 
    >>> getattr(cpt, '_foo_cache', None) is None
310
 
    True
311
 
    >>> cpt.foo
312
 
    foo computed
313
 
    23
314
 
    >>> cpt.foo
315
 
    23
316
 
    >>> cpt._foo_cache
317
 
    23
318
 
    >>> cpt.bar
319
 
    bar computed
320
 
    69
321
 
    >>> cpt._bar_cached_value
322
 
    69
323
 
 
324
 
    """
325
 
    if isinstance(attrname_or_fn, basestring):
326
 
        attrname = attrname_or_fn
327
 
        return _CachedPropertyForAttr(attrname)
328
 
    else:
329
 
        fn = attrname_or_fn
330
 
        attrname = '_%s_cached_value' % fn.__name__
331
 
        return _CachedProperty(attrname, fn)
332
 
 
333
 
 
334
 
class _CachedPropertyForAttr(object):
335
 
 
336
 
    def __init__(self, attrname):
337
 
        self.attrname = attrname
338
 
 
339
 
    def __call__(self, fn):
340
 
        return _CachedProperty(self.attrname, fn)
341
 
 
342
 
 
343
 
class _CachedProperty(object):
344
 
 
345
 
    def __init__(self, attrname, fn):
346
 
        self.fn = fn
347
 
        self.attrname = attrname
348
 
        self.marker = object()
349
 
 
350
 
    def __get__(self, inst, cls=None):
351
 
        if inst is None:
352
 
            return self
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)
357
 
            return result
358
 
        else:
359
 
            return cachedresult