Skip to content

Commit d2bb18b

Browse files
committed
Issue #6241: Better type checking for the arguments of io.StringIO.
1 parent e671fd2 commit d2bb18b

3 files changed

Lines changed: 38 additions & 5 deletions

File tree

Lib/_pyio.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1924,8 +1924,10 @@ def __init__(self, initial_value="", newline="\n"):
19241924
# C version, even under Windows.
19251925
if newline is None:
19261926
self._writetranslate = False
1927-
if initial_value:
1927+
if initial_value is not None:
19281928
if not isinstance(initial_value, str):
1929+
raise TypeError("initial_value must be str or None, not {0}"
1930+
.format(type(initial_value).__name__))
19291931
initial_value = str(initial_value)
19301932
self.write(initial_value)
19311933
self.seek(0)

Lib/test/test_memoryio.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ def test_init(self):
140140
self.assertEqual(memio.getvalue(), buf * 2)
141141
memio.__init__(buf)
142142
self.assertEqual(memio.getvalue(), buf)
143+
self.assertRaises(TypeError, memio.__init__, [])
143144

144145
def test_read(self):
145146
buf = self.buftype("1234567890")
@@ -530,6 +531,13 @@ def test_issue5265(self):
530531
memio = self.ioclass("a\r\nb\r\n", newline=None)
531532
self.assertEqual(memio.read(5), "a\nb\n")
532533

534+
def test_newline_argument(self):
535+
self.assertRaises(TypeError, self.ioclass, newline=b"\n")
536+
self.assertRaises(ValueError, self.ioclass, newline="error")
537+
# These should not raise an error
538+
for newline in (None, "", "\n", "\r", "\r\n"):
539+
self.ioclass(newline=newline)
540+
533541

534542
class CBytesIOTest(PyBytesIOTest):
535543
ioclass = io.BytesIO

Modules/_io/stringio.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -550,22 +550,42 @@ stringio_init(stringio *self, PyObject *args, PyObject *kwds)
550550
{
551551
char *kwlist[] = {"initial_value", "newline", NULL};
552552
PyObject *value = NULL;
553+
PyObject *newline_obj = NULL;
553554
char *newline = "\n";
554555

555-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oz:__init__", kwlist,
556-
&value, &newline))
556+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:__init__", kwlist,
557+
&value, &newline_obj))
557558
return -1;
558559

560+
/* Parse the newline argument. This used to be done with the 'z'
561+
specifier, however this allowed any object with the buffer interface to
562+
be converted. Thus we have to parse it manually since we only want to
563+
allow unicode objects or None. */
564+
if (newline_obj == Py_None) {
565+
newline = NULL;
566+
}
567+
else if (newline_obj) {
568+
if (!PyUnicode_Check(newline_obj)) {
569+
PyErr_Format(PyExc_TypeError,
570+
"newline must be str or None, not %.200s",
571+
Py_TYPE(newline_obj)->tp_name);
572+
return -1;
573+
}
574+
newline = _PyUnicode_AsString(newline_obj);
575+
if (newline == NULL)
576+
return -1;
577+
}
578+
559579
if (newline && newline[0] != '\0'
560580
&& !(newline[0] == '\n' && newline[1] == '\0')
561581
&& !(newline[0] == '\r' && newline[1] == '\0')
562582
&& !(newline[0] == '\r' && newline[1] == '\n' && newline[2] == '\0')) {
563583
PyErr_Format(PyExc_ValueError,
564-
"illegal newline value: %s", newline);
584+
"illegal newline value: %R", newline_obj);
565585
return -1;
566586
}
567587
if (value && value != Py_None && !PyUnicode_Check(value)) {
568-
PyErr_Format(PyExc_ValueError,
588+
PyErr_Format(PyExc_TypeError,
569589
"initial_value must be str or None, not %.200s",
570590
Py_TYPE(value)->tp_name);
571591
return -1;
@@ -577,6 +597,9 @@ stringio_init(stringio *self, PyObject *args, PyObject *kwds)
577597
Py_CLEAR(self->writenl);
578598
Py_CLEAR(self->decoder);
579599

600+
assert((newline != NULL && newline_obj != Py_None) ||
601+
(newline == NULL && newline_obj == Py_None));
602+
580603
if (newline) {
581604
self->readnl = PyUnicode_FromString(newline);
582605
if (self->readnl == NULL)

0 commit comments

Comments
 (0)