From 1f863475d5b9e882d104c41089e492d55c1fc579 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 30 May 2020 14:20:10 +0300 Subject: [PATCH] bpo-26407: Do not mask errors in csv. Unexpected errors in calling the __iter__ method are no longer masked by TypeError in csv.reader(), csv.writer.writerow() and csv.writer.writerows(). --- Lib/test/test_csv.py | 20 ++++++++++++++++++- .../2020-05-30-14-19-47.bpo-26407.MjWLO1.rst | 3 +++ Modules/_csv.c | 16 +++++++-------- 3 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-05-30-14-19-47.bpo-26407.MjWLO1.rst diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index a16d14019f341f..d421be075ca27f 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -14,6 +14,12 @@ from textwrap import dedent from collections import OrderedDict + +class BadIterable: + def __iter__(self): + raise OSError + + class Test_Csv(unittest.TestCase): """ Test the underlying C csv parser in ways that are not appropriate @@ -40,9 +46,15 @@ def _test_arg_valid(self, ctor, arg): def test_reader_arg_valid(self): self._test_arg_valid(csv.reader, []) + self.assertRaises(OSError, csv.reader, BadIterable()) def test_writer_arg_valid(self): self._test_arg_valid(csv.writer, StringIO()) + class BadWriter: + @property + def write(self): + raise OSError + self.assertRaises(OSError, csv.writer, BadWriter()) def _test_default_attrs(self, ctor, *args): obj = ctor(*args) @@ -141,6 +153,7 @@ def test_write_arg_valid(self): self._write_test([None], '""') self._write_error_test(csv.Error, [None], quoting = csv.QUOTE_NONE) # Check that exceptions are passed up the chain + self._write_error_test(OSError, BadIterable()) class BadList: def __len__(self): return 10; @@ -230,6 +243,12 @@ def test_writerows_with_none(self): fileobj.seek(0) self.assertEqual(fileobj.read(), 'a\r\n""\r\n') + def test_writerows_errors(self): + with TemporaryFile("w+", newline='') as fileobj: + writer = csv.writer(fileobj) + self.assertRaises(TypeError, writer.writerows, None) + self.assertRaises(OSError, writer.writerows, BadIterable()) + @support.cpython_only def test_writerows_legacy_strings(self): import _testcapi @@ -334,7 +353,6 @@ def test_read_linenum(self): def test_roundtrip_quoteed_newlines(self): with TemporaryFile("w+", newline='') as fileobj: writer = csv.writer(fileobj) - self.assertRaises(TypeError, writer.writerows, None) rows = [['a\nb','b'],['c','x\r\nd']] writer.writerows(rows) fileobj.seek(0) diff --git a/Misc/NEWS.d/next/Library/2020-05-30-14-19-47.bpo-26407.MjWLO1.rst b/Misc/NEWS.d/next/Library/2020-05-30-14-19-47.bpo-26407.MjWLO1.rst new file mode 100644 index 00000000000000..d0e45cf1b1f2f4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-30-14-19-47.bpo-26407.MjWLO1.rst @@ -0,0 +1,3 @@ +Unexpected errors in calling the ``__iter__`` method are no longer masked +by ``TypeError`` in :func:`csv.reader`, :func:`csv.writer.writerow` and +:meth:`csv.writer.writerows`. diff --git a/Modules/_csv.c b/Modules/_csv.c index 3a52632ccfd456..59109b018e7562 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -964,8 +964,6 @@ csv_reader(PyObject *module, PyObject *args, PyObject *keyword_args) } self->input_iter = PyObject_GetIter(iterator); if (self->input_iter == NULL) { - PyErr_SetString(PyExc_TypeError, - "argument 1 must be an iterator"); Py_DECREF(self); return NULL; } @@ -1171,10 +1169,14 @@ csv_writerow(WriterObj *self, PyObject *seq) PyObject *iter, *field, *line, *result; iter = PyObject_GetIter(seq); - if (iter == NULL) - return PyErr_Format(_csvstate_global->error_obj, - "iterable expected, not %.200s", - Py_TYPE(seq)->tp_name); + if (iter == NULL) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_Format(_csvstate_global->error_obj, + "iterable expected, not %.200s", + Py_TYPE(seq)->tp_name); + } + return NULL; + } /* Join all fields in internal buffer. */ @@ -1264,8 +1266,6 @@ csv_writerows(WriterObj *self, PyObject *seqseq) row_iter = PyObject_GetIter(seqseq); if (row_iter == NULL) { - PyErr_SetString(PyExc_TypeError, - "writerows() argument must be iterable"); return NULL; } while ((row_obj = PyIter_Next(row_iter))) {