Serhiy Storchaka | 2b57c43 | 2018-12-19 08:09:46 +0200 | [diff] [blame] | 1 | :mod:`!contextlib` --- Utilities for :keyword:`!with`\ -statement contexts |
| 2 | ========================================================================== |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 3 | |
| 4 | .. module:: contextlib |
| 5 | :synopsis: Utilities for with-statement contexts. |
| 6 | |
Raymond Hettinger | 1048094 | 2011-01-10 03:26:08 +0000 | [diff] [blame] | 7 | **Source code:** :source:`Lib/contextlib.py` |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 8 | |
Raymond Hettinger | 4f707fd | 2011-01-10 19:54:11 +0000 | [diff] [blame] | 9 | -------------- |
| 10 | |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 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 | |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 15 | |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 16 | Utilities |
| 17 | --------- |
| 18 | |
| 19 | Functions and classes provided: |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 20 | |
Brett Cannon | 9e080e0 | 2016-04-08 12:15:27 -0700 | [diff] [blame] | 21 | .. class:: AbstractContextManager |
| 22 | |
Brett Cannon | 516f546 | 2016-06-09 15:55:52 -0700 | [diff] [blame] | 23 | An :term:`abstract base class` for classes that implement |
Brett Cannon | 9e080e0 | 2016-04-08 12:15:27 -0700 | [diff] [blame] | 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 | |
Jelle Zijlstra | 176baa3 | 2017-12-13 17:19:17 -0800 | [diff] [blame] | 32 | .. class:: AbstractAsyncContextManager |
| 33 | |
| 34 | An :term:`abstract base class` for classes that implement |
| 35 | :meth:`object.__aenter__` and :meth:`object.__aexit__`. A default |
| 36 | implementation for :meth:`object.__aenter__` is provided which returns |
| 37 | ``self`` while :meth:`object.__aexit__` is an abstract method which by default |
| 38 | returns ``None``. See also the definition of |
| 39 | :ref:`async-context-managers`. |
| 40 | |
| 41 | .. versionadded:: 3.7 |
| 42 | |
Brett Cannon | 9e080e0 | 2016-04-08 12:15:27 -0700 | [diff] [blame] | 43 | |
Georg Brandl | 8a1caa2 | 2010-07-29 16:01:11 +0000 | [diff] [blame] | 44 | .. decorator:: contextmanager |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 45 | |
Christian Heimes | d8654cf | 2007-12-02 15:22:16 +0000 | [diff] [blame] | 46 | This function is a :term:`decorator` that can be used to define a factory |
| 47 | function for :keyword:`with` statement context managers, without needing to |
| 48 | create a class or separate :meth:`__enter__` and :meth:`__exit__` methods. |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 49 | |
Matthias Bussonnier | bde782b | 2018-07-23 14:10:56 -0700 | [diff] [blame] | 50 | While many objects natively support use in with statements, sometimes a |
| 51 | resource needs to be managed that isn't a context manager in its own right, |
| 52 | and doesn't implement a ``close()`` method for use with ``contextlib.closing`` |
| 53 | |
| 54 | An abstract example would be the following to ensure correct resource |
| 55 | management:: |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 56 | |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 57 | from contextlib import contextmanager |
| 58 | |
| 59 | @contextmanager |
Matthias Bussonnier | bde782b | 2018-07-23 14:10:56 -0700 | [diff] [blame] | 60 | def managed_resource(*args, **kwds): |
| 61 | # Code to acquire resource, e.g.: |
| 62 | resource = acquire_resource(*args, **kwds) |
| 63 | try: |
| 64 | yield resource |
| 65 | finally: |
| 66 | # Code to release resource, e.g.: |
| 67 | release_resource(resource) |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 68 | |
Matthias Bussonnier | bde782b | 2018-07-23 14:10:56 -0700 | [diff] [blame] | 69 | >>> with managed_resource(timeout=3600) as resource: |
| 70 | ... # Resource is released at the end of this block, |
| 71 | ... # even if code in the block raises an exception |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 72 | |
Georg Brandl | 9afde1c | 2007-11-01 20:32:30 +0000 | [diff] [blame] | 73 | The function being decorated must return a :term:`generator`-iterator when |
| 74 | called. This iterator must yield exactly one value, which will be bound to |
Serhiy Storchaka | 2b57c43 | 2018-12-19 08:09:46 +0200 | [diff] [blame] | 75 | the targets in the :keyword:`with` statement's :keyword:`!as` clause, if any. |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 76 | |
| 77 | At the point where the generator yields, the block nested in the :keyword:`with` |
| 78 | statement is executed. The generator is then resumed after the block is exited. |
| 79 | If an unhandled exception occurs in the block, it is reraised inside the |
| 80 | generator at the point where the yield occurred. Thus, you can use a |
| 81 | :keyword:`try`...\ :keyword:`except`...\ :keyword:`finally` statement to trap |
| 82 | the error (if any), or ensure that some cleanup takes place. If an exception is |
| 83 | trapped merely in order to log it or to perform some action (rather than to |
| 84 | suppress it entirely), the generator must reraise that exception. Otherwise the |
Serhiy Storchaka | 2b57c43 | 2018-12-19 08:09:46 +0200 | [diff] [blame] | 85 | generator context manager will indicate to the :keyword:`!with` statement that |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 86 | the exception has been handled, and execution will resume with the statement |
Serhiy Storchaka | 2b57c43 | 2018-12-19 08:09:46 +0200 | [diff] [blame] | 87 | immediately following the :keyword:`!with` statement. |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 88 | |
Nick Coghlan | 0ded3e3 | 2011-05-05 23:49:25 +1000 | [diff] [blame] | 89 | :func:`contextmanager` uses :class:`ContextDecorator` so the context managers |
| 90 | it creates can be used as decorators as well as in :keyword:`with` statements. |
| 91 | When used as a decorator, a new generator instance is implicitly created on |
| 92 | each function call (this allows the otherwise "one-shot" context managers |
| 93 | created by :func:`contextmanager` to meet the requirement that context |
| 94 | managers support multiple invocations in order to be used as decorators). |
Michael Foord | b3a8984 | 2010-06-30 12:17:50 +0000 | [diff] [blame] | 95 | |
| 96 | .. versionchanged:: 3.2 |
| 97 | Use of :class:`ContextDecorator`. |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 98 | |
Georg Brandl | 86e78d1 | 2010-07-18 13:43:32 +0000 | [diff] [blame] | 99 | |
Jelle Zijlstra | 2e62469 | 2017-04-30 18:25:58 -0700 | [diff] [blame] | 100 | .. decorator:: asynccontextmanager |
| 101 | |
| 102 | Similar to :func:`~contextlib.contextmanager`, but creates an |
| 103 | :ref:`asynchronous context manager <async-context-managers>`. |
| 104 | |
| 105 | This function is a :term:`decorator` that can be used to define a factory |
| 106 | function for :keyword:`async with` statement asynchronous context managers, |
| 107 | without needing to create a class or separate :meth:`__aenter__` and |
| 108 | :meth:`__aexit__` methods. It must be applied to an :term:`asynchronous |
| 109 | generator` function. |
| 110 | |
| 111 | A simple example:: |
| 112 | |
| 113 | from contextlib import asynccontextmanager |
| 114 | |
| 115 | @asynccontextmanager |
| 116 | async def get_connection(): |
| 117 | conn = await acquire_db_connection() |
| 118 | try: |
Alexander Vasin | 416cbce | 2018-08-25 05:38:11 +0300 | [diff] [blame] | 119 | yield conn |
Jelle Zijlstra | 2e62469 | 2017-04-30 18:25:58 -0700 | [diff] [blame] | 120 | finally: |
| 121 | await release_db_connection(conn) |
| 122 | |
| 123 | async def get_all_users(): |
| 124 | async with get_connection() as conn: |
| 125 | return conn.query('SELECT ...') |
| 126 | |
| 127 | .. versionadded:: 3.7 |
| 128 | |
Kazantcev Andrey | 178695b | 2020-11-05 11:52:24 +0300 | [diff] [blame] | 129 | Context managers defined with :func:`asynccontextmanager` can be used |
| 130 | either as decorators or with :keyword:`async with` statements:: |
| 131 | |
| 132 | import time |
| 133 | |
| 134 | async def timeit(): |
| 135 | now = time.monotonic() |
| 136 | try: |
| 137 | yield |
| 138 | finally: |
| 139 | print(f'it took {time.monotonic() - now}s to run') |
| 140 | |
| 141 | @timeit() |
| 142 | async def main(): |
| 143 | # ... async code ... |
| 144 | |
| 145 | When used as a decorator, a new generator instance is implicitly created on |
| 146 | each function call. This allows the otherwise "one-shot" context managers |
| 147 | created by :func:`asynccontextmanager` to meet the requirement that context |
| 148 | managers support multiple invocations in order to be used as decorators. |
| 149 | |
| 150 | .. versionchanged:: 3.10 |
| 151 | Async context managers created with :func:`asynccontextmanager` can |
| 152 | be used as decorators. |
| 153 | |
Jelle Zijlstra | 2e62469 | 2017-04-30 18:25:58 -0700 | [diff] [blame] | 154 | |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 155 | .. function:: closing(thing) |
| 156 | |
| 157 | Return a context manager that closes *thing* upon completion of the block. This |
| 158 | is basically equivalent to:: |
| 159 | |
| 160 | from contextlib import contextmanager |
| 161 | |
| 162 | @contextmanager |
| 163 | def closing(thing): |
| 164 | try: |
| 165 | yield thing |
| 166 | finally: |
| 167 | thing.close() |
| 168 | |
| 169 | And lets you write code like this:: |
| 170 | |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 171 | from contextlib import closing |
Georg Brandl | 0f7ede4 | 2008-06-23 11:23:31 +0000 | [diff] [blame] | 172 | from urllib.request import urlopen |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 173 | |
Georg Brandl | 0f7ede4 | 2008-06-23 11:23:31 +0000 | [diff] [blame] | 174 | with closing(urlopen('http://www.python.org')) as page: |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 175 | for line in page: |
Georg Brandl | 6911e3c | 2007-09-04 07:15:32 +0000 | [diff] [blame] | 176 | print(line) |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 177 | |
| 178 | without needing to explicitly close ``page``. Even if an error occurs, |
| 179 | ``page.close()`` will be called when the :keyword:`with` block is exited. |
| 180 | |
Georg Brandl | a7c17e5 | 2013-10-13 22:25:10 +0200 | [diff] [blame] | 181 | |
Joongi Kim | 6e8dcda | 2020-11-02 17:02:48 +0900 | [diff] [blame] | 182 | .. class:: aclosing(thing) |
| 183 | |
| 184 | Return an async context manager that calls the ``aclose()`` method of *thing* |
| 185 | upon completion of the block. This is basically equivalent to:: |
| 186 | |
| 187 | from contextlib import asynccontextmanager |
| 188 | |
| 189 | @asynccontextmanager |
| 190 | async def aclosing(thing): |
| 191 | try: |
| 192 | yield thing |
| 193 | finally: |
| 194 | await thing.aclose() |
| 195 | |
| 196 | Significantly, ``aclosing()`` supports deterministic cleanup of async |
| 197 | generators when they happen to exit early by :keyword:`break` or an |
| 198 | exception. For example:: |
| 199 | |
| 200 | from contextlib import aclosing |
| 201 | |
| 202 | async with aclosing(my_generator()) as values: |
| 203 | async for value in values: |
| 204 | if value == 42: |
| 205 | break |
| 206 | |
| 207 | This pattern ensures that the generator's async exit code is executed in |
| 208 | the same context as its iterations (so that exceptions and context |
| 209 | variables work as expected, and the exit code isn't run after the |
| 210 | lifetime of some task it depends on). |
| 211 | |
| 212 | .. versionadded:: 3.10 |
| 213 | |
| 214 | |
Jesse-Bakker | 0784a2e | 2017-11-23 01:23:28 +0100 | [diff] [blame] | 215 | .. _simplifying-support-for-single-optional-context-managers: |
| 216 | |
| 217 | .. function:: nullcontext(enter_result=None) |
| 218 | |
Daniel Porteous | c287545 | 2018-07-09 06:49:29 -0700 | [diff] [blame] | 219 | Return a context manager that returns *enter_result* from ``__enter__``, but |
Jesse-Bakker | 0784a2e | 2017-11-23 01:23:28 +0100 | [diff] [blame] | 220 | otherwise does nothing. It is intended to be used as a stand-in for an |
| 221 | optional context manager, for example:: |
| 222 | |
Daniel Porteous | c287545 | 2018-07-09 06:49:29 -0700 | [diff] [blame] | 223 | def myfunction(arg, ignore_exceptions=False): |
| 224 | if ignore_exceptions: |
| 225 | # Use suppress to ignore all exceptions. |
| 226 | cm = contextlib.suppress(Exception) |
| 227 | else: |
| 228 | # Do not ignore any exceptions, cm has no effect. |
| 229 | cm = contextlib.nullcontext() |
| 230 | with cm: |
| 231 | # Do something |
| 232 | |
| 233 | An example using *enter_result*:: |
| 234 | |
Jesse-Bakker | 0784a2e | 2017-11-23 01:23:28 +0100 | [diff] [blame] | 235 | def process_file(file_or_path): |
| 236 | if isinstance(file_or_path, str): |
| 237 | # If string, open file |
| 238 | cm = open(file_or_path) |
| 239 | else: |
| 240 | # Caller is responsible for closing file |
| 241 | cm = nullcontext(file_or_path) |
| 242 | |
| 243 | with cm as file: |
| 244 | # Perform processing on the file |
| 245 | |
Tom Gringauz | a117167 | 2020-11-09 14:34:07 +0200 | [diff] [blame] | 246 | It can also be used as a stand-in for |
| 247 | :ref:`asynchronous context managers <async-context-managers>`:: |
| 248 | |
| 249 | async def send_http(session=None): |
| 250 | if not session: |
| 251 | # If no http session, create it with aiohttp |
| 252 | cm = aiohttp.ClientSession() |
| 253 | else: |
| 254 | # Caller is responsible for closing the session |
| 255 | cm = nullcontext(session) |
| 256 | |
| 257 | async with cm as session: |
| 258 | # Send http requests with session |
| 259 | |
Jesse-Bakker | 0784a2e | 2017-11-23 01:23:28 +0100 | [diff] [blame] | 260 | .. versionadded:: 3.7 |
| 261 | |
Tom Gringauz | a117167 | 2020-11-09 14:34:07 +0200 | [diff] [blame] | 262 | .. versionchanged:: 3.10 |
| 263 | :term:`asynchronous context manager` support was added. |
| 264 | |
| 265 | |
Jesse-Bakker | 0784a2e | 2017-11-23 01:23:28 +0100 | [diff] [blame] | 266 | |
Nick Coghlan | 240f86d | 2013-10-17 23:40:57 +1000 | [diff] [blame] | 267 | .. function:: suppress(*exceptions) |
Raymond Hettinger | e318a88 | 2013-03-10 22:26:51 -0700 | [diff] [blame] | 268 | |
Nick Coghlan | 240f86d | 2013-10-17 23:40:57 +1000 | [diff] [blame] | 269 | Return a context manager that suppresses any of the specified exceptions |
| 270 | if they occur in the body of a with statement and then resumes execution |
| 271 | with the first statement following the end of the with statement. |
Raymond Hettinger | e318a88 | 2013-03-10 22:26:51 -0700 | [diff] [blame] | 272 | |
Nick Coghlan | 240f86d | 2013-10-17 23:40:57 +1000 | [diff] [blame] | 273 | As with any other mechanism that completely suppresses exceptions, this |
| 274 | context manager should be used only to cover very specific errors where |
| 275 | silently continuing with program execution is known to be the right |
| 276 | thing to do. |
Nick Coghlan | b4534ae | 2013-10-13 23:23:08 +1000 | [diff] [blame] | 277 | |
Raymond Hettinger | e318a88 | 2013-03-10 22:26:51 -0700 | [diff] [blame] | 278 | For example:: |
| 279 | |
Nick Coghlan | 240f86d | 2013-10-17 23:40:57 +1000 | [diff] [blame] | 280 | from contextlib import suppress |
Raymond Hettinger | e318a88 | 2013-03-10 22:26:51 -0700 | [diff] [blame] | 281 | |
Nick Coghlan | 240f86d | 2013-10-17 23:40:57 +1000 | [diff] [blame] | 282 | with suppress(FileNotFoundError): |
Raymond Hettinger | e318a88 | 2013-03-10 22:26:51 -0700 | [diff] [blame] | 283 | os.remove('somefile.tmp') |
| 284 | |
Nick Coghlan | 240f86d | 2013-10-17 23:40:57 +1000 | [diff] [blame] | 285 | with suppress(FileNotFoundError): |
| 286 | os.remove('someotherfile.tmp') |
| 287 | |
Raymond Hettinger | e318a88 | 2013-03-10 22:26:51 -0700 | [diff] [blame] | 288 | This code is equivalent to:: |
| 289 | |
| 290 | try: |
| 291 | os.remove('somefile.tmp') |
Nick Coghlan | b4534ae | 2013-10-13 23:23:08 +1000 | [diff] [blame] | 292 | except FileNotFoundError: |
Raymond Hettinger | e318a88 | 2013-03-10 22:26:51 -0700 | [diff] [blame] | 293 | pass |
| 294 | |
Nick Coghlan | 240f86d | 2013-10-17 23:40:57 +1000 | [diff] [blame] | 295 | try: |
| 296 | os.remove('someotherfile.tmp') |
| 297 | except FileNotFoundError: |
| 298 | pass |
| 299 | |
Nick Coghlan | 8608d26 | 2013-10-20 00:30:51 +1000 | [diff] [blame] | 300 | This context manager is :ref:`reentrant <reentrant-cms>`. |
| 301 | |
Raymond Hettinger | e318a88 | 2013-03-10 22:26:51 -0700 | [diff] [blame] | 302 | .. versionadded:: 3.4 |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 303 | |
Nick Coghlan | b4534ae | 2013-10-13 23:23:08 +1000 | [diff] [blame] | 304 | |
Raymond Hettinger | 088cbf2 | 2013-10-10 00:46:57 -0700 | [diff] [blame] | 305 | .. function:: redirect_stdout(new_target) |
| 306 | |
| 307 | Context manager for temporarily redirecting :data:`sys.stdout` to |
| 308 | another file or file-like object. |
| 309 | |
| 310 | This tool adds flexibility to existing functions or classes whose output |
| 311 | is hardwired to stdout. |
| 312 | |
| 313 | For example, the output of :func:`help` normally is sent to *sys.stdout*. |
Serhiy Storchaka | d65c949 | 2015-11-02 14:10:23 +0200 | [diff] [blame] | 314 | You can capture that output in a string by redirecting the output to an |
Raymond Hettinger | 088cbf2 | 2013-10-10 00:46:57 -0700 | [diff] [blame] | 315 | :class:`io.StringIO` object:: |
| 316 | |
| 317 | f = io.StringIO() |
| 318 | with redirect_stdout(f): |
| 319 | help(pow) |
| 320 | s = f.getvalue() |
| 321 | |
| 322 | To send the output of :func:`help` to a file on disk, redirect the output |
| 323 | to a regular file:: |
| 324 | |
| 325 | with open('help.txt', 'w') as f: |
| 326 | with redirect_stdout(f): |
| 327 | help(pow) |
| 328 | |
| 329 | To send the output of :func:`help` to *sys.stderr*:: |
| 330 | |
| 331 | with redirect_stdout(sys.stderr): |
| 332 | help(pow) |
| 333 | |
Nick Coghlan | b4534ae | 2013-10-13 23:23:08 +1000 | [diff] [blame] | 334 | Note that the global side effect on :data:`sys.stdout` means that this |
| 335 | context manager is not suitable for use in library code and most threaded |
| 336 | applications. It also has no effect on the output of subprocesses. |
| 337 | However, it is still a useful approach for many utility scripts. |
| 338 | |
Nick Coghlan | 36d8ef9 | 2014-10-12 10:25:00 +1000 | [diff] [blame] | 339 | This context manager is :ref:`reentrant <reentrant-cms>`. |
Nick Coghlan | 8608d26 | 2013-10-20 00:30:51 +1000 | [diff] [blame] | 340 | |
Raymond Hettinger | 088cbf2 | 2013-10-10 00:46:57 -0700 | [diff] [blame] | 341 | .. versionadded:: 3.4 |
| 342 | |
Georg Brandl | a7c17e5 | 2013-10-13 22:25:10 +0200 | [diff] [blame] | 343 | |
Berker Peksag | bb44fe0 | 2014-11-28 23:28:06 +0200 | [diff] [blame] | 344 | .. function:: redirect_stderr(new_target) |
| 345 | |
| 346 | Similar to :func:`~contextlib.redirect_stdout` but redirecting |
| 347 | :data:`sys.stderr` to another file or file-like object. |
| 348 | |
| 349 | This context manager is :ref:`reentrant <reentrant-cms>`. |
| 350 | |
| 351 | .. versionadded:: 3.5 |
| 352 | |
| 353 | |
Michael Foord | b3a8984 | 2010-06-30 12:17:50 +0000 | [diff] [blame] | 354 | .. class:: ContextDecorator() |
| 355 | |
| 356 | A base class that enables a context manager to also be used as a decorator. |
| 357 | |
| 358 | Context managers inheriting from ``ContextDecorator`` have to implement |
| 359 | ``__enter__`` and ``__exit__`` as normal. ``__exit__`` retains its optional |
| 360 | exception handling even when used as a decorator. |
| 361 | |
Georg Brandl | 86e78d1 | 2010-07-18 13:43:32 +0000 | [diff] [blame] | 362 | ``ContextDecorator`` is used by :func:`contextmanager`, so you get this |
| 363 | functionality automatically. |
| 364 | |
| 365 | Example of ``ContextDecorator``:: |
Michael Foord | b3a8984 | 2010-06-30 12:17:50 +0000 | [diff] [blame] | 366 | |
| 367 | from contextlib import ContextDecorator |
| 368 | |
| 369 | class mycontext(ContextDecorator): |
Georg Brandl | 86e78d1 | 2010-07-18 13:43:32 +0000 | [diff] [blame] | 370 | def __enter__(self): |
| 371 | print('Starting') |
| 372 | return self |
Michael Foord | b3a8984 | 2010-06-30 12:17:50 +0000 | [diff] [blame] | 373 | |
Georg Brandl | 86e78d1 | 2010-07-18 13:43:32 +0000 | [diff] [blame] | 374 | def __exit__(self, *exc): |
| 375 | print('Finishing') |
| 376 | return False |
Michael Foord | b3a8984 | 2010-06-30 12:17:50 +0000 | [diff] [blame] | 377 | |
| 378 | >>> @mycontext() |
| 379 | ... def function(): |
Georg Brandl | 86e78d1 | 2010-07-18 13:43:32 +0000 | [diff] [blame] | 380 | ... print('The bit in the middle') |
Michael Foord | b3a8984 | 2010-06-30 12:17:50 +0000 | [diff] [blame] | 381 | ... |
| 382 | >>> function() |
| 383 | Starting |
| 384 | The bit in the middle |
| 385 | Finishing |
| 386 | |
| 387 | >>> with mycontext(): |
Georg Brandl | 86e78d1 | 2010-07-18 13:43:32 +0000 | [diff] [blame] | 388 | ... print('The bit in the middle') |
Michael Foord | b3a8984 | 2010-06-30 12:17:50 +0000 | [diff] [blame] | 389 | ... |
| 390 | Starting |
| 391 | The bit in the middle |
| 392 | Finishing |
| 393 | |
Georg Brandl | 86e78d1 | 2010-07-18 13:43:32 +0000 | [diff] [blame] | 394 | This change is just syntactic sugar for any construct of the following form:: |
| 395 | |
| 396 | def f(): |
| 397 | with cm(): |
| 398 | # Do stuff |
| 399 | |
| 400 | ``ContextDecorator`` lets you instead write:: |
| 401 | |
| 402 | @cm() |
| 403 | def f(): |
| 404 | # Do stuff |
| 405 | |
| 406 | It makes it clear that the ``cm`` applies to the whole function, rather than |
| 407 | just a piece of it (and saving an indentation level is nice, too). |
| 408 | |
Michael Foord | b3a8984 | 2010-06-30 12:17:50 +0000 | [diff] [blame] | 409 | Existing context managers that already have a base class can be extended by |
| 410 | using ``ContextDecorator`` as a mixin class:: |
| 411 | |
| 412 | from contextlib import ContextDecorator |
| 413 | |
| 414 | class mycontext(ContextBaseClass, ContextDecorator): |
Georg Brandl | 86e78d1 | 2010-07-18 13:43:32 +0000 | [diff] [blame] | 415 | def __enter__(self): |
| 416 | return self |
Michael Foord | b3a8984 | 2010-06-30 12:17:50 +0000 | [diff] [blame] | 417 | |
Georg Brandl | 86e78d1 | 2010-07-18 13:43:32 +0000 | [diff] [blame] | 418 | def __exit__(self, *exc): |
| 419 | return False |
Michael Foord | b3a8984 | 2010-06-30 12:17:50 +0000 | [diff] [blame] | 420 | |
Nick Coghlan | 0ded3e3 | 2011-05-05 23:49:25 +1000 | [diff] [blame] | 421 | .. note:: |
| 422 | As the decorated function must be able to be called multiple times, the |
| 423 | underlying context manager must support use in multiple :keyword:`with` |
| 424 | statements. If this is not the case, then the original construct with the |
Serhiy Storchaka | 2b57c43 | 2018-12-19 08:09:46 +0200 | [diff] [blame] | 425 | explicit :keyword:`!with` statement inside the function should be used. |
Nick Coghlan | 0ded3e3 | 2011-05-05 23:49:25 +1000 | [diff] [blame] | 426 | |
Michael Foord | b3a8984 | 2010-06-30 12:17:50 +0000 | [diff] [blame] | 427 | .. versionadded:: 3.2 |
| 428 | |
| 429 | |
kj | 133aa2d | 2020-11-06 00:16:27 +0800 | [diff] [blame] | 430 | .. class:: AsyncContextDecorator |
Kazantcev Andrey | 178695b | 2020-11-05 11:52:24 +0300 | [diff] [blame] | 431 | |
kj | 133aa2d | 2020-11-06 00:16:27 +0800 | [diff] [blame] | 432 | Similar to :class:`ContextDecorator` but only for asynchronous functions. |
Kazantcev Andrey | 178695b | 2020-11-05 11:52:24 +0300 | [diff] [blame] | 433 | |
kj | 133aa2d | 2020-11-06 00:16:27 +0800 | [diff] [blame] | 434 | Example of ``AsyncContextDecorator``:: |
Kazantcev Andrey | 178695b | 2020-11-05 11:52:24 +0300 | [diff] [blame] | 435 | |
| 436 | from asyncio import run |
| 437 | from contextlib import AsyncContextDecorator |
| 438 | |
| 439 | class mycontext(AsyncContextDecorator): |
| 440 | async def __aenter__(self): |
| 441 | print('Starting') |
| 442 | return self |
| 443 | |
| 444 | async def __aexit__(self, *exc): |
| 445 | print('Finishing') |
| 446 | return False |
| 447 | |
| 448 | >>> @mycontext() |
| 449 | ... async def function(): |
| 450 | ... print('The bit in the middle') |
| 451 | ... |
| 452 | >>> run(function()) |
| 453 | Starting |
| 454 | The bit in the middle |
| 455 | Finishing |
| 456 | |
| 457 | >>> async def function(): |
| 458 | ... async with mycontext(): |
| 459 | ... print('The bit in the middle') |
| 460 | ... |
| 461 | >>> run(function()) |
| 462 | Starting |
| 463 | The bit in the middle |
| 464 | Finishing |
| 465 | |
kj | 133aa2d | 2020-11-06 00:16:27 +0800 | [diff] [blame] | 466 | .. versionadded:: 3.10 |
| 467 | |
Kazantcev Andrey | 178695b | 2020-11-05 11:52:24 +0300 | [diff] [blame] | 468 | |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 469 | .. class:: ExitStack() |
| 470 | |
| 471 | A context manager that is designed to make it easy to programmatically |
| 472 | combine other context managers and cleanup functions, especially those |
| 473 | that are optional or otherwise driven by input data. |
| 474 | |
| 475 | For example, a set of files may easily be handled in a single with |
| 476 | statement as follows:: |
| 477 | |
| 478 | with ExitStack() as stack: |
| 479 | files = [stack.enter_context(open(fname)) for fname in filenames] |
| 480 | # All opened files will automatically be closed at the end of |
| 481 | # the with statement, even if attempts to open files later |
Andrew Svetlov | 5b89840 | 2012-12-18 21:26:36 +0200 | [diff] [blame] | 482 | # in the list raise an exception |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 483 | |
| 484 | Each instance maintains a stack of registered callbacks that are called in |
| 485 | reverse order when the instance is closed (either explicitly or implicitly |
Nick Coghlan | 2722827 | 2012-05-31 22:17:08 +1000 | [diff] [blame] | 486 | at the end of a :keyword:`with` statement). Note that callbacks are *not* |
| 487 | invoked implicitly when the context stack instance is garbage collected. |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 488 | |
| 489 | This stack model is used so that context managers that acquire their |
| 490 | resources in their ``__init__`` method (such as file objects) can be |
| 491 | handled correctly. |
| 492 | |
| 493 | Since registered callbacks are invoked in the reverse order of |
Nick Coghlan | 2722827 | 2012-05-31 22:17:08 +1000 | [diff] [blame] | 494 | registration, this ends up behaving as if multiple nested :keyword:`with` |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 495 | statements had been used with the registered set of callbacks. This even |
| 496 | extends to exception handling - if an inner callback suppresses or replaces |
| 497 | an exception, then outer callbacks will be passed arguments based on that |
| 498 | updated state. |
| 499 | |
| 500 | This is a relatively low level API that takes care of the details of |
| 501 | correctly unwinding the stack of exit callbacks. It provides a suitable |
| 502 | foundation for higher level context managers that manipulate the exit |
| 503 | stack in application specific ways. |
| 504 | |
Nick Coghlan | a497b44 | 2012-05-22 23:02:00 +1000 | [diff] [blame] | 505 | .. versionadded:: 3.3 |
| 506 | |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 507 | .. method:: enter_context(cm) |
| 508 | |
| 509 | Enters a new context manager and adds its :meth:`__exit__` method to |
| 510 | the callback stack. The return value is the result of the context |
| 511 | manager's own :meth:`__enter__` method. |
| 512 | |
| 513 | These context managers may suppress exceptions just as they normally |
Nick Coghlan | 2722827 | 2012-05-31 22:17:08 +1000 | [diff] [blame] | 514 | would if used directly as part of a :keyword:`with` statement. |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 515 | |
| 516 | .. method:: push(exit) |
| 517 | |
| 518 | Adds a context manager's :meth:`__exit__` method to the callback stack. |
| 519 | |
| 520 | As ``__enter__`` is *not* invoked, this method can be used to cover |
| 521 | part of an :meth:`__enter__` implementation with a context manager's own |
| 522 | :meth:`__exit__` method. |
| 523 | |
| 524 | If passed an object that is not a context manager, this method assumes |
| 525 | it is a callback with the same signature as a context manager's |
| 526 | :meth:`__exit__` method and adds it directly to the callback stack. |
| 527 | |
| 528 | By returning true values, these callbacks can suppress exceptions the |
| 529 | same way context manager :meth:`__exit__` methods can. |
| 530 | |
| 531 | The passed in object is returned from the function, allowing this |
Nick Coghlan | 2722827 | 2012-05-31 22:17:08 +1000 | [diff] [blame] | 532 | method to be used as a function decorator. |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 533 | |
Serhiy Storchaka | 142566c | 2019-06-05 18:22:31 +0300 | [diff] [blame] | 534 | .. method:: callback(callback, /, *args, **kwds) |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 535 | |
| 536 | Accepts an arbitrary callback function and arguments and adds it to |
| 537 | the callback stack. |
| 538 | |
| 539 | Unlike the other methods, callbacks added this way cannot suppress |
| 540 | exceptions (as they are never passed the exception details). |
| 541 | |
| 542 | The passed in callback is returned from the function, allowing this |
Nick Coghlan | 2722827 | 2012-05-31 22:17:08 +1000 | [diff] [blame] | 543 | method to be used as a function decorator. |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 544 | |
| 545 | .. method:: pop_all() |
| 546 | |
| 547 | Transfers the callback stack to a fresh :class:`ExitStack` instance |
| 548 | and returns it. No callbacks are invoked by this operation - instead, |
| 549 | they will now be invoked when the new stack is closed (either |
Nick Coghlan | 2722827 | 2012-05-31 22:17:08 +1000 | [diff] [blame] | 550 | explicitly or implicitly at the end of a :keyword:`with` statement). |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 551 | |
| 552 | For example, a group of files can be opened as an "all or nothing" |
| 553 | operation as follows:: |
| 554 | |
| 555 | with ExitStack() as stack: |
| 556 | files = [stack.enter_context(open(fname)) for fname in filenames] |
Barry Warsaw | d8f870d | 2013-05-10 11:35:38 -0400 | [diff] [blame] | 557 | # Hold onto the close method, but don't call it yet. |
| 558 | close_files = stack.pop_all().close |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 559 | # If opening any file fails, all previously opened files will be |
| 560 | # closed automatically. If all files are opened successfully, |
| 561 | # they will remain open even after the with statement ends. |
Barry Warsaw | d8f870d | 2013-05-10 11:35:38 -0400 | [diff] [blame] | 562 | # close_files() can then be invoked explicitly to close them all. |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 563 | |
| 564 | .. method:: close() |
| 565 | |
| 566 | Immediately unwinds the callback stack, invoking callbacks in the |
| 567 | reverse order of registration. For any context managers and exit |
| 568 | callbacks registered, the arguments passed in will indicate that no |
| 569 | exception occurred. |
| 570 | |
Ilya Kulakov | 1aa094f | 2018-01-25 12:51:18 -0800 | [diff] [blame] | 571 | .. class:: AsyncExitStack() |
| 572 | |
| 573 | An :ref:`asynchronous context manager <async-context-managers>`, similar |
| 574 | to :class:`ExitStack`, that supports combining both synchronous and |
| 575 | asynchronous context managers, as well as having coroutines for |
| 576 | cleanup logic. |
| 577 | |
| 578 | The :meth:`close` method is not implemented, :meth:`aclose` must be used |
| 579 | instead. |
| 580 | |
| 581 | .. method:: enter_async_context(cm) |
| 582 | |
| 583 | Similar to :meth:`enter_context` but expects an asynchronous context |
| 584 | manager. |
| 585 | |
| 586 | .. method:: push_async_exit(exit) |
| 587 | |
| 588 | Similar to :meth:`push` but expects either an asynchronous context manager |
Nathaniel J. Smith | a3c88ef | 2018-09-18 14:27:59 -0700 | [diff] [blame] | 589 | or a coroutine function. |
Ilya Kulakov | 1aa094f | 2018-01-25 12:51:18 -0800 | [diff] [blame] | 590 | |
Serhiy Storchaka | 142566c | 2019-06-05 18:22:31 +0300 | [diff] [blame] | 591 | .. method:: push_async_callback(callback, /, *args, **kwds) |
Ilya Kulakov | 1aa094f | 2018-01-25 12:51:18 -0800 | [diff] [blame] | 592 | |
Nathaniel J. Smith | a3c88ef | 2018-09-18 14:27:59 -0700 | [diff] [blame] | 593 | Similar to :meth:`callback` but expects a coroutine function. |
Ilya Kulakov | 1aa094f | 2018-01-25 12:51:18 -0800 | [diff] [blame] | 594 | |
| 595 | .. method:: aclose() |
| 596 | |
| 597 | Similar to :meth:`close` but properly handles awaitables. |
| 598 | |
| 599 | Continuing the example for :func:`asynccontextmanager`:: |
| 600 | |
| 601 | async with AsyncExitStack() as stack: |
| 602 | connections = [await stack.enter_async_context(get_connection()) |
| 603 | for i in range(5)] |
| 604 | # All opened connections will automatically be released at the end of |
| 605 | # the async with statement, even if attempts to open a connection |
| 606 | # later in the list raise an exception. |
| 607 | |
| 608 | .. versionadded:: 3.7 |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 609 | |
| 610 | Examples and Recipes |
| 611 | -------------------- |
| 612 | |
| 613 | This section describes some examples and recipes for making effective use of |
| 614 | the tools provided by :mod:`contextlib`. |
| 615 | |
| 616 | |
Nick Coghlan | 2722827 | 2012-05-31 22:17:08 +1000 | [diff] [blame] | 617 | Supporting a variable number of context managers |
| 618 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 619 | |
| 620 | The primary use case for :class:`ExitStack` is the one given in the class |
| 621 | documentation: supporting a variable number of context managers and other |
| 622 | cleanup operations in a single :keyword:`with` statement. The variability |
| 623 | may come from the number of context managers needed being driven by user |
| 624 | input (such as opening a user specified collection of files), or from |
| 625 | some of the context managers being optional:: |
| 626 | |
| 627 | with ExitStack() as stack: |
| 628 | for resource in resources: |
| 629 | stack.enter_context(resource) |
Raymond Hettinger | e8e2df3 | 2014-05-25 18:06:04 -0700 | [diff] [blame] | 630 | if need_special_resource(): |
Nick Coghlan | 2722827 | 2012-05-31 22:17:08 +1000 | [diff] [blame] | 631 | special = acquire_special_resource() |
| 632 | stack.callback(release_special_resource, special) |
| 633 | # Perform operations that use the acquired resources |
| 634 | |
| 635 | As shown, :class:`ExitStack` also makes it quite easy to use :keyword:`with` |
| 636 | statements to manage arbitrary resources that don't natively support the |
| 637 | context management protocol. |
| 638 | |
| 639 | |
Nick Coghlan | 2722827 | 2012-05-31 22:17:08 +1000 | [diff] [blame] | 640 | Catching exceptions from ``__enter__`` methods |
| 641 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 642 | |
| 643 | It is occasionally desirable to catch exceptions from an ``__enter__`` |
| 644 | method implementation, *without* inadvertently catching exceptions from |
| 645 | the :keyword:`with` statement body or the context manager's ``__exit__`` |
| 646 | method. By using :class:`ExitStack` the steps in the context management |
| 647 | protocol can be separated slightly in order to allow this:: |
| 648 | |
| 649 | stack = ExitStack() |
| 650 | try: |
| 651 | x = stack.enter_context(cm) |
| 652 | except Exception: |
| 653 | # handle __enter__ exception |
| 654 | else: |
| 655 | with stack: |
| 656 | # Handle normal case |
| 657 | |
| 658 | Actually needing to do this is likely to indicate that the underlying API |
| 659 | should be providing a direct resource management interface for use with |
| 660 | :keyword:`try`/:keyword:`except`/:keyword:`finally` statements, but not |
| 661 | all APIs are well designed in that regard. When a context manager is the |
| 662 | only resource management API provided, then :class:`ExitStack` can make it |
| 663 | easier to handle various situations that can't be handled directly in a |
| 664 | :keyword:`with` statement. |
| 665 | |
| 666 | |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 667 | Cleaning up in an ``__enter__`` implementation |
| 668 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 669 | |
| 670 | As noted in the documentation of :meth:`ExitStack.push`, this |
| 671 | method can be useful in cleaning up an already allocated resource if later |
| 672 | steps in the :meth:`__enter__` implementation fail. |
| 673 | |
| 674 | Here's an example of doing this for a context manager that accepts resource |
| 675 | acquisition and release functions, along with an optional validation function, |
| 676 | and maps them to the context management protocol:: |
| 677 | |
Brett Cannon | 9e080e0 | 2016-04-08 12:15:27 -0700 | [diff] [blame] | 678 | from contextlib import contextmanager, AbstractContextManager, ExitStack |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 679 | |
Brett Cannon | 9e080e0 | 2016-04-08 12:15:27 -0700 | [diff] [blame] | 680 | class ResourceManager(AbstractContextManager): |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 681 | |
| 682 | def __init__(self, acquire_resource, release_resource, check_resource_ok=None): |
| 683 | self.acquire_resource = acquire_resource |
| 684 | self.release_resource = release_resource |
| 685 | if check_resource_ok is None: |
| 686 | def check_resource_ok(resource): |
| 687 | return True |
| 688 | self.check_resource_ok = check_resource_ok |
| 689 | |
| 690 | @contextmanager |
| 691 | def _cleanup_on_error(self): |
| 692 | with ExitStack() as stack: |
| 693 | stack.push(self) |
| 694 | yield |
| 695 | # The validation check passed and didn't raise an exception |
| 696 | # Accordingly, we want to keep the resource, and pass it |
| 697 | # back to our caller |
| 698 | stack.pop_all() |
| 699 | |
| 700 | def __enter__(self): |
| 701 | resource = self.acquire_resource() |
| 702 | with self._cleanup_on_error(): |
| 703 | if not self.check_resource_ok(resource): |
| 704 | msg = "Failed validation for {!r}" |
| 705 | raise RuntimeError(msg.format(resource)) |
| 706 | return resource |
| 707 | |
| 708 | def __exit__(self, *exc_details): |
| 709 | # We don't need to duplicate any of our resource release logic |
| 710 | self.release_resource() |
| 711 | |
| 712 | |
| 713 | Replacing any use of ``try-finally`` and flag variables |
| 714 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 715 | |
| 716 | A pattern you will sometimes see is a ``try-finally`` statement with a flag |
| 717 | variable to indicate whether or not the body of the ``finally`` clause should |
| 718 | be executed. In its simplest form (that can't already be handled just by |
| 719 | using an ``except`` clause instead), it looks something like this:: |
| 720 | |
| 721 | cleanup_needed = True |
| 722 | try: |
| 723 | result = perform_operation() |
| 724 | if result: |
| 725 | cleanup_needed = False |
| 726 | finally: |
| 727 | if cleanup_needed: |
| 728 | cleanup_resources() |
| 729 | |
| 730 | As with any ``try`` statement based code, this can cause problems for |
| 731 | development and review, because the setup code and the cleanup code can end |
| 732 | up being separated by arbitrarily long sections of code. |
| 733 | |
| 734 | :class:`ExitStack` makes it possible to instead register a callback for |
| 735 | execution at the end of a ``with`` statement, and then later decide to skip |
| 736 | executing that callback:: |
| 737 | |
| 738 | from contextlib import ExitStack |
| 739 | |
| 740 | with ExitStack() as stack: |
| 741 | stack.callback(cleanup_resources) |
| 742 | result = perform_operation() |
| 743 | if result: |
| 744 | stack.pop_all() |
| 745 | |
| 746 | This allows the intended cleanup up behaviour to be made explicit up front, |
| 747 | rather than requiring a separate flag variable. |
| 748 | |
| 749 | If a particular application uses this pattern a lot, it can be simplified |
| 750 | even further by means of a small helper class:: |
| 751 | |
| 752 | from contextlib import ExitStack |
| 753 | |
| 754 | class Callback(ExitStack): |
Serhiy Storchaka | 2085bd0 | 2019-06-01 11:00:15 +0300 | [diff] [blame] | 755 | def __init__(self, callback, /, *args, **kwds): |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 756 | super(Callback, self).__init__() |
| 757 | self.callback(callback, *args, **kwds) |
| 758 | |
| 759 | def cancel(self): |
| 760 | self.pop_all() |
| 761 | |
| 762 | with Callback(cleanup_resources) as cb: |
| 763 | result = perform_operation() |
| 764 | if result: |
| 765 | cb.cancel() |
| 766 | |
| 767 | If the resource cleanup isn't already neatly bundled into a standalone |
| 768 | function, then it is still possible to use the decorator form of |
| 769 | :meth:`ExitStack.callback` to declare the resource cleanup in |
| 770 | advance:: |
| 771 | |
| 772 | from contextlib import ExitStack |
| 773 | |
| 774 | with ExitStack() as stack: |
| 775 | @stack.callback |
| 776 | def cleanup_resources(): |
| 777 | ... |
| 778 | result = perform_operation() |
| 779 | if result: |
| 780 | stack.pop_all() |
| 781 | |
| 782 | Due to the way the decorator protocol works, a callback function |
| 783 | declared this way cannot take any parameters. Instead, any resources to |
Martin Panter | d21e0b5 | 2015-10-10 10:36:22 +0000 | [diff] [blame] | 784 | be released must be accessed as closure variables. |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 785 | |
| 786 | |
| 787 | Using a context manager as a function decorator |
| 788 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 789 | |
| 790 | :class:`ContextDecorator` makes it possible to use a context manager in |
| 791 | both an ordinary ``with`` statement and also as a function decorator. |
| 792 | |
| 793 | For example, it is sometimes useful to wrap functions or groups of statements |
| 794 | with a logger that can track the time of entry and time of exit. Rather than |
| 795 | writing both a function decorator and a context manager for the task, |
| 796 | inheriting from :class:`ContextDecorator` provides both capabilities in a |
| 797 | single definition:: |
| 798 | |
| 799 | from contextlib import ContextDecorator |
| 800 | import logging |
| 801 | |
| 802 | logging.basicConfig(level=logging.INFO) |
| 803 | |
| 804 | class track_entry_and_exit(ContextDecorator): |
| 805 | def __init__(self, name): |
| 806 | self.name = name |
| 807 | |
| 808 | def __enter__(self): |
Vinay Sajip | dd917f8 | 2016-08-31 08:22:29 +0100 | [diff] [blame] | 809 | logging.info('Entering: %s', self.name) |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 810 | |
| 811 | def __exit__(self, exc_type, exc, exc_tb): |
Vinay Sajip | dd917f8 | 2016-08-31 08:22:29 +0100 | [diff] [blame] | 812 | logging.info('Exiting: %s', self.name) |
Nick Coghlan | 3267a30 | 2012-05-21 22:54:43 +1000 | [diff] [blame] | 813 | |
| 814 | Instances of this class can be used as both a context manager:: |
| 815 | |
| 816 | with track_entry_and_exit('widget loader'): |
| 817 | print('Some time consuming activity goes here') |
| 818 | load_widget() |
| 819 | |
| 820 | And also as a function decorator:: |
| 821 | |
| 822 | @track_entry_and_exit('widget loader') |
| 823 | def activity(): |
| 824 | print('Some time consuming activity goes here') |
| 825 | load_widget() |
| 826 | |
| 827 | Note that there is one additional limitation when using context managers |
| 828 | as function decorators: there's no way to access the return value of |
| 829 | :meth:`__enter__`. If that value is needed, then it is still necessary to use |
| 830 | an explicit ``with`` statement. |
| 831 | |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 832 | .. seealso:: |
| 833 | |
Serhiy Storchaka | e4ba872 | 2016-03-31 15:30:54 +0300 | [diff] [blame] | 834 | :pep:`343` - The "with" statement |
Georg Brandl | 116aa62 | 2007-08-15 14:28:22 +0000 | [diff] [blame] | 835 | The specification, background, and examples for the Python :keyword:`with` |
| 836 | statement. |
| 837 | |
Nick Coghlan | 0acceb7 | 2013-10-20 13:22:21 +1000 | [diff] [blame] | 838 | .. _single-use-reusable-and-reentrant-cms: |
Nick Coghlan | 8608d26 | 2013-10-20 00:30:51 +1000 | [diff] [blame] | 839 | |
Nick Coghlan | 0acceb7 | 2013-10-20 13:22:21 +1000 | [diff] [blame] | 840 | Single use, reusable and reentrant context managers |
| 841 | --------------------------------------------------- |
Nick Coghlan | 8608d26 | 2013-10-20 00:30:51 +1000 | [diff] [blame] | 842 | |
| 843 | Most context managers are written in a way that means they can only be |
| 844 | used effectively in a :keyword:`with` statement once. These single use |
| 845 | context managers must be created afresh each time they're used - |
| 846 | attempting to use them a second time will trigger an exception or |
| 847 | otherwise not work correctly. |
| 848 | |
| 849 | This common limitation means that it is generally advisable to create |
| 850 | context managers directly in the header of the :keyword:`with` statement |
| 851 | where they are used (as shown in all of the usage examples above). |
| 852 | |
| 853 | Files are an example of effectively single use context managers, since |
| 854 | the first :keyword:`with` statement will close the file, preventing any |
| 855 | further IO operations using that file object. |
| 856 | |
| 857 | Context managers created using :func:`contextmanager` are also single use |
| 858 | context managers, and will complain about the underlying generator failing |
| 859 | to yield if an attempt is made to use them a second time:: |
| 860 | |
| 861 | >>> from contextlib import contextmanager |
| 862 | >>> @contextmanager |
| 863 | ... def singleuse(): |
| 864 | ... print("Before") |
| 865 | ... yield |
| 866 | ... print("After") |
| 867 | ... |
| 868 | >>> cm = singleuse() |
| 869 | >>> with cm: |
| 870 | ... pass |
| 871 | ... |
| 872 | Before |
| 873 | After |
| 874 | >>> with cm: |
Serhiy Storchaka | dba9039 | 2016-05-10 12:01:23 +0300 | [diff] [blame] | 875 | ... pass |
Nick Coghlan | 8608d26 | 2013-10-20 00:30:51 +1000 | [diff] [blame] | 876 | ... |
| 877 | Traceback (most recent call last): |
| 878 | ... |
| 879 | RuntimeError: generator didn't yield |
| 880 | |
| 881 | |
| 882 | .. _reentrant-cms: |
| 883 | |
| 884 | Reentrant context managers |
| 885 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 886 | |
| 887 | More sophisticated context managers may be "reentrant". These context |
| 888 | managers can not only be used in multiple :keyword:`with` statements, |
Serhiy Storchaka | 2b57c43 | 2018-12-19 08:09:46 +0200 | [diff] [blame] | 889 | but may also be used *inside* a :keyword:`!with` statement that is already |
Nick Coghlan | 8608d26 | 2013-10-20 00:30:51 +1000 | [diff] [blame] | 890 | using the same context manager. |
| 891 | |
Nick Coghlan | 8e113b4 | 2013-11-03 17:00:51 +1000 | [diff] [blame] | 892 | :class:`threading.RLock` is an example of a reentrant context manager, as are |
| 893 | :func:`suppress` and :func:`redirect_stdout`. Here's a very simple example of |
| 894 | reentrant use:: |
Nick Coghlan | 8608d26 | 2013-10-20 00:30:51 +1000 | [diff] [blame] | 895 | |
Nick Coghlan | 8e113b4 | 2013-11-03 17:00:51 +1000 | [diff] [blame] | 896 | >>> from contextlib import redirect_stdout |
| 897 | >>> from io import StringIO |
| 898 | >>> stream = StringIO() |
| 899 | >>> write_to_stream = redirect_stdout(stream) |
| 900 | >>> with write_to_stream: |
| 901 | ... print("This is written to the stream rather than stdout") |
| 902 | ... with write_to_stream: |
| 903 | ... print("This is also written to the stream") |
Nick Coghlan | 8608d26 | 2013-10-20 00:30:51 +1000 | [diff] [blame] | 904 | ... |
Nick Coghlan | 8e113b4 | 2013-11-03 17:00:51 +1000 | [diff] [blame] | 905 | >>> print("This is written directly to stdout") |
| 906 | This is written directly to stdout |
| 907 | >>> print(stream.getvalue()) |
| 908 | This is written to the stream rather than stdout |
| 909 | This is also written to the stream |
| 910 | |
| 911 | Real world examples of reentrancy are more likely to involve multiple |
| 912 | functions calling each other and hence be far more complicated than this |
| 913 | example. |
| 914 | |
| 915 | Note also that being reentrant is *not* the same thing as being thread safe. |
| 916 | :func:`redirect_stdout`, for example, is definitely not thread safe, as it |
| 917 | makes a global modification to the system state by binding :data:`sys.stdout` |
| 918 | to a different stream. |
Nick Coghlan | 8608d26 | 2013-10-20 00:30:51 +1000 | [diff] [blame] | 919 | |
| 920 | |
| 921 | .. _reusable-cms: |
| 922 | |
| 923 | Reusable context managers |
| 924 | ^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 925 | |
| 926 | Distinct from both single use and reentrant context managers are "reusable" |
| 927 | context managers (or, to be completely explicit, "reusable, but not |
| 928 | reentrant" context managers, since reentrant context managers are also |
| 929 | reusable). These context managers support being used multiple times, but |
| 930 | will fail (or otherwise not work correctly) if the specific context manager |
| 931 | instance has already been used in a containing with statement. |
| 932 | |
Nick Coghlan | 8e113b4 | 2013-11-03 17:00:51 +1000 | [diff] [blame] | 933 | :class:`threading.Lock` is an example of a reusable, but not reentrant, |
| 934 | context manager (for a reentrant lock, it is necessary to use |
| 935 | :class:`threading.RLock` instead). |
Nick Coghlan | 8608d26 | 2013-10-20 00:30:51 +1000 | [diff] [blame] | 936 | |
Nick Coghlan | 8e113b4 | 2013-11-03 17:00:51 +1000 | [diff] [blame] | 937 | Another example of a reusable, but not reentrant, context manager is |
| 938 | :class:`ExitStack`, as it invokes *all* currently registered callbacks |
| 939 | when leaving any with statement, regardless of where those callbacks |
| 940 | were added:: |
Nick Coghlan | 8608d26 | 2013-10-20 00:30:51 +1000 | [diff] [blame] | 941 | |
Nick Coghlan | 8e113b4 | 2013-11-03 17:00:51 +1000 | [diff] [blame] | 942 | >>> from contextlib import ExitStack |
| 943 | >>> stack = ExitStack() |
| 944 | >>> with stack: |
| 945 | ... stack.callback(print, "Callback: from first context") |
| 946 | ... print("Leaving first context") |
Nick Coghlan | 8608d26 | 2013-10-20 00:30:51 +1000 | [diff] [blame] | 947 | ... |
Nick Coghlan | 8e113b4 | 2013-11-03 17:00:51 +1000 | [diff] [blame] | 948 | Leaving first context |
| 949 | Callback: from first context |
| 950 | >>> with stack: |
| 951 | ... stack.callback(print, "Callback: from second context") |
| 952 | ... print("Leaving second context") |
| 953 | ... |
| 954 | Leaving second context |
| 955 | Callback: from second context |
| 956 | >>> with stack: |
| 957 | ... stack.callback(print, "Callback: from outer context") |
| 958 | ... with stack: |
| 959 | ... stack.callback(print, "Callback: from inner context") |
| 960 | ... print("Leaving inner context") |
| 961 | ... print("Leaving outer context") |
| 962 | ... |
| 963 | Leaving inner context |
| 964 | Callback: from inner context |
| 965 | Callback: from outer context |
| 966 | Leaving outer context |
| 967 | |
| 968 | As the output from the example shows, reusing a single stack object across |
| 969 | multiple with statements works correctly, but attempting to nest them |
| 970 | will cause the stack to be cleared at the end of the innermost with |
| 971 | statement, which is unlikely to be desirable behaviour. |
| 972 | |
| 973 | Using separate :class:`ExitStack` instances instead of reusing a single |
| 974 | instance avoids that problem:: |
| 975 | |
| 976 | >>> from contextlib import ExitStack |
| 977 | >>> with ExitStack() as outer_stack: |
| 978 | ... outer_stack.callback(print, "Callback: from outer context") |
| 979 | ... with ExitStack() as inner_stack: |
| 980 | ... inner_stack.callback(print, "Callback: from inner context") |
| 981 | ... print("Leaving inner context") |
| 982 | ... print("Leaving outer context") |
| 983 | ... |
| 984 | Leaving inner context |
| 985 | Callback: from inner context |
| 986 | Leaving outer context |
| 987 | Callback: from outer context |