Index: Lib/_pyio.py =================================================================== --- Lib/_pyio.py (revision 73267) +++ Lib/_pyio.py (working copy) @@ -1924,8 +1924,10 @@ # 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) Index: Lib/test/test_memoryio.py =================================================================== --- Lib/test/test_memoryio.py (revision 73267) +++ Lib/test/test_memoryio.py (working copy) @@ -140,6 +140,7 @@ 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") @@ -523,7 +524,14 @@ 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 UnsupportedOperation = io.UnsupportedOperation Index: Modules/_io/stringio.c =================================================================== --- Modules/_io/stringio.c (revision 73267) +++ Modules/_io/stringio.c (working copy) @@ -546,22 +546,42 @@ { 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; @@ -573,6 +593,9 @@ 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)