17db96d56Sopenharmony_ci:mod:`!contextlib` --- Utilities for :keyword:`!with`\ -statement contexts 27db96d56Sopenharmony_ci========================================================================== 37db96d56Sopenharmony_ci 47db96d56Sopenharmony_ci.. module:: contextlib 57db96d56Sopenharmony_ci :synopsis: Utilities for with-statement contexts. 67db96d56Sopenharmony_ci 77db96d56Sopenharmony_ci**Source code:** :source:`Lib/contextlib.py` 87db96d56Sopenharmony_ci 97db96d56Sopenharmony_ci-------------- 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ciThis module provides utilities for common tasks involving the :keyword:`with` 127db96d56Sopenharmony_cistatement. For more information see also :ref:`typecontextmanager` and 137db96d56Sopenharmony_ci:ref:`context-managers`. 147db96d56Sopenharmony_ci 157db96d56Sopenharmony_ci 167db96d56Sopenharmony_ciUtilities 177db96d56Sopenharmony_ci--------- 187db96d56Sopenharmony_ci 197db96d56Sopenharmony_ciFunctions and classes provided: 207db96d56Sopenharmony_ci 217db96d56Sopenharmony_ci.. class:: AbstractContextManager 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_ci An :term:`abstract base class` for classes that implement 247db96d56Sopenharmony_ci :meth:`object.__enter__` and :meth:`object.__exit__`. A default 257db96d56Sopenharmony_ci implementation for :meth:`object.__enter__` is provided which returns 267db96d56Sopenharmony_ci ``self`` while :meth:`object.__exit__` is an abstract method which by default 277db96d56Sopenharmony_ci returns ``None``. See also the definition of :ref:`typecontextmanager`. 287db96d56Sopenharmony_ci 297db96d56Sopenharmony_ci .. versionadded:: 3.6 307db96d56Sopenharmony_ci 317db96d56Sopenharmony_ci 327db96d56Sopenharmony_ci.. class:: AbstractAsyncContextManager 337db96d56Sopenharmony_ci 347db96d56Sopenharmony_ci An :term:`abstract base class` for classes that implement 357db96d56Sopenharmony_ci :meth:`object.__aenter__` and :meth:`object.__aexit__`. A default 367db96d56Sopenharmony_ci implementation for :meth:`object.__aenter__` is provided which returns 377db96d56Sopenharmony_ci ``self`` while :meth:`object.__aexit__` is an abstract method which by default 387db96d56Sopenharmony_ci returns ``None``. See also the definition of 397db96d56Sopenharmony_ci :ref:`async-context-managers`. 407db96d56Sopenharmony_ci 417db96d56Sopenharmony_ci .. versionadded:: 3.7 427db96d56Sopenharmony_ci 437db96d56Sopenharmony_ci 447db96d56Sopenharmony_ci.. decorator:: contextmanager 457db96d56Sopenharmony_ci 467db96d56Sopenharmony_ci This function is a :term:`decorator` that can be used to define a factory 477db96d56Sopenharmony_ci function for :keyword:`with` statement context managers, without needing to 487db96d56Sopenharmony_ci create a class or separate :meth:`__enter__` and :meth:`__exit__` methods. 497db96d56Sopenharmony_ci 507db96d56Sopenharmony_ci While many objects natively support use in with statements, sometimes a 517db96d56Sopenharmony_ci resource needs to be managed that isn't a context manager in its own right, 527db96d56Sopenharmony_ci and doesn't implement a ``close()`` method for use with ``contextlib.closing`` 537db96d56Sopenharmony_ci 547db96d56Sopenharmony_ci An abstract example would be the following to ensure correct resource 557db96d56Sopenharmony_ci management:: 567db96d56Sopenharmony_ci 577db96d56Sopenharmony_ci from contextlib import contextmanager 587db96d56Sopenharmony_ci 597db96d56Sopenharmony_ci @contextmanager 607db96d56Sopenharmony_ci def managed_resource(*args, **kwds): 617db96d56Sopenharmony_ci # Code to acquire resource, e.g.: 627db96d56Sopenharmony_ci resource = acquire_resource(*args, **kwds) 637db96d56Sopenharmony_ci try: 647db96d56Sopenharmony_ci yield resource 657db96d56Sopenharmony_ci finally: 667db96d56Sopenharmony_ci # Code to release resource, e.g.: 677db96d56Sopenharmony_ci release_resource(resource) 687db96d56Sopenharmony_ci 697db96d56Sopenharmony_ci The function can then be used like this:: 707db96d56Sopenharmony_ci 717db96d56Sopenharmony_ci >>> with managed_resource(timeout=3600) as resource: 727db96d56Sopenharmony_ci ... # Resource is released at the end of this block, 737db96d56Sopenharmony_ci ... # even if code in the block raises an exception 747db96d56Sopenharmony_ci 757db96d56Sopenharmony_ci The function being decorated must return a :term:`generator`-iterator when 767db96d56Sopenharmony_ci called. This iterator must yield exactly one value, which will be bound to 777db96d56Sopenharmony_ci the targets in the :keyword:`with` statement's :keyword:`!as` clause, if any. 787db96d56Sopenharmony_ci 797db96d56Sopenharmony_ci At the point where the generator yields, the block nested in the :keyword:`with` 807db96d56Sopenharmony_ci statement is executed. The generator is then resumed after the block is exited. 817db96d56Sopenharmony_ci If an unhandled exception occurs in the block, it is reraised inside the 827db96d56Sopenharmony_ci generator at the point where the yield occurred. Thus, you can use a 837db96d56Sopenharmony_ci :keyword:`try`...\ :keyword:`except`...\ :keyword:`finally` statement to trap 847db96d56Sopenharmony_ci the error (if any), or ensure that some cleanup takes place. If an exception is 857db96d56Sopenharmony_ci trapped merely in order to log it or to perform some action (rather than to 867db96d56Sopenharmony_ci suppress it entirely), the generator must reraise that exception. Otherwise the 877db96d56Sopenharmony_ci generator context manager will indicate to the :keyword:`!with` statement that 887db96d56Sopenharmony_ci the exception has been handled, and execution will resume with the statement 897db96d56Sopenharmony_ci immediately following the :keyword:`!with` statement. 907db96d56Sopenharmony_ci 917db96d56Sopenharmony_ci :func:`contextmanager` uses :class:`ContextDecorator` so the context managers 927db96d56Sopenharmony_ci it creates can be used as decorators as well as in :keyword:`with` statements. 937db96d56Sopenharmony_ci When used as a decorator, a new generator instance is implicitly created on 947db96d56Sopenharmony_ci each function call (this allows the otherwise "one-shot" context managers 957db96d56Sopenharmony_ci created by :func:`contextmanager` to meet the requirement that context 967db96d56Sopenharmony_ci managers support multiple invocations in order to be used as decorators). 977db96d56Sopenharmony_ci 987db96d56Sopenharmony_ci .. versionchanged:: 3.2 997db96d56Sopenharmony_ci Use of :class:`ContextDecorator`. 1007db96d56Sopenharmony_ci 1017db96d56Sopenharmony_ci 1027db96d56Sopenharmony_ci.. decorator:: asynccontextmanager 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ci Similar to :func:`~contextlib.contextmanager`, but creates an 1057db96d56Sopenharmony_ci :ref:`asynchronous context manager <async-context-managers>`. 1067db96d56Sopenharmony_ci 1077db96d56Sopenharmony_ci This function is a :term:`decorator` that can be used to define a factory 1087db96d56Sopenharmony_ci function for :keyword:`async with` statement asynchronous context managers, 1097db96d56Sopenharmony_ci without needing to create a class or separate :meth:`__aenter__` and 1107db96d56Sopenharmony_ci :meth:`__aexit__` methods. It must be applied to an :term:`asynchronous 1117db96d56Sopenharmony_ci generator` function. 1127db96d56Sopenharmony_ci 1137db96d56Sopenharmony_ci A simple example:: 1147db96d56Sopenharmony_ci 1157db96d56Sopenharmony_ci from contextlib import asynccontextmanager 1167db96d56Sopenharmony_ci 1177db96d56Sopenharmony_ci @asynccontextmanager 1187db96d56Sopenharmony_ci async def get_connection(): 1197db96d56Sopenharmony_ci conn = await acquire_db_connection() 1207db96d56Sopenharmony_ci try: 1217db96d56Sopenharmony_ci yield conn 1227db96d56Sopenharmony_ci finally: 1237db96d56Sopenharmony_ci await release_db_connection(conn) 1247db96d56Sopenharmony_ci 1257db96d56Sopenharmony_ci async def get_all_users(): 1267db96d56Sopenharmony_ci async with get_connection() as conn: 1277db96d56Sopenharmony_ci return conn.query('SELECT ...') 1287db96d56Sopenharmony_ci 1297db96d56Sopenharmony_ci .. versionadded:: 3.7 1307db96d56Sopenharmony_ci 1317db96d56Sopenharmony_ci Context managers defined with :func:`asynccontextmanager` can be used 1327db96d56Sopenharmony_ci either as decorators or with :keyword:`async with` statements:: 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_ci import time 1357db96d56Sopenharmony_ci from contextlib import asynccontextmanager 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_ci @asynccontextmanager 1387db96d56Sopenharmony_ci async def timeit(): 1397db96d56Sopenharmony_ci now = time.monotonic() 1407db96d56Sopenharmony_ci try: 1417db96d56Sopenharmony_ci yield 1427db96d56Sopenharmony_ci finally: 1437db96d56Sopenharmony_ci print(f'it took {time.monotonic() - now}s to run') 1447db96d56Sopenharmony_ci 1457db96d56Sopenharmony_ci @timeit() 1467db96d56Sopenharmony_ci async def main(): 1477db96d56Sopenharmony_ci # ... async code ... 1487db96d56Sopenharmony_ci 1497db96d56Sopenharmony_ci When used as a decorator, a new generator instance is implicitly created on 1507db96d56Sopenharmony_ci each function call. This allows the otherwise "one-shot" context managers 1517db96d56Sopenharmony_ci created by :func:`asynccontextmanager` to meet the requirement that context 1527db96d56Sopenharmony_ci managers support multiple invocations in order to be used as decorators. 1537db96d56Sopenharmony_ci 1547db96d56Sopenharmony_ci .. versionchanged:: 3.10 1557db96d56Sopenharmony_ci Async context managers created with :func:`asynccontextmanager` can 1567db96d56Sopenharmony_ci be used as decorators. 1577db96d56Sopenharmony_ci 1587db96d56Sopenharmony_ci 1597db96d56Sopenharmony_ci.. function:: closing(thing) 1607db96d56Sopenharmony_ci 1617db96d56Sopenharmony_ci Return a context manager that closes *thing* upon completion of the block. This 1627db96d56Sopenharmony_ci is basically equivalent to:: 1637db96d56Sopenharmony_ci 1647db96d56Sopenharmony_ci from contextlib import contextmanager 1657db96d56Sopenharmony_ci 1667db96d56Sopenharmony_ci @contextmanager 1677db96d56Sopenharmony_ci def closing(thing): 1687db96d56Sopenharmony_ci try: 1697db96d56Sopenharmony_ci yield thing 1707db96d56Sopenharmony_ci finally: 1717db96d56Sopenharmony_ci thing.close() 1727db96d56Sopenharmony_ci 1737db96d56Sopenharmony_ci And lets you write code like this:: 1747db96d56Sopenharmony_ci 1757db96d56Sopenharmony_ci from contextlib import closing 1767db96d56Sopenharmony_ci from urllib.request import urlopen 1777db96d56Sopenharmony_ci 1787db96d56Sopenharmony_ci with closing(urlopen('https://www.python.org')) as page: 1797db96d56Sopenharmony_ci for line in page: 1807db96d56Sopenharmony_ci print(line) 1817db96d56Sopenharmony_ci 1827db96d56Sopenharmony_ci without needing to explicitly close ``page``. Even if an error occurs, 1837db96d56Sopenharmony_ci ``page.close()`` will be called when the :keyword:`with` block is exited. 1847db96d56Sopenharmony_ci 1857db96d56Sopenharmony_ci 1867db96d56Sopenharmony_ci.. function:: aclosing(thing) 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ci Return an async context manager that calls the ``aclose()`` method of *thing* 1897db96d56Sopenharmony_ci upon completion of the block. This is basically equivalent to:: 1907db96d56Sopenharmony_ci 1917db96d56Sopenharmony_ci from contextlib import asynccontextmanager 1927db96d56Sopenharmony_ci 1937db96d56Sopenharmony_ci @asynccontextmanager 1947db96d56Sopenharmony_ci async def aclosing(thing): 1957db96d56Sopenharmony_ci try: 1967db96d56Sopenharmony_ci yield thing 1977db96d56Sopenharmony_ci finally: 1987db96d56Sopenharmony_ci await thing.aclose() 1997db96d56Sopenharmony_ci 2007db96d56Sopenharmony_ci Significantly, ``aclosing()`` supports deterministic cleanup of async 2017db96d56Sopenharmony_ci generators when they happen to exit early by :keyword:`break` or an 2027db96d56Sopenharmony_ci exception. For example:: 2037db96d56Sopenharmony_ci 2047db96d56Sopenharmony_ci from contextlib import aclosing 2057db96d56Sopenharmony_ci 2067db96d56Sopenharmony_ci async with aclosing(my_generator()) as values: 2077db96d56Sopenharmony_ci async for value in values: 2087db96d56Sopenharmony_ci if value == 42: 2097db96d56Sopenharmony_ci break 2107db96d56Sopenharmony_ci 2117db96d56Sopenharmony_ci This pattern ensures that the generator's async exit code is executed in 2127db96d56Sopenharmony_ci the same context as its iterations (so that exceptions and context 2137db96d56Sopenharmony_ci variables work as expected, and the exit code isn't run after the 2147db96d56Sopenharmony_ci lifetime of some task it depends on). 2157db96d56Sopenharmony_ci 2167db96d56Sopenharmony_ci .. versionadded:: 3.10 2177db96d56Sopenharmony_ci 2187db96d56Sopenharmony_ci 2197db96d56Sopenharmony_ci.. _simplifying-support-for-single-optional-context-managers: 2207db96d56Sopenharmony_ci 2217db96d56Sopenharmony_ci.. function:: nullcontext(enter_result=None) 2227db96d56Sopenharmony_ci 2237db96d56Sopenharmony_ci Return a context manager that returns *enter_result* from ``__enter__``, but 2247db96d56Sopenharmony_ci otherwise does nothing. It is intended to be used as a stand-in for an 2257db96d56Sopenharmony_ci optional context manager, for example:: 2267db96d56Sopenharmony_ci 2277db96d56Sopenharmony_ci def myfunction(arg, ignore_exceptions=False): 2287db96d56Sopenharmony_ci if ignore_exceptions: 2297db96d56Sopenharmony_ci # Use suppress to ignore all exceptions. 2307db96d56Sopenharmony_ci cm = contextlib.suppress(Exception) 2317db96d56Sopenharmony_ci else: 2327db96d56Sopenharmony_ci # Do not ignore any exceptions, cm has no effect. 2337db96d56Sopenharmony_ci cm = contextlib.nullcontext() 2347db96d56Sopenharmony_ci with cm: 2357db96d56Sopenharmony_ci # Do something 2367db96d56Sopenharmony_ci 2377db96d56Sopenharmony_ci An example using *enter_result*:: 2387db96d56Sopenharmony_ci 2397db96d56Sopenharmony_ci def process_file(file_or_path): 2407db96d56Sopenharmony_ci if isinstance(file_or_path, str): 2417db96d56Sopenharmony_ci # If string, open file 2427db96d56Sopenharmony_ci cm = open(file_or_path) 2437db96d56Sopenharmony_ci else: 2447db96d56Sopenharmony_ci # Caller is responsible for closing file 2457db96d56Sopenharmony_ci cm = nullcontext(file_or_path) 2467db96d56Sopenharmony_ci 2477db96d56Sopenharmony_ci with cm as file: 2487db96d56Sopenharmony_ci # Perform processing on the file 2497db96d56Sopenharmony_ci 2507db96d56Sopenharmony_ci It can also be used as a stand-in for 2517db96d56Sopenharmony_ci :ref:`asynchronous context managers <async-context-managers>`:: 2527db96d56Sopenharmony_ci 2537db96d56Sopenharmony_ci async def send_http(session=None): 2547db96d56Sopenharmony_ci if not session: 2557db96d56Sopenharmony_ci # If no http session, create it with aiohttp 2567db96d56Sopenharmony_ci cm = aiohttp.ClientSession() 2577db96d56Sopenharmony_ci else: 2587db96d56Sopenharmony_ci # Caller is responsible for closing the session 2597db96d56Sopenharmony_ci cm = nullcontext(session) 2607db96d56Sopenharmony_ci 2617db96d56Sopenharmony_ci async with cm as session: 2627db96d56Sopenharmony_ci # Send http requests with session 2637db96d56Sopenharmony_ci 2647db96d56Sopenharmony_ci .. versionadded:: 3.7 2657db96d56Sopenharmony_ci 2667db96d56Sopenharmony_ci .. versionchanged:: 3.10 2677db96d56Sopenharmony_ci :term:`asynchronous context manager` support was added. 2687db96d56Sopenharmony_ci 2697db96d56Sopenharmony_ci 2707db96d56Sopenharmony_ci 2717db96d56Sopenharmony_ci.. function:: suppress(*exceptions) 2727db96d56Sopenharmony_ci 2737db96d56Sopenharmony_ci Return a context manager that suppresses any of the specified exceptions 2747db96d56Sopenharmony_ci if they occur in the body of a :keyword:`!with` statement and then 2757db96d56Sopenharmony_ci resumes execution with the first statement following the end of the 2767db96d56Sopenharmony_ci :keyword:`!with` statement. 2777db96d56Sopenharmony_ci 2787db96d56Sopenharmony_ci As with any other mechanism that completely suppresses exceptions, this 2797db96d56Sopenharmony_ci context manager should be used only to cover very specific errors where 2807db96d56Sopenharmony_ci silently continuing with program execution is known to be the right 2817db96d56Sopenharmony_ci thing to do. 2827db96d56Sopenharmony_ci 2837db96d56Sopenharmony_ci For example:: 2847db96d56Sopenharmony_ci 2857db96d56Sopenharmony_ci from contextlib import suppress 2867db96d56Sopenharmony_ci 2877db96d56Sopenharmony_ci with suppress(FileNotFoundError): 2887db96d56Sopenharmony_ci os.remove('somefile.tmp') 2897db96d56Sopenharmony_ci 2907db96d56Sopenharmony_ci with suppress(FileNotFoundError): 2917db96d56Sopenharmony_ci os.remove('someotherfile.tmp') 2927db96d56Sopenharmony_ci 2937db96d56Sopenharmony_ci This code is equivalent to:: 2947db96d56Sopenharmony_ci 2957db96d56Sopenharmony_ci try: 2967db96d56Sopenharmony_ci os.remove('somefile.tmp') 2977db96d56Sopenharmony_ci except FileNotFoundError: 2987db96d56Sopenharmony_ci pass 2997db96d56Sopenharmony_ci 3007db96d56Sopenharmony_ci try: 3017db96d56Sopenharmony_ci os.remove('someotherfile.tmp') 3027db96d56Sopenharmony_ci except FileNotFoundError: 3037db96d56Sopenharmony_ci pass 3047db96d56Sopenharmony_ci 3057db96d56Sopenharmony_ci This context manager is :ref:`reentrant <reentrant-cms>`. 3067db96d56Sopenharmony_ci 3077db96d56Sopenharmony_ci .. versionadded:: 3.4 3087db96d56Sopenharmony_ci 3097db96d56Sopenharmony_ci 3107db96d56Sopenharmony_ci.. function:: redirect_stdout(new_target) 3117db96d56Sopenharmony_ci 3127db96d56Sopenharmony_ci Context manager for temporarily redirecting :data:`sys.stdout` to 3137db96d56Sopenharmony_ci another file or file-like object. 3147db96d56Sopenharmony_ci 3157db96d56Sopenharmony_ci This tool adds flexibility to existing functions or classes whose output 3167db96d56Sopenharmony_ci is hardwired to stdout. 3177db96d56Sopenharmony_ci 3187db96d56Sopenharmony_ci For example, the output of :func:`help` normally is sent to *sys.stdout*. 3197db96d56Sopenharmony_ci You can capture that output in a string by redirecting the output to an 3207db96d56Sopenharmony_ci :class:`io.StringIO` object. The replacement stream is returned from the 3217db96d56Sopenharmony_ci ``__enter__`` method and so is available as the target of the 3227db96d56Sopenharmony_ci :keyword:`with` statement:: 3237db96d56Sopenharmony_ci 3247db96d56Sopenharmony_ci with redirect_stdout(io.StringIO()) as f: 3257db96d56Sopenharmony_ci help(pow) 3267db96d56Sopenharmony_ci s = f.getvalue() 3277db96d56Sopenharmony_ci 3287db96d56Sopenharmony_ci To send the output of :func:`help` to a file on disk, redirect the output 3297db96d56Sopenharmony_ci to a regular file:: 3307db96d56Sopenharmony_ci 3317db96d56Sopenharmony_ci with open('help.txt', 'w') as f: 3327db96d56Sopenharmony_ci with redirect_stdout(f): 3337db96d56Sopenharmony_ci help(pow) 3347db96d56Sopenharmony_ci 3357db96d56Sopenharmony_ci To send the output of :func:`help` to *sys.stderr*:: 3367db96d56Sopenharmony_ci 3377db96d56Sopenharmony_ci with redirect_stdout(sys.stderr): 3387db96d56Sopenharmony_ci help(pow) 3397db96d56Sopenharmony_ci 3407db96d56Sopenharmony_ci Note that the global side effect on :data:`sys.stdout` means that this 3417db96d56Sopenharmony_ci context manager is not suitable for use in library code and most threaded 3427db96d56Sopenharmony_ci applications. It also has no effect on the output of subprocesses. 3437db96d56Sopenharmony_ci However, it is still a useful approach for many utility scripts. 3447db96d56Sopenharmony_ci 3457db96d56Sopenharmony_ci This context manager is :ref:`reentrant <reentrant-cms>`. 3467db96d56Sopenharmony_ci 3477db96d56Sopenharmony_ci .. versionadded:: 3.4 3487db96d56Sopenharmony_ci 3497db96d56Sopenharmony_ci 3507db96d56Sopenharmony_ci.. function:: redirect_stderr(new_target) 3517db96d56Sopenharmony_ci 3527db96d56Sopenharmony_ci Similar to :func:`~contextlib.redirect_stdout` but redirecting 3537db96d56Sopenharmony_ci :data:`sys.stderr` to another file or file-like object. 3547db96d56Sopenharmony_ci 3557db96d56Sopenharmony_ci This context manager is :ref:`reentrant <reentrant-cms>`. 3567db96d56Sopenharmony_ci 3577db96d56Sopenharmony_ci .. versionadded:: 3.5 3587db96d56Sopenharmony_ci 3597db96d56Sopenharmony_ci 3607db96d56Sopenharmony_ci.. function:: chdir(path) 3617db96d56Sopenharmony_ci 3627db96d56Sopenharmony_ci Non parallel-safe context manager to change the current working directory. 3637db96d56Sopenharmony_ci As this changes a global state, the working directory, it is not suitable 3647db96d56Sopenharmony_ci for use in most threaded or async contexts. It is also not suitable for most 3657db96d56Sopenharmony_ci non-linear code execution, like generators, where the program execution is 3667db96d56Sopenharmony_ci temporarily relinquished -- unless explicitly desired, you should not yield 3677db96d56Sopenharmony_ci when this context manager is active. 3687db96d56Sopenharmony_ci 3697db96d56Sopenharmony_ci This is a simple wrapper around :func:`~os.chdir`, it changes the current 3707db96d56Sopenharmony_ci working directory upon entering and restores the old one on exit. 3717db96d56Sopenharmony_ci 3727db96d56Sopenharmony_ci This context manager is :ref:`reentrant <reentrant-cms>`. 3737db96d56Sopenharmony_ci 3747db96d56Sopenharmony_ci .. versionadded:: 3.11 3757db96d56Sopenharmony_ci 3767db96d56Sopenharmony_ci 3777db96d56Sopenharmony_ci.. class:: ContextDecorator() 3787db96d56Sopenharmony_ci 3797db96d56Sopenharmony_ci A base class that enables a context manager to also be used as a decorator. 3807db96d56Sopenharmony_ci 3817db96d56Sopenharmony_ci Context managers inheriting from ``ContextDecorator`` have to implement 3827db96d56Sopenharmony_ci ``__enter__`` and ``__exit__`` as normal. ``__exit__`` retains its optional 3837db96d56Sopenharmony_ci exception handling even when used as a decorator. 3847db96d56Sopenharmony_ci 3857db96d56Sopenharmony_ci ``ContextDecorator`` is used by :func:`contextmanager`, so you get this 3867db96d56Sopenharmony_ci functionality automatically. 3877db96d56Sopenharmony_ci 3887db96d56Sopenharmony_ci Example of ``ContextDecorator``:: 3897db96d56Sopenharmony_ci 3907db96d56Sopenharmony_ci from contextlib import ContextDecorator 3917db96d56Sopenharmony_ci 3927db96d56Sopenharmony_ci class mycontext(ContextDecorator): 3937db96d56Sopenharmony_ci def __enter__(self): 3947db96d56Sopenharmony_ci print('Starting') 3957db96d56Sopenharmony_ci return self 3967db96d56Sopenharmony_ci 3977db96d56Sopenharmony_ci def __exit__(self, *exc): 3987db96d56Sopenharmony_ci print('Finishing') 3997db96d56Sopenharmony_ci return False 4007db96d56Sopenharmony_ci 4017db96d56Sopenharmony_ci The class can then be used like this:: 4027db96d56Sopenharmony_ci 4037db96d56Sopenharmony_ci >>> @mycontext() 4047db96d56Sopenharmony_ci ... def function(): 4057db96d56Sopenharmony_ci ... print('The bit in the middle') 4067db96d56Sopenharmony_ci ... 4077db96d56Sopenharmony_ci >>> function() 4087db96d56Sopenharmony_ci Starting 4097db96d56Sopenharmony_ci The bit in the middle 4107db96d56Sopenharmony_ci Finishing 4117db96d56Sopenharmony_ci 4127db96d56Sopenharmony_ci >>> with mycontext(): 4137db96d56Sopenharmony_ci ... print('The bit in the middle') 4147db96d56Sopenharmony_ci ... 4157db96d56Sopenharmony_ci Starting 4167db96d56Sopenharmony_ci The bit in the middle 4177db96d56Sopenharmony_ci Finishing 4187db96d56Sopenharmony_ci 4197db96d56Sopenharmony_ci This change is just syntactic sugar for any construct of the following form:: 4207db96d56Sopenharmony_ci 4217db96d56Sopenharmony_ci def f(): 4227db96d56Sopenharmony_ci with cm(): 4237db96d56Sopenharmony_ci # Do stuff 4247db96d56Sopenharmony_ci 4257db96d56Sopenharmony_ci ``ContextDecorator`` lets you instead write:: 4267db96d56Sopenharmony_ci 4277db96d56Sopenharmony_ci @cm() 4287db96d56Sopenharmony_ci def f(): 4297db96d56Sopenharmony_ci # Do stuff 4307db96d56Sopenharmony_ci 4317db96d56Sopenharmony_ci It makes it clear that the ``cm`` applies to the whole function, rather than 4327db96d56Sopenharmony_ci just a piece of it (and saving an indentation level is nice, too). 4337db96d56Sopenharmony_ci 4347db96d56Sopenharmony_ci Existing context managers that already have a base class can be extended by 4357db96d56Sopenharmony_ci using ``ContextDecorator`` as a mixin class:: 4367db96d56Sopenharmony_ci 4377db96d56Sopenharmony_ci from contextlib import ContextDecorator 4387db96d56Sopenharmony_ci 4397db96d56Sopenharmony_ci class mycontext(ContextBaseClass, ContextDecorator): 4407db96d56Sopenharmony_ci def __enter__(self): 4417db96d56Sopenharmony_ci return self 4427db96d56Sopenharmony_ci 4437db96d56Sopenharmony_ci def __exit__(self, *exc): 4447db96d56Sopenharmony_ci return False 4457db96d56Sopenharmony_ci 4467db96d56Sopenharmony_ci .. note:: 4477db96d56Sopenharmony_ci As the decorated function must be able to be called multiple times, the 4487db96d56Sopenharmony_ci underlying context manager must support use in multiple :keyword:`with` 4497db96d56Sopenharmony_ci statements. If this is not the case, then the original construct with the 4507db96d56Sopenharmony_ci explicit :keyword:`!with` statement inside the function should be used. 4517db96d56Sopenharmony_ci 4527db96d56Sopenharmony_ci .. versionadded:: 3.2 4537db96d56Sopenharmony_ci 4547db96d56Sopenharmony_ci 4557db96d56Sopenharmony_ci.. class:: AsyncContextDecorator 4567db96d56Sopenharmony_ci 4577db96d56Sopenharmony_ci Similar to :class:`ContextDecorator` but only for asynchronous functions. 4587db96d56Sopenharmony_ci 4597db96d56Sopenharmony_ci Example of ``AsyncContextDecorator``:: 4607db96d56Sopenharmony_ci 4617db96d56Sopenharmony_ci from asyncio import run 4627db96d56Sopenharmony_ci from contextlib import AsyncContextDecorator 4637db96d56Sopenharmony_ci 4647db96d56Sopenharmony_ci class mycontext(AsyncContextDecorator): 4657db96d56Sopenharmony_ci async def __aenter__(self): 4667db96d56Sopenharmony_ci print('Starting') 4677db96d56Sopenharmony_ci return self 4687db96d56Sopenharmony_ci 4697db96d56Sopenharmony_ci async def __aexit__(self, *exc): 4707db96d56Sopenharmony_ci print('Finishing') 4717db96d56Sopenharmony_ci return False 4727db96d56Sopenharmony_ci 4737db96d56Sopenharmony_ci The class can then be used like this:: 4747db96d56Sopenharmony_ci 4757db96d56Sopenharmony_ci >>> @mycontext() 4767db96d56Sopenharmony_ci ... async def function(): 4777db96d56Sopenharmony_ci ... print('The bit in the middle') 4787db96d56Sopenharmony_ci ... 4797db96d56Sopenharmony_ci >>> run(function()) 4807db96d56Sopenharmony_ci Starting 4817db96d56Sopenharmony_ci The bit in the middle 4827db96d56Sopenharmony_ci Finishing 4837db96d56Sopenharmony_ci 4847db96d56Sopenharmony_ci >>> async def function(): 4857db96d56Sopenharmony_ci ... async with mycontext(): 4867db96d56Sopenharmony_ci ... print('The bit in the middle') 4877db96d56Sopenharmony_ci ... 4887db96d56Sopenharmony_ci >>> run(function()) 4897db96d56Sopenharmony_ci Starting 4907db96d56Sopenharmony_ci The bit in the middle 4917db96d56Sopenharmony_ci Finishing 4927db96d56Sopenharmony_ci 4937db96d56Sopenharmony_ci .. versionadded:: 3.10 4947db96d56Sopenharmony_ci 4957db96d56Sopenharmony_ci 4967db96d56Sopenharmony_ci.. class:: ExitStack() 4977db96d56Sopenharmony_ci 4987db96d56Sopenharmony_ci A context manager that is designed to make it easy to programmatically 4997db96d56Sopenharmony_ci combine other context managers and cleanup functions, especially those 5007db96d56Sopenharmony_ci that are optional or otherwise driven by input data. 5017db96d56Sopenharmony_ci 5027db96d56Sopenharmony_ci For example, a set of files may easily be handled in a single with 5037db96d56Sopenharmony_ci statement as follows:: 5047db96d56Sopenharmony_ci 5057db96d56Sopenharmony_ci with ExitStack() as stack: 5067db96d56Sopenharmony_ci files = [stack.enter_context(open(fname)) for fname in filenames] 5077db96d56Sopenharmony_ci # All opened files will automatically be closed at the end of 5087db96d56Sopenharmony_ci # the with statement, even if attempts to open files later 5097db96d56Sopenharmony_ci # in the list raise an exception 5107db96d56Sopenharmony_ci 5117db96d56Sopenharmony_ci The :meth:`__enter__` method returns the :class:`ExitStack` instance, and 5127db96d56Sopenharmony_ci performs no additional operations. 5137db96d56Sopenharmony_ci 5147db96d56Sopenharmony_ci Each instance maintains a stack of registered callbacks that are called in 5157db96d56Sopenharmony_ci reverse order when the instance is closed (either explicitly or implicitly 5167db96d56Sopenharmony_ci at the end of a :keyword:`with` statement). Note that callbacks are *not* 5177db96d56Sopenharmony_ci invoked implicitly when the context stack instance is garbage collected. 5187db96d56Sopenharmony_ci 5197db96d56Sopenharmony_ci This stack model is used so that context managers that acquire their 5207db96d56Sopenharmony_ci resources in their ``__init__`` method (such as file objects) can be 5217db96d56Sopenharmony_ci handled correctly. 5227db96d56Sopenharmony_ci 5237db96d56Sopenharmony_ci Since registered callbacks are invoked in the reverse order of 5247db96d56Sopenharmony_ci registration, this ends up behaving as if multiple nested :keyword:`with` 5257db96d56Sopenharmony_ci statements had been used with the registered set of callbacks. This even 5267db96d56Sopenharmony_ci extends to exception handling - if an inner callback suppresses or replaces 5277db96d56Sopenharmony_ci an exception, then outer callbacks will be passed arguments based on that 5287db96d56Sopenharmony_ci updated state. 5297db96d56Sopenharmony_ci 5307db96d56Sopenharmony_ci This is a relatively low level API that takes care of the details of 5317db96d56Sopenharmony_ci correctly unwinding the stack of exit callbacks. It provides a suitable 5327db96d56Sopenharmony_ci foundation for higher level context managers that manipulate the exit 5337db96d56Sopenharmony_ci stack in application specific ways. 5347db96d56Sopenharmony_ci 5357db96d56Sopenharmony_ci .. versionadded:: 3.3 5367db96d56Sopenharmony_ci 5377db96d56Sopenharmony_ci .. method:: enter_context(cm) 5387db96d56Sopenharmony_ci 5397db96d56Sopenharmony_ci Enters a new context manager and adds its :meth:`__exit__` method to 5407db96d56Sopenharmony_ci the callback stack. The return value is the result of the context 5417db96d56Sopenharmony_ci manager's own :meth:`__enter__` method. 5427db96d56Sopenharmony_ci 5437db96d56Sopenharmony_ci These context managers may suppress exceptions just as they normally 5447db96d56Sopenharmony_ci would if used directly as part of a :keyword:`with` statement. 5457db96d56Sopenharmony_ci 5467db96d56Sopenharmony_ci .. versionchanged:: 3.11 5477db96d56Sopenharmony_ci Raises :exc:`TypeError` instead of :exc:`AttributeError` if *cm* 5487db96d56Sopenharmony_ci is not a context manager. 5497db96d56Sopenharmony_ci 5507db96d56Sopenharmony_ci .. method:: push(exit) 5517db96d56Sopenharmony_ci 5527db96d56Sopenharmony_ci Adds a context manager's :meth:`__exit__` method to the callback stack. 5537db96d56Sopenharmony_ci 5547db96d56Sopenharmony_ci As ``__enter__`` is *not* invoked, this method can be used to cover 5557db96d56Sopenharmony_ci part of an :meth:`__enter__` implementation with a context manager's own 5567db96d56Sopenharmony_ci :meth:`__exit__` method. 5577db96d56Sopenharmony_ci 5587db96d56Sopenharmony_ci If passed an object that is not a context manager, this method assumes 5597db96d56Sopenharmony_ci it is a callback with the same signature as a context manager's 5607db96d56Sopenharmony_ci :meth:`__exit__` method and adds it directly to the callback stack. 5617db96d56Sopenharmony_ci 5627db96d56Sopenharmony_ci By returning true values, these callbacks can suppress exceptions the 5637db96d56Sopenharmony_ci same way context manager :meth:`__exit__` methods can. 5647db96d56Sopenharmony_ci 5657db96d56Sopenharmony_ci The passed in object is returned from the function, allowing this 5667db96d56Sopenharmony_ci method to be used as a function decorator. 5677db96d56Sopenharmony_ci 5687db96d56Sopenharmony_ci .. method:: callback(callback, /, *args, **kwds) 5697db96d56Sopenharmony_ci 5707db96d56Sopenharmony_ci Accepts an arbitrary callback function and arguments and adds it to 5717db96d56Sopenharmony_ci the callback stack. 5727db96d56Sopenharmony_ci 5737db96d56Sopenharmony_ci Unlike the other methods, callbacks added this way cannot suppress 5747db96d56Sopenharmony_ci exceptions (as they are never passed the exception details). 5757db96d56Sopenharmony_ci 5767db96d56Sopenharmony_ci The passed in callback is returned from the function, allowing this 5777db96d56Sopenharmony_ci method to be used as a function decorator. 5787db96d56Sopenharmony_ci 5797db96d56Sopenharmony_ci .. method:: pop_all() 5807db96d56Sopenharmony_ci 5817db96d56Sopenharmony_ci Transfers the callback stack to a fresh :class:`ExitStack` instance 5827db96d56Sopenharmony_ci and returns it. No callbacks are invoked by this operation - instead, 5837db96d56Sopenharmony_ci they will now be invoked when the new stack is closed (either 5847db96d56Sopenharmony_ci explicitly or implicitly at the end of a :keyword:`with` statement). 5857db96d56Sopenharmony_ci 5867db96d56Sopenharmony_ci For example, a group of files can be opened as an "all or nothing" 5877db96d56Sopenharmony_ci operation as follows:: 5887db96d56Sopenharmony_ci 5897db96d56Sopenharmony_ci with ExitStack() as stack: 5907db96d56Sopenharmony_ci files = [stack.enter_context(open(fname)) for fname in filenames] 5917db96d56Sopenharmony_ci # Hold onto the close method, but don't call it yet. 5927db96d56Sopenharmony_ci close_files = stack.pop_all().close 5937db96d56Sopenharmony_ci # If opening any file fails, all previously opened files will be 5947db96d56Sopenharmony_ci # closed automatically. If all files are opened successfully, 5957db96d56Sopenharmony_ci # they will remain open even after the with statement ends. 5967db96d56Sopenharmony_ci # close_files() can then be invoked explicitly to close them all. 5977db96d56Sopenharmony_ci 5987db96d56Sopenharmony_ci .. method:: close() 5997db96d56Sopenharmony_ci 6007db96d56Sopenharmony_ci Immediately unwinds the callback stack, invoking callbacks in the 6017db96d56Sopenharmony_ci reverse order of registration. For any context managers and exit 6027db96d56Sopenharmony_ci callbacks registered, the arguments passed in will indicate that no 6037db96d56Sopenharmony_ci exception occurred. 6047db96d56Sopenharmony_ci 6057db96d56Sopenharmony_ci.. class:: AsyncExitStack() 6067db96d56Sopenharmony_ci 6077db96d56Sopenharmony_ci An :ref:`asynchronous context manager <async-context-managers>`, similar 6087db96d56Sopenharmony_ci to :class:`ExitStack`, that supports combining both synchronous and 6097db96d56Sopenharmony_ci asynchronous context managers, as well as having coroutines for 6107db96d56Sopenharmony_ci cleanup logic. 6117db96d56Sopenharmony_ci 6127db96d56Sopenharmony_ci The :meth:`close` method is not implemented, :meth:`aclose` must be used 6137db96d56Sopenharmony_ci instead. 6147db96d56Sopenharmony_ci 6157db96d56Sopenharmony_ci .. coroutinemethod:: enter_async_context(cm) 6167db96d56Sopenharmony_ci 6177db96d56Sopenharmony_ci Similar to :meth:`enter_context` but expects an asynchronous context 6187db96d56Sopenharmony_ci manager. 6197db96d56Sopenharmony_ci 6207db96d56Sopenharmony_ci .. versionchanged:: 3.11 6217db96d56Sopenharmony_ci Raises :exc:`TypeError` instead of :exc:`AttributeError` if *cm* 6227db96d56Sopenharmony_ci is not an asynchronous context manager. 6237db96d56Sopenharmony_ci 6247db96d56Sopenharmony_ci .. method:: push_async_exit(exit) 6257db96d56Sopenharmony_ci 6267db96d56Sopenharmony_ci Similar to :meth:`push` but expects either an asynchronous context manager 6277db96d56Sopenharmony_ci or a coroutine function. 6287db96d56Sopenharmony_ci 6297db96d56Sopenharmony_ci .. method:: push_async_callback(callback, /, *args, **kwds) 6307db96d56Sopenharmony_ci 6317db96d56Sopenharmony_ci Similar to :meth:`callback` but expects a coroutine function. 6327db96d56Sopenharmony_ci 6337db96d56Sopenharmony_ci .. coroutinemethod:: aclose() 6347db96d56Sopenharmony_ci 6357db96d56Sopenharmony_ci Similar to :meth:`close` but properly handles awaitables. 6367db96d56Sopenharmony_ci 6377db96d56Sopenharmony_ci Continuing the example for :func:`asynccontextmanager`:: 6387db96d56Sopenharmony_ci 6397db96d56Sopenharmony_ci async with AsyncExitStack() as stack: 6407db96d56Sopenharmony_ci connections = [await stack.enter_async_context(get_connection()) 6417db96d56Sopenharmony_ci for i in range(5)] 6427db96d56Sopenharmony_ci # All opened connections will automatically be released at the end of 6437db96d56Sopenharmony_ci # the async with statement, even if attempts to open a connection 6447db96d56Sopenharmony_ci # later in the list raise an exception. 6457db96d56Sopenharmony_ci 6467db96d56Sopenharmony_ci .. versionadded:: 3.7 6477db96d56Sopenharmony_ci 6487db96d56Sopenharmony_ciExamples and Recipes 6497db96d56Sopenharmony_ci-------------------- 6507db96d56Sopenharmony_ci 6517db96d56Sopenharmony_ciThis section describes some examples and recipes for making effective use of 6527db96d56Sopenharmony_cithe tools provided by :mod:`contextlib`. 6537db96d56Sopenharmony_ci 6547db96d56Sopenharmony_ci 6557db96d56Sopenharmony_ciSupporting a variable number of context managers 6567db96d56Sopenharmony_ci^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6577db96d56Sopenharmony_ci 6587db96d56Sopenharmony_ciThe primary use case for :class:`ExitStack` is the one given in the class 6597db96d56Sopenharmony_cidocumentation: supporting a variable number of context managers and other 6607db96d56Sopenharmony_cicleanup operations in a single :keyword:`with` statement. The variability 6617db96d56Sopenharmony_cimay come from the number of context managers needed being driven by user 6627db96d56Sopenharmony_ciinput (such as opening a user specified collection of files), or from 6637db96d56Sopenharmony_cisome of the context managers being optional:: 6647db96d56Sopenharmony_ci 6657db96d56Sopenharmony_ci with ExitStack() as stack: 6667db96d56Sopenharmony_ci for resource in resources: 6677db96d56Sopenharmony_ci stack.enter_context(resource) 6687db96d56Sopenharmony_ci if need_special_resource(): 6697db96d56Sopenharmony_ci special = acquire_special_resource() 6707db96d56Sopenharmony_ci stack.callback(release_special_resource, special) 6717db96d56Sopenharmony_ci # Perform operations that use the acquired resources 6727db96d56Sopenharmony_ci 6737db96d56Sopenharmony_ciAs shown, :class:`ExitStack` also makes it quite easy to use :keyword:`with` 6747db96d56Sopenharmony_cistatements to manage arbitrary resources that don't natively support the 6757db96d56Sopenharmony_cicontext management protocol. 6767db96d56Sopenharmony_ci 6777db96d56Sopenharmony_ci 6787db96d56Sopenharmony_ciCatching exceptions from ``__enter__`` methods 6797db96d56Sopenharmony_ci^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6807db96d56Sopenharmony_ci 6817db96d56Sopenharmony_ciIt is occasionally desirable to catch exceptions from an ``__enter__`` 6827db96d56Sopenharmony_cimethod implementation, *without* inadvertently catching exceptions from 6837db96d56Sopenharmony_cithe :keyword:`with` statement body or the context manager's ``__exit__`` 6847db96d56Sopenharmony_cimethod. By using :class:`ExitStack` the steps in the context management 6857db96d56Sopenharmony_ciprotocol can be separated slightly in order to allow this:: 6867db96d56Sopenharmony_ci 6877db96d56Sopenharmony_ci stack = ExitStack() 6887db96d56Sopenharmony_ci try: 6897db96d56Sopenharmony_ci x = stack.enter_context(cm) 6907db96d56Sopenharmony_ci except Exception: 6917db96d56Sopenharmony_ci # handle __enter__ exception 6927db96d56Sopenharmony_ci else: 6937db96d56Sopenharmony_ci with stack: 6947db96d56Sopenharmony_ci # Handle normal case 6957db96d56Sopenharmony_ci 6967db96d56Sopenharmony_ciActually needing to do this is likely to indicate that the underlying API 6977db96d56Sopenharmony_cishould be providing a direct resource management interface for use with 6987db96d56Sopenharmony_ci:keyword:`try`/:keyword:`except`/:keyword:`finally` statements, but not 6997db96d56Sopenharmony_ciall APIs are well designed in that regard. When a context manager is the 7007db96d56Sopenharmony_cionly resource management API provided, then :class:`ExitStack` can make it 7017db96d56Sopenharmony_cieasier to handle various situations that can't be handled directly in a 7027db96d56Sopenharmony_ci:keyword:`with` statement. 7037db96d56Sopenharmony_ci 7047db96d56Sopenharmony_ci 7057db96d56Sopenharmony_ciCleaning up in an ``__enter__`` implementation 7067db96d56Sopenharmony_ci^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 7077db96d56Sopenharmony_ci 7087db96d56Sopenharmony_ciAs noted in the documentation of :meth:`ExitStack.push`, this 7097db96d56Sopenharmony_cimethod can be useful in cleaning up an already allocated resource if later 7107db96d56Sopenharmony_cisteps in the :meth:`__enter__` implementation fail. 7117db96d56Sopenharmony_ci 7127db96d56Sopenharmony_ciHere's an example of doing this for a context manager that accepts resource 7137db96d56Sopenharmony_ciacquisition and release functions, along with an optional validation function, 7147db96d56Sopenharmony_ciand maps them to the context management protocol:: 7157db96d56Sopenharmony_ci 7167db96d56Sopenharmony_ci from contextlib import contextmanager, AbstractContextManager, ExitStack 7177db96d56Sopenharmony_ci 7187db96d56Sopenharmony_ci class ResourceManager(AbstractContextManager): 7197db96d56Sopenharmony_ci 7207db96d56Sopenharmony_ci def __init__(self, acquire_resource, release_resource, check_resource_ok=None): 7217db96d56Sopenharmony_ci self.acquire_resource = acquire_resource 7227db96d56Sopenharmony_ci self.release_resource = release_resource 7237db96d56Sopenharmony_ci if check_resource_ok is None: 7247db96d56Sopenharmony_ci def check_resource_ok(resource): 7257db96d56Sopenharmony_ci return True 7267db96d56Sopenharmony_ci self.check_resource_ok = check_resource_ok 7277db96d56Sopenharmony_ci 7287db96d56Sopenharmony_ci @contextmanager 7297db96d56Sopenharmony_ci def _cleanup_on_error(self): 7307db96d56Sopenharmony_ci with ExitStack() as stack: 7317db96d56Sopenharmony_ci stack.push(self) 7327db96d56Sopenharmony_ci yield 7337db96d56Sopenharmony_ci # The validation check passed and didn't raise an exception 7347db96d56Sopenharmony_ci # Accordingly, we want to keep the resource, and pass it 7357db96d56Sopenharmony_ci # back to our caller 7367db96d56Sopenharmony_ci stack.pop_all() 7377db96d56Sopenharmony_ci 7387db96d56Sopenharmony_ci def __enter__(self): 7397db96d56Sopenharmony_ci resource = self.acquire_resource() 7407db96d56Sopenharmony_ci with self._cleanup_on_error(): 7417db96d56Sopenharmony_ci if not self.check_resource_ok(resource): 7427db96d56Sopenharmony_ci msg = "Failed validation for {!r}" 7437db96d56Sopenharmony_ci raise RuntimeError(msg.format(resource)) 7447db96d56Sopenharmony_ci return resource 7457db96d56Sopenharmony_ci 7467db96d56Sopenharmony_ci def __exit__(self, *exc_details): 7477db96d56Sopenharmony_ci # We don't need to duplicate any of our resource release logic 7487db96d56Sopenharmony_ci self.release_resource() 7497db96d56Sopenharmony_ci 7507db96d56Sopenharmony_ci 7517db96d56Sopenharmony_ciReplacing any use of ``try-finally`` and flag variables 7527db96d56Sopenharmony_ci^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 7537db96d56Sopenharmony_ci 7547db96d56Sopenharmony_ciA pattern you will sometimes see is a ``try-finally`` statement with a flag 7557db96d56Sopenharmony_civariable to indicate whether or not the body of the ``finally`` clause should 7567db96d56Sopenharmony_cibe executed. In its simplest form (that can't already be handled just by 7577db96d56Sopenharmony_ciusing an ``except`` clause instead), it looks something like this:: 7587db96d56Sopenharmony_ci 7597db96d56Sopenharmony_ci cleanup_needed = True 7607db96d56Sopenharmony_ci try: 7617db96d56Sopenharmony_ci result = perform_operation() 7627db96d56Sopenharmony_ci if result: 7637db96d56Sopenharmony_ci cleanup_needed = False 7647db96d56Sopenharmony_ci finally: 7657db96d56Sopenharmony_ci if cleanup_needed: 7667db96d56Sopenharmony_ci cleanup_resources() 7677db96d56Sopenharmony_ci 7687db96d56Sopenharmony_ciAs with any ``try`` statement based code, this can cause problems for 7697db96d56Sopenharmony_cidevelopment and review, because the setup code and the cleanup code can end 7707db96d56Sopenharmony_ciup being separated by arbitrarily long sections of code. 7717db96d56Sopenharmony_ci 7727db96d56Sopenharmony_ci:class:`ExitStack` makes it possible to instead register a callback for 7737db96d56Sopenharmony_ciexecution at the end of a ``with`` statement, and then later decide to skip 7747db96d56Sopenharmony_ciexecuting that callback:: 7757db96d56Sopenharmony_ci 7767db96d56Sopenharmony_ci from contextlib import ExitStack 7777db96d56Sopenharmony_ci 7787db96d56Sopenharmony_ci with ExitStack() as stack: 7797db96d56Sopenharmony_ci stack.callback(cleanup_resources) 7807db96d56Sopenharmony_ci result = perform_operation() 7817db96d56Sopenharmony_ci if result: 7827db96d56Sopenharmony_ci stack.pop_all() 7837db96d56Sopenharmony_ci 7847db96d56Sopenharmony_ciThis allows the intended cleanup up behaviour to be made explicit up front, 7857db96d56Sopenharmony_cirather than requiring a separate flag variable. 7867db96d56Sopenharmony_ci 7877db96d56Sopenharmony_ciIf a particular application uses this pattern a lot, it can be simplified 7887db96d56Sopenharmony_cieven further by means of a small helper class:: 7897db96d56Sopenharmony_ci 7907db96d56Sopenharmony_ci from contextlib import ExitStack 7917db96d56Sopenharmony_ci 7927db96d56Sopenharmony_ci class Callback(ExitStack): 7937db96d56Sopenharmony_ci def __init__(self, callback, /, *args, **kwds): 7947db96d56Sopenharmony_ci super().__init__() 7957db96d56Sopenharmony_ci self.callback(callback, *args, **kwds) 7967db96d56Sopenharmony_ci 7977db96d56Sopenharmony_ci def cancel(self): 7987db96d56Sopenharmony_ci self.pop_all() 7997db96d56Sopenharmony_ci 8007db96d56Sopenharmony_ci with Callback(cleanup_resources) as cb: 8017db96d56Sopenharmony_ci result = perform_operation() 8027db96d56Sopenharmony_ci if result: 8037db96d56Sopenharmony_ci cb.cancel() 8047db96d56Sopenharmony_ci 8057db96d56Sopenharmony_ciIf the resource cleanup isn't already neatly bundled into a standalone 8067db96d56Sopenharmony_cifunction, then it is still possible to use the decorator form of 8077db96d56Sopenharmony_ci:meth:`ExitStack.callback` to declare the resource cleanup in 8087db96d56Sopenharmony_ciadvance:: 8097db96d56Sopenharmony_ci 8107db96d56Sopenharmony_ci from contextlib import ExitStack 8117db96d56Sopenharmony_ci 8127db96d56Sopenharmony_ci with ExitStack() as stack: 8137db96d56Sopenharmony_ci @stack.callback 8147db96d56Sopenharmony_ci def cleanup_resources(): 8157db96d56Sopenharmony_ci ... 8167db96d56Sopenharmony_ci result = perform_operation() 8177db96d56Sopenharmony_ci if result: 8187db96d56Sopenharmony_ci stack.pop_all() 8197db96d56Sopenharmony_ci 8207db96d56Sopenharmony_ciDue to the way the decorator protocol works, a callback function 8217db96d56Sopenharmony_cideclared this way cannot take any parameters. Instead, any resources to 8227db96d56Sopenharmony_cibe released must be accessed as closure variables. 8237db96d56Sopenharmony_ci 8247db96d56Sopenharmony_ci 8257db96d56Sopenharmony_ciUsing a context manager as a function decorator 8267db96d56Sopenharmony_ci^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 8277db96d56Sopenharmony_ci 8287db96d56Sopenharmony_ci:class:`ContextDecorator` makes it possible to use a context manager in 8297db96d56Sopenharmony_ciboth an ordinary ``with`` statement and also as a function decorator. 8307db96d56Sopenharmony_ci 8317db96d56Sopenharmony_ciFor example, it is sometimes useful to wrap functions or groups of statements 8327db96d56Sopenharmony_ciwith a logger that can track the time of entry and time of exit. Rather than 8337db96d56Sopenharmony_ciwriting both a function decorator and a context manager for the task, 8347db96d56Sopenharmony_ciinheriting from :class:`ContextDecorator` provides both capabilities in a 8357db96d56Sopenharmony_cisingle definition:: 8367db96d56Sopenharmony_ci 8377db96d56Sopenharmony_ci from contextlib import ContextDecorator 8387db96d56Sopenharmony_ci import logging 8397db96d56Sopenharmony_ci 8407db96d56Sopenharmony_ci logging.basicConfig(level=logging.INFO) 8417db96d56Sopenharmony_ci 8427db96d56Sopenharmony_ci class track_entry_and_exit(ContextDecorator): 8437db96d56Sopenharmony_ci def __init__(self, name): 8447db96d56Sopenharmony_ci self.name = name 8457db96d56Sopenharmony_ci 8467db96d56Sopenharmony_ci def __enter__(self): 8477db96d56Sopenharmony_ci logging.info('Entering: %s', self.name) 8487db96d56Sopenharmony_ci 8497db96d56Sopenharmony_ci def __exit__(self, exc_type, exc, exc_tb): 8507db96d56Sopenharmony_ci logging.info('Exiting: %s', self.name) 8517db96d56Sopenharmony_ci 8527db96d56Sopenharmony_ciInstances of this class can be used as both a context manager:: 8537db96d56Sopenharmony_ci 8547db96d56Sopenharmony_ci with track_entry_and_exit('widget loader'): 8557db96d56Sopenharmony_ci print('Some time consuming activity goes here') 8567db96d56Sopenharmony_ci load_widget() 8577db96d56Sopenharmony_ci 8587db96d56Sopenharmony_ciAnd also as a function decorator:: 8597db96d56Sopenharmony_ci 8607db96d56Sopenharmony_ci @track_entry_and_exit('widget loader') 8617db96d56Sopenharmony_ci def activity(): 8627db96d56Sopenharmony_ci print('Some time consuming activity goes here') 8637db96d56Sopenharmony_ci load_widget() 8647db96d56Sopenharmony_ci 8657db96d56Sopenharmony_ciNote that there is one additional limitation when using context managers 8667db96d56Sopenharmony_cias function decorators: there's no way to access the return value of 8677db96d56Sopenharmony_ci:meth:`__enter__`. If that value is needed, then it is still necessary to use 8687db96d56Sopenharmony_cian explicit ``with`` statement. 8697db96d56Sopenharmony_ci 8707db96d56Sopenharmony_ci.. seealso:: 8717db96d56Sopenharmony_ci 8727db96d56Sopenharmony_ci :pep:`343` - The "with" statement 8737db96d56Sopenharmony_ci The specification, background, and examples for the Python :keyword:`with` 8747db96d56Sopenharmony_ci statement. 8757db96d56Sopenharmony_ci 8767db96d56Sopenharmony_ci.. _single-use-reusable-and-reentrant-cms: 8777db96d56Sopenharmony_ci 8787db96d56Sopenharmony_ciSingle use, reusable and reentrant context managers 8797db96d56Sopenharmony_ci--------------------------------------------------- 8807db96d56Sopenharmony_ci 8817db96d56Sopenharmony_ciMost context managers are written in a way that means they can only be 8827db96d56Sopenharmony_ciused effectively in a :keyword:`with` statement once. These single use 8837db96d56Sopenharmony_cicontext managers must be created afresh each time they're used - 8847db96d56Sopenharmony_ciattempting to use them a second time will trigger an exception or 8857db96d56Sopenharmony_ciotherwise not work correctly. 8867db96d56Sopenharmony_ci 8877db96d56Sopenharmony_ciThis common limitation means that it is generally advisable to create 8887db96d56Sopenharmony_cicontext managers directly in the header of the :keyword:`with` statement 8897db96d56Sopenharmony_ciwhere they are used (as shown in all of the usage examples above). 8907db96d56Sopenharmony_ci 8917db96d56Sopenharmony_ciFiles are an example of effectively single use context managers, since 8927db96d56Sopenharmony_cithe first :keyword:`with` statement will close the file, preventing any 8937db96d56Sopenharmony_cifurther IO operations using that file object. 8947db96d56Sopenharmony_ci 8957db96d56Sopenharmony_ciContext managers created using :func:`contextmanager` are also single use 8967db96d56Sopenharmony_cicontext managers, and will complain about the underlying generator failing 8977db96d56Sopenharmony_cito yield if an attempt is made to use them a second time:: 8987db96d56Sopenharmony_ci 8997db96d56Sopenharmony_ci >>> from contextlib import contextmanager 9007db96d56Sopenharmony_ci >>> @contextmanager 9017db96d56Sopenharmony_ci ... def singleuse(): 9027db96d56Sopenharmony_ci ... print("Before") 9037db96d56Sopenharmony_ci ... yield 9047db96d56Sopenharmony_ci ... print("After") 9057db96d56Sopenharmony_ci ... 9067db96d56Sopenharmony_ci >>> cm = singleuse() 9077db96d56Sopenharmony_ci >>> with cm: 9087db96d56Sopenharmony_ci ... pass 9097db96d56Sopenharmony_ci ... 9107db96d56Sopenharmony_ci Before 9117db96d56Sopenharmony_ci After 9127db96d56Sopenharmony_ci >>> with cm: 9137db96d56Sopenharmony_ci ... pass 9147db96d56Sopenharmony_ci ... 9157db96d56Sopenharmony_ci Traceback (most recent call last): 9167db96d56Sopenharmony_ci ... 9177db96d56Sopenharmony_ci RuntimeError: generator didn't yield 9187db96d56Sopenharmony_ci 9197db96d56Sopenharmony_ci 9207db96d56Sopenharmony_ci.. _reentrant-cms: 9217db96d56Sopenharmony_ci 9227db96d56Sopenharmony_ciReentrant context managers 9237db96d56Sopenharmony_ci^^^^^^^^^^^^^^^^^^^^^^^^^^ 9247db96d56Sopenharmony_ci 9257db96d56Sopenharmony_ciMore sophisticated context managers may be "reentrant". These context 9267db96d56Sopenharmony_cimanagers can not only be used in multiple :keyword:`with` statements, 9277db96d56Sopenharmony_cibut may also be used *inside* a :keyword:`!with` statement that is already 9287db96d56Sopenharmony_ciusing the same context manager. 9297db96d56Sopenharmony_ci 9307db96d56Sopenharmony_ci:class:`threading.RLock` is an example of a reentrant context manager, as are 9317db96d56Sopenharmony_ci:func:`suppress`, :func:`redirect_stdout`, and :func:`chdir`. Here's a very 9327db96d56Sopenharmony_cisimple example of reentrant use:: 9337db96d56Sopenharmony_ci 9347db96d56Sopenharmony_ci >>> from contextlib import redirect_stdout 9357db96d56Sopenharmony_ci >>> from io import StringIO 9367db96d56Sopenharmony_ci >>> stream = StringIO() 9377db96d56Sopenharmony_ci >>> write_to_stream = redirect_stdout(stream) 9387db96d56Sopenharmony_ci >>> with write_to_stream: 9397db96d56Sopenharmony_ci ... print("This is written to the stream rather than stdout") 9407db96d56Sopenharmony_ci ... with write_to_stream: 9417db96d56Sopenharmony_ci ... print("This is also written to the stream") 9427db96d56Sopenharmony_ci ... 9437db96d56Sopenharmony_ci >>> print("This is written directly to stdout") 9447db96d56Sopenharmony_ci This is written directly to stdout 9457db96d56Sopenharmony_ci >>> print(stream.getvalue()) 9467db96d56Sopenharmony_ci This is written to the stream rather than stdout 9477db96d56Sopenharmony_ci This is also written to the stream 9487db96d56Sopenharmony_ci 9497db96d56Sopenharmony_ciReal world examples of reentrancy are more likely to involve multiple 9507db96d56Sopenharmony_cifunctions calling each other and hence be far more complicated than this 9517db96d56Sopenharmony_ciexample. 9527db96d56Sopenharmony_ci 9537db96d56Sopenharmony_ciNote also that being reentrant is *not* the same thing as being thread safe. 9547db96d56Sopenharmony_ci:func:`redirect_stdout`, for example, is definitely not thread safe, as it 9557db96d56Sopenharmony_cimakes a global modification to the system state by binding :data:`sys.stdout` 9567db96d56Sopenharmony_cito a different stream. 9577db96d56Sopenharmony_ci 9587db96d56Sopenharmony_ci 9597db96d56Sopenharmony_ci.. _reusable-cms: 9607db96d56Sopenharmony_ci 9617db96d56Sopenharmony_ciReusable context managers 9627db96d56Sopenharmony_ci^^^^^^^^^^^^^^^^^^^^^^^^^ 9637db96d56Sopenharmony_ci 9647db96d56Sopenharmony_ciDistinct from both single use and reentrant context managers are "reusable" 9657db96d56Sopenharmony_cicontext managers (or, to be completely explicit, "reusable, but not 9667db96d56Sopenharmony_cireentrant" context managers, since reentrant context managers are also 9677db96d56Sopenharmony_cireusable). These context managers support being used multiple times, but 9687db96d56Sopenharmony_ciwill fail (or otherwise not work correctly) if the specific context manager 9697db96d56Sopenharmony_ciinstance has already been used in a containing with statement. 9707db96d56Sopenharmony_ci 9717db96d56Sopenharmony_ci:class:`threading.Lock` is an example of a reusable, but not reentrant, 9727db96d56Sopenharmony_cicontext manager (for a reentrant lock, it is necessary to use 9737db96d56Sopenharmony_ci:class:`threading.RLock` instead). 9747db96d56Sopenharmony_ci 9757db96d56Sopenharmony_ciAnother example of a reusable, but not reentrant, context manager is 9767db96d56Sopenharmony_ci:class:`ExitStack`, as it invokes *all* currently registered callbacks 9777db96d56Sopenharmony_ciwhen leaving any with statement, regardless of where those callbacks 9787db96d56Sopenharmony_ciwere added:: 9797db96d56Sopenharmony_ci 9807db96d56Sopenharmony_ci >>> from contextlib import ExitStack 9817db96d56Sopenharmony_ci >>> stack = ExitStack() 9827db96d56Sopenharmony_ci >>> with stack: 9837db96d56Sopenharmony_ci ... stack.callback(print, "Callback: from first context") 9847db96d56Sopenharmony_ci ... print("Leaving first context") 9857db96d56Sopenharmony_ci ... 9867db96d56Sopenharmony_ci Leaving first context 9877db96d56Sopenharmony_ci Callback: from first context 9887db96d56Sopenharmony_ci >>> with stack: 9897db96d56Sopenharmony_ci ... stack.callback(print, "Callback: from second context") 9907db96d56Sopenharmony_ci ... print("Leaving second context") 9917db96d56Sopenharmony_ci ... 9927db96d56Sopenharmony_ci Leaving second context 9937db96d56Sopenharmony_ci Callback: from second context 9947db96d56Sopenharmony_ci >>> with stack: 9957db96d56Sopenharmony_ci ... stack.callback(print, "Callback: from outer context") 9967db96d56Sopenharmony_ci ... with stack: 9977db96d56Sopenharmony_ci ... stack.callback(print, "Callback: from inner context") 9987db96d56Sopenharmony_ci ... print("Leaving inner context") 9997db96d56Sopenharmony_ci ... print("Leaving outer context") 10007db96d56Sopenharmony_ci ... 10017db96d56Sopenharmony_ci Leaving inner context 10027db96d56Sopenharmony_ci Callback: from inner context 10037db96d56Sopenharmony_ci Callback: from outer context 10047db96d56Sopenharmony_ci Leaving outer context 10057db96d56Sopenharmony_ci 10067db96d56Sopenharmony_ciAs the output from the example shows, reusing a single stack object across 10077db96d56Sopenharmony_cimultiple with statements works correctly, but attempting to nest them 10087db96d56Sopenharmony_ciwill cause the stack to be cleared at the end of the innermost with 10097db96d56Sopenharmony_cistatement, which is unlikely to be desirable behaviour. 10107db96d56Sopenharmony_ci 10117db96d56Sopenharmony_ciUsing separate :class:`ExitStack` instances instead of reusing a single 10127db96d56Sopenharmony_ciinstance avoids that problem:: 10137db96d56Sopenharmony_ci 10147db96d56Sopenharmony_ci >>> from contextlib import ExitStack 10157db96d56Sopenharmony_ci >>> with ExitStack() as outer_stack: 10167db96d56Sopenharmony_ci ... outer_stack.callback(print, "Callback: from outer context") 10177db96d56Sopenharmony_ci ... with ExitStack() as inner_stack: 10187db96d56Sopenharmony_ci ... inner_stack.callback(print, "Callback: from inner context") 10197db96d56Sopenharmony_ci ... print("Leaving inner context") 10207db96d56Sopenharmony_ci ... print("Leaving outer context") 10217db96d56Sopenharmony_ci ... 10227db96d56Sopenharmony_ci Leaving inner context 10237db96d56Sopenharmony_ci Callback: from inner context 10247db96d56Sopenharmony_ci Leaving outer context 10257db96d56Sopenharmony_ci Callback: from outer context 1026