diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index 26e4500ae8cfcd7..561ee6bd0fc93cc 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -154,6 +154,27 @@ def test_none_args(self): def test_reject(self): self.assertRaises(TypeError, self.f.write, "Hello!") + def test_subclass_repr(self): + class CustomFileIO(self.FileIO): + pass + + custom_file_io = CustomFileIO(TESTFN, 'w') + + self.assertIn( + "CustomFileIO name=%r mode=%r closefd=True>" % + (custom_file_io.name, custom_file_io.mode), + repr(custom_file_io)) + + del custom_file_io.name + self.assertIn( + "CustomFileIO fd=%r mode=%r closefd=True>" % + (custom_file_io.fileno(), custom_file_io.mode), + repr(custom_file_io)) + + custom_file_io.close() + self.assertIn("CustomFileIO [closed]", repr(custom_file_io)) + + def testRepr(self): self.assertEqual(repr(self.f), "<%s.FileIO name=%r mode=%r closefd=True>" % diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 0bfa4d249e9a6bf..276e6a9b46826b5 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -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' + 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() diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-14-13-12-40.bpo-21861.wxAr8e.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-14-13-12-40.bpo-21861.wxAr8e.rst new file mode 100644 index 000000000000000..7ac4069a8b5bee7 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-07-14-13-12-40.bpo-21861.wxAr8e.rst @@ -0,0 +1,2 @@ +Remove the hardcoded classes names from ``_io`` module classes' ``__repr__`` in +favor of the actual object type name to make it more subclass friendly. diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 1855b83449d2a74..3228d954507bda4 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -1080,14 +1080,17 @@ fileio_repr(fileio *self) PyObject *nameobj, *res; if (self->fd < 0) - return PyUnicode_FromFormat("<_io.FileIO [closed]>"); + return PyUnicode_FromFormat( + "<%s [closed]>", + 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 { @@ -1095,7 +1098,8 @@ fileio_repr(fileio *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); } diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 3a9ce93a5eb5eb1..e108ad03ea00b50 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2886,7 +2886,7 @@ textiowrapper_repr(textio *self) CHECK_INITIALIZED(self); - res = PyUnicode_FromString("<_io.TextIOWrapper"); + res = PyUnicode_FromFormat("<%s", Py_TYPE((PyObject *) self)->tp_name); if (res == NULL) return NULL; diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 94760acd47bf251..5a2a0a3a3da139f 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -1034,13 +1034,19 @@ static PyObject * winconsoleio_repr(winconsoleio *self) { if (self->handle == INVALID_HANDLE_VALUE) - return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>"); + return PyUnicode_FromFormat( + "<%s [closed]>", + Py_TYPE((PyObject *) self)->tp_name); if (self->readable) - return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>", + return PyUnicode_FromFormat( + "<%s mode='rb' closefd=%s>", + Py_TYPE((PyObject *) self)->tp_name, self->closehandle ? "True" : "False"); if (self->writable) - return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>", + return PyUnicode_FromFormat( + "<%s mode='wb' closefd=%s>", + Py_TYPE((PyObject *) self)->tp_name, self->closehandle ? "True" : "False"); PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode");