diff options
author | Benjamin Peterson <benjamin@python.org> | 2014-12-22 02:51:50 (GMT) |
---|---|---|
committer | Benjamin Peterson <benjamin@python.org> | 2014-12-22 02:51:50 (GMT) |
commit | 53ae6145a0b8f3380f819bf1c60b5dfc676f05ec (patch) | |
tree | b14f0b0cc4f3e49a5ab6e8a9444f068f12c08b30 | |
parent | 4e9dbfba217301b20f0891746ba81556858f6495 (diff) | |
download | cpython-53ae6145a0b8f3380f819bf1c60b5dfc676f05ec.zip cpython-53ae6145a0b8f3380f819bf1c60b5dfc676f05ec.tar.gz cpython-53ae6145a0b8f3380f819bf1c60b5dfc676f05ec.tar.bz2 |
allow more operations to work on detached streams (closes #23093)
Patch by Martin Panter.
-rw-r--r-- | Lib/_pyio.py | 4 | ||||
-rw-r--r-- | Lib/test/test_io.py | 14 | ||||
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Modules/_io/bufferedio.c | 2 | ||||
-rw-r--r-- | Modules/_io/textio.c | 75 |
5 files changed, 58 insertions, 40 deletions
diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 3acbc65..1c194d5 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -775,7 +775,7 @@ class _BufferedIOMixin(BufferedIOBase): clsname = self.__class__.__name__ try: name = self.name - except AttributeError: + except Exception: return "<_pyio.{0}>".format(clsname) else: return "<_pyio.{0} name={1!r}>".format(clsname, name) @@ -1538,7 +1538,7 @@ class TextIOWrapper(TextIOBase): def __repr__(self): try: name = self.name - except AttributeError: + except Exception: return "<_pyio.TextIOWrapper encoding='{0}'>".format(self.encoding) else: return "<_pyio.TextIOWrapper name={0!r} encoding='{1}'>".format( diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 2a3b4a3..ba3701a 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -654,6 +654,8 @@ class CommonBufferedTests: self.assertIs(buf.detach(), raw) self.assertRaises(ValueError, buf.detach) + repr(buf) # Should still work + def test_fileno(self): rawio = self.MockRawIO() bufio = self.tp(rawio) @@ -1922,6 +1924,12 @@ class TextIOWrapperTest(unittest.TestCase): self.assertEqual(r.getvalue(), b"howdy") self.assertRaises(ValueError, t.detach) + # Operations independent of the detached stream should still work + repr(t) + self.assertEqual(t.encoding, "ascii") + self.assertEqual(t.errors, "strict") + self.assertFalse(t.line_buffering) + def test_repr(self): raw = self.BytesIO("hello".encode("utf-8")) b = self.BufferedReader(raw) @@ -1936,6 +1944,9 @@ class TextIOWrapperTest(unittest.TestCase): self.assertEqual(repr(t), "<%s.TextIOWrapper name='dummy' encoding='utf-8'>" % modname) + t.buffer.detach() + repr(t) # Should not raise an exception + def test_line_buffering(self): r = self.BytesIO() b = self.BufferedWriter(r, 1000) @@ -2537,6 +2548,9 @@ class CTextIOWrapperTest(TextIOWrapperTest): self.assertRaises(ValueError, t.__init__, b, newline='xyzzy') self.assertRaises(ValueError, t.read) + t = self.TextIOWrapper.__new__(self.TextIOWrapper) + self.assertRaises(Exception, repr, t) + def test_garbage_collection(self): # C TextIOWrapper objects are collected, and collecting them flushes # all data to disk. @@ -15,6 +15,9 @@ Core and Builtins Library ------- +- Issue #23093: In the io, module allow more operations to work on detached + streams. + - Issue #23071: Added missing names to codecs.__all__. Patch by Martin Panter. - Issue #23016: A warning no longer produces an AttributeError when sys.stderr diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 62a350b..28c4678 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -1222,7 +1222,7 @@ buffered_repr(buffered *self) nameobj = PyObject_GetAttrString((PyObject *) self, "name"); if (nameobj == NULL) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) + if (PyErr_ExceptionMatches(PyExc_Exception)) PyErr_Clear(); else return NULL; diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 6802758..48e0c66 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1152,25 +1152,27 @@ textiowrapper_closed_get(textio *self, void *context); #define CHECK_INITIALIZED(self) \ if (self->ok <= 0) { \ - if (self->detached) { \ - PyErr_SetString(PyExc_ValueError, \ - "underlying buffer has been detached"); \ - } else { \ - PyErr_SetString(PyExc_ValueError, \ - "I/O operation on uninitialized object"); \ - } \ + PyErr_SetString(PyExc_ValueError, \ + "I/O operation on uninitialized object"); \ + return NULL; \ + } + +#define CHECK_ATTACHED(self) \ + CHECK_INITIALIZED(self); \ + if (self->detached) { \ + PyErr_SetString(PyExc_ValueError, \ + "underlying buffer has been detached"); \ return NULL; \ } -#define CHECK_INITIALIZED_INT(self) \ +#define CHECK_ATTACHED_INT(self) \ if (self->ok <= 0) { \ - if (self->detached) { \ - PyErr_SetString(PyExc_ValueError, \ - "underlying buffer has been detached"); \ - } else { \ - PyErr_SetString(PyExc_ValueError, \ - "I/O operation on uninitialized object"); \ - } \ + PyErr_SetString(PyExc_ValueError, \ + "I/O operation on uninitialized object"); \ + return -1; \ + } else if (self->detached) { \ + PyErr_SetString(PyExc_ValueError, \ + "underlying buffer has been detached"); \ return -1; \ } @@ -1179,7 +1181,7 @@ static PyObject * textiowrapper_detach(textio *self) { PyObject *buffer, *res; - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL); if (res == NULL) return NULL; @@ -1187,7 +1189,6 @@ textiowrapper_detach(textio *self) buffer = self->buffer; self->buffer = NULL; self->detached = 1; - self->ok = 0; return buffer; } @@ -1244,7 +1245,7 @@ textiowrapper_write(textio *self, PyObject *args) int haslf = 0; int needflush = 0; - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); if (!PyArg_ParseTuple(args, "U:write", &text)) { return NULL; @@ -1486,7 +1487,7 @@ textiowrapper_read(textio *self, PyObject *args) Py_ssize_t n = -1; PyObject *result = NULL, *chunks = NULL; - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); if (!PyArg_ParseTuple(args, "|O&:read", &_PyIO_ConvertSsize_t, &n)) return NULL; @@ -1849,7 +1850,7 @@ textiowrapper_readline(textio *self, PyObject *args) PyObject *limitobj = NULL; Py_ssize_t limit = -1; - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); if (!PyArg_ParseTuple(args, "|O:readline", &limitobj)) { return NULL; } @@ -2004,7 +2005,7 @@ textiowrapper_seek(textio *self, PyObject *args) PyObject *res; int cmp; - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); if (!PyArg_ParseTuple(args, "O|i:seek", &cookieObj, &whence)) return NULL; @@ -2189,7 +2190,7 @@ textiowrapper_tell(textio *self, PyObject *args) PyObject *saved_state = NULL; char *input, *input_end; - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); CHECK_CLOSED(self); if (!self->seekable) { @@ -2353,7 +2354,7 @@ textiowrapper_truncate(textio *self, PyObject *args) PyObject *pos = Py_None; PyObject *res; - CHECK_INITIALIZED(self) + CHECK_ATTACHED(self) if (!PyArg_ParseTuple(args, "|O:truncate", &pos)) { return NULL; } @@ -2376,7 +2377,7 @@ textiowrapper_repr(textio *self) nameobj = PyObject_GetAttrString((PyObject *) self, "name"); if (nameobj == NULL) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) + if (PyErr_ExceptionMatches(PyExc_Exception)) PyErr_Clear(); else goto error; @@ -2408,42 +2409,42 @@ error: static PyObject * textiowrapper_fileno(textio *self, PyObject *args) { - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); return PyObject_CallMethod(self->buffer, "fileno", NULL); } static PyObject * textiowrapper_seekable(textio *self, PyObject *args) { - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); return PyObject_CallMethod(self->buffer, "seekable", NULL); } static PyObject * textiowrapper_readable(textio *self, PyObject *args) { - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); return PyObject_CallMethod(self->buffer, "readable", NULL); } static PyObject * textiowrapper_writable(textio *self, PyObject *args) { - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); return PyObject_CallMethod(self->buffer, "writable", NULL); } static PyObject * textiowrapper_isatty(textio *self, PyObject *args) { - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); return PyObject_CallMethod(self->buffer, "isatty", NULL); } static PyObject * textiowrapper_flush(textio *self, PyObject *args) { - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); CHECK_CLOSED(self); self->telling = self->seekable; if (_textiowrapper_writeflush(self) < 0) @@ -2456,7 +2457,7 @@ textiowrapper_close(textio *self, PyObject *args) { PyObject *res; int r; - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); res = textiowrapper_closed_get(self, NULL); if (res == NULL) @@ -2498,7 +2499,7 @@ textiowrapper_iternext(textio *self) { PyObject *line; - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); self->telling = 0; if (Py_TYPE(self) == &PyTextIOWrapper_Type) { @@ -2534,14 +2535,14 @@ textiowrapper_iternext(textio *self) static PyObject * textiowrapper_name_get(textio *self, void *context) { - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); return PyObject_GetAttrString(self->buffer, "name"); } static PyObject * textiowrapper_closed_get(textio *self, void *context) { - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); return PyObject_GetAttr(self->buffer, _PyIO_str_closed); } @@ -2549,7 +2550,7 @@ static PyObject * textiowrapper_newlines_get(textio *self, void *context) { PyObject *res; - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); if (self->decoder == NULL) Py_RETURN_NONE; res = PyObject_GetAttr(self->decoder, _PyIO_str_newlines); @@ -2576,7 +2577,7 @@ textiowrapper_errors_get(textio *self, void *context) static PyObject * textiowrapper_chunk_size_get(textio *self, void *context) { - CHECK_INITIALIZED(self); + CHECK_ATTACHED(self); return PyLong_FromSsize_t(self->chunk_size); } @@ -2584,7 +2585,7 @@ static int textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) { Py_ssize_t n; - CHECK_INITIALIZED_INT(self); + CHECK_ATTACHED_INT(self); n = PyNumber_AsSsize_t(arg, PyExc_TypeError); if (n == -1 && PyErr_Occurred()) return -1; |