/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 breezy/cleanup.py

  • Committer: Jelmer Vernooij
  • Date: 2020-02-07 02:14:30 UTC
  • mto: This revision was merged to the branch mainline in revision 7492.
  • Revision ID: jelmer@jelmer.uk-20200207021430-m49iq3x4x8xlib6x
Drop python2 support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009, 2010 Canonical Ltd
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
 
 
19
 
This currently just contains a copy of contextlib.ExitStack, available
20
 
even on older versions of Python.
21
 
"""
22
 
 
23
 
from __future__ import absolute_import
24
 
 
25
 
from collections import deque
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.
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.
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
88
 
        exec("raise exc_type, exc_value, exc_tb")
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
 
 
103
 
        """
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