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)
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)
81
97
class OperationWithCleanups(object):
82
"""A way to run some code with a dynamic cleanup list.
98
"""A helper for using do_with_cleanups with a dynamic cleanup list.
84
100
This provides a way to add cleanups while the function-with-cleanups is
92
108
where `some_func` is::
94
def some_func(operation, args, ...):
110
def some_func(operation, args, ...)
96
112
operation.add_cleanup(something)
99
Note that the first argument passed to `some_func` will be the
100
OperationWithCleanups object. To invoke `some_func` without that, use
101
`run_simple` instead of `run`.
104
116
def __init__(self, func):
106
self.cleanups = deque()
108
def add_cleanup(self, cleanup_func, *args, **kwargs):
109
"""Add a cleanup to run.
111
Cleanups may be added at any time before or during the execution of
112
self.func. Cleanups will be executed in LIFO order.
114
self.cleanups.appendleft((cleanup_func, args, kwargs))
120
def add_cleanup(self, cleanup_func):
121
"""Add a cleanup to run. Cleanups will be executed in LIFO order."""
122
self.cleanups.insert(0, cleanup_func)
116
124
def run(self, *args, **kwargs):
117
return _do_with_cleanups(
118
self.cleanups, self.func, self, *args, **kwargs)
120
def run_simple(self, *args, **kwargs):
121
return _do_with_cleanups(
122
self.cleanups, self.func, *args, **kwargs)
124
def cleanup_now(self):
125
_run_cleanups(self.cleanups)
126
self.cleanups.clear()
129
def _do_with_cleanups(cleanup_funcs, func, *args, **kwargs):
125
func = lambda: self.func(self, *args, **kwargs)
126
return do_with_cleanups(func, self.cleanups)
129
def do_with_cleanups(func, cleanup_funcs):
130
130
"""Run `func`, then call all the cleanup_funcs.
132
132
All the cleanup_funcs are guaranteed to be run. The first exception raised
136
136
Conceptually similar to::
139
return func(*args, **kwargs)
141
for cleanup, cargs, ckwargs in cleanup_funcs:
142
cleanup(*cargs, **ckwargs)
141
for cleanup in cleanup_funcs:
144
144
It avoids several problems with using try/finally directly:
145
145
* an exception from func will not be obscured by a subsequent exception
148
148
running (but the first exception encountered is still the one
151
Unike `_run_cleanup`, `_do_with_cleanups` can propagate an exception from a
151
Unike `_run_cleanup`, `do_with_cleanups` can propagate an exception from a
152
152
cleanup, but only if there is no exception from func.
154
154
# As correct as Python 2.4 allows.
156
result = func(*args, **kwargs)
158
158
# We have an exception from func already, so suppress cleanup errors.
159
159
_run_cleanups(cleanup_funcs)
163
163
# cleanup_funcs to propagate if one occurs (but only after running all
166
for cleanup, c_args, c_kwargs in cleanup_funcs:
166
for cleanup in cleanup_funcs:
167
167
# XXX: Hmm, if KeyboardInterrupt arrives at exactly this line, we
168
168
# won't run all cleanups... perhaps we should temporarily install a
169
169
# SIGINT handler?
170
170
if exc_info is None:
172
cleanup(*c_args, **c_kwargs)
174
174
# This is the first cleanup to fail, so remember its
178
178
# We already have an exception to propagate, so log any errors
179
179
# but don't propagate them.
180
_run_cleanup(cleanup, *c_args, **kwargs)
180
_run_cleanup(cleanup)
181
181
if exc_info is not None:
182
182
raise exc_info[0], exc_info[1], exc_info[2]
183
183
# No error, so we can return the result