/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4797.32.2 by John Arbash Meinel
merge 2.1, resolving NEWS conflict.
1
# Copyright (C) 2009, 2010 Canonical Ltd
4634.85.1 by Andrew Bennetts
Begin defining cleanup helpers and their tests.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
"""Helpers for managing cleanup functions and the errors they might raise.
18
7356.1.1 by Jelmer Vernooij
Use ExitStack context rather than brz-specific OperationWithCleanup.
19
This currently just contains a copy of contextlib.ExitStack, available
20
even on older versions of Python.
4634.85.1 by Andrew Bennetts
Begin defining cleanup helpers and their tests.
21
"""
22
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
23
from __future__ import absolute_import
4634.85.1 by Andrew Bennetts
Begin defining cleanup helpers and their tests.
24
4744.3.4 by Andrew Bennetts
Make OperationWithCleanups the only public API in bzrlib.cleanup, add test for it, add support for *args and **kwargs for func and for cleanups, use deque.appendleft rather than list.insert(0, ...).
25
from collections import deque
7356.1.1 by Jelmer Vernooij
Use ExitStack context rather than brz-specific OperationWithCleanup.
26
import sys
27
28
29
try:
30
    from contextlib import ExitStack
31
except ImportError:
32
    # Copied from the Python standard library on Python 3.4.
7356.1.6 by Jelmer Vernooij
Add copyright for contextlib code.
33
    # Copyright: Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
34
    #   2009, 2010, 2011 Python Software Foundation
35
    #
36
    # PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
37
    # --------------------------------------------
38
    # .
39
    # 1. This LICENSE AGREEMENT is between the Python Software Foundation
40
    # ("PSF"), and the Individual or Organization ("Licensee") accessing and
41
    # otherwise using this software ("Python") in source or binary form and
42
    # its associated documentation.
43
    # .
44
    # 2. Subject to the terms and conditions of this License Agreement, PSF hereby
45
    # grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
46
    # analyze, test, perform and/or display publicly, prepare derivative works,
47
    # distribute, and otherwise use Python alone or in any derivative version,
48
    # provided, however, that PSF's License Agreement and PSF's notice of copyright,
49
    # i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
50
    # 2011 Python Software Foundation; All Rights Reserved" are retained in Python
51
    # alone or in any derivative version prepared by Licensee.
52
    # .
53
    # 3. In the event Licensee prepares a derivative work that is based on
54
    # or incorporates Python or any part thereof, and wants to make
55
    # the derivative work available to others as provided herein, then
56
    # Licensee hereby agrees to include in any such work a brief summary of
57
    # the changes made to Python.
58
    # .
59
    # 4. PSF is making Python available to Licensee on an "AS IS"
60
    # basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
61
    # IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
62
    # DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
63
    # FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
64
    # INFRINGE ANY THIRD PARTY RIGHTS.
65
    # .
66
    # 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
67
    # FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
68
    # A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
69
    # OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
70
    # .
71
    # 6. This License Agreement will automatically terminate upon a material
72
    # breach of its terms and conditions.
73
    # .
74
    # 7. Nothing in this License Agreement shall be deemed to create any
75
    # relationship of agency, partnership, or joint venture between PSF and
76
    # Licensee.  This License Agreement does not grant permission to use PSF
77
    # trademarks or trade name in a trademark sense to endorse or promote
78
    # products or services of Licensee, or any third party.
79
    # .
80
    # 8. By copying, installing or otherwise using Python, Licensee
81
    # agrees to be bound by the terms and conditions of this License
82
    # Agreement.
7356.1.1 by Jelmer Vernooij
Use ExitStack context rather than brz-specific OperationWithCleanup.
83
84
    def _reraise_with_existing_context(exc_details):
85
        # Use 3 argument raise in Python 2,
86
        # but use exec to avoid SyntaxError in Python 3
87
        exc_type, exc_value, exc_tb = exc_details
7356.1.3 by Jelmer Vernooij
Fix tests.
88
        exec("raise exc_type, exc_value, exc_tb")
7356.1.1 by Jelmer Vernooij
Use ExitStack context rather than brz-specific OperationWithCleanup.
89
90
91
    # Inspired by discussions on http://bugs.python.org/issue13585
92
    class ExitStack(object):
93
        """Context manager for dynamic management of a stack of exit callbacks
94
95
        For example:
96
97
            with ExitStack() as stack:
98
                files = [stack.enter_context(open(fname)) for fname in filenames]
99
                # All opened files will automatically be closed at the end of
100
                # the with statement, even if attempts to open files later
101
                # in the list raise an exception
102
5158.8.1 by Martin Pool
Split out ObjectWithCleanups
103
        """
7356.1.1 by Jelmer Vernooij
Use ExitStack context rather than brz-specific OperationWithCleanup.
104
        def __init__(self):
105
            self._exit_callbacks = deque()
106
107
        def pop_all(self):
108
            """Preserve the context stack by transferring it to a new instance"""
109
            new_stack = type(self)()
110
            new_stack._exit_callbacks = self._exit_callbacks
111
            self._exit_callbacks = deque()
112
            return new_stack
113
114
        def _push_cm_exit(self, cm, cm_exit):
115
            """Helper to correctly register callbacks to __exit__ methods"""
116
            def _exit_wrapper(*exc_details):
117
                return cm_exit(cm, *exc_details)
118
            _exit_wrapper.__self__ = cm
119
            self.push(_exit_wrapper)
120
121
        def push(self, exit):
122
            """Registers a callback with the standard __exit__ method signature
123
124
            Can suppress exceptions the same way __exit__ methods can.
125
126
            Also accepts any object with an __exit__ method (registering a call
127
            to the method instead of the object itself)
128
            """
129
            # We use an unbound method rather than a bound method to follow
130
            # the standard lookup behaviour for special methods
131
            _cb_type = type(exit)
132
            try:
133
                exit_method = _cb_type.__exit__
134
            except AttributeError:
135
                # Not a context manager, so assume its a callable
136
                self._exit_callbacks.append(exit)
137
            else:
138
                self._push_cm_exit(exit, exit_method)
139
            return exit # Allow use as a decorator
140
141
        def callback(self, callback, *args, **kwds):
142
            """Registers an arbitrary callback and arguments.
143
144
            Cannot suppress exceptions.
145
            """
146
            def _exit_wrapper(exc_type, exc, tb):
147
                callback(*args, **kwds)
148
            # We changed the signature, so using @wraps is not appropriate, but
149
            # setting __wrapped__ may still help with introspection
150
            _exit_wrapper.__wrapped__ = callback
151
            self.push(_exit_wrapper)
152
            return callback # Allow use as a decorator
153
154
        def enter_context(self, cm):
155
            """Enters the supplied context manager
156
157
            If successful, also pushes its __exit__ method as a callback and
158
            returns the result of the __enter__ method.
159
            """
160
            # We look up the special methods on the type to match the with statement
161
            _cm_type = type(cm)
162
            _exit = _cm_type.__exit__
163
            result = _cm_type.__enter__(cm)
164
            self._push_cm_exit(cm, _exit)
165
            return result
166
167
        def close(self):
168
            """Immediately unwind the context stack"""
169
            self.__exit__(None, None, None)
170
171
        def __enter__(self):
172
            return self
173
174
        def __exit__(self, *exc_details):
175
            received_exc = exc_details[0] is not None
176
177
            # We manipulate the exception state so it behaves as though
178
            # we were actually nesting multiple with statements
179
            frame_exc = sys.exc_info()[1]
180
            def _make_context_fixer(frame_exc):
181
                return lambda new_exc, old_exc: None
182
            _fix_exception_context = _make_context_fixer(frame_exc)
183
184
            # Callbacks are invoked in LIFO order to match the behaviour of
185
            # nested context managers
186
            suppressed_exc = False
187
            pending_raise = False
188
            while self._exit_callbacks:
189
                cb = self._exit_callbacks.pop()
190
                try:
191
                    if cb(*exc_details):
192
                        suppressed_exc = True
193
                        pending_raise = False
194
                        exc_details = (None, None, None)
195
                except:
196
                    new_exc_details = sys.exc_info()
197
                    # simulate the stack of exceptions by setting the context
198
                    _fix_exception_context(new_exc_details[1], exc_details[1])
199
                    pending_raise = True
200
                    exc_details = new_exc_details
201
            if pending_raise:
202
                _reraise_with_existing_context(exc_details)
203
            return received_exc and suppressed_exc