Skip to content

Conversation

@dorosch
Copy link
Contributor

@dorosch dorosch commented Oct 7, 2024

@dorosch dorosch force-pushed the gh-85052-implement-repr-for-csv-module-classes branch from 00d444f to 87d086d Compare October 7, 2024 11:17
return d

def __repr__(self):
return "%s('%s')" % (self.__class__.__name__, self.dialect)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use an f-string instead? I don't see the need to use the ancient % operator:

Suggested change
return "%s('%s')" % (self.__class__.__name__, self.dialect)
return f"{self.__class__.__name__}(self.dialect!r})"

(This goes for all the other cases as well.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also not sure whether we should a fully-qualified name or not (namely, should we include the module's name or not?)

}

static PyObject *
Dialect_str(DialectObj *self) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP 7, and I'm not sure we need to take this as a DialectObj *. Since, as far as I can tell, we don't use any of the fields, we can use PyObject * instead to prevent a cast in the loop:

Suggested change
Dialect_str(DialectObj *self) {
Dialect_str(PyObject *self)
{

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also fixes any future UBsan

Dialect_str(DialectObj *self) {
Py_ssize_t pos = 0;
PyObject *key, *value;
PyObject *module = PyType_GetModule(Py_TYPE(self));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PyType_GetModule can fail, we need to check for NULL

Comment on lines +615 to +617
Py_INCREF(key);

return key;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplify:

Suggested change
Py_INCREF(key);
return key;
return Py_NewRef(key);


static PyObject *
Dialect_repr(DialectObj *self) {
PyObject *str = Dialect_str(self);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also return NULL (PyUnicode_FromString can fail), so we need to check for that.

}

static PyObject *
Dialect_repr(DialectObj *self) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP 7 nitpick again

Suggested change
Dialect_repr(DialectObj *self) {
Dialect_repr(DialectObj *self)
{

@ZeroIntensity
Copy link
Member

Bénédikt, can you review the Python side of this? You (hopefully) have looked at csv more than I have.

}

static PyObject *
Dialect_str(DialectObj *self) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also fixes any future UBsan

PyObject *dialects = get_csv_state(module)->dialects;

while (PyDict_Next(dialects, &pos, &key, &value)) {
if (value == (PyObject *)self) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove this when you change the signature.

}
}

return PyUnicode_FromString("unknown");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could intern the string in the module's state.

@@ -0,0 +1 @@
Implement a ``__repr__`` method to :class:`csv.Dialect`, :class:`csv.DictReader` and :class:`csv.DictWriter`.
Copy link
Member

@picnixz picnixz Nov 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Implement a ``__repr__`` method to :class:`csv.Dialect`, :class:`csv.DictReader` and :class:`csv.DictWriter`.
Add :meth:`~object.__repr__` to :class:`csv.Dialect`,
:class:`csv.DictReader` and :class:`csv.DictWriter`.

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.assertRegex(repr(dialect), r"\w+\.Dialect\('%s'\)" % name)
self.assertRegex(repr(dialect), rf"\w+\.Dialect\({name!r}\)")

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition, you could use with self.subTest(name) just to know which dialect is failing if there's any.

return d

def __repr__(self):
return "%s('%s')" % (self.__class__.__name__, self.dialect)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also not sure whether we should a fully-qualified name or not (namely, should we include the module's name or not?)

writer = csv.DictWriter(fileobj, fieldnames=[])
expected = "%s('%s')" % (writer.__class__.__name__, writer.dialect)
self.assertEqual(repr(writer), expected)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

for name in csv.list_dialects():
dialect = csv.get_dialect(name)
self.assertRegex(repr(dialect), r"\w+\.Dialect\('%s'\)" % name)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants