summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_io.py
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2023-11-14 16:15:10 (GMT)
committerGitHub <noreply@github.com>2023-11-14 16:15:10 (GMT)
commit24216d0530fda84b482ab29490ddd0d496605e43 (patch)
tree21a8a75c5d8228862d040c23dfd7bfcb69ee0928 /Lib/test/test_io.py
parentc003de946793fe2f4e168ba231ea4ee9e88cc3fe (diff)
downloadcpython-24216d0530fda84b482ab29490ddd0d496605e43.zip
cpython-24216d0530fda84b482ab29490ddd0d496605e43.tar.gz
cpython-24216d0530fda84b482ab29490ddd0d496605e43.tar.bz2
[3.12] gh-111942: Fix crashes in TextIOWrapper.reconfigure() (GH-111976) (GH-112058)
* Fix crash when encoding is not string or None. * Fix crash when both line_buffering and write_through raise exception when converted ti int. * Add a number of tests for constructor and reconfigure() method with invalid arguments. (cherry picked from commit ee06fffd38cb51ce1c045da9d8336d9ce13c318a) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Diffstat (limited to 'Lib/test/test_io.py')
-rw-r--r--Lib/test/test_io.py86
1 files changed, 84 insertions, 2 deletions
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index 247eceb..0f4a560 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -81,6 +81,10 @@ requires_alarm = unittest.skipUnless(
)
+class BadIndex:
+ def __index__(self):
+ 1/0
+
class MockRawIOWithoutRead:
"""A RawIO implementation without read(), so as to exercise the default
RawIO.read() which calls readinto()."""
@@ -2716,8 +2720,31 @@ class TextIOWrapperTest(unittest.TestCase):
self.assertEqual(t.encoding, "utf-8")
self.assertEqual(t.line_buffering, True)
self.assertEqual("\xe9\n", t.readline())
- self.assertRaises(TypeError, t.__init__, b, encoding="utf-8", newline=42)
- self.assertRaises(ValueError, t.__init__, b, encoding="utf-8", newline='xyzzy')
+ invalid_type = TypeError if self.is_C else ValueError
+ with self.assertRaises(invalid_type):
+ t.__init__(b, encoding=42)
+ with self.assertRaises(UnicodeEncodeError):
+ t.__init__(b, encoding='\udcfe')
+ with self.assertRaises(ValueError):
+ t.__init__(b, encoding='utf-8\0')
+ with self.assertRaises(invalid_type):
+ t.__init__(b, encoding="utf-8", errors=42)
+ if support.Py_DEBUG or sys.flags.dev_mode or self.is_C:
+ with self.assertRaises(UnicodeEncodeError):
+ t.__init__(b, encoding="utf-8", errors='\udcfe')
+ if support.Py_DEBUG or sys.flags.dev_mode:
+ # TODO: If encoded to UTF-8, should also be checked for
+ # embedded null characters.
+ with self.assertRaises(ValueError):
+ t.__init__(b, encoding="utf-8", errors='replace\0')
+ with self.assertRaises(TypeError):
+ t.__init__(b, encoding="utf-8", newline=42)
+ with self.assertRaises(ValueError):
+ t.__init__(b, encoding="utf-8", newline='\udcfe')
+ with self.assertRaises(ValueError):
+ t.__init__(b, encoding="utf-8", newline='\n\0')
+ with self.assertRaises(ValueError):
+ t.__init__(b, encoding="utf-8", newline='xyzzy')
def test_uninitialized(self):
t = self.TextIOWrapper.__new__(self.TextIOWrapper)
@@ -3766,6 +3793,59 @@ class TextIOWrapperTest(unittest.TestCase):
self.assertEqual(txt.detach().getvalue(), b'LF\nCRLF\r\n')
+ def test_reconfigure_errors(self):
+ txt = self.TextIOWrapper(self.BytesIO(), 'ascii', 'replace', '\r')
+ with self.assertRaises(TypeError): # there was a crash
+ txt.reconfigure(encoding=42)
+ if self.is_C:
+ with self.assertRaises(UnicodeEncodeError):
+ txt.reconfigure(encoding='\udcfe')
+ with self.assertRaises(LookupError):
+ txt.reconfigure(encoding='locale\0')
+ # TODO: txt.reconfigure(encoding='utf-8\0')
+ # TODO: txt.reconfigure(encoding='nonexisting')
+ with self.assertRaises(TypeError):
+ txt.reconfigure(errors=42)
+ if self.is_C:
+ with self.assertRaises(UnicodeEncodeError):
+ txt.reconfigure(errors='\udcfe')
+ # TODO: txt.reconfigure(errors='ignore\0')
+ # TODO: txt.reconfigure(errors='nonexisting')
+ with self.assertRaises(TypeError):
+ txt.reconfigure(newline=42)
+ with self.assertRaises(ValueError):
+ txt.reconfigure(newline='\udcfe')
+ with self.assertRaises(ValueError):
+ txt.reconfigure(newline='xyz')
+ if not self.is_C:
+ # TODO: Should fail in C too.
+ with self.assertRaises(ValueError):
+ txt.reconfigure(newline='\n\0')
+ if self.is_C:
+ # TODO: Use __bool__(), not __index__().
+ with self.assertRaises(ZeroDivisionError):
+ txt.reconfigure(line_buffering=BadIndex())
+ with self.assertRaises(OverflowError):
+ txt.reconfigure(line_buffering=2**1000)
+ with self.assertRaises(ZeroDivisionError):
+ txt.reconfigure(write_through=BadIndex())
+ with self.assertRaises(OverflowError):
+ txt.reconfigure(write_through=2**1000)
+ with self.assertRaises(ZeroDivisionError): # there was a crash
+ txt.reconfigure(line_buffering=BadIndex(),
+ write_through=BadIndex())
+ self.assertEqual(txt.encoding, 'ascii')
+ self.assertEqual(txt.errors, 'replace')
+ self.assertIs(txt.line_buffering, False)
+ self.assertIs(txt.write_through, False)
+
+ txt.reconfigure(encoding='latin1', errors='ignore', newline='\r\n',
+ line_buffering=True, write_through=True)
+ self.assertEqual(txt.encoding, 'latin1')
+ self.assertEqual(txt.errors, 'ignore')
+ self.assertIs(txt.line_buffering, True)
+ self.assertIs(txt.write_through, True)
+
def test_reconfigure_newline(self):
raw = self.BytesIO(b'CR\rEOF')
txt = self.TextIOWrapper(raw, 'ascii', newline='\n')
@@ -4791,9 +4871,11 @@ def load_tests(loader, tests, pattern):
if test.__name__.startswith("C"):
for name, obj in c_io_ns.items():
setattr(test, name, obj)
+ test.is_C = True
elif test.__name__.startswith("Py"):
for name, obj in py_io_ns.items():
setattr(test, name, obj)
+ test.is_C = False
suite = loader.suiteClass()
for test in tests: