44
from __future__ import absolute_import
45
46
from collections import deque
52
53
def _log_cleanup_error(exc):
53
54
trace.mutter('Cleanup failed:')
54
55
trace.log_exception_quietly()
55
56
if 'cleanup' in debug.debug_flags:
56
trace.warning('bzr: warning: Cleanup failed: %s', exc)
57
trace.warning('brz: warning: Cleanup failed: %s', exc)
59
60
def _run_cleanup(func, *args, **kwargs):
78
79
_run_cleanup(func, *args, **kwargs)
81
class OperationWithCleanups(object):
82
class ObjectWithCleanups(object):
83
"""A mixin for objects that hold a cleanup list.
85
Subclass or client code can call add_cleanup and then later `cleanup_now`.
89
self.cleanups = deque()
91
def add_cleanup(self, cleanup_func, *args, **kwargs):
92
"""Add a cleanup to run.
94
Cleanups may be added at any time.
95
Cleanups will be executed in LIFO order.
97
self.cleanups.appendleft((cleanup_func, args, kwargs))
99
def cleanup_now(self):
100
_run_cleanups(self.cleanups)
101
self.cleanups.clear()
104
class OperationWithCleanups(ObjectWithCleanups):
82
105
"""A way to run some code with a dynamic cleanup list.
84
107
This provides a way to add cleanups while the function-with-cleanups is
104
127
def __init__(self, func):
128
super(OperationWithCleanups, self).__init__()
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))
116
131
def run(self, *args, **kwargs):
117
132
return _do_with_cleanups(
121
136
return _do_with_cleanups(
122
137
self.cleanups, self.func, *args, **kwargs)
124
def cleanup_now(self):
125
_run_cleanups(self.cleanups)
126
self.cleanups.clear()
129
140
def _do_with_cleanups(cleanup_funcs, func, *args, **kwargs):
130
141
"""Run `func`, then call all the cleanup_funcs.
151
162
Unike `_run_cleanup`, `_do_with_cleanups` can propagate an exception from a
152
163
cleanup, but only if there is no exception from func.
154
# As correct as Python 2.4 allows.
156
166
result = func(*args, **kwargs)
167
except BaseException:
158
168
# We have an exception from func already, so suppress cleanup errors.
159
169
_run_cleanups(cleanup_funcs)
162
# No exception from func, so allow the first exception from
163
# cleanup_funcs to propagate if one occurs (but only after running all
166
for cleanup, c_args, c_kwargs in cleanup_funcs:
167
# XXX: Hmm, if KeyboardInterrupt arrives at exactly this line, we
168
# won't run all cleanups... perhaps we should temporarily install a
172
cleanup(*c_args, **c_kwargs)
174
# This is the first cleanup to fail, so remember its
176
exc_info = sys.exc_info()
178
# We already have an exception to propagate, so log any errors
179
# but don't propagate them.
180
_run_cleanup(cleanup, *c_args, **kwargs)
181
if exc_info is not None:
182
raise exc_info[0], exc_info[1], exc_info[2]
183
# No error, so we can return the result
171
# No exception from func, so allow first cleanup error to propgate.
172
pending_cleanups = iter(cleanup_funcs)
174
for cleanup, c_args, c_kwargs in pending_cleanups:
175
cleanup(*c_args, **c_kwargs)
176
except BaseException:
177
# Still run the remaining cleanups but suppress any further errors.
178
_run_cleanups(pending_cleanups)
180
# No error, so we can return the result