changeset: 91100:a3b7b89da34f branch: 3.4 parent: 91098:454d4a9a3088 user: Serhiy Storchaka date: Mon Jun 09 09:13:04 2014 +0300 files: Lib/test/test_io.py Misc/NEWS Modules/_io/bufferedio.c Modules/_io/textio.c description: Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods. diff -r 454d4a9a3088 -r a3b7b89da34f Lib/test/test_io.py --- a/Lib/test/test_io.py Mon Jun 09 13:14:54 2014 +1000 +++ b/Lib/test/test_io.py Mon Jun 09 09:13:04 2014 +0300 @@ -792,9 +792,27 @@ 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 @@ 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 -r 454d4a9a3088 -r a3b7b89da34f Misc/NEWS --- a/Misc/NEWS Mon Jun 09 13:14:54 2014 +1000 +++ b/Misc/NEWS Mon Jun 09 09:13:04 2014 +0300 @@ -22,6 +22,8 @@ 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 -r 454d4a9a3088 -r a3b7b89da34f Modules/_io/bufferedio.c --- a/Modules/_io/bufferedio.c Mon Jun 09 13:14:54 2014 +1000 +++ b/Modules/_io/bufferedio.c Mon Jun 09 09:13:04 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 454d4a9a3088 -r a3b7b89da34f Modules/_io/textio.c --- a/Modules/_io/textio.c Mon Jun 09 13:14:54 2014 +1000 +++ b/Modules/_io/textio.c Mon Jun 09 09:13:04 2014 +0300 @@ -2614,6 +2614,7 @@ } else { PyObject *val2; + PyErr_NormalizeException(&exc, &val, &tb); Py_DECREF(exc); Py_XDECREF(tb); PyErr_Fetch(&exc, &val2, &tb);