Commit cc479d0d authored by Benjamin Peterson's avatar Benjamin Peterson

allow more operations to work on detached streams (closes #23093)

Patch by Martin Panter.
parent 4f29b374
...@@ -793,7 +793,7 @@ class _BufferedIOMixin(BufferedIOBase): ...@@ -793,7 +793,7 @@ class _BufferedIOMixin(BufferedIOBase):
clsname = self.__class__.__name__ clsname = self.__class__.__name__
try: try:
name = self.name name = self.name
except AttributeError: except Exception:
return "<_pyio.{0}>".format(clsname) return "<_pyio.{0}>".format(clsname)
else: else:
return "<_pyio.{0} name={1!r}>".format(clsname, name) return "<_pyio.{0} name={1!r}>".format(clsname, name)
...@@ -1561,13 +1561,13 @@ class TextIOWrapper(TextIOBase): ...@@ -1561,13 +1561,13 @@ class TextIOWrapper(TextIOBase):
result = "<_pyio.TextIOWrapper" result = "<_pyio.TextIOWrapper"
try: try:
name = self.name name = self.name
except AttributeError: except Exception:
pass pass
else: else:
result += " name={0!r}".format(name) result += " name={0!r}".format(name)
try: try:
mode = self.mode mode = self.mode
except AttributeError: except Exception:
pass pass
else: else:
result += " mode={0!r}".format(mode) result += " mode={0!r}".format(mode)
......
...@@ -701,6 +701,8 @@ class CommonBufferedTests: ...@@ -701,6 +701,8 @@ class CommonBufferedTests:
self.assertIs(buf.detach(), raw) self.assertIs(buf.detach(), raw)
self.assertRaises(ValueError, buf.detach) self.assertRaises(ValueError, buf.detach)
repr(buf) # Should still work
def test_fileno(self): def test_fileno(self):
rawio = self.MockRawIO() rawio = self.MockRawIO()
bufio = self.tp(rawio) bufio = self.tp(rawio)
...@@ -2026,6 +2028,12 @@ class TextIOWrapperTest(unittest.TestCase): ...@@ -2026,6 +2028,12 @@ class TextIOWrapperTest(unittest.TestCase):
self.assertEqual(r.getvalue(), b"howdy") self.assertEqual(r.getvalue(), b"howdy")
self.assertRaises(ValueError, t.detach) 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): def test_repr(self):
raw = self.BytesIO("hello".encode("utf-8")) raw = self.BytesIO("hello".encode("utf-8"))
b = self.BufferedReader(raw) b = self.BufferedReader(raw)
...@@ -2043,6 +2051,9 @@ class TextIOWrapperTest(unittest.TestCase): ...@@ -2043,6 +2051,9 @@ class TextIOWrapperTest(unittest.TestCase):
self.assertEqual(repr(t), self.assertEqual(repr(t),
"<%s.TextIOWrapper name=b'dummy' mode='r' encoding='utf-8'>" % modname) "<%s.TextIOWrapper name=b'dummy' mode='r' encoding='utf-8'>" % modname)
t.buffer.detach()
repr(t) # Should not raise an exception
def test_line_buffering(self): def test_line_buffering(self):
r = self.BytesIO() r = self.BytesIO()
b = self.BufferedWriter(r, 1000) b = self.BufferedWriter(r, 1000)
...@@ -2813,6 +2824,9 @@ class CTextIOWrapperTest(TextIOWrapperTest): ...@@ -2813,6 +2824,9 @@ class CTextIOWrapperTest(TextIOWrapperTest):
self.assertRaises(ValueError, t.__init__, b, newline='xyzzy') self.assertRaises(ValueError, t.__init__, b, newline='xyzzy')
self.assertRaises(ValueError, t.read) self.assertRaises(ValueError, t.read)
t = self.TextIOWrapper.__new__(self.TextIOWrapper)
self.assertRaises(Exception, repr, t)
def test_garbage_collection(self): def test_garbage_collection(self):
# C TextIOWrapper objects are collected, and collecting them flushes # C TextIOWrapper objects are collected, and collecting them flushes
# all data to disk. # all data to disk.
......
...@@ -41,6 +41,9 @@ Core and Builtins ...@@ -41,6 +41,9 @@ Core and Builtins
Library Library
------- -------
- Issue #23093: In the io, module allow more operations to work on detached
streams.
- Issue #19104: pprint now produces evaluable output for wrapped strings. - Issue #19104: pprint now produces evaluable output for wrapped strings.
- Issue #23071: Added missing names to codecs.__all__. Patch by Martin Panter. - Issue #23071: Added missing names to codecs.__all__. Patch by Martin Panter.
......
...@@ -1359,7 +1359,7 @@ buffered_repr(buffered *self) ...@@ -1359,7 +1359,7 @@ buffered_repr(buffered *self)
nameobj = _PyObject_GetAttrId((PyObject *) self, &PyId_name); nameobj = _PyObject_GetAttrId((PyObject *) self, &PyId_name);
if (nameobj == NULL) { if (nameobj == NULL) {
if (PyErr_ExceptionMatches(PyExc_AttributeError)) if (PyErr_ExceptionMatches(PyExc_Exception))
PyErr_Clear(); PyErr_Clear();
else else
return NULL; return NULL;
......
...@@ -1219,25 +1219,27 @@ textiowrapper_closed_get(textio *self, void *context); ...@@ -1219,25 +1219,27 @@ textiowrapper_closed_get(textio *self, void *context);
#define CHECK_INITIALIZED(self) \ #define CHECK_INITIALIZED(self) \
if (self->ok <= 0) { \ if (self->ok <= 0) { \
if (self->detached) { \ PyErr_SetString(PyExc_ValueError, \
PyErr_SetString(PyExc_ValueError, \ "I/O operation on uninitialized object"); \
"underlying buffer has been detached"); \ return NULL; \
} else { \ }
PyErr_SetString(PyExc_ValueError, \
"I/O operation on uninitialized object"); \ #define CHECK_ATTACHED(self) \
} \ CHECK_INITIALIZED(self); \
if (self->detached) { \
PyErr_SetString(PyExc_ValueError, \
"underlying buffer has been detached"); \
return NULL; \ return NULL; \
} }
#define CHECK_INITIALIZED_INT(self) \ #define CHECK_ATTACHED_INT(self) \
if (self->ok <= 0) { \ if (self->ok <= 0) { \
if (self->detached) { \ PyErr_SetString(PyExc_ValueError, \
PyErr_SetString(PyExc_ValueError, \ "I/O operation on uninitialized object"); \
"underlying buffer has been detached"); \ return -1; \
} else { \ } else if (self->detached) { \
PyErr_SetString(PyExc_ValueError, \ PyErr_SetString(PyExc_ValueError, \
"I/O operation on uninitialized object"); \ "underlying buffer has been detached"); \
} \
return -1; \ return -1; \
} }
...@@ -1246,7 +1248,7 @@ static PyObject * ...@@ -1246,7 +1248,7 @@ static PyObject *
textiowrapper_detach(textio *self) textiowrapper_detach(textio *self)
{ {
PyObject *buffer, *res; PyObject *buffer, *res;
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL); res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL);
if (res == NULL) if (res == NULL)
return NULL; return NULL;
...@@ -1254,7 +1256,6 @@ textiowrapper_detach(textio *self) ...@@ -1254,7 +1256,6 @@ textiowrapper_detach(textio *self)
buffer = self->buffer; buffer = self->buffer;
self->buffer = NULL; self->buffer = NULL;
self->detached = 1; self->detached = 1;
self->ok = 0;
return buffer; return buffer;
} }
...@@ -1299,7 +1300,7 @@ textiowrapper_write(textio *self, PyObject *args) ...@@ -1299,7 +1300,7 @@ textiowrapper_write(textio *self, PyObject *args)
int haslf = 0; int haslf = 0;
int needflush = 0, text_needflush = 0; int needflush = 0, text_needflush = 0;
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
if (!PyArg_ParseTuple(args, "U:write", &text)) { if (!PyArg_ParseTuple(args, "U:write", &text)) {
return NULL; return NULL;
...@@ -1556,7 +1557,7 @@ textiowrapper_read(textio *self, PyObject *args) ...@@ -1556,7 +1557,7 @@ textiowrapper_read(textio *self, PyObject *args)
Py_ssize_t n = -1; Py_ssize_t n = -1;
PyObject *result = NULL, *chunks = NULL; PyObject *result = NULL, *chunks = NULL;
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
if (!PyArg_ParseTuple(args, "|O&:read", &_PyIO_ConvertSsize_t, &n)) if (!PyArg_ParseTuple(args, "|O&:read", &_PyIO_ConvertSsize_t, &n))
return NULL; return NULL;
...@@ -1931,7 +1932,7 @@ textiowrapper_readline(textio *self, PyObject *args) ...@@ -1931,7 +1932,7 @@ textiowrapper_readline(textio *self, PyObject *args)
{ {
Py_ssize_t limit = -1; Py_ssize_t limit = -1;
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
if (!PyArg_ParseTuple(args, "|n:readline", &limit)) { if (!PyArg_ParseTuple(args, "|n:readline", &limit)) {
return NULL; return NULL;
} }
...@@ -2069,7 +2070,7 @@ textiowrapper_seek(textio *self, PyObject *args) ...@@ -2069,7 +2070,7 @@ textiowrapper_seek(textio *self, PyObject *args)
PyObject *res; PyObject *res;
int cmp; int cmp;
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
if (!PyArg_ParseTuple(args, "O|i:seek", &cookieObj, &whence)) if (!PyArg_ParseTuple(args, "O|i:seek", &cookieObj, &whence))
return NULL; return NULL;
...@@ -2249,7 +2250,7 @@ textiowrapper_tell(textio *self, PyObject *args) ...@@ -2249,7 +2250,7 @@ textiowrapper_tell(textio *self, PyObject *args)
Py_ssize_t dec_buffer_len; Py_ssize_t dec_buffer_len;
int dec_flags; int dec_flags;
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
CHECK_CLOSED(self); CHECK_CLOSED(self);
if (!self->seekable) { if (!self->seekable) {
...@@ -2452,7 +2453,7 @@ textiowrapper_truncate(textio *self, PyObject *args) ...@@ -2452,7 +2453,7 @@ textiowrapper_truncate(textio *self, PyObject *args)
PyObject *pos = Py_None; PyObject *pos = Py_None;
PyObject *res; PyObject *res;
CHECK_INITIALIZED(self) CHECK_ATTACHED(self)
if (!PyArg_ParseTuple(args, "|O:truncate", &pos)) { if (!PyArg_ParseTuple(args, "|O:truncate", &pos)) {
return NULL; return NULL;
} }
...@@ -2475,9 +2476,10 @@ textiowrapper_repr(textio *self) ...@@ -2475,9 +2476,10 @@ textiowrapper_repr(textio *self)
res = PyUnicode_FromString("<_io.TextIOWrapper"); res = PyUnicode_FromString("<_io.TextIOWrapper");
if (res == NULL) if (res == NULL)
return NULL; return NULL;
nameobj = _PyObject_GetAttrId((PyObject *) self, &PyId_name); nameobj = _PyObject_GetAttrId((PyObject *) self, &PyId_name);
if (nameobj == NULL) { if (nameobj == NULL) {
if (PyErr_ExceptionMatches(PyExc_AttributeError)) if (PyErr_ExceptionMatches(PyExc_Exception))
PyErr_Clear(); PyErr_Clear();
else else
goto error; goto error;
...@@ -2493,7 +2495,7 @@ textiowrapper_repr(textio *self) ...@@ -2493,7 +2495,7 @@ textiowrapper_repr(textio *self)
} }
modeobj = _PyObject_GetAttrId((PyObject *) self, &PyId_mode); modeobj = _PyObject_GetAttrId((PyObject *) self, &PyId_mode);
if (modeobj == NULL) { if (modeobj == NULL) {
if (PyErr_ExceptionMatches(PyExc_AttributeError)) if (PyErr_ExceptionMatches(PyExc_Exception))
PyErr_Clear(); PyErr_Clear();
else else
goto error; goto error;
...@@ -2522,35 +2524,35 @@ error: ...@@ -2522,35 +2524,35 @@ error:
static PyObject * static PyObject *
textiowrapper_fileno(textio *self, PyObject *args) textiowrapper_fileno(textio *self, PyObject *args)
{ {
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
return _PyObject_CallMethodId(self->buffer, &PyId_fileno, NULL); return _PyObject_CallMethodId(self->buffer, &PyId_fileno, NULL);
} }
static PyObject * static PyObject *
textiowrapper_seekable(textio *self, PyObject *args) textiowrapper_seekable(textio *self, PyObject *args)
{ {
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
return _PyObject_CallMethodId(self->buffer, &PyId_seekable, NULL); return _PyObject_CallMethodId(self->buffer, &PyId_seekable, NULL);
} }
static PyObject * static PyObject *
textiowrapper_readable(textio *self, PyObject *args) textiowrapper_readable(textio *self, PyObject *args)
{ {
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
return _PyObject_CallMethodId(self->buffer, &PyId_readable, NULL); return _PyObject_CallMethodId(self->buffer, &PyId_readable, NULL);
} }
static PyObject * static PyObject *
textiowrapper_writable(textio *self, PyObject *args) textiowrapper_writable(textio *self, PyObject *args)
{ {
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
return _PyObject_CallMethodId(self->buffer, &PyId_writable, NULL); return _PyObject_CallMethodId(self->buffer, &PyId_writable, NULL);
} }
static PyObject * static PyObject *
textiowrapper_isatty(textio *self, PyObject *args) textiowrapper_isatty(textio *self, PyObject *args)
{ {
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
return _PyObject_CallMethodId(self->buffer, &PyId_isatty, NULL); return _PyObject_CallMethodId(self->buffer, &PyId_isatty, NULL);
} }
...@@ -2565,7 +2567,7 @@ textiowrapper_getstate(textio *self, PyObject *args) ...@@ -2565,7 +2567,7 @@ textiowrapper_getstate(textio *self, PyObject *args)
static PyObject * static PyObject *
textiowrapper_flush(textio *self, PyObject *args) textiowrapper_flush(textio *self, PyObject *args)
{ {
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
CHECK_CLOSED(self); CHECK_CLOSED(self);
self->telling = self->seekable; self->telling = self->seekable;
if (_textiowrapper_writeflush(self) < 0) if (_textiowrapper_writeflush(self) < 0)
...@@ -2578,7 +2580,7 @@ textiowrapper_close(textio *self, PyObject *args) ...@@ -2578,7 +2580,7 @@ textiowrapper_close(textio *self, PyObject *args)
{ {
PyObject *res; PyObject *res;
int r; int r;
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
res = textiowrapper_closed_get(self, NULL); res = textiowrapper_closed_get(self, NULL);
if (res == NULL) if (res == NULL)
...@@ -2620,7 +2622,7 @@ textiowrapper_iternext(textio *self) ...@@ -2620,7 +2622,7 @@ textiowrapper_iternext(textio *self)
{ {
PyObject *line; PyObject *line;
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
self->telling = 0; self->telling = 0;
if (Py_TYPE(self) == &PyTextIOWrapper_Type) { if (Py_TYPE(self) == &PyTextIOWrapper_Type) {
...@@ -2656,14 +2658,14 @@ textiowrapper_iternext(textio *self) ...@@ -2656,14 +2658,14 @@ textiowrapper_iternext(textio *self)
static PyObject * static PyObject *
textiowrapper_name_get(textio *self, void *context) textiowrapper_name_get(textio *self, void *context)
{ {
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
return _PyObject_GetAttrId(self->buffer, &PyId_name); return _PyObject_GetAttrId(self->buffer, &PyId_name);
} }
static PyObject * static PyObject *
textiowrapper_closed_get(textio *self, void *context) textiowrapper_closed_get(textio *self, void *context)
{ {
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
return PyObject_GetAttr(self->buffer, _PyIO_str_closed); return PyObject_GetAttr(self->buffer, _PyIO_str_closed);
} }
...@@ -2671,7 +2673,7 @@ static PyObject * ...@@ -2671,7 +2673,7 @@ static PyObject *
textiowrapper_newlines_get(textio *self, void *context) textiowrapper_newlines_get(textio *self, void *context)
{ {
PyObject *res; PyObject *res;
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
if (self->decoder == NULL) if (self->decoder == NULL)
Py_RETURN_NONE; Py_RETURN_NONE;
res = PyObject_GetAttr(self->decoder, _PyIO_str_newlines); res = PyObject_GetAttr(self->decoder, _PyIO_str_newlines);
...@@ -2697,7 +2699,7 @@ textiowrapper_errors_get(textio *self, void *context) ...@@ -2697,7 +2699,7 @@ textiowrapper_errors_get(textio *self, void *context)
static PyObject * static PyObject *
textiowrapper_chunk_size_get(textio *self, void *context) textiowrapper_chunk_size_get(textio *self, void *context)
{ {
CHECK_INITIALIZED(self); CHECK_ATTACHED(self);
return PyLong_FromSsize_t(self->chunk_size); return PyLong_FromSsize_t(self->chunk_size);
} }
...@@ -2705,7 +2707,7 @@ static int ...@@ -2705,7 +2707,7 @@ static int
textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context)
{ {
Py_ssize_t n; Py_ssize_t n;
CHECK_INITIALIZED_INT(self); CHECK_ATTACHED_INT(self);
n = PyNumber_AsSsize_t(arg, PyExc_ValueError); n = PyNumber_AsSsize_t(arg, PyExc_ValueError);
if (n == -1 && PyErr_Occurred()) if (n == -1 && PyErr_Occurred())
return -1; return -1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment