Commits

Jeff Allen  committed 7eab1d8

Check buffer and decoder types in _jyio.TextIOWrapper
We now pass the tests in test.test_io that require TextIOWrapper to verify
that the buffer returns bytes, and the decoder returns unicode.
We go one beyond CPython in requiring this of the unbounded read(),
as not doing so appears to have been an oversight in textio.c.

  • Participants
  • Parent commits cf6a2f6

Comments (0)

Files changed (2)

File Lib/_jyio.py

                )[self.seennl]
 
 
+def _check_decoded_chars(chars):
+    """Check decoder output is unicode"""
+    if not isinstance(chars, unicode):
+        raise TypeError("decoder should return a string result, not '%s'" %
+                        type(chars))
+
+def _check_buffered_bytes(b, context="read"):
+    """Check buffer has returned bytes"""
+    if not isinstance(b, str):
+        raise TypeError("underlying %s() should have returned a bytes object, not '%s'" %
+                        (context, type(b)))
+
+
+
 class TextIOWrapper(_TextIOBase):
 
     r"""Character and line based layer over a _BufferedIOBase object, buffer.
 
         # Read a chunk, decode it, and put the result in self._decoded_chars.
         input_chunk = self.buffer.read1(self._CHUNK_SIZE)
+        _check_buffered_bytes(input_chunk, "read1")
+
         eof = not input_chunk
-        self._set_decoded_chars(self._decoder.decode(input_chunk, eof))
+        decoded_chunk = self._decoder.decode(input_chunk, eof)
+        _check_decoded_chars(decoded_chunk)
+        self._set_decoded_chars(decoded_chunk)
 
         if self._telling:
             # At the snapshot point, len(dec_buffer) bytes before the read,
         if chars_to_skip:
             # Just like _read_chunk, feed the decoder and save a snapshot.
             input_chunk = self.buffer.read(bytes_to_feed)
-            self._set_decoded_chars(
-                self._decoder.decode(input_chunk, need_eof))
+            _check_buffered_bytes(input_chunk)
+            decoded_chunk = self._decoder.decode(input_chunk, need_eof)
+            _check_decoded_chars(decoded_chunk)
+            self._set_decoded_chars(decoded_chunk)
+
             self._snapshot = (dec_flags, input_chunk)
 
             # Skip chars_to_skip of the decoded characters.
             raise TypeError("an integer is required")
         if n < 0:
             # Read everything.
-            result = (self._get_decoded_chars() +
-                      decoder.decode(self.buffer.read(), final=True))
+            input_chunk = self.buffer.read()
+            # Jython difference: CPython textio.c omits:
+            _check_buffered_bytes(input_chunk)
+            decoded_chunk = decoder.decode(input_chunk, final=True)
+            _check_decoded_chars(decoded_chunk)
+            result = self._get_decoded_chars() + decoded_chunk
             self._set_decoded_chars('')
             self._snapshot = None
             return result

File Lib/test/test_io.py

         with self.assertRaises((AttributeError, TypeError)):
             txt.buffer = buf
 
-    @unittest.skipIf(support.is_jython, "FIXME: missing check?")
     def test_read_nonbytes(self):
         # Issue #17106
         # Crash when underlying read() returns non-bytes
         with self.maybeRaises(TypeError):
             t.readline()
         t = self.TextIOWrapper(NonbytesStream('a'))
-        self.assertEqual(t.read(), u'a')
-
-    @unittest.skipIf(support.is_jython, "FIXME: missing check?")
+        with self.maybeRaises(TypeError):
+            # Jython difference: also detect the error in
+            t.read()
+
     def test_illegal_decoder(self):
         # Issue #17106
         # Crash when decoder returns non-string