31
31
If you want to be certain that the first, and only the first, error is raised,
34
operation = OperationWithCleanups(do_something)
35
operation.add_cleanup(cleanup_something)
36
operation.run_simple()
34
do_with_cleanups(do_something, cleanups)
38
36
This is more inconvenient (because you need to make every try block a
39
37
function), but will ensure that the first error encountered is the one raised,
40
while also ensuring all cleanups are run. See OperationWithCleanups for more
38
while also ensuring all cleanups are run.
45
from collections import deque
47
43
from bzrlib import (
75
def _run_cleanups(funcs):
76
"""Run a series of cleanup functions."""
77
for func, args, kwargs in funcs:
78
_run_cleanup(func, *args, **kwargs)
81
class ObjectWithCleanups(object):
82
"""A mixin for objects that hold a cleanup list.
84
Subclass or client code can call add_cleanup and then later `cleanup_now`.
87
self.cleanups = deque()
89
def add_cleanup(self, cleanup_func, *args, **kwargs):
90
"""Add a cleanup to run.
92
Cleanups may be added at any time.
93
Cleanups will be executed in LIFO order.
95
self.cleanups.appendleft((cleanup_func, args, kwargs))
97
def cleanup_now(self):
98
_run_cleanups(self.cleanups)
102
class OperationWithCleanups(ObjectWithCleanups):
103
"""A way to run some code with a dynamic cleanup list.
105
This provides a way to add cleanups while the function-with-cleanups is
110
operation = OperationWithCleanups(some_func)
111
operation.run(args...)
113
where `some_func` is::
115
def some_func(operation, args, ...):
117
operation.add_cleanup(something)
120
Note that the first argument passed to `some_func` will be the
121
OperationWithCleanups object. To invoke `some_func` without that, use
122
`run_simple` instead of `run`.
125
def __init__(self, func):
126
super(OperationWithCleanups, self).__init__()
129
def run(self, *args, **kwargs):
130
return _do_with_cleanups(
131
self.cleanups, self.func, self, *args, **kwargs)
133
def run_simple(self, *args, **kwargs):
134
return _do_with_cleanups(
135
self.cleanups, self.func, *args, **kwargs)
138
def _do_with_cleanups(cleanup_funcs, func, *args, **kwargs):
71
def _run_cleanup_reporting_errors(func, *args, **kwargs):
74
except KeyboardInterrupt:
76
except Exception, exc:
77
trace.mutter('Cleanup failed:')
78
trace.log_exception_quietly()
79
trace.warning('Cleanup failed: %s', exc)
84
def _run_cleanups(funcs, on_error='log'):
85
"""Run a series of cleanup functions.
87
:param errors: One of 'log', 'warn first', 'warn all'
91
if on_error == 'log' or (on_error == 'warn first' and seen_error):
92
seen_error |= _run_cleanup(func)
94
seen_error |= _run_cleanup_reporting_errors(func)
97
def do_with_cleanups(func, cleanup_funcs):
139
98
"""Run `func`, then call all the cleanup_funcs.
141
100
All the cleanup_funcs are guaranteed to be run. The first exception raised
145
104
Conceptually similar to::
148
return func(*args, **kwargs)
150
for cleanup, cargs, ckwargs in cleanup_funcs:
151
cleanup(*cargs, **ckwargs)
109
for cleanup in cleanup_funcs:
153
112
It avoids several problems with using try/finally directly:
154
113
* an exception from func will not be obscured by a subsequent exception
157
116
running (but the first exception encountered is still the one
160
Unike `_run_cleanup`, `_do_with_cleanups` can propagate an exception from a
119
Unike `run_cleanup`, `do_with_cleanups` can propagate an exception from a
161
120
cleanup, but only if there is no exception from func.
163
122
# As correct as Python 2.4 allows.
165
result = func(*args, **kwargs)
167
126
# We have an exception from func already, so suppress cleanup errors.
168
127
_run_cleanups(cleanup_funcs)
172
131
# cleanup_funcs to propagate if one occurs (but only after running all
175
for cleanup, c_args, c_kwargs in cleanup_funcs:
134
for cleanup in cleanup_funcs:
176
135
# XXX: Hmm, if KeyboardInterrupt arrives at exactly this line, we
177
136
# won't run all cleanups... perhaps we should temporarily install a
178
137
# SIGINT handler?
179
138
if exc_info is None:
181
cleanup(*c_args, **c_kwargs)
183
142
# This is the first cleanup to fail, so remember its
187
146
# We already have an exception to propagate, so log any errors
188
147
# but don't propagate them.
189
_run_cleanup(cleanup, *c_args, **kwargs)
148
_run_cleanup(cleanup)
190
149
if exc_info is not None:
192
raise exc_info[0], exc_info[1], exc_info[2]
150
raise exc_info[0], exc_info[1], exc_info[2]
195
151
# No error, so we can return the result