From: Serhiy Storchaka Date: Mon, 9 Jun 2014 06:13:04 +0000 (+0300) Subject: Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. X-Git-Tag: v3.4.2rc1~415 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8a8f7f983099f172d1f7c25d4fd99f5c0eb14072;p=python Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. --- diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index ef1e05622b..a94b85aae3 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -792,9 +792,27 @@ class CommonBufferedTests: with self.assertRaises(OSError) as err: # exception not swallowed b.close() self.assertEqual(err.exception.args, ('close',)) + self.assertIsInstance(err.exception.__context__, OSError) self.assertEqual(err.exception.__context__.args, ('flush',)) self.assertFalse(b.closed) + def test_nonnormalized_close_error_on_close(self): + # Issue #21677 + raw = self.MockRawIO() + def bad_flush(): + raise non_existing_flush + def bad_close(): + raise non_existing_close + raw.close = bad_close + b = self.tp(raw) + b.flush = bad_flush + with self.assertRaises(NameError) as err: # exception not swallowed + b.close() + self.assertIn('non_existing_close', str(err.exception)) + self.assertIsInstance(err.exception.__context__, NameError) + self.assertIn('non_existing_flush', str(err.exception.__context__)) + self.assertFalse(b.closed) + def test_multi_close(self): raw = self.MockRawIO() b = self.tp(raw) @@ -2576,6 +2594,39 @@ class TextIOWrapperTest(unittest.TestCase): self.assertRaises(OSError, txt.close) # exception not swallowed self.assertTrue(txt.closed) + def test_close_error_on_close(self): + buffer = self.BytesIO(self.testdata) + def bad_flush(): + raise OSError('flush') + def bad_close(): + raise OSError('close') + buffer.close = bad_close + txt = self.TextIOWrapper(buffer, encoding="ascii") + txt.flush = bad_flush + with self.assertRaises(OSError) as err: # exception not swallowed + txt.close() + self.assertEqual(err.exception.args, ('close',)) + self.assertIsInstance(err.exception.__context__, OSError) + self.assertEqual(err.exception.__context__.args, ('flush',)) + self.assertFalse(txt.closed) + + def test_nonnormalized_close_error_on_close(self): + # Issue #21677 + buffer = self.BytesIO(self.testdata) + def bad_flush(): + raise non_existing_flush + def bad_close(): + raise non_existing_close + buffer.close = bad_close + txt = self.TextIOWrapper(buffer, encoding="ascii") + txt.flush = bad_flush + with self.assertRaises(NameError) as err: # exception not swallowed + txt.close() + self.assertIn('non_existing_close', str(err.exception)) + self.assertIsInstance(err.exception.__context__, NameError) + self.assertIn('non_existing_flush', str(err.exception.__context__)) + self.assertFalse(txt.closed) + def test_multi_close(self): txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") txt.close() diff --git a/Misc/NEWS b/Misc/NEWS index 6979b2f107..6185590dac 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,8 @@ Core and Builtins Library ------- +- Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. + - Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a valid file. diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 7494646342..d0e92e5f7f 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -549,6 +549,7 @@ buffered_close(buffered *self, PyObject *args) } else { PyObject *val2; + PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); PyErr_Fetch(&exc, &val2, &tb); diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index ba5789d3c7..55392b435f 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2614,6 +2614,7 @@ textiowrapper_close(textio *self, PyObject *args) } else { PyObject *val2; + PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); PyErr_Fetch(&exc, &val2, &tb);