diff options
-rw-r--r-- | Lib/_pyio.py | 4 | ||||
-rw-r--r-- | Lib/test/test_memoryio.py | 8 | ||||
-rw-r--r-- | Modules/_io/stringio.c | 31 |
3 files changed, 38 insertions, 5 deletions
diff --git a/Lib/_pyio.py b/Lib/_pyio.py index bbf65bc..5458f99 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1924,8 +1924,10 @@ class StringIO(TextIOWrapper): # C version, even under Windows. if newline is None: self._writetranslate = False - if initial_value: + if initial_value is not None: if not isinstance(initial_value, str): + raise TypeError("initial_value must be str or None, not {0}" + .format(type(initial_value).__name__)) initial_value = str(initial_value) self.write(initial_value) self.seek(0) diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py index 0b25283..670dab9 100644 --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -140,6 +140,7 @@ class MemoryTestMixin: self.assertEqual(memio.getvalue(), buf * 2) memio.__init__(buf) self.assertEqual(memio.getvalue(), buf) + self.assertRaises(TypeError, memio.__init__, []) def test_read(self): buf = self.buftype("1234567890") @@ -530,6 +531,13 @@ class PyStringIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase): memio = self.ioclass("a\r\nb\r\n", newline=None) self.assertEqual(memio.read(5), "a\nb\n") + def test_newline_argument(self): + self.assertRaises(TypeError, self.ioclass, newline=b"\n") + self.assertRaises(ValueError, self.ioclass, newline="error") + # These should not raise an error + for newline in (None, "", "\n", "\r", "\r\n"): + self.ioclass(newline=newline) + class CBytesIOTest(PyBytesIOTest): ioclass = io.BytesIO diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index d773723..bfb099c 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -550,22 +550,42 @@ stringio_init(stringio *self, PyObject *args, PyObject *kwds) { char *kwlist[] = {"initial_value", "newline", NULL}; PyObject *value = NULL; + PyObject *newline_obj = NULL; char *newline = "\n"; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oz:__init__", kwlist, - &value, &newline)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:__init__", kwlist, + &value, &newline_obj)) return -1; + /* Parse the newline argument. This used to be done with the 'z' + specifier, however this allowed any object with the buffer interface to + be converted. Thus we have to parse it manually since we only want to + allow unicode objects or None. */ + if (newline_obj == Py_None) { + newline = NULL; + } + else if (newline_obj) { + if (!PyUnicode_Check(newline_obj)) { + PyErr_Format(PyExc_TypeError, + "newline must be str or None, not %.200s", + Py_TYPE(newline_obj)->tp_name); + return -1; + } + newline = _PyUnicode_AsString(newline_obj); + if (newline == NULL) + return -1; + } + if (newline && newline[0] != '\0' && !(newline[0] == '\n' && newline[1] == '\0') && !(newline[0] == '\r' && newline[1] == '\0') && !(newline[0] == '\r' && newline[1] == '\n' && newline[2] == '\0')) { PyErr_Format(PyExc_ValueError, - "illegal newline value: %s", newline); + "illegal newline value: %R", newline_obj); return -1; } if (value && value != Py_None && !PyUnicode_Check(value)) { - PyErr_Format(PyExc_ValueError, + PyErr_Format(PyExc_TypeError, "initial_value must be str or None, not %.200s", Py_TYPE(value)->tp_name); return -1; @@ -577,6 +597,9 @@ stringio_init(stringio *self, PyObject *args, PyObject *kwds) Py_CLEAR(self->writenl); Py_CLEAR(self->decoder); + assert((newline != NULL && newline_obj != Py_None) || + (newline == NULL && newline_obj == Py_None)); + if (newline) { self->readnl = PyUnicode_FromString(newline); if (self->readnl == NULL) |