Skip to content

Commit 623b338

Browse files
authored
gh-66060: Use actual class name in _io type's __repr__ (#30824)
Use the object's actual class name in the following _io type's __repr__: - FileIO - TextIOWrapper - _WindowsConsoleIO
1 parent 1092cfb commit 623b338

File tree

7 files changed

+59
-20
lines changed

7 files changed

+59
-20
lines changed

Lib/test/test_fileio.py

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

177+
def test_subclass_repr(self):
178+
class TestSubclass(self.FileIO):
179+
pass
180+
181+
f = TestSubclass(TESTFN)
182+
with f:
183+
self.assertIn(TestSubclass.__name__, repr(f))
184+
185+
self.assertIn(TestSubclass.__name__, repr(f))
186+
177187
def testReprNoCloseFD(self):
178188
fd = os.open(TESTFN, os.O_RDONLY)
179189
try:

Lib/test/test_io.py

+7
Original file line numberDiff line numberDiff line change
@@ -2806,6 +2806,13 @@ def test_recursive_repr(self):
28062806
with self.assertRaises(RuntimeError):
28072807
repr(t) # Should not crash
28082808

2809+
def test_subclass_repr(self):
2810+
class TestSubclass(self.TextIOWrapper):
2811+
pass
2812+
2813+
f = TestSubclass(self.StringIO())
2814+
self.assertIn(TestSubclass.__name__, repr(f))
2815+
28092816
def test_line_buffering(self):
28102817
r = self.BytesIO()
28112818
b = self.BufferedWriter(r, 1000)

Lib/test/test_winconsoleio.py

+10
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ 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+
with f:
107+
self.assertIn(TestSubclass.__name__, repr(f))
108+
109+
self.assertIn(TestSubclass.__name__, repr(f))
110+
101111
@unittest.skipIf(sys.getwindowsversion()[:2] <= (6, 1),
102112
"test does not work on Windows 7 and earlier")
103113
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
@@ -1100,31 +1100,32 @@ static PyObject *
11001100
fileio_repr(fileio *self)
11011101
{
11021102
PyObject *nameobj, *res;
1103+
const char *type_name = Py_TYPE((PyObject *) self)->tp_name;
11031104

1104-
if (self->fd < 0)
1105-
return PyUnicode_FromFormat("<_io.FileIO [closed]>");
1105+
if (self->fd < 0) {
1106+
return PyUnicode_FromFormat("<%.100s [closed]>", type_name);
1107+
}
11061108

11071109
if (PyObject_GetOptionalAttr((PyObject *) self, &_Py_ID(name), &nameobj) < 0) {
11081110
return NULL;
11091111
}
11101112
if (nameobj == NULL) {
11111113
res = PyUnicode_FromFormat(
1112-
"<_io.FileIO fd=%d mode='%s' closefd=%s>",
1113-
self->fd, mode_string(self), self->closefd ? "True" : "False");
1114+
"<%.100s fd=%d mode='%s' closefd=%s>",
1115+
type_name, self->fd, mode_string(self), self->closefd ? "True" : "False");
11141116
}
11151117
else {
11161118
int status = Py_ReprEnter((PyObject *)self);
11171119
res = NULL;
11181120
if (status == 0) {
11191121
res = PyUnicode_FromFormat(
1120-
"<_io.FileIO name=%R mode='%s' closefd=%s>",
1121-
nameobj, mode_string(self), self->closefd ? "True" : "False");
1122+
"<%.100s name=%R mode='%s' closefd=%s>",
1123+
type_name, nameobj, mode_string(self), self->closefd ? "True" : "False");
11221124
Py_ReprLeave((PyObject *)self);
11231125
}
11241126
else if (status > 0) {
11251127
PyErr_Format(PyExc_RuntimeError,
1126-
"reentrant call inside %s.__repr__",
1127-
Py_TYPE(self)->tp_name);
1128+
"reentrant call inside %.100s.__repr__", type_name);
11281129
}
11291130
Py_DECREF(nameobj);
11301131
}

Modules/_io/textio.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -2948,19 +2948,20 @@ textiowrapper_repr(textio *self)
29482948
{
29492949
PyObject *nameobj, *modeobj, *res, *s;
29502950
int status;
2951+
const char *type_name = Py_TYPE(self)->tp_name;
29512952

29522953
CHECK_INITIALIZED(self);
29532954

2954-
res = PyUnicode_FromString("<_io.TextIOWrapper");
2955+
res = PyUnicode_FromFormat("<%.100s", type_name);
29552956
if (res == NULL)
29562957
return NULL;
29572958

29582959
status = Py_ReprEnter((PyObject *)self);
29592960
if (status != 0) {
29602961
if (status > 0) {
29612962
PyErr_Format(PyExc_RuntimeError,
2962-
"reentrant call inside %s.__repr__",
2963-
Py_TYPE(self)->tp_name);
2963+
"reentrant call inside %.100s.__repr__",
2964+
type_name);
29642965
}
29652966
goto error;
29662967
}

Modules/_io/winconsoleio.c

+16-9
Original file line numberDiff line numberDiff line change
@@ -1070,15 +1070,22 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, PyTypeObject *cls,
10701070
static PyObject *
10711071
winconsoleio_repr(winconsoleio *self)
10721072
{
1073-
if (self->fd == -1)
1074-
return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>");
1075-
1076-
if (self->readable)
1077-
return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>",
1078-
self->closefd ? "True" : "False");
1079-
if (self->writable)
1080-
return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>",
1081-
self->closefd ? "True" : "False");
1073+
const char *type_name = (Py_TYPE((PyObject *)self)->tp_name);
1074+
1075+
if (self->fd == -1) {
1076+
return PyUnicode_FromFormat("<%.100s [closed]>", type_name);
1077+
}
1078+
1079+
if (self->readable) {
1080+
return PyUnicode_FromFormat("<%.100s mode='rb' closefd=%s>",
1081+
type_name,
1082+
self->closefd ? "True" : "False");
1083+
}
1084+
if (self->writable) {
1085+
return PyUnicode_FromFormat("<%.100s mode='wb' closefd=%s>",
1086+
type_name,
1087+
self->closefd ? "True" : "False");
1088+
}
10821089

10831090
PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode");
10841091
return NULL;

0 commit comments

Comments
 (0)