diff -r 585ad5d806bd Lib/test/test_io.py --- a/Lib/test/test_io.py Fri Jun 06 17:43:37 2014 -0400 +++ b/Lib/test/test_io.py Sat Jun 07 14:42:06 2014 +0300 @@ -792,9 +792,29 @@ 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.assertEqual(err.exception.args, + ("name 'non_existing_close' is not defined",)) + self.assertIsInstance(err.exception.__context__, NameError) + self.assertEqual(err.exception.__context__.args, + ("name 'non_existing_flush' is not defined",)) + self.assertFalse(b.closed) + def test_multi_close(self): raw = self.MockRawIO() b = self.tp(raw) @@ -2576,6 +2596,41 @@ 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.assertEqual(err.exception.args, + ("name 'non_existing_close' is not defined",)) + self.assertIsInstance(err.exception.__context__, NameError) + self.assertEqual(err.exception.__context__.args, + ("name 'non_existing_flush' is not defined",)) + self.assertFalse(txt.closed) + def test_multi_close(self): txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") txt.close() diff -r 585ad5d806bd Modules/_io/bufferedio.c --- a/Modules/_io/bufferedio.c Fri Jun 06 17:43:37 2014 -0400 +++ b/Modules/_io/bufferedio.c Sat Jun 07 14:42:06 2014 +0300 @@ -549,6 +549,7 @@ } else { PyObject *val2; + PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); PyErr_Fetch(&exc, &val2, &tb); diff -r 585ad5d806bd Modules/_io/textio.c --- a/Modules/_io/textio.c Fri Jun 06 17:43:37 2014 -0400 +++ b/Modules/_io/textio.c Sat Jun 07 14:42:06 2014 +0300 @@ -2620,6 +2620,7 @@ } else { PyObject *val2; + PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); PyErr_Fetch(&exc, &val2, &tb);