diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst index 41dfded4b5d..4c8fe7803d0 100644 --- a/Doc/library/contextlib.rst +++ b/Doc/library/contextlib.rst @@ -94,6 +94,26 @@ Functions and classes provided: without needing to explicitly close ``page``. Even if an error occurs, ``page.close()`` will be called when the :keyword:`with` block is exited. +.. function:: ignored(*exceptions) + + Return a context manager that ignores the specified expections if they + occur in the body of a with-statement. + + For example:: + + from contextlib import ignored + + with ignored(OSError): + os.remove('somefile.tmp') + + This code is equivalent to:: + + try: + os.remove('somefile.tmp') + except OSError: + pass + + .. versionadded:: 3.4 .. class:: ContextDecorator() diff --git a/Lib/contextlib.py b/Lib/contextlib.py index 0b6bf71b08c..03c56da3975 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -4,7 +4,7 @@ import sys from collections import deque from functools import wraps -__all__ = ["contextmanager", "closing", "ContextDecorator", "ExitStack"] +__all__ = ["contextmanager", "closing", "ContextDecorator", "ExitStack", "ignored"] class ContextDecorator(object): @@ -140,6 +140,18 @@ class closing(object): def __exit__(self, *exc_info): self.thing.close() +@contextmanager +def ignored(*exceptions): + """Context manager to ignore specifed exceptions + + with ignored(OSError): + os.remove(somefile) + + """ + try: + yield + except exceptions: + pass # Inspired by discussions on http://bugs.python.org/issue13585 class ExitStack(object): diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index e52ed91a585..d13659d95c3 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -594,6 +594,28 @@ class TestExitStack(unittest.TestCase): stack.push(cm) self.assertIs(stack._exit_callbacks[-1], cm) +class TestIgnored(unittest.TestCase): + + def test_no_exception(self): + + with ignored(ValueError): + self.assertEqual(pow(2, 5), 32) + + def test_exact_exception(self): + + with ignored(TypeError): + len(5) + + def test_multiple_exception_args(self): + + with ignored(ZeroDivisionError, TypeError): + len(5) + + def test_exception_hierarchy(self): + + with ignored(LookupError): + 'Hello'[50] + # This is needed to make the test actually run under regrtest.py! def test_main(): diff --git a/Misc/NEWS b/Misc/NEWS index dcf4812f146..607ad5efd27 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -280,6 +280,9 @@ Library _ Issue #17385: Fix quadratic behavior in threading.Condition. The FIFO queue now uses a deque instead of a list. +- Issue #15806: Add contextlib.ignored(). This creates a context manager + to ignore specified exceptions, replacing the "except Exc: pass" idiom. + - Issue #14645: The email generator classes now produce output using the specified linesep throughout. Previously if the prolog, epilog, or body were stored with a different linesep, that linesep was used. This