diff --git a/Lib/csv.py b/Lib/csv.py index cd202659873811..c549d6d63bcc16 100644 --- a/Lib/csv.py +++ b/Lib/csv.py @@ -193,6 +193,9 @@ def __next__(self): d[key] = self.restval return d + def __repr__(self): + return "%s('%s')" % (self.__class__.__name__, self.dialect) + __class_getitem__ = classmethod(types.GenericAlias) @@ -208,6 +211,7 @@ def __init__(self, f, fieldnames, restval="", extrasaction="raise", raise ValueError("extrasaction (%s) must be 'raise' or 'ignore'" % extrasaction) self.extrasaction = extrasaction + self.dialect = dialect self.writer = writer(f, dialect, *args, **kwds) def writeheader(self): @@ -228,6 +232,9 @@ def writerow(self, rowdict): def writerows(self, rowdicts): return self.writer.writerows(map(self._dict_to_list, rowdicts)) + def __repr__(self): + return "%s('%s')" % (self.__class__.__name__, self.dialect) + __class_getitem__ = classmethod(types.GenericAlias) diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index ce5c03659f1979..96a04b872225f3 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -204,6 +204,7 @@ class BadItem: def __str__(self): raise OSError self._write_error_test(OSError, [BadItem()]) + def test_write_bigfield(self): # This exercises the buffer realloc functionality bigstring = 'X' * 50000 @@ -551,6 +552,19 @@ def test_roundtrip_escaped_unquoted_newlines(self): escapechar="\\")): self.assertEqual(row, rows[i]) + def test_dict_reader_repr(self): + fileobj = StringIO() + reader = csv.DictReader(fileobj) + expected = "%s('%s')" % (reader.__class__.__name__, reader.dialect) + self.assertEqual(repr(reader), expected) + + def test_dict_writer_repr(self): + fileobj = StringIO() + writer = csv.DictWriter(fileobj, fieldnames=[]) + expected = "%s('%s')" % (writer.__class__.__name__, writer.dialect) + self.assertEqual(repr(writer), expected) + + class TestDialectRegistry(unittest.TestCase): def test_registry_badargs(self): @@ -685,6 +699,16 @@ def test_pickle(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): self.assertRaises(TypeError, pickle.dumps, dialect, proto) + def test_dialect_str(self): + for name in csv.list_dialects(): + dialect = csv.get_dialect(name) + self.assertEqual(str(dialect), name) + + def test_dialect_repr(self): + for name in csv.list_dialects(): + dialect = csv.get_dialect(name) + self.assertRegex(repr(dialect), r"\w+\.Dialect\('%s'\)" % name) + class TestCsvBase(unittest.TestCase): def readerAssertEqual(self, input, expected_result): with TemporaryFile("w+", encoding="utf-8", newline='') as fileobj: diff --git a/Misc/NEWS.d/next/Library/2024-10-07-13-58-34.gh-issue-85052.7ZTIuX.rst b/Misc/NEWS.d/next/Library/2024-10-07-13-58-34.gh-issue-85052.7ZTIuX.rst new file mode 100644 index 00000000000000..36310cec5a5873 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-07-13-58-34.gh-issue-85052.7ZTIuX.rst @@ -0,0 +1 @@ +Implement a ``__repr__`` method to :class:`csv.Dialect`, :class:`csv.DictReader` and :class:`csv.DictWriter`. diff --git a/Modules/_csv.c b/Modules/_csv.c index a623ea449da779..a1d170cd167f1b 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -603,6 +603,34 @@ Dialect_traverse(DialectObj *self, visitproc visit, void *arg) return 0; } +static PyObject * +Dialect_str(DialectObj *self) { + Py_ssize_t pos = 0; + PyObject *key, *value; + PyObject *module = PyType_GetModule(Py_TYPE(self)); + PyObject *dialects = get_csv_state(module)->dialects; + + while (PyDict_Next(dialects, &pos, &key, &value)) { + if (value == (PyObject *)self) { + Py_INCREF(key); + + return key; + } + } + + return PyUnicode_FromString("unknown"); +} + +static PyObject * +Dialect_repr(DialectObj *self) { + PyObject *str = Dialect_str(self); + PyObject *repr = PyUnicode_FromFormat("%s(%R)", Py_TYPE(self)->tp_name, str); + + Py_DECREF(str); + + return repr; +} + static PyType_Slot Dialect_Type_slots[] = { {Py_tp_doc, (char*)Dialect_Type_doc}, {Py_tp_members, Dialect_memberlist}, @@ -612,6 +640,8 @@ static PyType_Slot Dialect_Type_slots[] = { {Py_tp_dealloc, Dialect_dealloc}, {Py_tp_clear, Dialect_clear}, {Py_tp_traverse, Dialect_traverse}, + {Py_tp_str, Dialect_str}, + {Py_tp_repr, Dialect_repr}, {0, NULL} };