diff --git a/Doc/library/io.rst b/Doc/library/io.rst index d8a498b3415..26285b4fcd8 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -600,6 +600,10 @@ Text I/O The name of the encoding used to decode the stream's bytes into strings, and to encode strings into bytes. + .. attribute:: errors + + The error setting of the decoder or encoder. + .. attribute:: newlines A string, a tuple of strings, or ``None``, indicating the newlines @@ -665,13 +669,9 @@ Text I/O If *line_buffering* is ``True``, :meth:`flush` is implied when a call to write contains a newline character. - :class:`TextIOWrapper` provides these data attributes in addition to those of + :class:`TextIOWrapper` provides one attribute in addition to those of :class:`TextIOBase` and its parents: - .. attribute:: errors - - The encoding and decoding error setting. - .. attribute:: line_buffering Whether line buffering is enabled. diff --git a/Lib/_pyio.py b/Lib/_pyio.py index c1cdf439e12..bbf65bc0b18 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1286,6 +1286,13 @@ class TextIOBase(IOBase): """ return None + @property + def errors(self): + """Error setting of the decoder or encoder. + + Subclasses should override.""" + return None + io.TextIOBase.register(TextIOBase) @@ -1932,6 +1939,10 @@ class StringIO(TextIOWrapper): # that's a implementation detail. return object.__repr__(self) + @property + def errors(self): + return None + @property def encoding(self): return None diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 1337b7c096b..4405d99f06b 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -2024,6 +2024,12 @@ class TextIOWrapperTest(unittest.TestCase): with self.open(filename, 'rb') as f: self.assertEquals(f.read(), 'bbbzzz'.encode(charset)) + def test_errors_property(self): + with self.open(support.TESTFN, "w") as f: + self.assertEqual(f.errors, "strict") + with self.open(support.TESTFN, "w", errors="replace") as f: + self.assertEqual(f.errors, "replace") + class CTextIOWrapperTest(TextIOWrapperTest): diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py index d94d9dd552a..0d044e96ba1 100644 --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -459,9 +459,9 @@ class PyStringIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase): # These are just dummy values but we nevertheless check them for fear # of unexpected breakage. - self.assertTrue(memio.encoding is None) - self.assertEqual(memio.errors, "strict") - self.assertEqual(memio.line_buffering, False) + self.assertIsNone(memio.encoding) + self.assertIsNone(memio.errors) + self.assertFalse(memio.line_buffering) def test_newline_none(self): # newline=None diff --git a/Misc/NEWS b/Misc/NEWS index 2c37a808ccd..dd544dd82dd 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,10 @@ Core and Builtins Library ------- +- Issue #6217: The C implementation of io.TextIOWrapper didn't include the + errors property. Additionally, the errors and encoding properties of StringIO + are always None now. + - Issue #6137: The pickle module now translates module names when loading or dumping pickles with a 2.x-compatible protocol, in order to make data sharing and migration easier. This behaviour can be disabled using the diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 136f41d4427..a0d8a514cda 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -660,22 +660,6 @@ stringio_closed(StringIOObject *self, void *context) return PyBool_FromLong(self->closed); } -static PyObject * -stringio_encoding(StringIOObject *self, void *context) -{ - CHECK_INITIALIZED(self); - CHECK_CLOSED(self); - Py_RETURN_NONE; -} - -static PyObject * -stringio_errors(StringIOObject *self, void *context) -{ - CHECK_INITIALIZED(self); - CHECK_CLOSED(self); - return PyUnicode_FromString("strict"); -} - static PyObject * stringio_line_buffering(StringIOObject *self, void *context) { @@ -720,8 +704,6 @@ static PyGetSetDef stringio_getset[] = { will be found. */ {"buffer", (getter)stringio_buffer, NULL, NULL}, - {"encoding", (getter)stringio_encoding, NULL, NULL}, - {"errors", (getter)stringio_errors, NULL, NULL}, {"line_buffering", (getter)stringio_line_buffering, NULL, NULL}, {NULL} }; diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index c8d28334c14..3ec8be62e1c 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -104,6 +104,18 @@ TextIOBase_newlines_get(PyObject *self, void *context) Py_RETURN_NONE; } +PyDoc_STRVAR(TextIOBase_errors_doc, + "The error setting of the decoder or encoder.\n" + "\n" + "Subclasses should override.\n" + ); + +static PyObject * +TextIOBase_errors_get(PyObject *self, void *context) +{ + Py_RETURN_NONE; +} + static PyMethodDef TextIOBase_methods[] = { {"detach", (PyCFunction)TextIOBase_detach, METH_NOARGS, TextIOBase_detach_doc}, @@ -116,6 +128,7 @@ static PyMethodDef TextIOBase_methods[] = { static PyGetSetDef TextIOBase_getset[] = { {"encoding", (getter)TextIOBase_encoding_get, NULL, TextIOBase_encoding_doc}, {"newlines", (getter)TextIOBase_newlines_get, NULL, TextIOBase_newlines_doc}, + {"errors", (getter)TextIOBase_errors_get, NULL, TextIOBase_errors_doc}, {NULL} }; @@ -2461,6 +2474,13 @@ TextIOWrapper_newlines_get(PyTextIOWrapperObject *self, void *context) return res; } +static PyObject * +TextIOWrapper_errors_get(PyTextIOWrapperObject *self, void *context) +{ + CHECK_INITIALIZED(self); + return PyUnicode_FromString(PyBytes_AS_STRING(self->errors)); +} + static PyObject * TextIOWrapper_chunk_size_get(PyTextIOWrapperObject *self, void *context) { @@ -2519,6 +2539,7 @@ static PyGetSetDef TextIOWrapper_getset[] = { /* {"mode", (getter)TextIOWrapper_mode_get, NULL, NULL}, */ {"newlines", (getter)TextIOWrapper_newlines_get, NULL, NULL}, + {"errors", (getter)TextIOWrapper_errors_get, NULL, NULL}, {"_CHUNK_SIZE", (getter)TextIOWrapper_chunk_size_get, (setter)TextIOWrapper_chunk_size_set, NULL}, {NULL}