From 716c444edcac0f32c6d82d530db2e6495e3d2be9 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 23 May 2009 19:04:03 +0000 Subject: [PATCH] Issue #5761: Add the name of the underlying file to the repr() of various IO objects. --- Lib/_pyio.py | 17 ++++++++++++++++- Lib/test/test_fileio.py | 10 +++++++--- Lib/test/test_io.py | 20 +++++++++++++++++++- Misc/NEWS | 3 +++ Modules/_io/bufferedio.c | 27 ++++++++++++++++++++++++--- Modules/_io/fileio.c | 23 +++++++++++++++++++---- Modules/_io/textio.c | 21 +++++++++++++++++++-- 7 files changed, 107 insertions(+), 14 deletions(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py index c9a7c5e3153..c1cdf439e12 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -736,6 +736,15 @@ class _BufferedIOMixin(BufferedIOBase): def mode(self): return self.raw.mode + def __repr__(self): + clsname = self.__class__.__name__ + try: + name = self.name + except AttributeError: + return "<_pyio.{0}>".format(clsname) + else: + return "<_pyio.{0} name={1!r}>".format(clsname, name) + ### Lower-level APIs ### def fileno(self): @@ -1455,7 +1464,13 @@ class TextIOWrapper(TextIOBase): # - "chars_..." for integer variables that count decoded characters def __repr__(self): - return "".format(self.encoding) + try: + name = self.name + except AttributeError: + return "<_pyio.TextIOWrapper encoding={0!r}>".format(self.encoding) + else: + return "<_pyio.TextIOWrapper name={0!r} encoding={1!r}>".format( + name, self.encoding) @property def encoding(self): diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index 9188e70e30a..657401e8edd 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -70,9 +70,13 @@ class AutoFileTests(unittest.TestCase): self.assertEquals(array('b', [1, 2]), a[:n]) def testRepr(self): - self.assertEquals(repr(self.f), - "io.FileIO(%d, %s)" % (self.f.fileno(), - repr(self.f.mode))) + self.assertEquals(repr(self.f), "<_io.FileIO name=%r mode=%r>" + % (self.f.name, self.f.mode)) + del self.f.name + self.assertEquals(repr(self.f), "<_io.FileIO fd=%r mode=%r>" + % (self.f.fileno(), self.f.mode)) + self.f.close() + self.assertEquals(repr(self.f), "<_io.FileIO [closed]>") def testErrors(self): f = self.f diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 08e0f1396f4..1337b7c096b 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -618,6 +618,16 @@ class CommonBufferedTests: self.assert_(s.startswith("Exception IOError: "), s) self.assert_(s.endswith(" ignored"), s) + def test_repr(self): + raw = self.MockRawIO() + b = self.tp(raw) + clsname = "%s.%s" % (self.tp.__module__, self.tp.__name__) + self.assertEqual(repr(b), "<%s>" % clsname) + raw.name = "dummy" + self.assertEqual(repr(b), "<%s name='dummy'>" % clsname) + raw.name = b"dummy" + self.assertEqual(repr(b), "<%s name=b'dummy'>" % clsname) + class BufferedReaderTest(unittest.TestCase, CommonBufferedTests): read_mode = "rb" @@ -1528,7 +1538,15 @@ class TextIOWrapperTest(unittest.TestCase): raw = self.BytesIO("hello".encode("utf-8")) b = self.BufferedReader(raw) t = self.TextIOWrapper(b, encoding="utf-8") - self.assertEqual(repr(t), "") + modname = self.TextIOWrapper.__module__ + self.assertEqual(repr(t), + "<%s.TextIOWrapper encoding='utf-8'>" % modname) + raw.name = "dummy" + self.assertEqual(repr(t), + "<%s.TextIOWrapper name='dummy' encoding='utf-8'>" % modname) + raw.name = b"dummy" + self.assertEqual(repr(t), + "<%s.TextIOWrapper name=b'dummy' encoding='utf-8'>" % modname) def test_line_buffering(self): r = self.BytesIO() diff --git a/Misc/NEWS b/Misc/NEWS index d5345789463..dc9fe457064 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -32,6 +32,9 @@ Core and Builtins Library ------- +- Issue #5761: Add the name of the underlying file to the repr() of various + IO objects. + - Issue #5259: smtplib plain auth login no longer gives a traceback. Fix by Musashi Tamura, tests by Marcin Bachry. diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 2c652078ee6..034fe51f3a5 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -1123,6 +1123,27 @@ Buffered_iternext(BufferedObject *self) return line; } +static PyObject * +Buffered_repr(BufferedObject *self) +{ + PyObject *nameobj, *res; + + nameobj = PyObject_GetAttrString((PyObject *) self, "name"); + if (nameobj == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + else + return NULL; + res = PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name); + } + else { + res = PyUnicode_FromFormat("<%s name=%R>", + Py_TYPE(self)->tp_name, nameobj); + Py_DECREF(nameobj); + } + return res; +} + /* * class BufferedReader */ @@ -1472,7 +1493,7 @@ PyTypeObject PyBufferedReader_Type = { 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare */ - 0, /*tp_repr*/ + (reprfunc)Buffered_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ @@ -1828,7 +1849,7 @@ PyTypeObject PyBufferedWriter_Type = { 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare */ - 0, /*tp_repr*/ + (reprfunc)Buffered_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ @@ -2219,7 +2240,7 @@ PyTypeObject PyBufferedRandom_Type = { 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare */ - 0, /*tp_repr*/ + (reprfunc)Buffered_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 9400c91ab16..d063fbf2b99 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -846,11 +846,26 @@ mode_string(PyFileIOObject *self) static PyObject * fileio_repr(PyFileIOObject *self) { - if (self->fd < 0) - return PyUnicode_FromFormat("io.FileIO(-1)"); + PyObject *nameobj, *res; - return PyUnicode_FromFormat("io.FileIO(%d, '%s')", - self->fd, mode_string(self)); + if (self->fd < 0) + return PyUnicode_FromFormat("<_io.FileIO [closed]>"); + + nameobj = PyObject_GetAttrString((PyObject *) self, "name"); + if (nameobj == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + else + return NULL; + res = PyUnicode_FromFormat("<_io.FileIO fd=%d mode='%s'>", + self->fd, mode_string(self)); + } + else { + res = PyUnicode_FromFormat("<_io.FileIO name=%R mode='%s'>", + nameobj, mode_string(self)); + Py_DECREF(nameobj); + } + return res; } static PyObject * diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index b78256e2ed6..c8d28334c14 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2308,8 +2308,25 @@ TextIOWrapper_truncate(PyTextIOWrapperObject *self, PyObject *args) static PyObject * TextIOWrapper_repr(PyTextIOWrapperObject *self) { - CHECK_INITIALIZED(self); - return PyUnicode_FromFormat("", self->encoding); + PyObject *nameobj, *res; + + CHECK_INITIALIZED(self); + + nameobj = PyObject_GetAttrString((PyObject *) self, "name"); + if (nameobj == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + else + return NULL; + res = PyUnicode_FromFormat("<_io.TextIOWrapper encoding=%R>", + self->encoding); + } + else { + res = PyUnicode_FromFormat("<_io.TextIOWrapper name=%R encoding=%R>", + nameobj, self->encoding); + Py_DECREF(nameobj); + } + return res; }