Skip to content

Commit 9a80e2b

Browse files
committed
bpo-21861: fix __repr__ for io subclasses
1 parent 973a520 commit 9a80e2b

File tree

7 files changed

+48
-15
lines changed

7 files changed

+48
-15
lines changed

Lib/test/test_fileio.py

+10
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,16 @@ def testRepr(self):
168168
self.assertEqual(repr(self.f),
169169
"<%s.FileIO [closed]>" % (self.modulename,))
170170

171+
def test_subclass_repr(self):
172+
class TestSubclass(self.FileIO):
173+
pass
174+
175+
f = TestSubclass(TESTFN)
176+
self.assertIn(TestSubclass.__name__, repr(f))
177+
178+
f.close()
179+
self.assertIn(TestSubclass.__name__, repr(f))
180+
171181
def testReprNoCloseFD(self):
172182
fd = os.open(TESTFN, os.O_RDONLY)
173183
try:

Lib/test/test_io.py

+7
Original file line numberDiff line numberDiff line change
@@ -2677,6 +2677,13 @@ def test_recursive_repr(self):
26772677
except RuntimeError:
26782678
pass
26792679

2680+
def test_subclass_repr(self):
2681+
class TestSubclass(self.TextIOWrapper):
2682+
pass
2683+
2684+
f = TestSubclass(self.StringIO())
2685+
self.assertIn(TestSubclass.__name__, repr(f))
2686+
26802687
def test_line_buffering(self):
26812688
r = self.BytesIO()
26822689
b = self.BufferedWriter(r, 1000)

Lib/test/test_winconsoleio.py

+9
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ def test_open_name(self):
9898
self.assertIsInstance(f, ConIO)
9999
f.close()
100100

101+
def test_subclass_repr(self):
102+
class TestSubclass(ConIO):
103+
pass
104+
105+
f = TestSubclass("CON")
106+
self.assertIn(TestSubclass.__name__, repr(f))
107+
f.close()
108+
self.assertIn(TestSubclass.__name__, repr(f))
109+
101110
@unittest.skipIf(sys.getwindowsversion()[:2] <= (6, 1),
102111
"test does not work on Windows 7 and earlier")
103112
def test_conin_conout_names(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Use the object's actual class name in :meth:`_io.FileIO.__repr__`,
2+
:meth:`_io._WindowsConsoleIO` and :meth:`_io.TextIOWrapper.__repr__`, to
3+
make these methods subclass friendly.

Modules/_io/fileio.c

+9-8
Original file line numberDiff line numberDiff line change
@@ -1078,31 +1078,32 @@ static PyObject *
10781078
fileio_repr(fileio *self)
10791079
{
10801080
PyObject *nameobj, *res;
1081+
const char *type_name = Py_TYPE((PyObject *) self)->tp_name;
10811082

1082-
if (self->fd < 0)
1083-
return PyUnicode_FromFormat("<_io.FileIO [closed]>");
1083+
if (self->fd < 0) {
1084+
return PyUnicode_FromFormat("<%s [closed]>", type_name);
1085+
}
10841086

10851087
if (_PyObject_LookupAttr((PyObject *) self, &_Py_ID(name), &nameobj) < 0) {
10861088
return NULL;
10871089
}
10881090
if (nameobj == NULL) {
10891091
res = PyUnicode_FromFormat(
1090-
"<_io.FileIO fd=%d mode='%s' closefd=%s>",
1091-
self->fd, mode_string(self), self->closefd ? "True" : "False");
1092+
"<%s fd=%d mode='%s' closefd=%s>",
1093+
type_name, self->fd, mode_string(self), self->closefd ? "True" : "False");
10921094
}
10931095
else {
10941096
int status = Py_ReprEnter((PyObject *)self);
10951097
res = NULL;
10961098
if (status == 0) {
10971099
res = PyUnicode_FromFormat(
1098-
"<_io.FileIO name=%R mode='%s' closefd=%s>",
1099-
nameobj, mode_string(self), self->closefd ? "True" : "False");
1100+
"<%s name=%R mode='%s' closefd=%s>",
1101+
type_name, nameobj, mode_string(self), self->closefd ? "True" : "False");
11001102
Py_ReprLeave((PyObject *)self);
11011103
}
11021104
else if (status > 0) {
11031105
PyErr_Format(PyExc_RuntimeError,
1104-
"reentrant call inside %s.__repr__",
1105-
Py_TYPE(self)->tp_name);
1106+
"reentrant call inside %s.__repr__", type_name);
11061107
}
11071108
Py_DECREF(nameobj);
11081109
}

Modules/_io/textio.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -2854,10 +2854,11 @@ textiowrapper_repr(textio *self)
28542854
{
28552855
PyObject *nameobj, *modeobj, *res, *s;
28562856
int status;
2857+
const char *type_name = Py_TYPE(self)->tp_name;
28572858

28582859
CHECK_INITIALIZED(self);
28592860

2860-
res = PyUnicode_FromString("<_io.TextIOWrapper");
2861+
res = PyUnicode_FromFormat("<%s", type_name);
28612862
if (res == NULL)
28622863
return NULL;
28632864

@@ -2866,7 +2867,7 @@ textiowrapper_repr(textio *self)
28662867
if (status > 0) {
28672868
PyErr_Format(PyExc_RuntimeError,
28682869
"reentrant call inside %s.__repr__",
2869-
Py_TYPE(self)->tp_name);
2870+
type_name);
28702871
}
28712872
goto error;
28722873
}

Modules/_io/winconsoleio.c

+7-5
Original file line numberDiff line numberDiff line change
@@ -1028,15 +1028,17 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b)
10281028
static PyObject *
10291029
winconsoleio_repr(winconsoleio *self)
10301030
{
1031+
const char *type_name = (Py_TYPE((PyObject *)self)->tp_name);
1032+
10311033
if (self->fd == -1)
1032-
return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>");
1034+
return PyUnicode_FromFormat("<%s [closed]>", type_name);
10331035

10341036
if (self->readable)
1035-
return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>",
1036-
self->closefd ? "True" : "False");
1037+
return PyUnicode_FromFormat("<%s mode='rb' closefd=%s>",
1038+
type_name, self->closefd ? "True" : "False");
10371039
if (self->writable)
1038-
return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>",
1039-
self->closefd ? "True" : "False");
1040+
return PyUnicode_FromFormat("<%s mode='wb' closefd=%s>",
1041+
type_name, self->closefd ? "True" : "False");
10401042

10411043
PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode");
10421044
return NULL;

0 commit comments

Comments
 (0)