~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.cleanup import run_cleanup
28
from bzrlib import trace
29
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.
30
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
31
def _get_parameters(func):
32
    """Recreate the parameters for a function using introspection.
33
2230.2.6 by John Arbash Meinel
Clean up the documentation and imports for decorators (per Martin's suggestions)
34
    :return: (function_params, calling_params)
35
        function_params: is a string representing the parameters of the
36
            function. (such as "a, b, c=None, d=1")
37
            This is used in the function declaration.
38
        calling_params: is another string representing how you would call the
39
            function with the correct parameters. (such as "a, b, c=c, d=d")
40
            Assuming you sued function_params in the function declaration, this
41
            is the parameters to put in the function call.
42
43
        For example:
44
45
        def wrapper(%(function_params)s):
46
            return original(%(calling_params)s)
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
47
    """
2230.2.6 by John Arbash Meinel
Clean up the documentation and imports for decorators (per Martin's suggestions)
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
50
    # it globally.
51
    import inspect
52
    args, varargs, varkw, defaults = inspect.getargspec(func)
53
    formatted = inspect.formatargspec(args, varargs=varargs,
54
                                      varkw=varkw,
55
                                      defaults=defaults)
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
56
    if defaults is None:
57
        args_passed = args
58
    else:
59
        first_default = len(args) - len(defaults)
60
        args_passed = args[:first_default]
61
        for arg in args[first_default:]:
62
            args_passed.append("%s=%s" % (arg, arg))
63
    if varargs is not None:
64
        args_passed.append('*' + varargs)
65
    if varkw is not None:
66
        args_passed.append('**' + varkw)
67
    args_passed = ', '.join(args_passed)
68
69
    return formatted[1:-1], args_passed
70
71
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.
72
def _pretty_needs_read_lock(unbound):
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
73
    """Decorate unbound to take out and release a read lock.
74
75
    This decorator can be applied to methods of any class with lock_read() and
76
    unlock() methods.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
77
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
78
    Typical usage:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
79
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
80
    class Branch(...):
81
        @needs_read_lock
82
        def branch_method(self, ...):
83
            stuff
84
    """
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
85
    # This compiles a function with a similar name, but wrapped with
86
    # lock_read/unlock calls. We use dynamic creation, because we need the
87
    # internal name of the function to be modified so that --lsprof will see
88
    # the correct name.
89
    # TODO: jam 20070111 Modify this template so that the generated function
90
    #       has the same argument signature as the original function, which
91
    #       will help commands like epydoc.
92
    #       This seems possible by introspecting foo.func_defaults, and
93
    #       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
94
    template = """\
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
95
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
96
    self.lock_read()
97
    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.
98
        result = unbound(%(passed_params)s)
99
    except:
100
        import sys
101
        exc_info = sys.exc_info()
102
        try:
103
            self.unlock()
104
        finally:
105
            raise exc_info[0], exc_info[1], exc_info[2]
106
    else:
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
107
        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.
108
        return result
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
109
read_locked = %(name)s_read_locked
110
"""
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
111
    params, passed_params = _get_parameters(unbound)
112
    variables = {'name':unbound.__name__,
113
                 'params':params,
114
                 'passed_params':passed_params,
115
                }
116
    func_def = template % variables
117
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
118
    exec func_def in locals()
119
1534.4.48 by Robert Collins
Make needs_read_lock and needs_write_lock more visible in tracebacks
120
    read_locked.__doc__ = unbound.__doc__
121
    read_locked.__name__ = unbound.__name__
122
    return read_locked
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
123
124
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.
125
def _fast_needs_read_lock(unbound):
126
    """Decorate unbound to take out and release a read lock.
127
128
    This decorator can be applied to methods of any class with lock_read() and
129
    unlock() methods.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
130
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.
131
    Typical usage:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
132
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.
133
    class Branch(...):
134
        @needs_read_lock
135
        def branch_method(self, ...):
136
            stuff
137
    """
138
    def read_locked(self, *args, **kwargs):
139
        self.lock_read()
140
        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.
141
            result = unbound(self, *args, **kwargs)
142
        except:
143
            import sys
144
            exc_info = sys.exc_info()
145
            try:
146
                self.unlock()
147
            finally:
148
                raise exc_info[0], exc_info[1], exc_info[2]
149
        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.
150
            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.
151
            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.
152
    read_locked.__doc__ = unbound.__doc__
153
    read_locked.__name__ = unbound.__name__
154
    return read_locked
155
156
157
def _pretty_needs_write_lock(unbound):
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
158
    """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
159
    template = """\
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
160
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
161
    self.lock_write()
162
    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.
163
        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.
164
    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.
165
        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.
166
        exc_info = sys.exc_info()
167
        try:
168
            self.unlock()
169
        finally:
170
            raise exc_info[0], exc_info[1], exc_info[2]
171
    else:
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
172
        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.
173
        return result
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
174
write_locked = %(name)s_write_locked
175
"""
2230.2.2 by John Arbash Meinel
Change decorators to define the same parameters as the wrapped func.
176
    params, passed_params = _get_parameters(unbound)
177
    variables = {'name':unbound.__name__,
178
                 'params':params,
179
                 'passed_params':passed_params,
180
                }
181
    func_def = template % variables
182
2230.2.1 by John Arbash Meinel
Change the read_lock and write_lock decorators to use custom names
183
    exec func_def in locals()
184
1534.4.48 by Robert Collins
Make needs_read_lock and needs_write_lock more visible in tracebacks
185
    write_locked.__doc__ = unbound.__doc__
186
    write_locked.__name__ = unbound.__name__
187
    return write_locked
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
188
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.
189
190
def _fast_needs_write_lock(unbound):
191
    """Decorate unbound to take out and release a write lock."""
192
    def write_locked(self, *args, **kwargs):
193
        self.lock_write()
194
        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.
195
            result = unbound(self, *args, **kwargs)
196
        except:
197
            exc_info = sys.exc_info()
198
            try:
199
                self.unlock()
200
            finally:
201
                raise exc_info[0], exc_info[1], exc_info[2]
202
        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.
203
            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.
204
            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.
205
    write_locked.__doc__ = unbound.__doc__
206
    write_locked.__name__ = unbound.__name__
207
    return write_locked
208
209
4634.85.9 by Andrew Bennetts
Add some experimental decorators: @only_raises(..) and @cleanup_method.
210
def cleanup_method(unbound):
211
    """Decorate unbound...    """
212
    def cleanup_wrapper(*args, **kwargs):
213
        run_cleanup(unbound, *args, **kwargs)
214
    cleanup_wrapper.__doc__ = unbound.__doc__
215
    cleanup_wrapper.__name__ = unbound.__name__
216
    return cleanup_wrapper
217
218
219
def only_raises(*errors):
4634.62.2 by Andrew Bennetts
Update test_decorators, add docstring.
220
    """Make a decorator that will only allow the given error classes to be
221
    raised.  All other errors will be logged and then discarded.
222
223
    Typical use is something like::
224
225
        @only_raises(LockNotHeld, LockBroken)
226
        def unlock(self):
227
            # etc
228
    """
4634.85.9 by Andrew Bennetts
Add some experimental decorators: @only_raises(..) and @cleanup_method.
229
    def decorator(unbound):
230
        def wrapped(*args, **kwargs):
231
            try:
232
                return unbound(*args, **kwargs)
233
            except errors:
234
                raise
235
            except:
236
                trace.mutter('Error suppressed by only_raises:')
237
                trace.log_exception_quietly()
238
        wrapped.__doc__ = unbound.__doc__
239
        wrapped.__name__ = unbound.__name__
240
        return wrapped
241
    return decorator
242
243
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.
244
# Default is more functionality, 'bzr' the commandline will request fast
245
# versions.
246
needs_read_lock = _pretty_needs_read_lock
247
needs_write_lock = _pretty_needs_write_lock
248
249
250
def use_fast_decorators():
251
    """Change the default decorators to be fast loading ones.
252
253
    The alternative is to have decorators that do more work to produce
254
    nice-looking decorated functions, but this slows startup time.
255
    """
256
    global needs_read_lock, needs_write_lock
257
    needs_read_lock = _fast_needs_read_lock
258
    needs_write_lock = _fast_needs_write_lock
259
260
261
def use_pretty_decorators():
262
    """Change the default decorators to be pretty ones."""
263
    global needs_read_lock, needs_write_lock
264
    needs_read_lock = _pretty_needs_read_lock
265
    needs_write_lock = _pretty_needs_write_lock