Skip to content

Commit 996fb04

Browse files
author
Michal Kuffa
committed
bpo-21861: Improve _io classes __repr__
Instead of hardcoding the class name in the __repr__ string, resolve the actuall one which makes the __repr__ more subclass friendly.
1 parent 925dc7f commit 996fb04

File tree

6 files changed

+64
-7
lines changed

6 files changed

+64
-7
lines changed

Lib/test/test_fileio.py

+21
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,27 @@ def test_none_args(self):
154154
def test_reject(self):
155155
self.assertRaises(TypeError, self.f.write, "Hello!")
156156

157+
def test_subclass_repr(self):
158+
class CustomFileIO(self.FileIO):
159+
pass
160+
161+
custom_file_io = CustomFileIO(TESTFN, 'w')
162+
163+
self.assertIn(
164+
"CustomFileIO name=%r mode=%r closefd=True>" %
165+
(custom_file_io.name, custom_file_io.mode),
166+
repr(custom_file_io))
167+
168+
del custom_file_io.name
169+
self.assertIn(
170+
"CustomFileIO fd=%r mode=%r closefd=True>" %
171+
(custom_file_io.fileno(), custom_file_io.mode),
172+
repr(custom_file_io))
173+
174+
custom_file_io.close()
175+
self.assertIn("CustomFileIO [closed]", repr(custom_file_io))
176+
177+
157178
def testRepr(self):
158179
self.assertEqual(repr(self.f),
159180
"<%s.FileIO name=%r mode=%r closefd=True>" %

Lib/test/test_io.py

+24
Original file line numberDiff line numberDiff line change
@@ -2630,6 +2630,30 @@ def test_repr(self):
26302630
t.buffer.detach()
26312631
repr(t) # Should not raise an exception
26322632

2633+
def test_subclass_repr(self):
2634+
class SubTextIOWrapper(self.TextIOWrapper):
2635+
pass
2636+
2637+
clsname = 'SubTextIOWrapper'
2638+
raw = self.BytesIO("hello".encode("utf-8"))
2639+
b = self.BufferedReader(raw)
2640+
t = SubTextIOWrapper(b, encoding="utf-8")
2641+
self.assertRegex(repr(t),
2642+
r"%s encoding='utf-8'>" % clsname)
2643+
raw.name = "dummy"
2644+
self.assertRegex(repr(t),
2645+
r"%s name='dummy' encoding='utf-8'>" % clsname)
2646+
t.mode = "r"
2647+
self.assertRegex(repr(t),
2648+
r"%s name='dummy' mode='r' encoding='utf-8'>" %
2649+
clsname)
2650+
raw.name = b"dummy"
2651+
self.assertRegex(
2652+
repr(t),
2653+
r"%s name=b'dummy' mode='r' encoding='utf-8'>" % clsname)
2654+
t.buffer.detach()
2655+
repr(t) # Should not raise an exception
2656+
26332657
def test_recursive_repr(self):
26342658
# Issue #25455
26352659
raw = self.BytesIO()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Remove the hardcoded classes names from ``_io`` module classes' ``__repr__`` in
2+
favor of the actual object type name to make it more subclass friendly.

Modules/_io/fileio.c

+7-3
Original file line numberDiff line numberDiff line change
@@ -1080,22 +1080,26 @@ fileio_repr(fileio *self)
10801080
PyObject *nameobj, *res;
10811081

10821082
if (self->fd < 0)
1083-
return PyUnicode_FromFormat("<_io.FileIO [closed]>");
1083+
return PyUnicode_FromFormat(
1084+
"<%s [closed]>",
1085+
Py_TYPE((PyObject *) self)->tp_name);
10841086

10851087
if (_PyObject_LookupAttrId((PyObject *) self, &PyId_name, &nameobj) < 0) {
10861088
return NULL;
10871089
}
10881090
if (nameobj == NULL) {
10891091
res = PyUnicode_FromFormat(
1090-
"<_io.FileIO fd=%d mode='%s' closefd=%s>",
1092+
"<%s fd=%d mode='%s' closefd=%s>",
1093+
Py_TYPE((PyObject *) self)->tp_name,
10911094
self->fd, mode_string(self), self->closefd ? "True" : "False");
10921095
}
10931096
else {
10941097
int status = Py_ReprEnter((PyObject *)self);
10951098
res = NULL;
10961099
if (status == 0) {
10971100
res = PyUnicode_FromFormat(
1098-
"<_io.FileIO name=%R mode='%s' closefd=%s>",
1101+
"<%s name=%R mode='%s' closefd=%s>",
1102+
Py_TYPE((PyObject *) self)->tp_name,
10991103
nameobj, mode_string(self), self->closefd ? "True" : "False");
11001104
Py_ReprLeave((PyObject *)self);
11011105
}

Modules/_io/textio.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -2886,7 +2886,7 @@ textiowrapper_repr(textio *self)
28862886

28872887
CHECK_INITIALIZED(self);
28882888

2889-
res = PyUnicode_FromString("<_io.TextIOWrapper");
2889+
res = PyUnicode_FromFormat("<%s", Py_TYPE((PyObject *) self)->tp_name);
28902890
if (res == NULL)
28912891
return NULL;
28922892

Modules/_io/winconsoleio.c

+9-3
Original file line numberDiff line numberDiff line change
@@ -1034,13 +1034,19 @@ static PyObject *
10341034
winconsoleio_repr(winconsoleio *self)
10351035
{
10361036
if (self->handle == INVALID_HANDLE_VALUE)
1037-
return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>");
1037+
return PyUnicode_FromFormat(
1038+
"<%s [closed]>",
1039+
Py_TYPE((PyObject *) self)->tp_name);
10381040

10391041
if (self->readable)
1040-
return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>",
1042+
return PyUnicode_FromFormat(
1043+
"<%s mode='rb' closefd=%s>",
1044+
Py_TYPE((PyObject *) self)->tp_name,
10411045
self->closehandle ? "True" : "False");
10421046
if (self->writable)
1043-
return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>",
1047+
return PyUnicode_FromFormat(
1048+
"<%s mode='wb' closefd=%s>",
1049+
Py_TYPE((PyObject *) self)->tp_name,
10441050
self->closehandle ? "True" : "False");
10451051

10461052
PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode");

0 commit comments

Comments
 (0)