Commit f8c2001f authored by Benjamin Peterson's avatar Benjamin Peterson

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

Patch by Martin Panter.
parent 22b21357
......@@ -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(
......
......@@ -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
......
......@@ -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;
......
......@@ -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;
......
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