/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/cleanup.py

  • Committer: Andrew Bennetts
  • Date: 2009-10-15 02:19:43 UTC
  • mfrom: (4634.85.12 cleanup-hof)
  • mto: This revision was merged to the branch mainline in revision 4775.
  • Revision ID: andrew.bennetts@canonical.com-20091015021943-l8tuonz6q30tc4t6
Merge do_with_cleanups from cleanup-hof, and drop (or at least make private) everything else from that branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2009 Canonical Ltd
2
2
#
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,
32
32
then use::
33
33
 
34
 
    operation = OperationWithCleanups(do_something)
35
 
    operation.add_cleanup(cleanup_something)
36
 
    operation.run_simple()
 
34
    do_with_cleanups(do_something, cleanups)
37
35
 
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
41
 
details.
 
38
while also ensuring all cleanups are run.
42
39
"""
43
40
 
44
41
 
45
 
from collections import deque
46
42
import sys
47
43
from bzrlib import (
48
44
    debug,
72
68
    return True
73
69
 
74
70
 
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)
79
 
 
80
 
 
81
 
class OperationWithCleanups(object):
82
 
    """A way to run some code with a dynamic cleanup list.
83
 
 
84
 
    This provides a way to add cleanups while the function-with-cleanups is
85
 
    running.
86
 
 
87
 
    Typical use::
88
 
 
89
 
        operation = OperationWithCleanups(some_func)
90
 
        operation.run(args...)
91
 
 
92
 
    where `some_func` is::
93
 
 
94
 
        def some_func(operation, args, ...):
95
 
            do_something()
96
 
            operation.add_cleanup(something)
97
 
            # etc
98
 
 
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`.
 
71
def _run_cleanup_reporting_errors(func, *args, **kwargs):
 
72
    try:
 
73
        func(*args, **kwargs)
 
74
    except KeyboardInterrupt:
 
75
        raise
 
76
    except Exception, exc:
 
77
        trace.mutter('Cleanup failed:')
 
78
        trace.log_exception_quietly()
 
79
        trace.warning('Cleanup failed: %s', exc)
 
80
        return False
 
81
    return True
 
82
 
 
83
 
 
84
def _run_cleanups(funcs, on_error='log'):
 
85
    """Run a series of cleanup functions.
 
86
 
 
87
    :param errors: One of 'log', 'warn first', 'warn all'
102
88
    """
103
 
 
104
 
    def __init__(self, func):
105
 
        self.func = func
106
 
        self.cleanups = deque()
107
 
 
108
 
    def add_cleanup(self, cleanup_func, *args, **kwargs):
109
 
        """Add a cleanup to run.
110
 
 
111
 
        Cleanups may be added at any time before or during the execution of
112
 
        self.func.  Cleanups will be executed in LIFO order.
113
 
        """
114
 
        self.cleanups.appendleft((cleanup_func, args, kwargs))
115
 
 
116
 
    def run(self, *args, **kwargs):
117
 
        return _do_with_cleanups(
118
 
            self.cleanups, self.func, self, *args, **kwargs)
119
 
 
120
 
    def run_simple(self, *args, **kwargs):
121
 
        return _do_with_cleanups(
122
 
            self.cleanups, self.func, *args, **kwargs)
123
 
 
124
 
    def cleanup_now(self):
125
 
        _run_cleanups(self.cleanups)
126
 
        self.cleanups.clear()
127
 
 
128
 
 
129
 
def _do_with_cleanups(cleanup_funcs, func, *args, **kwargs):
 
89
    seen_error = False
 
90
    for func in funcs:
 
91
        if on_error == 'log' or (on_error == 'warn first' and seen_error):
 
92
            seen_error |= _run_cleanup(func)
 
93
        else:
 
94
            seen_error |= _run_cleanup_reporting_errors(func)
 
95
 
 
96
 
 
97
def do_with_cleanups(func, cleanup_funcs):
130
98
    """Run `func`, then call all the cleanup_funcs.
131
99
 
132
100
    All the cleanup_funcs are guaranteed to be run.  The first exception raised
136
104
    Conceptually similar to::
137
105
 
138
106
        try:
139
 
            return func(*args, **kwargs)
 
107
            return func()
140
108
        finally:
141
 
            for cleanup, cargs, ckwargs in cleanup_funcs:
142
 
                cleanup(*cargs, **ckwargs)
 
109
            for cleanup in cleanup_funcs:
 
110
                cleanup()
143
111
 
144
112
    It avoids several problems with using try/finally directly:
145
113
     * an exception from func will not be obscured by a subsequent exception
148
116
       running (but the first exception encountered is still the one
149
117
       propagated).
150
118
 
151
 
    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
152
120
    cleanup, but only if there is no exception from func.
153
121
    """
154
122
    # As correct as Python 2.4 allows.
155
123
    try:
156
 
        result = func(*args, **kwargs)
 
124
        result = func()
157
125
    except:
158
126
        # We have an exception from func already, so suppress cleanup errors.
159
127
        _run_cleanups(cleanup_funcs)
163
131
        # cleanup_funcs to propagate if one occurs (but only after running all
164
132
        # of them).
165
133
        exc_info = None
166
 
        for cleanup, c_args, c_kwargs in cleanup_funcs:
 
134
        for cleanup in cleanup_funcs:
167
135
            # XXX: Hmm, if KeyboardInterrupt arrives at exactly this line, we
168
136
            # won't run all cleanups... perhaps we should temporarily install a
169
137
            # SIGINT handler?
170
138
            if exc_info is None:
171
139
                try:
172
 
                    cleanup(*c_args, **c_kwargs)
 
140
                    cleanup()
173
141
                except:
174
142
                    # This is the first cleanup to fail, so remember its
175
143
                    # details.
177
145
            else:
178
146
                # We already have an exception to propagate, so log any errors
179
147
                # but don't propagate them.
180
 
                _run_cleanup(cleanup, *c_args, **kwargs)
 
148
                _run_cleanup(cleanup)
181
149
        if exc_info is not None:
182
150
            raise exc_info[0], exc_info[1], exc_info[2]
183
151
        # No error, so we can return the result