[Python-checkins] r68372 - in sandbox/trunk/io-c: _bufferedio.c _iobase.c test_io.py

antoine.pitrou python-checkins at python.org
Wed Jan 7 02:03:52 CET 2009


Author: antoine.pitrou
Date: Wed Jan  7 02:03:52 2009
New Revision: 68372

Log:
Ensure we can't use a closed file as a context manager.
This fixes one test in test_tempfile.



Modified:
   sandbox/trunk/io-c/_bufferedio.c
   sandbox/trunk/io-c/_iobase.c
   sandbox/trunk/io-c/test_io.py

Modified: sandbox/trunk/io-c/_bufferedio.c
==============================================================================
--- sandbox/trunk/io-c/_bufferedio.c	(original)
+++ sandbox/trunk/io-c/_bufferedio.c	Wed Jan  7 02:03:52 2009
@@ -333,22 +333,36 @@
 static PyObject *
 BufferedIOMixin_close(BufferedObject *self, PyObject *args)
 {
-    PyObject *res;
+    PyObject *res = NULL;
+    int r;
 
     CHECK_INITIALIZED(self)
-    if (BufferedIOMixin_closed(self))
-        Py_RETURN_NONE;
+    ENTER_BUFFERED(self)
 
+    r = BufferedIOMixin_closed(self);
+    if (r < 0)
+        goto end;
+    if (r > 0) {
+        res = Py_None;
+        Py_INCREF(res);
+        goto end;
+    }
+    /* flush() will most probably re-take the lock, so drop it first */
+    LEAVE_BUFFERED(self)
     res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL);
+    ENTER_BUFFERED(self)
     if (res == NULL) {
         /* If flush() fails, just give up */
         if (PyErr_ExceptionMatches(PyExc_IOError))
             PyErr_Clear();
         else
-            return NULL;
+            goto end;
     }
+    res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_close, NULL);
 
-    return PyObject_CallMethod(self->raw, "close", NULL);
+end:
+    LEAVE_BUFFERED(self)
+    return res;
 }
 
 /* Inquiries */

Modified: sandbox/trunk/io-c/_iobase.c
==============================================================================
--- sandbox/trunk/io-c/_iobase.c	(original)
+++ sandbox/trunk/io-c/_iobase.c	Wed Jan  7 02:03:52 2009
@@ -36,6 +36,13 @@
     "with open('spam.txt', 'r') as fp:\n"
     "    fp.write('Spam and eggs!')\n");
 
+/* Use this macro whenever you want to check the internal `closed` status
+   of the IOBase object rather than the virtual `closed` attribute as returned
+   by whatever subclass. */
+
+#define IS_CLOSED(self) \
+    PyObject_HasAttrString(self, "__IOBase_closed")
+
 /* Internal methods */
 static PyObject *
 IOBase_unsupported(const char *message)
@@ -108,13 +115,20 @@
 static int
 IOBase_closed(PyObject *self)
 {
-    return PyObject_HasAttrString(self, "__IOBase_closed");
+    PyObject *res;
+    int closed;
+    /* This gets the derived attribute, which is *not* __IOBase_closed
+       in most cases! */
+    res = PyObject_GetAttr(self, _PyIO_str_closed);
+    closed = PyObject_IsTrue(res);
+    Py_DECREF(res);
+    return closed;
 }
 
 static PyObject *
 IOBase_closed_get(PyObject *self, void *context)
 {
-    return PyBool_FromLong(IOBase_closed(self));
+    return PyBool_FromLong(IS_CLOSED(self));
 }
 
 
@@ -128,17 +142,20 @@
     Py_RETURN_NONE;
 }
 
+/* XXX: IOBase thinks it has to maintain its own internal state in
+   `__IOBase_closed` and call flush() by itself, but it is redundant with
+   whatever behaviour a non-trivial derived class will implement. */
+
 static PyObject *
 IOBase_close(PyObject *self, PyObject *args)
 {
     PyObject *res;
 
-    if (IOBase_closed(self))
+    if (IS_CLOSED(self))
         Py_RETURN_NONE;
 
-    PyObject_SetAttrString(self, "__IOBase_closed", Py_True);
-
     res = PyObject_CallMethodObjArgs(self, _PyIO_str_flush, NULL);
+    PyObject_SetAttrString(self, "__IOBase_closed", Py_True);
     if (res == NULL) {
         /* If flush() fails, just give up */
         if (PyErr_ExceptionMatches(PyExc_IOError))

Modified: sandbox/trunk/io-c/test_io.py
==============================================================================
--- sandbox/trunk/io-c/test_io.py	(original)
+++ sandbox/trunk/io-c/test_io.py	Wed Jan  7 02:03:52 2009
@@ -450,6 +450,18 @@
             self.assertEqual(record, [1, 2, 3])
         else:
             self.assertEqual(record, [1, 2])
+    
+    def testContext(self):
+        # Test usability as a context manager
+        rawio = MockRawIO()
+        bufio = self.tp(rawio)
+        def _with():
+            with bufio:
+                pass
+        _with()
+        # bufio should now be closed, and using it a second time should raise
+        # a ValueError.
+        self.assertRaises(ValueError, _with)
 
 class BufferedReaderTest(unittest.TestCase, CommonBufferedTests):
     tp = io.BufferedReader


More information about the Python-checkins mailing list