~bzr-pqm/bzr/bzr.dev

1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
1
# Copyright (C) 2005 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
16
17
1185.65.27 by Robert Collins
Tweak storage towards mergability.
18
__all__ = ['needs_read_lock',
19
           'needs_write_lock',
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
20
           'use_fast_decorators',
21
           'use_pretty_decorators',
1185.65.27 by Robert Collins
Tweak storage towards mergability.
22
           ]
23
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
24
3316.3.1 by Andrew Bennetts
Try not to lose the original exception in needs_write_lock decorator if the unlock raises a secondary exception.
25
import sys
26
4634.85.9 by Andrew Bennetts
Add some experimental decorators: @only_raises(..) and @cleanup_method.
27
from bzrlib import trace
28
3316.3.1 by Andrew Bennetts
Try not to lose the original exception in needs_write_lock decorator if the unlock raises a secondary exception.
29
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
30
def _get_parameters(func):
31
    """Recreate the parameters for a function using introspection.
32
2230.2.6 by John Arbash Meinel
Clean up the documentation and imports for decorators (per Martin's suggestions)
33
    :return: (function_params, calling_params)
34
        function_params: is a string representing the parameters of the
35
            function. (such as "a, b, c=None, d=1")
36
            This is used in the function declaration.
37
        calling_params: is another string representing how you would call the
38
            function with the correct parameters. (such as "a, b, c=c, d=d")
39
            Assuming you sued function_params in the function declaration, this
40
            is the parameters to put in the function call.
41
42
        For example:
43
44
        def wrapper(%(function_params)s):
45
            return original(%(calling_params)s)
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
46
    """
2230.2.6 by John Arbash Meinel
Clean up the documentation and imports for decorators (per Martin's suggestions)
47
    # "import inspect" should stay in local scope. 'inspect' takes a long time
48
    # to import the first time. And since we don't always need it, don't import
49
    # it globally.
50
    import inspect
51
    args, varargs, varkw, defaults = inspect.getargspec(func)
52
    formatted = inspect.formatargspec(args, varargs=varargs,
53
                                      varkw=varkw,
54
                                      defaults=defaults)
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
55
    if defaults is None:
56
        args_passed = args
57
    else:
58
        first_default = len(args) - len(defaults)
59
        args_passed = args[:first_default]
60
        for arg in args[first_default:]:
61
            args_passed.append("%s=%s" % (arg, arg))
62
    if varargs is not None:
63
        args_passed.append('*' + varargs)
64
    if varkw is not None:
65
        args_passed.append('**' + varkw)
66
    args_passed = ', '.join(args_passed)
67
68
    return formatted[1:-1], args_passed
69
70
2230.2.3 by John Arbash Meinel
Add the ability to have fast decorators as well as pretty ones, and have 'bzr' select the right one at startup.
71
def _pretty_needs_read_lock(unbound):
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
72
    """Decorate unbound to take out and release a read lock.
73
74
    This decorator can be applied to methods of any class with lock_read() and
75
    unlock() methods.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
76
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
77
    Typical usage:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
78
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
79
    class Branch(...):
80
        @needs_read_lock
81
        def branch_method(self, ...):
82
            stuff
83
    """
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
84
    # This compiles a function with a similar name, but wrapped with
85
    # lock_read/unlock calls. We use dynamic creation, because we need the
86
    # internal name of the function to be modified so that --lsprof will see
87
    # the correct name.
88
    # TODO: jam 20070111 Modify this template so that the generated function
89
    #       has the same argument signature as the original function, which
90
    #       will help commands like epydoc.
91
    #       This seems possible by introspecting foo.func_defaults, and
92
    #       foo.func_code.co_argcount and foo.func_code.co_varnames
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
93
    template = """\
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
94
def %(name)s_read_locked(%(params)s):
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
95
    self.lock_read()
96
    try:
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
97
        result = unbound(%(passed_params)s)
98
    except:
99
        import sys
100
        exc_info = sys.exc_info()
101
        try:
102
            self.unlock()
103
        finally:
104
            raise exc_info[0], exc_info[1], exc_info[2]
105
    else:
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
106
        self.unlock()
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
107
        return result
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
108
read_locked = %(name)s_read_locked
109
"""
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
110
    params, passed_params = _get_parameters(unbound)
111
    variables = {'name':unbound.__name__,
112
                 'params':params,
113
                 'passed_params':passed_params,
114
                }
115
    func_def = template % variables
116
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
117
    exec func_def in locals()
118
1534.4.48 by Robert Collins
Make needs_read_lock and needs_write_lock more visible in tracebacks
119
    read_locked.__doc__ = unbound.__doc__
120
    read_locked.__name__ = unbound.__name__
121
    return read_locked
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
122
123
2230.2.3 by John Arbash Meinel
Add the ability to have fast decorators as well as pretty ones, and have 'bzr' select the right one at startup.
124
def _fast_needs_read_lock(unbound):
125
    """Decorate unbound to take out and release a read lock.
126
127
    This decorator can be applied to methods of any class with lock_read() and
128
    unlock() methods.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
129
2230.2.3 by John Arbash Meinel
Add the ability to have fast decorators as well as pretty ones, and have 'bzr' select the right one at startup.
130
    Typical usage:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
131
2230.2.3 by John Arbash Meinel
Add the ability to have fast decorators as well as pretty ones, and have 'bzr' select the right one at startup.
132
    class Branch(...):
133
        @needs_read_lock
134
        def branch_method(self, ...):
135
            stuff
136
    """
137
    def read_locked(self, *args, **kwargs):
138
        self.lock_read()
139
        try:
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
140
            result = unbound(self, *args, **kwargs)
141
        except:
142
            import sys
143
            exc_info = sys.exc_info()
144
            try:
145
                self.unlock()
146
            finally:
147
                raise exc_info[0], exc_info[1], exc_info[2]
148
        else:
2230.2.3 by John Arbash Meinel
Add the ability to have fast decorators as well as pretty ones, and have 'bzr' select the right one at startup.
149
            self.unlock()
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
150
            return result
2230.2.3 by John Arbash Meinel
Add the ability to have fast decorators as well as pretty ones, and have 'bzr' select the right one at startup.
151
    read_locked.__doc__ = unbound.__doc__
152
    read_locked.__name__ = unbound.__name__
153
    return read_locked
154
155
156
def _pretty_needs_write_lock(unbound):
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
157
    """Decorate unbound to take out and release a write lock."""
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
158
    template = """\
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
159
def %(name)s_write_locked(%(params)s):
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
160
    self.lock_write()
161
    try:
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
162
        result = unbound(%(passed_params)s)
3316.3.1 by Andrew Bennetts
Try not to lose the original exception in needs_write_lock decorator if the unlock raises a secondary exception.
163
    except:
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
164
        import sys
3316.3.1 by Andrew Bennetts
Try not to lose the original exception in needs_write_lock decorator if the unlock raises a secondary exception.
165
        exc_info = sys.exc_info()
166
        try:
167
            self.unlock()
168
        finally:
169
            raise exc_info[0], exc_info[1], exc_info[2]
170
    else:
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
171
        self.unlock()
3316.3.1 by Andrew Bennetts
Try not to lose the original exception in needs_write_lock decorator if the unlock raises a secondary exception.
172
        return result
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
173
write_locked = %(name)s_write_locked
174
"""
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
175
    params, passed_params = _get_parameters(unbound)
176
    variables = {'name':unbound.__name__,
177
                 'params':params,
178
                 'passed_params':passed_params,
179
                }
180
    func_def = template % variables
181
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
182
    exec func_def in locals()
183
1534.4.48 by Robert Collins
Make needs_read_lock and needs_write_lock more visible in tracebacks
184
    write_locked.__doc__ = unbound.__doc__
185
    write_locked.__name__ = unbound.__name__
186
    return write_locked
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
187
2230.2.3 by John Arbash Meinel
Add the ability to have fast decorators as well as pretty ones, and have 'bzr' select the right one at startup.
188
189
def _fast_needs_write_lock(unbound):
190
    """Decorate unbound to take out and release a write lock."""
191
    def write_locked(self, *args, **kwargs):
192
        self.lock_write()
193
        try:
3316.3.1 by Andrew Bennetts
Try not to lose the original exception in needs_write_lock decorator if the unlock raises a secondary exception.
194
            result = unbound(self, *args, **kwargs)
195
        except:
196
            exc_info = sys.exc_info()
197
            try:
198
                self.unlock()
199
            finally:
200
                raise exc_info[0], exc_info[1], exc_info[2]
201
        else:
2230.2.3 by John Arbash Meinel
Add the ability to have fast decorators as well as pretty ones, and have 'bzr' select the right one at startup.
202
            self.unlock()
3316.3.1 by Andrew Bennetts
Try not to lose the original exception in needs_write_lock decorator if the unlock raises a secondary exception.
203
            return result
2230.2.3 by John Arbash Meinel
Add the ability to have fast decorators as well as pretty ones, and have 'bzr' select the right one at startup.
204
    write_locked.__doc__ = unbound.__doc__
205
    write_locked.__name__ = unbound.__name__
206
    return write_locked
207
208
4634.85.9 by Andrew Bennetts
Add some experimental decorators: @only_raises(..) and @cleanup_method.
209
def only_raises(*errors):
4634.62.2 by Andrew Bennetts
Update test_decorators, add docstring.
210
    """Make a decorator that will only allow the given error classes to be
211
    raised.  All other errors will be logged and then discarded.
212
213
    Typical use is something like::
214
215
        @only_raises(LockNotHeld, LockBroken)
216
        def unlock(self):
217
            # etc
218
    """
4634.85.9 by Andrew Bennetts
Add some experimental decorators: @only_raises(..) and @cleanup_method.
219
    def decorator(unbound):
220
        def wrapped(*args, **kwargs):
221
            try:
222
                return unbound(*args, **kwargs)
223
            except errors:
224
                raise
225
            except:
226
                trace.mutter('Error suppressed by only_raises:')
227
                trace.log_exception_quietly()
228
        wrapped.__doc__ = unbound.__doc__
229
        wrapped.__name__ = unbound.__name__
230
        return wrapped
231
    return decorator
232
233
2230.2.3 by John Arbash Meinel
Add the ability to have fast decorators as well as pretty ones, and have 'bzr' select the right one at startup.
234
# Default is more functionality, 'bzr' the commandline will request fast
235
# versions.
236
needs_read_lock = _pretty_needs_read_lock
237
needs_write_lock = _pretty_needs_write_lock
238
239
240
def use_fast_decorators():
241
    """Change the default decorators to be fast loading ones.
242
243
    The alternative is to have decorators that do more work to produce
244
    nice-looking decorated functions, but this slows startup time.
245
    """
246
    global needs_read_lock, needs_write_lock
247
    needs_read_lock = _fast_needs_read_lock
248
    needs_write_lock = _fast_needs_write_lock
249
250
251
def use_pretty_decorators():
252
    """Change the default decorators to be pretty ones."""
253
    global needs_read_lock, needs_write_lock
254
    needs_read_lock = _pretty_needs_read_lock
255
    needs_write_lock = _pretty_needs_write_lock
4869.3.13 by Andrew Bennetts
Add simple cachedproperty decorator, and add {this,other,base}_lines cachedproperties to MergeHookParams.
256
257
4869.3.32 by Andrew Bennetts
Use Launchpad's cachedproperty decorator instead of my stupidly broken one.
258
# This implementation of cachedproperty is copied from Launchpad's
4869.3.34 by Vincent Ladeuil
Finish the patch based on reviews.
259
# canonical.launchpad.cachedproperty module (with permission from flacoste)
260
# -- spiv & vila 100120
4869.3.32 by Andrew Bennetts
Use Launchpad's cachedproperty decorator instead of my stupidly broken one.
261
def cachedproperty(attrname_or_fn):
262
    """A decorator for methods that makes them properties with their return
263
    value cached.
264
265
    The value is cached on the instance, using the attribute name provided.
266
267
    If you don't provide a name, the mangled name of the property is used.
268
269
    >>> class CachedPropertyTest(object):
270
    ...
271
    ...     @cachedproperty('_foo_cache')
272
    ...     def foo(self):
273
    ...         print 'foo computed'
274
    ...         return 23
275
    ...
276
    ...     @cachedproperty
277
    ...     def bar(self):
278
    ...         print 'bar computed'
279
    ...         return 69
280
281
    >>> cpt = CachedPropertyTest()
282
    >>> getattr(cpt, '_foo_cache', None) is None
283
    True
284
    >>> cpt.foo
285
    foo computed
286
    23
287
    >>> cpt.foo
288
    23
289
    >>> cpt._foo_cache
290
    23
291
    >>> cpt.bar
292
    bar computed
293
    69
294
    >>> cpt._bar_cached_value
295
    69
296
4869.3.13 by Andrew Bennetts
Add simple cachedproperty decorator, and add {this,other,base}_lines cachedproperties to MergeHookParams.
297
    """
4869.3.32 by Andrew Bennetts
Use Launchpad's cachedproperty decorator instead of my stupidly broken one.
298
    if isinstance(attrname_or_fn, basestring):
299
        attrname = attrname_or_fn
300
        return _CachedPropertyForAttr(attrname)
301
    else:
302
        fn = attrname_or_fn
303
        attrname = '_%s_cached_value' % fn.__name__
304
        return _CachedProperty(attrname, fn)
305
306
307
class _CachedPropertyForAttr(object):
308
309
    def __init__(self, attrname):
310
        self.attrname = attrname
311
312
    def __call__(self, fn):
313
        return _CachedProperty(self.attrname, fn)
314
315
316
class _CachedProperty(object):
317
318
    def __init__(self, attrname, fn):
319
        self.fn = fn
320
        self.attrname = attrname
321
        self.marker = object()
322
323
    def __get__(self, inst, cls=None):
324
        if inst is None:
325
            return self
326
        cachedresult = getattr(inst, self.attrname, self.marker)
327
        if cachedresult is self.marker:
328
            result = self.fn(inst)
329
            setattr(inst, self.attrname, result)
330
            return result
331
        else:
332
            return cachedresult