implement a detach() method for BufferedIOBase and TextIOBase #5883
This commit is contained in:
parent
155374d95d
commit
d2e0c7955f
|
@ -361,6 +361,17 @@ I/O Base Classes
|
||||||
:class:`BufferedIOBase` provides or overrides these methods in addition to
|
:class:`BufferedIOBase` provides or overrides these methods in addition to
|
||||||
those from :class:`IOBase`:
|
those from :class:`IOBase`:
|
||||||
|
|
||||||
|
.. method:: detach()
|
||||||
|
|
||||||
|
Separate the underlying raw stream from the buffer and return it.
|
||||||
|
|
||||||
|
After the raw stream has been detached, the buffer is in an unusable
|
||||||
|
state.
|
||||||
|
|
||||||
|
Some buffers, like :class:`BytesIO`, do not have the concept of a single
|
||||||
|
raw stream to return from this method. They raise
|
||||||
|
:exc:`UnsupportedOperation`.
|
||||||
|
|
||||||
.. method:: read([n])
|
.. method:: read([n])
|
||||||
|
|
||||||
Read and return up to *n* bytes. If the argument is omitted, ``None``, or
|
Read and return up to *n* bytes. If the argument is omitted, ``None``, or
|
||||||
|
@ -547,7 +558,9 @@ Buffered Streams
|
||||||
|
|
||||||
*max_buffer_size* is unused and deprecated.
|
*max_buffer_size* is unused and deprecated.
|
||||||
|
|
||||||
:class:`BufferedRWPair` implements all of :class:`BufferedIOBase`\'s methods.
|
:class:`BufferedRWPair` implements all of :class:`BufferedIOBase`\'s methods
|
||||||
|
except for :meth:`~BufferedIOBase.detach`, which raises
|
||||||
|
:exc:`UnsupportedOperation`.
|
||||||
|
|
||||||
|
|
||||||
.. class:: BufferedRandom(raw[, buffer_size[, max_buffer_size]])
|
.. class:: BufferedRandom(raw[, buffer_size[, max_buffer_size]])
|
||||||
|
@ -588,6 +601,17 @@ Text I/O
|
||||||
A string, a tuple of strings, or ``None``, indicating the newlines
|
A string, a tuple of strings, or ``None``, indicating the newlines
|
||||||
translated so far.
|
translated so far.
|
||||||
|
|
||||||
|
.. method:: detach()
|
||||||
|
|
||||||
|
Separate the underlying buffer from the :class:`TextIOBase` and return it.
|
||||||
|
|
||||||
|
After the underlying buffer has been detached, the :class:`TextIOBase` is
|
||||||
|
in an unusable state.
|
||||||
|
|
||||||
|
Some :class:`TextIOBase` implementations, like :class:`StringIO`, may not
|
||||||
|
have the concept of an underlying buffer and calling this method will
|
||||||
|
raise :exc:`UnsupportedOperation`.
|
||||||
|
|
||||||
.. method:: read(n)
|
.. method:: read(n)
|
||||||
|
|
||||||
Read and return at most *n* characters from the stream as a single
|
Read and return at most *n* characters from the stream as a single
|
||||||
|
|
41
Lib/_pyio.py
41
Lib/_pyio.py
|
@ -642,6 +642,15 @@ class BufferedIOBase(IOBase):
|
||||||
"""
|
"""
|
||||||
self._unsupported("write")
|
self._unsupported("write")
|
||||||
|
|
||||||
|
def detach(self) -> None:
|
||||||
|
"""
|
||||||
|
Separate the underlying raw stream from the buffer and return it.
|
||||||
|
|
||||||
|
After the raw stream has been detached, the buffer is in an unusable
|
||||||
|
state.
|
||||||
|
"""
|
||||||
|
self._unsupported("detach")
|
||||||
|
|
||||||
io.BufferedIOBase.register(BufferedIOBase)
|
io.BufferedIOBase.register(BufferedIOBase)
|
||||||
|
|
||||||
|
|
||||||
|
@ -689,13 +698,21 @@ class _BufferedIOMixin(BufferedIOBase):
|
||||||
self.raw.flush()
|
self.raw.flush()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if not self.closed:
|
if not self.closed and self.raw is not None:
|
||||||
try:
|
try:
|
||||||
self.flush()
|
self.flush()
|
||||||
except IOError:
|
except IOError:
|
||||||
pass # If flush() fails, just give up
|
pass # If flush() fails, just give up
|
||||||
self.raw.close()
|
self.raw.close()
|
||||||
|
|
||||||
|
def detach(self):
|
||||||
|
if self.raw is None:
|
||||||
|
raise ValueError("raw stream already detached")
|
||||||
|
self.flush()
|
||||||
|
raw = self.raw
|
||||||
|
self.raw = None
|
||||||
|
return raw
|
||||||
|
|
||||||
### Inquiries ###
|
### Inquiries ###
|
||||||
|
|
||||||
def seekable(self):
|
def seekable(self):
|
||||||
|
@ -1236,6 +1253,15 @@ class TextIOBase(IOBase):
|
||||||
"""
|
"""
|
||||||
self._unsupported("readline")
|
self._unsupported("readline")
|
||||||
|
|
||||||
|
def detach(self) -> None:
|
||||||
|
"""
|
||||||
|
Separate the underlying buffer from the TextIOBase and return it.
|
||||||
|
|
||||||
|
After the underlying buffer has been detached, the TextIO is in an
|
||||||
|
unusable state.
|
||||||
|
"""
|
||||||
|
self._unsupported("detach")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def encoding(self):
|
def encoding(self):
|
||||||
"""Subclasses should override."""
|
"""Subclasses should override."""
|
||||||
|
@ -1448,6 +1474,7 @@ class TextIOWrapper(TextIOBase):
|
||||||
self._telling = self._seekable
|
self._telling = self._seekable
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
if self.buffer is not None:
|
||||||
try:
|
try:
|
||||||
self.flush()
|
self.flush()
|
||||||
except IOError:
|
except IOError:
|
||||||
|
@ -1647,6 +1674,14 @@ class TextIOWrapper(TextIOBase):
|
||||||
self.seek(pos)
|
self.seek(pos)
|
||||||
return self.buffer.truncate()
|
return self.buffer.truncate()
|
||||||
|
|
||||||
|
def detach(self):
|
||||||
|
if self.buffer is None:
|
||||||
|
raise ValueError("buffer is already detached")
|
||||||
|
self.flush()
|
||||||
|
buffer = self.buffer
|
||||||
|
self.buffer = None
|
||||||
|
return buffer
|
||||||
|
|
||||||
def seek(self, cookie, whence=0):
|
def seek(self, cookie, whence=0):
|
||||||
if self.closed:
|
if self.closed:
|
||||||
raise ValueError("tell on closed file")
|
raise ValueError("tell on closed file")
|
||||||
|
@ -1865,3 +1900,7 @@ class StringIO(TextIOWrapper):
|
||||||
@property
|
@property
|
||||||
def encoding(self):
|
def encoding(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def detach(self):
|
||||||
|
# This doesn't make sense on StringIO.
|
||||||
|
self._unsupported("detach")
|
||||||
|
|
|
@ -526,6 +526,12 @@ class PyIOTest(IOTest):
|
||||||
class CommonBufferedTests:
|
class CommonBufferedTests:
|
||||||
# Tests common to BufferedReader, BufferedWriter and BufferedRandom
|
# Tests common to BufferedReader, BufferedWriter and BufferedRandom
|
||||||
|
|
||||||
|
def test_detach(self):
|
||||||
|
raw = self.MockRawIO()
|
||||||
|
buf = self.tp(raw)
|
||||||
|
self.assertIs(buf.detach(), raw)
|
||||||
|
self.assertRaises(ValueError, buf.detach)
|
||||||
|
|
||||||
def test_fileno(self):
|
def test_fileno(self):
|
||||||
rawio = self.MockRawIO()
|
rawio = self.MockRawIO()
|
||||||
bufio = self.tp(rawio)
|
bufio = self.tp(rawio)
|
||||||
|
@ -811,6 +817,14 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests):
|
||||||
bufio.flush()
|
bufio.flush()
|
||||||
self.assertEquals(b"".join(rawio._write_stack), b"abcghi")
|
self.assertEquals(b"".join(rawio._write_stack), b"abcghi")
|
||||||
|
|
||||||
|
def test_detach_flush(self):
|
||||||
|
raw = self.MockRawIO()
|
||||||
|
buf = self.tp(raw)
|
||||||
|
buf.write(b"howdy!")
|
||||||
|
self.assertFalse(raw._write_stack)
|
||||||
|
buf.detach()
|
||||||
|
self.assertEqual(raw._write_stack, [b"howdy!"])
|
||||||
|
|
||||||
def test_write(self):
|
def test_write(self):
|
||||||
# Write to the buffered IO but don't overflow the buffer.
|
# Write to the buffered IO but don't overflow the buffer.
|
||||||
writer = self.MockRawIO()
|
writer = self.MockRawIO()
|
||||||
|
@ -1052,6 +1066,10 @@ class BufferedRWPairTest(unittest.TestCase):
|
||||||
pair = self.tp(self.MockRawIO(), self.MockRawIO())
|
pair = self.tp(self.MockRawIO(), self.MockRawIO())
|
||||||
self.assertFalse(pair.closed)
|
self.assertFalse(pair.closed)
|
||||||
|
|
||||||
|
def test_detach(self):
|
||||||
|
pair = self.tp(self.MockRawIO(), self.MockRawIO())
|
||||||
|
self.assertRaises(self.UnsupportedOperation, pair.detach)
|
||||||
|
|
||||||
def test_constructor_max_buffer_size_deprecation(self):
|
def test_constructor_max_buffer_size_deprecation(self):
|
||||||
with support.check_warnings() as w:
|
with support.check_warnings() as w:
|
||||||
warnings.simplefilter("always", DeprecationWarning)
|
warnings.simplefilter("always", DeprecationWarning)
|
||||||
|
@ -1480,6 +1498,19 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||||
self.assertRaises(TypeError, t.__init__, b, newline=42)
|
self.assertRaises(TypeError, t.__init__, b, newline=42)
|
||||||
self.assertRaises(ValueError, t.__init__, b, newline='xyzzy')
|
self.assertRaises(ValueError, t.__init__, b, newline='xyzzy')
|
||||||
|
|
||||||
|
def test_detach(self):
|
||||||
|
r = self.BytesIO()
|
||||||
|
b = self.BufferedWriter(r)
|
||||||
|
t = self.TextIOWrapper(b)
|
||||||
|
self.assertIs(t.detach(), b)
|
||||||
|
|
||||||
|
t = self.TextIOWrapper(b, encoding="ascii")
|
||||||
|
t.write("howdy")
|
||||||
|
self.assertFalse(r.getvalue())
|
||||||
|
t.detach()
|
||||||
|
self.assertEqual(r.getvalue(), b"howdy")
|
||||||
|
self.assertRaises(ValueError, t.detach)
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
@ -57,6 +57,10 @@ class MemorySeekTestMixin:
|
||||||
|
|
||||||
class MemoryTestMixin:
|
class MemoryTestMixin:
|
||||||
|
|
||||||
|
def test_detach(self):
|
||||||
|
buf = self.ioclass()
|
||||||
|
self.assertRaises(self.UnsupportedOperation, buf.detach)
|
||||||
|
|
||||||
def write_ops(self, f, t):
|
def write_ops(self, f, t):
|
||||||
self.assertEqual(f.write(t("blah.")), 5)
|
self.assertEqual(f.write(t("blah.")), 5)
|
||||||
self.assertEqual(f.seek(0), 0)
|
self.assertEqual(f.seek(0), 0)
|
||||||
|
@ -336,6 +340,9 @@ class MemoryTestMixin:
|
||||||
|
|
||||||
|
|
||||||
class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase):
|
class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase):
|
||||||
|
|
||||||
|
UnsupportedOperation = pyio.UnsupportedOperation
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def buftype(s):
|
def buftype(s):
|
||||||
return s.encode("ascii")
|
return s.encode("ascii")
|
||||||
|
@ -413,6 +420,7 @@ class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase):
|
||||||
class PyStringIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase):
|
class PyStringIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase):
|
||||||
buftype = str
|
buftype = str
|
||||||
ioclass = pyio.StringIO
|
ioclass = pyio.StringIO
|
||||||
|
UnsupportedOperation = pyio.UnsupportedOperation
|
||||||
EOF = ""
|
EOF = ""
|
||||||
|
|
||||||
# TextIO-specific behaviour.
|
# TextIO-specific behaviour.
|
||||||
|
@ -518,9 +526,11 @@ class PyStringIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase):
|
||||||
|
|
||||||
class CBytesIOTest(PyBytesIOTest):
|
class CBytesIOTest(PyBytesIOTest):
|
||||||
ioclass = io.BytesIO
|
ioclass = io.BytesIO
|
||||||
|
UnsupportedOperation = io.UnsupportedOperation
|
||||||
|
|
||||||
class CStringIOTest(PyStringIOTest):
|
class CStringIOTest(PyStringIOTest):
|
||||||
ioclass = io.StringIO
|
ioclass = io.StringIO
|
||||||
|
UnsupportedOperation = io.UnsupportedOperation
|
||||||
|
|
||||||
# XXX: For the Python version of io.StringIO, this is highly
|
# XXX: For the Python version of io.StringIO, this is highly
|
||||||
# dependent on the encoding used for the underlying buffer.
|
# dependent on the encoding used for the underlying buffer.
|
||||||
|
|
|
@ -12,6 +12,10 @@ What's New in Python 3.1 beta 1?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #5883: In the io module, the BufferedIOBase and TextIOBase ABCs have
|
||||||
|
received a new method, detach(). detach() disconnects the underlying stream
|
||||||
|
from the buffer or text IO and returns it.
|
||||||
|
|
||||||
- Issue #5859: Remove switch from '%f' to '%g'-style formatting for
|
- Issue #5859: Remove switch from '%f' to '%g'-style formatting for
|
||||||
floats with absolute value over 1e50. Also remove length
|
floats with absolute value over 1e50. Also remove length
|
||||||
restrictions for float formatting: '%.67f' % 12.34 and '%.120e' %
|
restrictions for float formatting: '%.67f' % 12.34 and '%.120e' %
|
||||||
|
|
|
@ -73,6 +73,18 @@ BufferedIOBase_unsupported(const char *message)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(BufferedIOBase_detach_doc,
|
||||||
|
"Disconnect this buffer from its underlying raw stream and return it.\n"
|
||||||
|
"\n"
|
||||||
|
"After the raw stream has been detached, the buffer is in an unusable\n"
|
||||||
|
"state.\n");
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
BufferedIOBase_detach(PyObject *self)
|
||||||
|
{
|
||||||
|
return BufferedIOBase_unsupported("detach");
|
||||||
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(BufferedIOBase_read_doc,
|
PyDoc_STRVAR(BufferedIOBase_read_doc,
|
||||||
"Read and return up to n bytes.\n"
|
"Read and return up to n bytes.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -127,6 +139,7 @@ BufferedIOBase_write(PyObject *self, PyObject *args)
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef BufferedIOBase_methods[] = {
|
static PyMethodDef BufferedIOBase_methods[] = {
|
||||||
|
{"detach", (PyCFunction)BufferedIOBase_detach, METH_NOARGS, BufferedIOBase_detach_doc},
|
||||||
{"read", BufferedIOBase_read, METH_VARARGS, BufferedIOBase_read_doc},
|
{"read", BufferedIOBase_read, METH_VARARGS, BufferedIOBase_read_doc},
|
||||||
{"read1", BufferedIOBase_read1, METH_VARARGS, BufferedIOBase_read1_doc},
|
{"read1", BufferedIOBase_read1, METH_VARARGS, BufferedIOBase_read1_doc},
|
||||||
{"readinto", BufferedIOBase_readinto, METH_VARARGS, NULL},
|
{"readinto", BufferedIOBase_readinto, METH_VARARGS, NULL},
|
||||||
|
@ -181,6 +194,7 @@ typedef struct {
|
||||||
|
|
||||||
PyObject *raw;
|
PyObject *raw;
|
||||||
int ok; /* Initialized? */
|
int ok; /* Initialized? */
|
||||||
|
int detached;
|
||||||
int readable;
|
int readable;
|
||||||
int writable;
|
int writable;
|
||||||
|
|
||||||
|
@ -260,15 +274,25 @@ typedef struct {
|
||||||
|
|
||||||
#define CHECK_INITIALIZED(self) \
|
#define CHECK_INITIALIZED(self) \
|
||||||
if (self->ok <= 0) { \
|
if (self->ok <= 0) { \
|
||||||
|
if (self->detached) { \
|
||||||
|
PyErr_SetString(PyExc_ValueError, \
|
||||||
|
"raw stream has been detached"); \
|
||||||
|
} else { \
|
||||||
PyErr_SetString(PyExc_ValueError, \
|
PyErr_SetString(PyExc_ValueError, \
|
||||||
"I/O operation on uninitialized object"); \
|
"I/O operation on uninitialized object"); \
|
||||||
|
} \
|
||||||
return NULL; \
|
return NULL; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_INITIALIZED_INT(self) \
|
#define CHECK_INITIALIZED_INT(self) \
|
||||||
if (self->ok <= 0) { \
|
if (self->ok <= 0) { \
|
||||||
|
if (self->detached) { \
|
||||||
|
PyErr_SetString(PyExc_ValueError, \
|
||||||
|
"raw stream has been detached"); \
|
||||||
|
} else { \
|
||||||
PyErr_SetString(PyExc_ValueError, \
|
PyErr_SetString(PyExc_ValueError, \
|
||||||
"I/O operation on uninitialized object"); \
|
"I/O operation on uninitialized object"); \
|
||||||
|
} \
|
||||||
return -1; \
|
return -1; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,6 +454,24 @@ end:
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* detach */
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
BufferedIOMixin_detach(BufferedObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *raw, *res;
|
||||||
|
CHECK_INITIALIZED(self)
|
||||||
|
res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL);
|
||||||
|
if (res == NULL)
|
||||||
|
return NULL;
|
||||||
|
Py_DECREF(res);
|
||||||
|
raw = self->raw;
|
||||||
|
self->raw = NULL;
|
||||||
|
self->detached = 1;
|
||||||
|
self->ok = 0;
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
/* Inquiries */
|
/* Inquiries */
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -1101,6 +1143,7 @@ BufferedReader_init(BufferedObject *self, PyObject *args, PyObject *kwds)
|
||||||
PyObject *raw;
|
PyObject *raw;
|
||||||
|
|
||||||
self->ok = 0;
|
self->ok = 0;
|
||||||
|
self->detached = 0;
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|n:BufferedReader", kwlist,
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|n:BufferedReader", kwlist,
|
||||||
&raw, &buffer_size)) {
|
&raw, &buffer_size)) {
|
||||||
|
@ -1387,6 +1430,7 @@ _BufferedReader_peek_unlocked(BufferedObject *self, Py_ssize_t n)
|
||||||
|
|
||||||
static PyMethodDef BufferedReader_methods[] = {
|
static PyMethodDef BufferedReader_methods[] = {
|
||||||
/* BufferedIOMixin methods */
|
/* BufferedIOMixin methods */
|
||||||
|
{"detach", (PyCFunction)BufferedIOMixin_detach, METH_NOARGS},
|
||||||
{"flush", (PyCFunction)BufferedIOMixin_flush, METH_NOARGS},
|
{"flush", (PyCFunction)BufferedIOMixin_flush, METH_NOARGS},
|
||||||
{"close", (PyCFunction)BufferedIOMixin_close, METH_NOARGS},
|
{"close", (PyCFunction)BufferedIOMixin_close, METH_NOARGS},
|
||||||
{"seekable", (PyCFunction)BufferedIOMixin_seekable, METH_NOARGS},
|
{"seekable", (PyCFunction)BufferedIOMixin_seekable, METH_NOARGS},
|
||||||
|
@ -1499,6 +1543,7 @@ BufferedWriter_init(BufferedObject *self, PyObject *args, PyObject *kwds)
|
||||||
PyObject *raw;
|
PyObject *raw;
|
||||||
|
|
||||||
self->ok = 0;
|
self->ok = 0;
|
||||||
|
self->detached = 0;
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|nn:BufferedReader", kwlist,
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|nn:BufferedReader", kwlist,
|
||||||
&raw, &buffer_size, &max_buffer_size)) {
|
&raw, &buffer_size, &max_buffer_size)) {
|
||||||
|
@ -1745,6 +1790,7 @@ error:
|
||||||
static PyMethodDef BufferedWriter_methods[] = {
|
static PyMethodDef BufferedWriter_methods[] = {
|
||||||
/* BufferedIOMixin methods */
|
/* BufferedIOMixin methods */
|
||||||
{"close", (PyCFunction)BufferedIOMixin_close, METH_NOARGS},
|
{"close", (PyCFunction)BufferedIOMixin_close, METH_NOARGS},
|
||||||
|
{"detach", (PyCFunction)BufferedIOMixin_detach, METH_NOARGS},
|
||||||
{"seekable", (PyCFunction)BufferedIOMixin_seekable, METH_NOARGS},
|
{"seekable", (PyCFunction)BufferedIOMixin_seekable, METH_NOARGS},
|
||||||
{"readable", (PyCFunction)BufferedIOMixin_readable, METH_NOARGS},
|
{"readable", (PyCFunction)BufferedIOMixin_readable, METH_NOARGS},
|
||||||
{"writable", (PyCFunction)BufferedIOMixin_writable, METH_NOARGS},
|
{"writable", (PyCFunction)BufferedIOMixin_writable, METH_NOARGS},
|
||||||
|
@ -2089,6 +2135,7 @@ BufferedRandom_init(BufferedObject *self, PyObject *args, PyObject *kwds)
|
||||||
PyObject *raw;
|
PyObject *raw;
|
||||||
|
|
||||||
self->ok = 0;
|
self->ok = 0;
|
||||||
|
self->detached = 0;
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|nn:BufferedReader", kwlist,
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|nn:BufferedReader", kwlist,
|
||||||
&raw, &buffer_size, &max_buffer_size)) {
|
&raw, &buffer_size, &max_buffer_size)) {
|
||||||
|
@ -2128,6 +2175,7 @@ BufferedRandom_init(BufferedObject *self, PyObject *args, PyObject *kwds)
|
||||||
static PyMethodDef BufferedRandom_methods[] = {
|
static PyMethodDef BufferedRandom_methods[] = {
|
||||||
/* BufferedIOMixin methods */
|
/* BufferedIOMixin methods */
|
||||||
{"close", (PyCFunction)BufferedIOMixin_close, METH_NOARGS},
|
{"close", (PyCFunction)BufferedIOMixin_close, METH_NOARGS},
|
||||||
|
{"detach", (PyCFunction)BufferedIOMixin_detach, METH_NOARGS},
|
||||||
{"seekable", (PyCFunction)BufferedIOMixin_seekable, METH_NOARGS},
|
{"seekable", (PyCFunction)BufferedIOMixin_seekable, METH_NOARGS},
|
||||||
{"readable", (PyCFunction)BufferedIOMixin_readable, METH_NOARGS},
|
{"readable", (PyCFunction)BufferedIOMixin_readable, METH_NOARGS},
|
||||||
{"writable", (PyCFunction)BufferedIOMixin_writable, METH_NOARGS},
|
{"writable", (PyCFunction)BufferedIOMixin_writable, METH_NOARGS},
|
||||||
|
|
|
@ -28,6 +28,19 @@ _unsupported(const char *message)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(TextIOBase_detach_doc,
|
||||||
|
"Separate the underlying buffer from the TextIOBase and return it.\n"
|
||||||
|
"\n"
|
||||||
|
"After the underlying buffer has been detached, the TextIO is in an\n"
|
||||||
|
"unusable state.\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
TextIOBase_detach(PyObject *self)
|
||||||
|
{
|
||||||
|
return _unsupported("detach");
|
||||||
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(TextIOBase_read_doc,
|
PyDoc_STRVAR(TextIOBase_read_doc,
|
||||||
"Read at most n characters from stream.\n"
|
"Read at most n characters from stream.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -93,6 +106,7 @@ TextIOBase_newlines_get(PyObject *self, void *context)
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef TextIOBase_methods[] = {
|
static PyMethodDef TextIOBase_methods[] = {
|
||||||
|
{"detach", (PyCFunction)TextIOBase_detach, METH_NOARGS, TextIOBase_detach_doc},
|
||||||
{"read", TextIOBase_read, METH_VARARGS, TextIOBase_read_doc},
|
{"read", TextIOBase_read, METH_VARARGS, TextIOBase_read_doc},
|
||||||
{"readline", TextIOBase_readline, METH_VARARGS, TextIOBase_readline_doc},
|
{"readline", TextIOBase_readline, METH_VARARGS, TextIOBase_readline_doc},
|
||||||
{"write", TextIOBase_write, METH_VARARGS, TextIOBase_write_doc},
|
{"write", TextIOBase_write, METH_VARARGS, TextIOBase_write_doc},
|
||||||
|
@ -616,6 +630,7 @@ typedef struct
|
||||||
{
|
{
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
int ok; /* initialized? */
|
int ok; /* initialized? */
|
||||||
|
int detached;
|
||||||
Py_ssize_t chunk_size;
|
Py_ssize_t chunk_size;
|
||||||
PyObject *buffer;
|
PyObject *buffer;
|
||||||
PyObject *encoding;
|
PyObject *encoding;
|
||||||
|
@ -759,6 +774,7 @@ TextIOWrapper_init(PyTextIOWrapperObject *self, PyObject *args, PyObject *kwds)
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
self->ok = 0;
|
self->ok = 0;
|
||||||
|
self->detached = 0;
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|zzzi:fileio",
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|zzzi:fileio",
|
||||||
kwlist, &buffer, &encoding, &errors,
|
kwlist, &buffer, &encoding, &errors,
|
||||||
&newline, &line_buffering))
|
&newline, &line_buffering))
|
||||||
|
@ -1059,19 +1075,45 @@ TextIOWrapper_closed_get(PyTextIOWrapperObject *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, \
|
||||||
|
"underlying buffer has been detached"); \
|
||||||
|
} else { \
|
||||||
PyErr_SetString(PyExc_ValueError, \
|
PyErr_SetString(PyExc_ValueError, \
|
||||||
"I/O operation on uninitialized object"); \
|
"I/O operation on uninitialized object"); \
|
||||||
|
} \
|
||||||
return NULL; \
|
return NULL; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_INITIALIZED_INT(self) \
|
#define CHECK_INITIALIZED_INT(self) \
|
||||||
if (self->ok <= 0) { \
|
if (self->ok <= 0) { \
|
||||||
|
if (self->detached) { \
|
||||||
|
PyErr_SetString(PyExc_ValueError, \
|
||||||
|
"underlying buffer has been detached"); \
|
||||||
|
} else { \
|
||||||
PyErr_SetString(PyExc_ValueError, \
|
PyErr_SetString(PyExc_ValueError, \
|
||||||
"I/O operation on uninitialized object"); \
|
"I/O operation on uninitialized object"); \
|
||||||
|
} \
|
||||||
return -1; \
|
return -1; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
TextIOWrapper_detach(PyTextIOWrapperObject *self)
|
||||||
|
{
|
||||||
|
PyObject *buffer, *res;
|
||||||
|
CHECK_INITIALIZED(self);
|
||||||
|
res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL);
|
||||||
|
if (res == NULL)
|
||||||
|
return NULL;
|
||||||
|
Py_DECREF(res);
|
||||||
|
buffer = self->buffer;
|
||||||
|
self->buffer = NULL;
|
||||||
|
self->detached = 1;
|
||||||
|
self->ok = 0;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
Py_LOCAL_INLINE(const Py_UNICODE *)
|
Py_LOCAL_INLINE(const Py_UNICODE *)
|
||||||
findchar(const Py_UNICODE *s, Py_ssize_t size, Py_UNICODE ch)
|
findchar(const Py_UNICODE *s, Py_ssize_t size, Py_UNICODE ch)
|
||||||
{
|
{
|
||||||
|
@ -2341,6 +2383,7 @@ TextIOWrapper_chunk_size_set(PyTextIOWrapperObject *self,
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyMethodDef TextIOWrapper_methods[] = {
|
static PyMethodDef TextIOWrapper_methods[] = {
|
||||||
|
{"detach", (PyCFunction)TextIOWrapper_detach, METH_NOARGS},
|
||||||
{"write", (PyCFunction)TextIOWrapper_write, METH_VARARGS},
|
{"write", (PyCFunction)TextIOWrapper_write, METH_VARARGS},
|
||||||
{"read", (PyCFunction)TextIOWrapper_read, METH_VARARGS},
|
{"read", (PyCFunction)TextIOWrapper_read, METH_VARARGS},
|
||||||
{"readline", (PyCFunction)TextIOWrapper_readline, METH_VARARGS},
|
{"readline", (PyCFunction)TextIOWrapper_readline, METH_VARARGS},
|
||||||
|
|
Loading…
Reference in New Issue