1
# Copyright (C) 2009 Canonical Ltd
1
# Copyright (C) 2009, 2010 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
31
31
If you want to be certain that the first, and only the first, error is raised,
34
operation = OperationWithCleanups(lambda operation: do_something())
34
operation = OperationWithCleanups(do_something)
35
35
operation.add_cleanup(cleanup_something)
36
operation.run_simple()
38
38
This is more inconvenient (because you need to make every try block a
39
39
function), but will ensure that the first error encountered is the one raised,
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`.
88
self.cleanups = deque()
90
def add_cleanup(self, cleanup_func, *args, **kwargs):
91
"""Add a cleanup to run.
93
Cleanups may be added at any time.
94
Cleanups will be executed in LIFO order.
96
self.cleanups.appendleft((cleanup_func, args, kwargs))
98
def cleanup_now(self):
99
_run_cleanups(self.cleanups)
100
self.cleanups.clear()
103
class OperationWithCleanups(ObjectWithCleanups):
82
104
"""A way to run some code with a dynamic cleanup list.
84
106
This provides a way to add cleanups while the function-with-cleanups is
92
114
where `some_func` is::
94
def some_func(operation, args, ...)
116
def some_func(operation, args, ...):
96
118
operation.add_cleanup(something)
99
121
Note that the first argument passed to `some_func` will be the
100
OperationWithCleanups object.
122
OperationWithCleanups object. To invoke `some_func` without that, use
123
`run_simple` instead of `run`.
103
126
def __init__(self, func):
127
super(OperationWithCleanups, self).__init__()
105
self.cleanups = deque()
107
def add_cleanup(self, cleanup_func, *args, **kwargs):
108
"""Add a cleanup to run.
110
Cleanups may be added at any time before or during the execution of
111
self.func. Cleanups will be executed in LIFO order.
113
self.cleanups.appendleft((cleanup_func, args, kwargs))
115
130
def run(self, *args, **kwargs):
116
131
return _do_with_cleanups(
117
132
self.cleanups, self.func, self, *args, **kwargs)
134
def run_simple(self, *args, **kwargs):
135
return _do_with_cleanups(
136
self.cleanups, self.func, *args, **kwargs)
120
139
def _do_with_cleanups(cleanup_funcs, func, *args, **kwargs):
121
140
"""Run `func`, then call all the cleanup_funcs.
170
189
# but don't propagate them.
171
190
_run_cleanup(cleanup, *c_args, **kwargs)
172
191
if exc_info is not None:
173
raise exc_info[0], exc_info[1], exc_info[2]
193
raise exc_info[0], exc_info[1], exc_info[2]
174
196
# No error, so we can return the result