-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
bpo-21861: Improve _io.FileIO.__repr__ #14774
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2630,6 +2630,30 @@ def test_repr(self): | |
t.buffer.detach() | ||
repr(t) # Should not raise an exception | ||
|
||
def test_subclass_repr(self): | ||
class SubTextIOWrapper(self.TextIOWrapper): | ||
pass | ||
|
||
clsname = 'SubTextIOWrapper' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be simpler and clearer to just hard-code the class name in the strings below. |
||
raw = self.BytesIO("hello".encode("utf-8")) | ||
b = self.BufferedReader(raw) | ||
t = SubTextIOWrapper(b, encoding="utf-8") | ||
self.assertRegex(repr(t), | ||
r"%s encoding='utf-8'>" % clsname) | ||
raw.name = "dummy" | ||
self.assertRegex(repr(t), | ||
r"%s name='dummy' encoding='utf-8'>" % clsname) | ||
t.mode = "r" | ||
self.assertRegex(repr(t), | ||
r"%s name='dummy' mode='r' encoding='utf-8'>" % | ||
clsname) | ||
raw.name = b"dummy" | ||
self.assertRegex( | ||
repr(t), | ||
r"%s name=b'dummy' mode='r' encoding='utf-8'>" % clsname) | ||
t.buffer.detach() | ||
repr(t) # Should not raise an exception | ||
|
||
def test_recursive_repr(self): | ||
# Issue #25455 | ||
raw = self.BytesIO() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Remove the hardcoded classes names from ``_io`` module classes' ``__repr__`` in | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that simply mentioning Also I think phrasing this positively would be better, but this is a matter of style: Use the object's actual class name in :meth:`_io.FileIO.__repr__` and
:meth:`_io.TextIOWrapper.__repr__`, to make these methods subclass friendly. (For more info about the inline markup used above, see the devguide.) |
||
favor of the actual object type name to make it more subclass friendly. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1080,22 +1080,26 @@ fileio_repr(fileio *self) | |
PyObject *nameobj, *res; | ||
|
||
if (self->fd < 0) | ||
return PyUnicode_FromFormat("<_io.FileIO [closed]>"); | ||
return PyUnicode_FromFormat( | ||
"<%s [closed]>", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be good to have a test for different cases? Something like below in test_fileio.py to ensure C and Python implementations behave the same. def test_custom_repr(self):
class CustomFileIO(self.FileIO):
pass
custom_file_io = CustomFileIO(TESTFN, 'w')
custom_file_io.close()
self.assertIn("CustomFileIO", repr(custom_file_io)) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This PR definitely needs tests such as the one proposed here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @taleinat @tirkarthi thanks for comments. I added some tests as suggested. |
||
Py_TYPE((PyObject *) self)->tp_name); | ||
|
||
if (_PyObject_LookupAttrId((PyObject *) self, &PyId_name, &nameobj) < 0) { | ||
return NULL; | ||
} | ||
if (nameobj == NULL) { | ||
res = PyUnicode_FromFormat( | ||
"<_io.FileIO fd=%d mode='%s' closefd=%s>", | ||
"<%s fd=%d mode='%s' closefd=%s>", | ||
Py_TYPE((PyObject *) self)->tp_name, | ||
self->fd, mode_string(self), self->closefd ? "True" : "False"); | ||
} | ||
else { | ||
int status = Py_ReprEnter((PyObject *)self); | ||
res = NULL; | ||
if (status == 0) { | ||
res = PyUnicode_FromFormat( | ||
"<_io.FileIO name=%R mode='%s' closefd=%s>", | ||
"<%s name=%R mode='%s' closefd=%s>", | ||
Py_TYPE((PyObject *) self)->tp_name, | ||
nameobj, mode_string(self), self->closefd ? "True" : "False"); | ||
Py_ReprLeave((PyObject *)self); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is overly specific: it is testing the implementation's details (the specific formatting, including the order of the values) rather than the intent (that certain values are included).
I suggest something like:
This is also true for the similar assertion below, and for those in the
TextIOWrapper
test as well.