1 :mod:`contextlib` --- Utilities for :keyword:`with`\ -statement contexts 2 ======================================================================== 3 4 .. module:: contextlib 5 :synopsis: Utilities for with-statement contexts. 6 7 **Source code:** :source:`Lib/contextlib.py` 8 9 -------------- 10 11 This module provides utilities for common tasks involving the :keyword:`with` 12 statement. For more information see also :ref:`typecontextmanager` and 13 :ref:`context-managers`. 14 15 16 Utilities 17 --------- 18 19 Functions and classes provided: 20 21 .. class:: AbstractContextManager 22 23 An :term:`abstract base class` for classes that implement 24 :meth:`object.__enter__` and :meth:`object.__exit__`. A default 25 implementation for :meth:`object.__enter__` is provided which returns 26 ``self`` while :meth:`object.__exit__` is an abstract method which by default 27 returns ``None``. See also the definition of :ref:`typecontextmanager`. 28 29 .. versionadded:: 3.6 30 31 32 33 .. decorator:: contextmanager 34 35 This function is a :term:`decorator` that can be used to define a factory 36 function for :keyword:`with` statement context managers, without needing to 37 create a class or separate :meth:`__enter__` and :meth:`__exit__` methods. 38 39 A simple example (this is not recommended as a real way of generating HTML!):: 40 41 from contextlib import contextmanager 42 43 @contextmanager 44 def tag(name): 45 print("<%s>" % name) 46 yield 47 print("</%s>" % name) 48 49 >>> with tag("h1"): 50 ... print("foo") 51 ... 52 <h1> 53 foo 54 </h1> 55 56 The function being decorated must return a :term:`generator`-iterator when 57 called. This iterator must yield exactly one value, which will be bound to 58 the targets in the :keyword:`with` statement's :keyword:`as` clause, if any. 59 60 At the point where the generator yields, the block nested in the :keyword:`with` 61 statement is executed. The generator is then resumed after the block is exited. 62 If an unhandled exception occurs in the block, it is reraised inside the 63 generator at the point where the yield occurred. Thus, you can use a 64 :keyword:`try`...\ :keyword:`except`...\ :keyword:`finally` statement to trap 65 the error (if any), or ensure that some cleanup takes place. If an exception is 66 trapped merely in order to log it or to perform some action (rather than to 67 suppress it entirely), the generator must reraise that exception. Otherwise the 68 generator context manager will indicate to the :keyword:`with` statement that 69 the exception has been handled, and execution will resume with the statement 70 immediately following the :keyword:`with` statement. 71 72 :func:`contextmanager` uses :class:`ContextDecorator` so the context managers 73 it creates can be used as decorators as well as in :keyword:`with` statements. 74 When used as a decorator, a new generator instance is implicitly created on 75 each function call (this allows the otherwise "one-shot" context managers 76 created by :func:`contextmanager` to meet the requirement that context 77 managers support multiple invocations in order to be used as decorators). 78 79 .. versionchanged:: 3.2 80 Use of :class:`ContextDecorator`. 81 82 83 .. function:: closing(thing) 84 85 Return a context manager that closes *thing* upon completion of the block. This 86 is basically equivalent to:: 87 88 from contextlib import contextmanager 89 90 @contextmanager 91 def closing(thing): 92 try: 93 yield thing 94 finally: 95 thing.close() 96 97 And lets you write code like this:: 98 99 from contextlib import closing 100 from urllib.request import urlopen 101 102 with closing(urlopen('http://www.python.org')) as page: 103 for line in page: 104 print(line) 105 106 without needing to explicitly close ``page``. Even if an error occurs, 107 ``page.close()`` will be called when the :keyword:`with` block is exited. 108 109 110 .. function:: suppress(*exceptions) 111 112 Return a context manager that suppresses any of the specified exceptions 113 if they occur in the body of a with statement and then resumes execution 114 with the first statement following the end of the with statement. 115 116 As with any other mechanism that completely suppresses exceptions, this 117 context manager should be used only to cover very specific errors where 118 silently continuing with program execution is known to be the right 119 thing to do. 120 121 For example:: 122 123 from contextlib import suppress 124 125 with suppress(FileNotFoundError): 126 os.remove('somefile.tmp') 127 128 with suppress(FileNotFoundError): 129 os.remove('someotherfile.tmp') 130 131 This code is equivalent to:: 132 133 try: 134 os.remove('somefile.tmp') 135 except FileNotFoundError: 136 pass 137 138 try: 139 os.remove('someotherfile.tmp') 140 except FileNotFoundError: 141 pass 142 143 This context manager is :ref:`reentrant <reentrant-cms>`. 144 145 .. versionadded:: 3.4 146 147 148 .. function:: redirect_stdout(new_target) 149 150 Context manager for temporarily redirecting :data:`sys.stdout` to 151 another file or file-like object. 152 153 This tool adds flexibility to existing functions or classes whose output 154 is hardwired to stdout. 155 156 For example, the output of :func:`help` normally is sent to *sys.stdout*. 157 You can capture that output in a string by redirecting the output to an 158 :class:`io.StringIO` object:: 159 160 f = io.StringIO() 161 with redirect_stdout(f): 162 help(pow) 163 s = f.getvalue() 164 165 To send the output of :func:`help` to a file on disk, redirect the output 166 to a regular file:: 167 168 with open('help.txt', 'w') as f: 169 with redirect_stdout(f): 170 help(pow) 171 172 To send the output of :func:`help` to *sys.stderr*:: 173 174 with redirect_stdout(sys.stderr): 175 help(pow) 176 177 Note that the global side effect on :data:`sys.stdout` means that this 178 context manager is not suitable for use in library code and most threaded 179 applications. It also has no effect on the output of subprocesses. 180 However, it is still a useful approach for many utility scripts. 181 182 This context manager is :ref:`reentrant <reentrant-cms>`. 183 184 .. versionadded:: 3.4 185 186 187 .. function:: redirect_stderr(new_target) 188 189 Similar to :func:`~contextlib.redirect_stdout` but redirecting 190 :data:`sys.stderr` to another file or file-like object. 191 192 This context manager is :ref:`reentrant <reentrant-cms>`. 193 194 .. versionadded:: 3.5 195 196 197 .. class:: ContextDecorator() 198 199 A base class that enables a context manager to also be used as a decorator. 200 201 Context managers inheriting from ``ContextDecorator`` have to implement 202 ``__enter__`` and ``__exit__`` as normal. ``__exit__`` retains its optional 203 exception handling even when used as a decorator. 204 205 ``ContextDecorator`` is used by :func:`contextmanager`, so you get this 206 functionality automatically. 207 208 Example of ``ContextDecorator``:: 209 210 from contextlib import ContextDecorator 211 212 class mycontext(ContextDecorator): 213 def __enter__(self): 214 print('Starting') 215 return self 216 217 def __exit__(self, *exc): 218 print('Finishing') 219 return False 220 221 >>> @mycontext() 222 ... def function(): 223 ... print('The bit in the middle') 224 ... 225 >>> function() 226 Starting 227 The bit in the middle 228 Finishing 229 230 >>> with mycontext(): 231 ... print('The bit in the middle') 232 ... 233 Starting 234 The bit in the middle 235 Finishing 236 237 This change is just syntactic sugar for any construct of the following form:: 238 239 def f(): 240 with cm(): 241 # Do stuff 242 243 ``ContextDecorator`` lets you instead write:: 244 245 @cm() 246 def f(): 247 # Do stuff 248 249 It makes it clear that the ``cm`` applies to the whole function, rather than 250 just a piece of it (and saving an indentation level is nice, too). 251 252 Existing context managers that already have a base class can be extended by 253 using ``ContextDecorator`` as a mixin class:: 254 255 from contextlib import ContextDecorator 256 257 class mycontext(ContextBaseClass, ContextDecorator): 258 def __enter__(self): 259 return self 260 261 def __exit__(self, *exc): 262 return False 263 264 .. note:: 265 As the decorated function must be able to be called multiple times, the 266 underlying context manager must support use in multiple :keyword:`with` 267 statements. If this is not the case, then the original construct with the 268 explicit :keyword:`with` statement inside the function should be used. 269 270 .. versionadded:: 3.2 271 272 273 .. class:: ExitStack() 274 275 A context manager that is designed to make it easy to programmatically 276 combine other context managers and cleanup functions, especially those 277 that are optional or otherwise driven by input data. 278 279 For example, a set of files may easily be handled in a single with 280 statement as follows:: 281 282 with ExitStack() as stack: 283 files = [stack.enter_context(open(fname)) for fname in filenames] 284 # All opened files will automatically be closed at the end of 285 # the with statement, even if attempts to open files later 286 # in the list raise an exception 287 288 Each instance maintains a stack of registered callbacks that are called in 289 reverse order when the instance is closed (either explicitly or implicitly 290 at the end of a :keyword:`with` statement). Note that callbacks are *not* 291 invoked implicitly when the context stack instance is garbage collected. 292 293 This stack model is used so that context managers that acquire their 294 resources in their ``__init__`` method (such as file objects) can be 295 handled correctly. 296 297 Since registered callbacks are invoked in the reverse order of 298 registration, this ends up behaving as if multiple nested :keyword:`with` 299 statements had been used with the registered set of callbacks. This even 300 extends to exception handling - if an inner callback suppresses or replaces 301 an exception, then outer callbacks will be passed arguments based on that 302 updated state. 303 304 This is a relatively low level API that takes care of the details of 305 correctly unwinding the stack of exit callbacks. It provides a suitable 306 foundation for higher level context managers that manipulate the exit 307 stack in application specific ways. 308 309 .. versionadded:: 3.3 310 311 .. method:: enter_context(cm) 312 313 Enters a new context manager and adds its :meth:`__exit__` method to 314 the callback stack. The return value is the result of the context 315 manager's own :meth:`__enter__` method. 316 317 These context managers may suppress exceptions just as they normally 318 would if used directly as part of a :keyword:`with` statement. 319 320 .. method:: push(exit) 321 322 Adds a context manager's :meth:`__exit__` method to the callback stack. 323 324 As ``__enter__`` is *not* invoked, this method can be used to cover 325 part of an :meth:`__enter__` implementation with a context manager's own 326 :meth:`__exit__` method. 327 328 If passed an object that is not a context manager, this method assumes 329 it is a callback with the same signature as a context manager's 330 :meth:`__exit__` method and adds it directly to the callback stack. 331 332 By returning true values, these callbacks can suppress exceptions the 333 same way context manager :meth:`__exit__` methods can. 334 335 The passed in object is returned from the function, allowing this 336 method to be used as a function decorator. 337 338 .. method:: callback(callback, *args, **kwds) 339 340 Accepts an arbitrary callback function and arguments and adds it to 341 the callback stack. 342 343 Unlike the other methods, callbacks added this way cannot suppress 344 exceptions (as they are never passed the exception details). 345 346 The passed in callback is returned from the function, allowing this 347 method to be used as a function decorator. 348 349 .. method:: pop_all() 350 351 Transfers the callback stack to a fresh :class:`ExitStack` instance 352 and returns it. No callbacks are invoked by this operation - instead, 353 they will now be invoked when the new stack is closed (either 354 explicitly or implicitly at the end of a :keyword:`with` statement). 355 356 For example, a group of files can be opened as an "all or nothing" 357 operation as follows:: 358 359 with ExitStack() as stack: 360 files = [stack.enter_context(open(fname)) for fname in filenames] 361 # Hold onto the close method, but don't call it yet. 362 close_files = stack.pop_all().close 363 # If opening any file fails, all previously opened files will be 364 # closed automatically. If all files are opened successfully, 365 # they will remain open even after the with statement ends. 366 # close_files() can then be invoked explicitly to close them all. 367 368 .. method:: close() 369 370 Immediately unwinds the callback stack, invoking callbacks in the 371 reverse order of registration. For any context managers and exit 372 callbacks registered, the arguments passed in will indicate that no 373 exception occurred. 374 375 376 Examples and Recipes 377 -------------------- 378 379 This section describes some examples and recipes for making effective use of 380 the tools provided by :mod:`contextlib`. 381 382 383 Supporting a variable number of context managers 384 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 385 386 The primary use case for :class:`ExitStack` is the one given in the class 387 documentation: supporting a variable number of context managers and other 388 cleanup operations in a single :keyword:`with` statement. The variability 389 may come from the number of context managers needed being driven by user 390 input (such as opening a user specified collection of files), or from 391 some of the context managers being optional:: 392 393 with ExitStack() as stack: 394 for resource in resources: 395 stack.enter_context(resource) 396 if need_special_resource(): 397 special = acquire_special_resource() 398 stack.callback(release_special_resource, special) 399 # Perform operations that use the acquired resources 400 401 As shown, :class:`ExitStack` also makes it quite easy to use :keyword:`with` 402 statements to manage arbitrary resources that don't natively support the 403 context management protocol. 404 405 406 Simplifying support for single optional context managers 407 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 408 409 In the specific case of a single optional context manager, :class:`ExitStack` 410 instances can be used as a "do nothing" context manager, allowing a context 411 manager to easily be omitted without affecting the overall structure of 412 the source code:: 413 414 def debug_trace(details): 415 if __debug__: 416 return TraceContext(details) 417 # Don't do anything special with the context in release mode 418 return ExitStack() 419 420 with debug_trace(): 421 # Suite is traced in debug mode, but runs normally otherwise 422 423 424 Catching exceptions from ``__enter__`` methods 425 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 426 427 It is occasionally desirable to catch exceptions from an ``__enter__`` 428 method implementation, *without* inadvertently catching exceptions from 429 the :keyword:`with` statement body or the context manager's ``__exit__`` 430 method. By using :class:`ExitStack` the steps in the context management 431 protocol can be separated slightly in order to allow this:: 432 433 stack = ExitStack() 434 try: 435 x = stack.enter_context(cm) 436 except Exception: 437 # handle __enter__ exception 438 else: 439 with stack: 440 # Handle normal case 441 442 Actually needing to do this is likely to indicate that the underlying API 443 should be providing a direct resource management interface for use with 444 :keyword:`try`/:keyword:`except`/:keyword:`finally` statements, but not 445 all APIs are well designed in that regard. When a context manager is the 446 only resource management API provided, then :class:`ExitStack` can make it 447 easier to handle various situations that can't be handled directly in a 448 :keyword:`with` statement. 449 450 451 Cleaning up in an ``__enter__`` implementation 452 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 453 454 As noted in the documentation of :meth:`ExitStack.push`, this 455 method can be useful in cleaning up an already allocated resource if later 456 steps in the :meth:`__enter__` implementation fail. 457 458 Here's an example of doing this for a context manager that accepts resource 459 acquisition and release functions, along with an optional validation function, 460 and maps them to the context management protocol:: 461 462 from contextlib import contextmanager, AbstractContextManager, ExitStack 463 464 class ResourceManager(AbstractContextManager): 465 466 def __init__(self, acquire_resource, release_resource, check_resource_ok=None): 467 self.acquire_resource = acquire_resource 468 self.release_resource = release_resource 469 if check_resource_ok is None: 470 def check_resource_ok(resource): 471 return True 472 self.check_resource_ok = check_resource_ok 473 474 @contextmanager 475 def _cleanup_on_error(self): 476 with ExitStack() as stack: 477 stack.push(self) 478 yield 479 # The validation check passed and didn't raise an exception 480 # Accordingly, we want to keep the resource, and pass it 481 # back to our caller 482 stack.pop_all() 483 484 def __enter__(self): 485 resource = self.acquire_resource() 486 with self._cleanup_on_error(): 487 if not self.check_resource_ok(resource): 488 msg = "Failed validation for {!r}" 489 raise RuntimeError(msg.format(resource)) 490 return resource 491 492 def __exit__(self, *exc_details): 493 # We don't need to duplicate any of our resource release logic 494 self.release_resource() 495 496 497 Replacing any use of ``try-finally`` and flag variables 498 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 499 500 A pattern you will sometimes see is a ``try-finally`` statement with a flag 501 variable to indicate whether or not the body of the ``finally`` clause should 502 be executed. In its simplest form (that can't already be handled just by 503 using an ``except`` clause instead), it looks something like this:: 504 505 cleanup_needed = True 506 try: 507 result = perform_operation() 508 if result: 509 cleanup_needed = False 510 finally: 511 if cleanup_needed: 512 cleanup_resources() 513 514 As with any ``try`` statement based code, this can cause problems for 515 development and review, because the setup code and the cleanup code can end 516 up being separated by arbitrarily long sections of code. 517 518 :class:`ExitStack` makes it possible to instead register a callback for 519 execution at the end of a ``with`` statement, and then later decide to skip 520 executing that callback:: 521 522 from contextlib import ExitStack 523 524 with ExitStack() as stack: 525 stack.callback(cleanup_resources) 526 result = perform_operation() 527 if result: 528 stack.pop_all() 529 530 This allows the intended cleanup up behaviour to be made explicit up front, 531 rather than requiring a separate flag variable. 532 533 If a particular application uses this pattern a lot, it can be simplified 534 even further by means of a small helper class:: 535 536 from contextlib import ExitStack 537 538 class Callback(ExitStack): 539 def __init__(self, callback, *args, **kwds): 540 super(Callback, self).__init__() 541 self.callback(callback, *args, **kwds) 542 543 def cancel(self): 544 self.pop_all() 545 546 with Callback(cleanup_resources) as cb: 547 result = perform_operation() 548 if result: 549 cb.cancel() 550 551 If the resource cleanup isn't already neatly bundled into a standalone 552 function, then it is still possible to use the decorator form of 553 :meth:`ExitStack.callback` to declare the resource cleanup in 554 advance:: 555 556 from contextlib import ExitStack 557 558 with ExitStack() as stack: 559 @stack.callback 560 def cleanup_resources(): 561 ... 562 result = perform_operation() 563 if result: 564 stack.pop_all() 565 566 Due to the way the decorator protocol works, a callback function 567 declared this way cannot take any parameters. Instead, any resources to 568 be released must be accessed as closure variables. 569 570 571 Using a context manager as a function decorator 572 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 573 574 :class:`ContextDecorator` makes it possible to use a context manager in 575 both an ordinary ``with`` statement and also as a function decorator. 576 577 For example, it is sometimes useful to wrap functions or groups of statements 578 with a logger that can track the time of entry and time of exit. Rather than 579 writing both a function decorator and a context manager for the task, 580 inheriting from :class:`ContextDecorator` provides both capabilities in a 581 single definition:: 582 583 from contextlib import ContextDecorator 584 import logging 585 586 logging.basicConfig(level=logging.INFO) 587 588 class track_entry_and_exit(ContextDecorator): 589 def __init__(self, name): 590 self.name = name 591 592 def __enter__(self): 593 logging.info('Entering: %s', self.name) 594 595 def __exit__(self, exc_type, exc, exc_tb): 596 logging.info('Exiting: %s', self.name) 597 598 Instances of this class can be used as both a context manager:: 599 600 with track_entry_and_exit('widget loader'): 601 print('Some time consuming activity goes here') 602 load_widget() 603 604 And also as a function decorator:: 605 606 @track_entry_and_exit('widget loader') 607 def activity(): 608 print('Some time consuming activity goes here') 609 load_widget() 610 611 Note that there is one additional limitation when using context managers 612 as function decorators: there's no way to access the return value of 613 :meth:`__enter__`. If that value is needed, then it is still necessary to use 614 an explicit ``with`` statement. 615 616 .. seealso:: 617 618 :pep:`343` - The "with" statement 619 The specification, background, and examples for the Python :keyword:`with` 620 statement. 621 622 .. _single-use-reusable-and-reentrant-cms: 623 624 Single use, reusable and reentrant context managers 625 --------------------------------------------------- 626 627 Most context managers are written in a way that means they can only be 628 used effectively in a :keyword:`with` statement once. These single use 629 context managers must be created afresh each time they're used - 630 attempting to use them a second time will trigger an exception or 631 otherwise not work correctly. 632 633 This common limitation means that it is generally advisable to create 634 context managers directly in the header of the :keyword:`with` statement 635 where they are used (as shown in all of the usage examples above). 636 637 Files are an example of effectively single use context managers, since 638 the first :keyword:`with` statement will close the file, preventing any 639 further IO operations using that file object. 640 641 Context managers created using :func:`contextmanager` are also single use 642 context managers, and will complain about the underlying generator failing 643 to yield if an attempt is made to use them a second time:: 644 645 >>> from contextlib import contextmanager 646 >>> @contextmanager 647 ... def singleuse(): 648 ... print("Before") 649 ... yield 650 ... print("After") 651 ... 652 >>> cm = singleuse() 653 >>> with cm: 654 ... pass 655 ... 656 Before 657 After 658 >>> with cm: 659 ... pass 660 ... 661 Traceback (most recent call last): 662 ... 663 RuntimeError: generator didn't yield 664 665 666 .. _reentrant-cms: 667 668 Reentrant context managers 669 ^^^^^^^^^^^^^^^^^^^^^^^^^^ 670 671 More sophisticated context managers may be "reentrant". These context 672 managers can not only be used in multiple :keyword:`with` statements, 673 but may also be used *inside* a :keyword:`with` statement that is already 674 using the same context manager. 675 676 :class:`threading.RLock` is an example of a reentrant context manager, as are 677 :func:`suppress` and :func:`redirect_stdout`. Here's a very simple example of 678 reentrant use:: 679 680 >>> from contextlib import redirect_stdout 681 >>> from io import StringIO 682 >>> stream = StringIO() 683 >>> write_to_stream = redirect_stdout(stream) 684 >>> with write_to_stream: 685 ... print("This is written to the stream rather than stdout") 686 ... with write_to_stream: 687 ... print("This is also written to the stream") 688 ... 689 >>> print("This is written directly to stdout") 690 This is written directly to stdout 691 >>> print(stream.getvalue()) 692 This is written to the stream rather than stdout 693 This is also written to the stream 694 695 Real world examples of reentrancy are more likely to involve multiple 696 functions calling each other and hence be far more complicated than this 697 example. 698 699 Note also that being reentrant is *not* the same thing as being thread safe. 700 :func:`redirect_stdout`, for example, is definitely not thread safe, as it 701 makes a global modification to the system state by binding :data:`sys.stdout` 702 to a different stream. 703 704 705 .. _reusable-cms: 706 707 Reusable context managers 708 ^^^^^^^^^^^^^^^^^^^^^^^^^ 709 710 Distinct from both single use and reentrant context managers are "reusable" 711 context managers (or, to be completely explicit, "reusable, but not 712 reentrant" context managers, since reentrant context managers are also 713 reusable). These context managers support being used multiple times, but 714 will fail (or otherwise not work correctly) if the specific context manager 715 instance has already been used in a containing with statement. 716 717 :class:`threading.Lock` is an example of a reusable, but not reentrant, 718 context manager (for a reentrant lock, it is necessary to use 719 :class:`threading.RLock` instead). 720 721 Another example of a reusable, but not reentrant, context manager is 722 :class:`ExitStack`, as it invokes *all* currently registered callbacks 723 when leaving any with statement, regardless of where those callbacks 724 were added:: 725 726 >>> from contextlib import ExitStack 727 >>> stack = ExitStack() 728 >>> with stack: 729 ... stack.callback(print, "Callback: from first context") 730 ... print("Leaving first context") 731 ... 732 Leaving first context 733 Callback: from first context 734 >>> with stack: 735 ... stack.callback(print, "Callback: from second context") 736 ... print("Leaving second context") 737 ... 738 Leaving second context 739 Callback: from second context 740 >>> with stack: 741 ... stack.callback(print, "Callback: from outer context") 742 ... with stack: 743 ... stack.callback(print, "Callback: from inner context") 744 ... print("Leaving inner context") 745 ... print("Leaving outer context") 746 ... 747 Leaving inner context 748 Callback: from inner context 749 Callback: from outer context 750 Leaving outer context 751 752 As the output from the example shows, reusing a single stack object across 753 multiple with statements works correctly, but attempting to nest them 754 will cause the stack to be cleared at the end of the innermost with 755 statement, which is unlikely to be desirable behaviour. 756 757 Using separate :class:`ExitStack` instances instead of reusing a single 758 instance avoids that problem:: 759 760 >>> from contextlib import ExitStack 761 >>> with ExitStack() as outer_stack: 762 ... outer_stack.callback(print, "Callback: from outer context") 763 ... with ExitStack() as inner_stack: 764 ... inner_stack.callback(print, "Callback: from inner context") 765 ... print("Leaving inner context") 766 ... print("Leaving outer context") 767 ... 768 Leaving inner context 769 Callback: from inner context 770 Leaving outer context 771 Callback: from outer context 772