bpo-10049: Add a "no-op" (null) context manager to contextlib (GH-4464)
Adds a simpler and faster alternative to ExitStack for handling single optional context managers without having to change the lexical structure of your code.
This commit is contained in:
parent
20d48a44a5
commit
0784a2e5b1
|
@ -137,6 +137,28 @@ Functions and classes provided:
|
||||||
``page.close()`` will be called when the :keyword:`with` block is exited.
|
``page.close()`` will be called when the :keyword:`with` block is exited.
|
||||||
|
|
||||||
|
|
||||||
|
.. _simplifying-support-for-single-optional-context-managers:
|
||||||
|
|
||||||
|
.. function:: nullcontext(enter_result=None)
|
||||||
|
|
||||||
|
Return a context manager that returns enter_result from ``__enter__``, but
|
||||||
|
otherwise does nothing. It is intended to be used as a stand-in for an
|
||||||
|
optional context manager, for example::
|
||||||
|
|
||||||
|
def process_file(file_or_path):
|
||||||
|
if isinstance(file_or_path, str):
|
||||||
|
# If string, open file
|
||||||
|
cm = open(file_or_path)
|
||||||
|
else:
|
||||||
|
# Caller is responsible for closing file
|
||||||
|
cm = nullcontext(file_or_path)
|
||||||
|
|
||||||
|
with cm as file:
|
||||||
|
# Perform processing on the file
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
|
|
||||||
.. function:: suppress(*exceptions)
|
.. function:: suppress(*exceptions)
|
||||||
|
|
||||||
Return a context manager that suppresses any of the specified exceptions
|
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
|
||||||
context management protocol.
|
context management protocol.
|
||||||
|
|
||||||
|
|
||||||
Simplifying support for single optional context managers
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
In the specific case of a single optional context manager, :class:`ExitStack`
|
|
||||||
instances can be used as a "do nothing" context manager, allowing a context
|
|
||||||
manager to easily be omitted without affecting the overall structure of
|
|
||||||
the source code::
|
|
||||||
|
|
||||||
def debug_trace(details):
|
|
||||||
if __debug__:
|
|
||||||
return TraceContext(details)
|
|
||||||
# Don't do anything special with the context in release mode
|
|
||||||
return ExitStack()
|
|
||||||
|
|
||||||
with debug_trace():
|
|
||||||
# Suite is traced in debug mode, but runs normally otherwise
|
|
||||||
|
|
||||||
|
|
||||||
Catching exceptions from ``__enter__`` methods
|
Catching exceptions from ``__enter__`` methods
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import _collections_abc
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
__all__ = ["asynccontextmanager", "contextmanager", "closing",
|
__all__ = ["asynccontextmanager", "contextmanager", "closing", "nullcontext",
|
||||||
"AbstractContextManager", "ContextDecorator", "ExitStack",
|
"AbstractContextManager", "ContextDecorator", "ExitStack",
|
||||||
"redirect_stdout", "redirect_stderr", "suppress"]
|
"redirect_stdout", "redirect_stderr", "suppress"]
|
||||||
|
|
||||||
|
@ -469,3 +469,24 @@ class ExitStack(AbstractContextManager):
|
||||||
exc_details[1].__context__ = fixed_ctx
|
exc_details[1].__context__ = fixed_ctx
|
||||||
raise
|
raise
|
||||||
return received_exc and suppressed_exc
|
return received_exc and suppressed_exc
|
||||||
|
|
||||||
|
|
||||||
|
class nullcontext(AbstractContextManager):
|
||||||
|
"""Context manager that does no additional processing.
|
||||||
|
|
||||||
|
Used as a stand-in for a normal context manager, when a particular
|
||||||
|
block of code is only sometimes used with a normal context manager:
|
||||||
|
|
||||||
|
cm = optional_cm if condition else nullcontext()
|
||||||
|
with cm:
|
||||||
|
# Perform operation, using optional_cm if condition is True
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, enter_result=None):
|
||||||
|
self.enter_result = enter_result
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self.enter_result
|
||||||
|
|
||||||
|
def __exit__(self, *excinfo):
|
||||||
|
pass
|
||||||
|
|
|
@ -252,6 +252,16 @@ class ClosingTestCase(unittest.TestCase):
|
||||||
1 / 0
|
1 / 0
|
||||||
self.assertEqual(state, [1])
|
self.assertEqual(state, [1])
|
||||||
|
|
||||||
|
|
||||||
|
class NullcontextTestCase(unittest.TestCase):
|
||||||
|
def test_nullcontext(self):
|
||||||
|
class C:
|
||||||
|
pass
|
||||||
|
c = C()
|
||||||
|
with nullcontext(c) as c_in:
|
||||||
|
self.assertIs(c_in, c)
|
||||||
|
|
||||||
|
|
||||||
class FileContextTestCase(unittest.TestCase):
|
class FileContextTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def testWithOpen(self):
|
def testWithOpen(self):
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Added *nullcontext* no-op context manager to contextlib. This provides a
|
||||||
|
simpler and faster alternative to ExitStack() when handling optional context
|
||||||
|
managers.
|
Loading…
Reference in New Issue