bpo-10049: Add a "no-op" (null) context manager to contextlib (GH-4464) · python/cpython@0784a2e (original) (raw)
4 files changed
lines changed
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -137,6 +137,28 @@ Functions and classes provided: | ||
137 | 137 | ``page.close()`` will be called when the :keyword:`with` block is exited. |
138 | 138 | |
139 | 139 | |
140 | +.. _simplifying-support-for-single-optional-context-managers: | |
141 | + | |
142 | +.. function:: nullcontext(enter_result=None) | |
143 | + | |
144 | + Return a context manager that returns enter_result from ``__enter__``, but | |
145 | + otherwise does nothing. It is intended to be used as a stand-in for an | |
146 | + optional context manager, for example:: | |
147 | + | |
148 | + def process_file(file_or_path): | |
149 | + if isinstance(file_or_path, str): | |
150 | + # If string, open file | |
151 | + cm = open(file_or_path) | |
152 | + else: | |
153 | + # Caller is responsible for closing file | |
154 | + cm = nullcontext(file_or_path) | |
155 | + | |
156 | + with cm as file: | |
157 | + # Perform processing on the file | |
158 | + | |
159 | + .. versionadded:: 3.7 | |
160 | + | |
161 | + | |
140 | 162 | .. function:: suppress(*exceptions) |
141 | 163 | |
142 | 164 | Return a context manager that suppresses any of the specified exceptions |
@@ -433,24 +455,6 @@ statements to manage arbitrary resources that don't natively support the | ||
433 | 455 | context management protocol. |
434 | 456 | |
435 | 457 | |
436 | -Simplifying support for single optional context managers | |
437 | -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
438 | - | |
439 | -In the specific case of a single optional context manager, :class:`ExitStack` | |
440 | -instances can be used as a "do nothing" context manager, allowing a context | |
441 | -manager to easily be omitted without affecting the overall structure of | |
442 | -the source code:: | |
443 | - | |
444 | - def debug_trace(details): | |
445 | - if __debug__: | |
446 | - return TraceContext(details) | |
447 | - # Don't do anything special with the context in release mode | |
448 | - return ExitStack() | |
449 | - | |
450 | - with debug_trace(): | |
451 | - # Suite is traced in debug mode, but runs normally otherwise | |
452 | - | |
453 | - | |
454 | 458 | Catching exceptions from ``__enter__`` methods |
455 | 459 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
456 | 460 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -5,7 +5,7 @@ | ||
5 | 5 | from collections import deque |
6 | 6 | from functools import wraps |
7 | 7 | |
8 | -__all__ = ["asynccontextmanager", "contextmanager", "closing", | |
8 | +__all__ = ["asynccontextmanager", "contextmanager", "closing", "nullcontext", | |
9 | 9 | "AbstractContextManager", "ContextDecorator", "ExitStack", |
10 | 10 | "redirect_stdout", "redirect_stderr", "suppress"] |
11 | 11 | |
@@ -469,3 +469,24 @@ def _fix_exception_context(new_exc, old_exc): | ||
469 | 469 | exc_details[1].__context__ = fixed_ctx |
470 | 470 | raise |
471 | 471 | return received_exc and suppressed_exc |
472 | + | |
473 | + | |
474 | +class nullcontext(AbstractContextManager): | |
475 | +"""Context manager that does no additional processing. | |
476 | + | |
477 | + Used as a stand-in for a normal context manager, when a particular | |
478 | + block of code is only sometimes used with a normal context manager: | |
479 | + | |
480 | + cm = optional_cm if condition else nullcontext() | |
481 | + with cm: | |
482 | + # Perform operation, using optional_cm if condition is True | |
483 | + """ | |
484 | + | |
485 | +def __init__(self, enter_result=None): | |
486 | +self.enter_result = enter_result | |
487 | + | |
488 | +def __enter__(self): | |
489 | +return self.enter_result | |
490 | + | |
491 | +def __exit__(self, *excinfo): | |
492 | +pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -252,6 +252,16 @@ def close(self): | ||
252 | 252 | 1 / 0 |
253 | 253 | self.assertEqual(state, [1]) |
254 | 254 | |
255 | + | |
256 | +class NullcontextTestCase(unittest.TestCase): | |
257 | +def test_nullcontext(self): | |
258 | +class C: | |
259 | +pass | |
260 | +c = C() | |
261 | +with nullcontext(c) as c_in: | |
262 | +self.assertIs(c_in, c) | |
263 | + | |
264 | + | |
255 | 265 | class FileContextTestCase(unittest.TestCase): |
256 | 266 | |
257 | 267 | def testWithOpen(self): |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
1 | +Added *nullcontext* no-op context manager to contextlib. This provides a | |
2 | +simpler and faster alternative to ExitStack() when handling optional context | |
3 | +managers. |