From b89786b8fc80fe83bae323c2de42bf293f989f89 Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Thu, 24 Aug 2017 21:53:11 +0300 Subject: [PATCH 1/3] init commit --- Lib/test/test_io.py | 14 ++++++++++++++ Modules/_io/textio.c | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index ab0cbe163613ec..501a098e881491 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3225,6 +3225,20 @@ def test_read_nonbytes(self): t = self.TextIOWrapper(self.StringIO('a')) self.assertRaises(TypeError, t.read) + def test_illegal_encoder(self): + # Issue 31271: Calling write() while the return value of encoder's + # encode() is invalid shouldn't cause an assertion failure. + class BadEncoder(): + def encode(self, dummy): + return 42 + def _get_bad_encoder(dummy): + return BadEncoder() + quopri = codecs.lookup("quopri") + with support.swap_attr(quopri, '_is_text_encoding', True), \ + support.swap_attr(quopri, 'incrementalencoder', _get_bad_encoder): + t = io.TextIOWrapper(io.BytesIO(b'foo'), encoding="quopri") + self.assertRaises(TypeError, t.write, 'bar') + def test_illegal_decoder(self): # Issue #17106 # Bypass the early encoding check added in issue 20404 diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 57b66d66c33745..81ebdae219ec85 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1379,6 +1379,14 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text) Py_DECREF(text); if (b == NULL) return NULL; + if (!PyBytes_Check(b)) { + PyErr_Format(PyExc_TypeError, + "encode() should have returned a bytes object, not " + "'%.200s'", + Py_TYPE(b)->tp_name); + Py_DECREF(b); + return NULL; + } if (self->pending_bytes == NULL) { self->pending_bytes = PyList_New(0); From b4ccaa6b586ca4619f3d13211a1e40cd40ba4971 Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Fri, 25 Aug 2017 10:52:48 +0300 Subject: [PATCH 2/3] simplify the test --- Lib/test/test_io.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 501a098e881491..ba95c1442fa49d 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3228,15 +3228,9 @@ def test_read_nonbytes(self): def test_illegal_encoder(self): # Issue 31271: Calling write() while the return value of encoder's # encode() is invalid shouldn't cause an assertion failure. - class BadEncoder(): - def encode(self, dummy): - return 42 - def _get_bad_encoder(dummy): - return BadEncoder() - quopri = codecs.lookup("quopri") - with support.swap_attr(quopri, '_is_text_encoding', True), \ - support.swap_attr(quopri, 'incrementalencoder', _get_bad_encoder): - t = io.TextIOWrapper(io.BytesIO(b'foo'), encoding="quopri") + rot13 = codecs.lookup("rot13") + with support.swap_attr(rot13, '_is_text_encoding', True): + t = io.TextIOWrapper(io.BytesIO(b'foo'), encoding="rot13") self.assertRaises(TypeError, t.write, 'bar') def test_illegal_decoder(self): From 287275407ee485cad5f8041aa7ffc99ab8056c7c Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Fri, 25 Aug 2017 20:52:04 +0300 Subject: [PATCH 3/3] added a NEWS.d item, and made the error msg more like that of the similar error when the decoder doesn't return a str. --- .../Core and Builtins/2017-08-25-20-43-22.bpo-31271.YMduKF.rst | 2 ++ Modules/_io/textio.c | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2017-08-25-20-43-22.bpo-31271.YMduKF.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-08-25-20-43-22.bpo-31271.YMduKF.rst b/Misc/NEWS.d/next/Core and Builtins/2017-08-25-20-43-22.bpo-31271.YMduKF.rst new file mode 100644 index 00000000000000..7bb7880105781e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2017-08-25-20-43-22.bpo-31271.YMduKF.rst @@ -0,0 +1,2 @@ +Fix an assertion failure in the write() method of `io.TextIOWrapper`, when +the encoder doesn't return a bytes object. Patch by Oren Milman. diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 81ebdae219ec85..cbb1728c396a6a 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1381,8 +1381,7 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text) return NULL; if (!PyBytes_Check(b)) { PyErr_Format(PyExc_TypeError, - "encode() should have returned a bytes object, not " - "'%.200s'", + "encoder should return a bytes object, not '%.200s'", Py_TYPE(b)->tp_name); Py_DECREF(b); return NULL;