| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -225,32 +225,21 @@ def __enter__(self): | |||
| 225 | 225 | return self | |
| 226 | 226 | ||
| 227 | 227 | def __exit__(self, *exc_details): | |
| 228 | - if not self._exit_callbacks: | ||
| 229 | - return | ||
| 230 | - # This looks complicated, but it is really just | ||
| 231 | - # setting up a chain of try-expect statements to ensure | ||
| 232 | - # that outer callbacks still get invoked even if an | ||
| 233 | - # inner one throws an exception | ||
| 234 | - def _invoke_next_callback(exc_details): | ||
| 235 | - # Callbacks are removed from the list in FIFO order | ||
| 236 | - # but the recursion means they're invoked in LIFO order | ||
| 237 | - cb = self._exit_callbacks.popleft() | ||
| 238 | - if not self._exit_callbacks: | ||
| 239 | - # Innermost callback is invoked directly | ||
| 240 | - return cb(*exc_details) | ||
| 241 | - # More callbacks left, so descend another level in the stack | ||
| 228 | + # Callbacks are invoked in LIFO order to match the behaviour of | ||
| 229 | + # nested context managers | ||
| 230 | + suppressed_exc = False | ||
| 231 | + while self._exit_callbacks: | ||
| 232 | + cb = self._exit_callbacks.pop() | ||
| 242 | 233 | try: | |
| 243 | - suppress_exc = _invoke_next_callback(exc_details) | ||
| 234 | + if cb(*exc_details): | ||
| 235 | + suppressed_exc = True | ||
| 236 | + exc_details = (None, None, None) | ||
| 244 | 237 | except: | |
| 245 | - suppress_exc = cb(*sys.exc_info()) | ||
| 246 | - # Check if this cb suppressed the inner exception | ||
| 247 | - if not suppress_exc: | ||
| 238 | + new_exc_details = sys.exc_info() | ||
| 239 | + if exc_details != (None, None, None): | ||
| 240 | + # simulate the stack of exceptions by setting the context | ||
| 241 | + new_exc_details[1].__context__ = exc_details[1] | ||
| 242 | + if not self._exit_callbacks: | ||
| 248 | 243 | raise | |
| 249 | - else: | ||
| 250 | - # Check if inner cb suppressed the original exception | ||
| 251 | - if suppress_exc: | ||
| 252 | - exc_details = (None, None, None) | ||
| 253 | - suppress_exc = cb(*exc_details) or suppress_exc | ||
| 254 | - return suppress_exc | ||
| 255 | - # Kick off the recursive chain | ||
| 256 | - return _invoke_next_callback(exc_details) | ||
| 244 | + exc_details = new_exc_details | ||
| 245 | + return suppressed_exc | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -572,6 +572,12 @@ def test_exit_exception_chaining_suppress(self): | |||
| 572 | 572 | stack.push(lambda *exc: 1/0) | |
| 573 | 573 | stack.push(lambda *exc: {}[1]) | |
| 574 | 574 | ||
| 575 | + def test_excessive_nesting(self): | ||
| 576 | + # The original implementation would die with RecursionError here | ||
| 577 | + with ExitStack() as stack: | ||
| 578 | + for i in range(10000): | ||
| 579 | + stack.callback(int) | ||
| 580 | + | ||
| 575 | 581 | def test_instance_bypass(self): | |
| 576 | 582 | class Example(object): pass | |
| 577 | 583 | cm = Example() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,11 +7,17 @@ What's New in Python 3.3.0 Beta 1? | |||
| 7 | 7 | ||
| 8 | 8 | *Release date: TBD* | |
| 9 | 9 | ||
| 10 | + Library | ||
| 11 | + ------- | ||
| 12 | + | ||
| 13 | + - Issue #14963: Convert contextlib.ExitStack.__exit__ to use an iterative | ||
| 14 | + algorithm (Patch by Alon Horev) | ||
| 15 | + | ||
| 10 | 16 | Tests | |
| 11 | 17 | ----- | |
| 12 | 18 | ||
| 13 | 19 | - Issue #14963 (partial): Add test cases for exception handling behaviour | |
| 14 | - in contextlib.ContextStack (Initial patch by Alon Horev) | ||
| 20 | + in contextlib.ExitStack (Initial patch by Alon Horev) | ||
| 15 | 21 | ||
| 16 | 22 | What's New in Python 3.3.0 Alpha 4? | |
| 17 | 23 | =================================== | |
| Back | FazBrowse Home | New Git URL |
0 commit comments