Skip to content

Commit 8a8f7f9

Browse files
Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods.
1 parent b1f59ce commit 8a8f7f9

File tree

4 files changed

+55
-0
lines changed

4 files changed

+55
-0
lines changed

Lib/test/test_io.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,9 +792,27 @@ def bad_close():
792792
with self.assertRaises(OSError) as err: # exception not swallowed
793793
b.close()
794794
self.assertEqual(err.exception.args, ('close',))
795+
self.assertIsInstance(err.exception.__context__, OSError)
795796
self.assertEqual(err.exception.__context__.args, ('flush',))
796797
self.assertFalse(b.closed)
797798

799+
def test_nonnormalized_close_error_on_close(self):
800+
# Issue #21677
801+
raw = self.MockRawIO()
802+
def bad_flush():
803+
raise non_existing_flush
804+
def bad_close():
805+
raise non_existing_close
806+
raw.close = bad_close
807+
b = self.tp(raw)
808+
b.flush = bad_flush
809+
with self.assertRaises(NameError) as err: # exception not swallowed
810+
b.close()
811+
self.assertIn('non_existing_close', str(err.exception))
812+
self.assertIsInstance(err.exception.__context__, NameError)
813+
self.assertIn('non_existing_flush', str(err.exception.__context__))
814+
self.assertFalse(b.closed)
815+
798816
def test_multi_close(self):
799817
raw = self.MockRawIO()
800818
b = self.tp(raw)
@@ -2576,6 +2594,39 @@ def bad_flush():
25762594
self.assertRaises(OSError, txt.close) # exception not swallowed
25772595
self.assertTrue(txt.closed)
25782596

2597+
def test_close_error_on_close(self):
2598+
buffer = self.BytesIO(self.testdata)
2599+
def bad_flush():
2600+
raise OSError('flush')
2601+
def bad_close():
2602+
raise OSError('close')
2603+
buffer.close = bad_close
2604+
txt = self.TextIOWrapper(buffer, encoding="ascii")
2605+
txt.flush = bad_flush
2606+
with self.assertRaises(OSError) as err: # exception not swallowed
2607+
txt.close()
2608+
self.assertEqual(err.exception.args, ('close',))
2609+
self.assertIsInstance(err.exception.__context__, OSError)
2610+
self.assertEqual(err.exception.__context__.args, ('flush',))
2611+
self.assertFalse(txt.closed)
2612+
2613+
def test_nonnormalized_close_error_on_close(self):
2614+
# Issue #21677
2615+
buffer = self.BytesIO(self.testdata)
2616+
def bad_flush():
2617+
raise non_existing_flush
2618+
def bad_close():
2619+
raise non_existing_close
2620+
buffer.close = bad_close
2621+
txt = self.TextIOWrapper(buffer, encoding="ascii")
2622+
txt.flush = bad_flush
2623+
with self.assertRaises(NameError) as err: # exception not swallowed
2624+
txt.close()
2625+
self.assertIn('non_existing_close', str(err.exception))
2626+
self.assertIsInstance(err.exception.__context__, NameError)
2627+
self.assertIn('non_existing_flush', str(err.exception.__context__))
2628+
self.assertFalse(txt.closed)
2629+
25792630
def test_multi_close(self):
25802631
txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii")
25812632
txt.close()

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Core and Builtins
2222
Library
2323
-------
2424

25+
- Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods.
26+
2527
- Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a
2628
valid file.
2729

Modules/_io/bufferedio.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ buffered_close(buffered *self, PyObject *args)
549549
}
550550
else {
551551
PyObject *val2;
552+
PyErr_NormalizeException(&exc, &val, &tb);
552553
Py_DECREF(exc);
553554
Py_XDECREF(tb);
554555
PyErr_Fetch(&exc, &val2, &tb);

Modules/_io/textio.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2614,6 +2614,7 @@ textiowrapper_close(textio *self, PyObject *args)
26142614
}
26152615
else {
26162616
PyObject *val2;
2617+
PyErr_NormalizeException(&exc, &val, &tb);
26172618
Py_DECREF(exc);
26182619
Py_XDECREF(tb);
26192620
PyErr_Fetch(&exc, &val2, &tb);

0 commit comments

Comments
 (0)