[Python-checkins] r72175 - in python/branches/py3k: Doc/library/io.rst Lib/_pyio.py Lib/test/test_io.py Lib/test/test_memoryio.py Misc/NEWS Modules/_io/bufferedio.c Modules/_io/textio.c

benjamin.peterson python-checkins at python.org
Fri May 1 22:40:59 CEST 2009


Author: benjamin.peterson
Date: Fri May 1 22:40:59 2009
New Revision: 72175
Log:
implement a detach() method for BufferedIOBase and TextIOBase #5883
Modified:
 python/branches/py3k/Doc/library/io.rst
 python/branches/py3k/Lib/_pyio.py
 python/branches/py3k/Lib/test/test_io.py
 python/branches/py3k/Lib/test/test_memoryio.py
 python/branches/py3k/Misc/NEWS
 python/branches/py3k/Modules/_io/bufferedio.c
 python/branches/py3k/Modules/_io/textio.c
Modified: python/branches/py3k/Doc/library/io.rst
==============================================================================
--- python/branches/py3k/Doc/library/io.rst	(original)
+++ python/branches/py3k/Doc/library/io.rst	Fri May 1 22:40:59 2009
@@ -361,6 +361,17 @@
 :class:`BufferedIOBase` provides or overrides these methods in addition to
 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])
 
 Read and return up to *n* bytes. If the argument is omitted, ``None``, or
@@ -547,7 +558,9 @@
 
 *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]])
@@ -588,6 +601,17 @@
 A string, a tuple of strings, or ``None``, indicating the newlines
 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)
 
 Read and return at most *n* characters from the stream as a single
Modified: python/branches/py3k/Lib/_pyio.py
==============================================================================
--- python/branches/py3k/Lib/_pyio.py	(original)
+++ python/branches/py3k/Lib/_pyio.py	Fri May 1 22:40:59 2009
@@ -642,6 +642,15 @@
 """
 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)
 
 
@@ -689,13 +698,21 @@
 self.raw.flush()
 
 def close(self):
- if not self.closed:
+ if not self.closed and self.raw is not None:
 try:
 self.flush()
 except IOError:
 pass # If flush() fails, just give up
 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 ###
 
 def seekable(self):
@@ -1236,6 +1253,15 @@
 """
 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
 def encoding(self):
 """Subclasses should override."""
@@ -1448,11 +1474,12 @@
 self._telling = self._seekable
 
 def close(self):
- try:
- self.flush()
- except IOError:
- pass # If flush() fails, just give up
- self.buffer.close()
+ if self.buffer is not None:
+ try:
+ self.flush()
+ except IOError:
+ pass # If flush() fails, just give up
+ self.buffer.close()
 
 @property
 def closed(self):
@@ -1647,6 +1674,14 @@
 self.seek(pos)
 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):
 if self.closed:
 raise ValueError("tell on closed file")
@@ -1865,3 +1900,7 @@
 @property
 def encoding(self):
 return None
+
+ def detach(self):
+ # This doesn't make sense on StringIO.
+ self._unsupported("detach")
Modified: python/branches/py3k/Lib/test/test_io.py
==============================================================================
--- python/branches/py3k/Lib/test/test_io.py	(original)
+++ python/branches/py3k/Lib/test/test_io.py	Fri May 1 22:40:59 2009
@@ -526,6 +526,12 @@
 class CommonBufferedTests:
 # 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):
 rawio = self.MockRawIO()
 bufio = self.tp(rawio)
@@ -811,6 +817,14 @@
 bufio.flush()
 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):
 # Write to the buffered IO but don't overflow the buffer.
 writer = self.MockRawIO()
@@ -1052,6 +1066,10 @@
 pair = self.tp(self.MockRawIO(), self.MockRawIO())
 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):
 with support.check_warnings() as w:
 warnings.simplefilter("always", DeprecationWarning)
@@ -1480,6 +1498,19 @@
 self.assertRaises(TypeError, t.__init__, b, newline=42)
 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):
 raw = self.BytesIO("hello".encode("utf-8"))
 b = self.BufferedReader(raw)
Modified: python/branches/py3k/Lib/test/test_memoryio.py
==============================================================================
--- python/branches/py3k/Lib/test/test_memoryio.py	(original)
+++ python/branches/py3k/Lib/test/test_memoryio.py	Fri May 1 22:40:59 2009
@@ -57,6 +57,10 @@
 
 class MemoryTestMixin:
 
+ def test_detach(self):
+ buf = self.ioclass()
+ self.assertRaises(self.UnsupportedOperation, buf.detach)
+
 def write_ops(self, f, t):
 self.assertEqual(f.write(t("blah.")), 5)
 self.assertEqual(f.seek(0), 0)
@@ -336,6 +340,9 @@
 
 
 class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase):
+
+ UnsupportedOperation = pyio.UnsupportedOperation
+
 @staticmethod
 def buftype(s):
 return s.encode("ascii")
@@ -413,6 +420,7 @@
 class PyStringIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase):
 buftype = str
 ioclass = pyio.StringIO
+ UnsupportedOperation = pyio.UnsupportedOperation
 EOF = ""
 
 # TextIO-specific behaviour.
@@ -518,9 +526,11 @@
 
 class CBytesIOTest(PyBytesIOTest):
 ioclass = io.BytesIO
+ UnsupportedOperation = io.UnsupportedOperation
 
 class CStringIOTest(PyStringIOTest):
 ioclass = io.StringIO
+ UnsupportedOperation = io.UnsupportedOperation
 
 # XXX: For the Python version of io.StringIO, this is highly
 # dependent on the encoding used for the underlying buffer.
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Fri May 1 22:40:59 2009
@@ -12,6 +12,10 @@
 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
 floats with absolute value over 1e50. Also remove length
 restrictions for float formatting: '%.67f' % 12.34 and '%.120e' %
Modified: python/branches/py3k/Modules/_io/bufferedio.c
==============================================================================
--- python/branches/py3k/Modules/_io/bufferedio.c	(original)
+++ python/branches/py3k/Modules/_io/bufferedio.c	Fri May 1 22:40:59 2009
@@ -73,6 +73,18 @@
 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,
 "Read and return up to n bytes.\n"
 "\n"
@@ -127,6 +139,7 @@
 
 
 static PyMethodDef BufferedIOBase_methods[] = {
+ {"detach", (PyCFunction)BufferedIOBase_detach, METH_NOARGS, BufferedIOBase_detach_doc},
 {"read", BufferedIOBase_read, METH_VARARGS, BufferedIOBase_read_doc},
 {"read1", BufferedIOBase_read1, METH_VARARGS, BufferedIOBase_read1_doc},
 {"readinto", BufferedIOBase_readinto, METH_VARARGS, NULL},
@@ -181,6 +194,7 @@
 
 PyObject *raw;
 int ok; /* Initialized? */
+ int detached;
 int readable;
 int writable;
 
@@ -260,15 +274,25 @@
 
 #define CHECK_INITIALIZED(self) \
 if (self->ok <= 0) { \
- PyErr_SetString(PyExc_ValueError, \
- "I/O operation on uninitialized object"); \
+ if (self->detached) { \
+ PyErr_SetString(PyExc_ValueError, \
+ "raw stream has been detached"); \
+ } else { \
+ PyErr_SetString(PyExc_ValueError, \
+ "I/O operation on uninitialized object"); \
+ } \
 return NULL; \
 }
 
 #define CHECK_INITIALIZED_INT(self) \
 if (self->ok <= 0) { \
- PyErr_SetString(PyExc_ValueError, \
- "I/O operation on uninitialized object"); \
+ if (self->detached) { \
+ PyErr_SetString(PyExc_ValueError, \
+ "raw stream has been detached"); \
+ } else { \
+ PyErr_SetString(PyExc_ValueError, \
+ "I/O operation on uninitialized object"); \
+ } \
 return -1; \
 }
 
@@ -430,6 +454,24 @@
 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 */
 
 static PyObject *
@@ -1101,6 +1143,7 @@
 PyObject *raw;
 
 self->ok = 0;
+ self->detached = 0;
 
 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|n:BufferedReader", kwlist,
 &raw, &buffer_size)) {
@@ -1387,6 +1430,7 @@
 
 static PyMethodDef BufferedReader_methods[] = {
 /* BufferedIOMixin methods */
+ {"detach", (PyCFunction)BufferedIOMixin_detach, METH_NOARGS},
 {"flush", (PyCFunction)BufferedIOMixin_flush, METH_NOARGS},
 {"close", (PyCFunction)BufferedIOMixin_close, METH_NOARGS},
 {"seekable", (PyCFunction)BufferedIOMixin_seekable, METH_NOARGS},
@@ -1499,6 +1543,7 @@
 PyObject *raw;
 
 self->ok = 0;
+ self->detached = 0;
 
 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|nn:BufferedReader", kwlist,
 &raw, &buffer_size, &max_buffer_size)) {
@@ -1745,6 +1790,7 @@
 static PyMethodDef BufferedWriter_methods[] = {
 /* BufferedIOMixin methods */
 {"close", (PyCFunction)BufferedIOMixin_close, METH_NOARGS},
+ {"detach", (PyCFunction)BufferedIOMixin_detach, METH_NOARGS},
 {"seekable", (PyCFunction)BufferedIOMixin_seekable, METH_NOARGS},
 {"readable", (PyCFunction)BufferedIOMixin_readable, METH_NOARGS},
 {"writable", (PyCFunction)BufferedIOMixin_writable, METH_NOARGS},
@@ -2089,6 +2135,7 @@
 PyObject *raw;
 
 self->ok = 0;
+ self->detached = 0;
 
 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|nn:BufferedReader", kwlist,
 &raw, &buffer_size, &max_buffer_size)) {
@@ -2128,6 +2175,7 @@
 static PyMethodDef BufferedRandom_methods[] = {
 /* BufferedIOMixin methods */
 {"close", (PyCFunction)BufferedIOMixin_close, METH_NOARGS},
+ {"detach", (PyCFunction)BufferedIOMixin_detach, METH_NOARGS},
 {"seekable", (PyCFunction)BufferedIOMixin_seekable, METH_NOARGS},
 {"readable", (PyCFunction)BufferedIOMixin_readable, METH_NOARGS},
 {"writable", (PyCFunction)BufferedIOMixin_writable, METH_NOARGS},
Modified: python/branches/py3k/Modules/_io/textio.c
==============================================================================
--- python/branches/py3k/Modules/_io/textio.c	(original)
+++ python/branches/py3k/Modules/_io/textio.c	Fri May 1 22:40:59 2009
@@ -28,6 +28,19 @@
 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,
 "Read at most n characters from stream.\n"
 "\n"
@@ -93,6 +106,7 @@
 
 
 static PyMethodDef TextIOBase_methods[] = {
+ {"detach", (PyCFunction)TextIOBase_detach, METH_NOARGS, TextIOBase_detach_doc},
 {"read", TextIOBase_read, METH_VARARGS, TextIOBase_read_doc},
 {"readline", TextIOBase_readline, METH_VARARGS, TextIOBase_readline_doc},
 {"write", TextIOBase_write, METH_VARARGS, TextIOBase_write_doc},
@@ -616,6 +630,7 @@
 {
 PyObject_HEAD
 int ok; /* initialized? */
+ int detached;
 Py_ssize_t chunk_size;
 PyObject *buffer;
 PyObject *encoding;
@@ -759,6 +774,7 @@
 int r;
 
 self->ok = 0;
+ self->detached = 0;
 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|zzzi:fileio",
 kwlist, &buffer, &encoding, &errors,
 &newline, &line_buffering))
@@ -1059,19 +1075,45 @@
 
 #define CHECK_INITIALIZED(self) \
 if (self->ok <= 0) { \
- PyErr_SetString(PyExc_ValueError, \
- "I/O operation on uninitialized object"); \
+ if (self->detached) { \
+ PyErr_SetString(PyExc_ValueError, \
+ "underlying buffer has been detached"); \
+ } else { \
+ PyErr_SetString(PyExc_ValueError, \
+ "I/O operation on uninitialized object"); \
+ } \
 return NULL; \
 }
 
 #define CHECK_INITIALIZED_INT(self) \
 if (self->ok <= 0) { \
- PyErr_SetString(PyExc_ValueError, \
- "I/O operation on uninitialized object"); \
+ if (self->detached) { \
+ PyErr_SetString(PyExc_ValueError, \
+ "underlying buffer has been detached"); \
+ } else { \
+ PyErr_SetString(PyExc_ValueError, \
+ "I/O operation on uninitialized object"); \
+ } \
 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 *)
 findchar(const Py_UNICODE *s, Py_ssize_t size, Py_UNICODE ch)
 {
@@ -2341,6 +2383,7 @@
 }
 
 static PyMethodDef TextIOWrapper_methods[] = {
+ {"detach", (PyCFunction)TextIOWrapper_detach, METH_NOARGS},
 {"write", (PyCFunction)TextIOWrapper_write, METH_VARARGS},
 {"read", (PyCFunction)TextIOWrapper_read, METH_VARARGS},
 {"readline", (PyCFunction)TextIOWrapper_readline, METH_VARARGS},


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /