This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author dabeaz
Recipients dabeaz
Date 2015-10-25.21:46:31
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1445809592.17.0.674825265911.issue25476@psf.upfronthosting.co.za>
In-reply-to
Content
First comment: In the I/O library, there is documented behavior for how things work in the presence of non-blocking I/O.  For example, read/write methods returning None on raw file objects.  Methods on BufferedIO instances raise a BlockingIOError for operations that can't complete. 

However, the implementation of close() is currently broken.  If buffered I/O is being used and a file is closed, it's possible that the close will fail due to a BlockingIOError occurring as buffered data is flushed to output.  However, in this case, the file is closed anyways and there is no possibility to retry.  Here is an example to illustrate:

>>> from socket import *
>>> s = socket(AF_INET, SOCK_STREAM)
>>> s.connect(('somehost', port))
>>> s.setblocking(False)
>>> f = s.makefile('wb', buffering=10000000)   # Large buffer
>>> f.write(b'x'*1000000)
>>>

Now, watch carefully

>>> f
<_io.BufferedWriter name=4>
>>> f.closed
False
>>> f.close()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
BlockingIOError: [Errno 35] write could not complete without blocking
>>> f
<_io.BufferedWriter name=-1>
>>> f.closed
True
>>>

I believe this can be fixed by changing a single line in Modules/_io/bufferedio.c:

--- bufferedio_orig.c	2015-10-25 16:40:22.000000000 -0500
+++ bufferedio.c	2015-10-25 16:40:35.000000000 -0500
@@ -530,10 +530,10 @@
     res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL);
     if (!ENTER_BUFFERED(self))
         return NULL;
-    if (res == NULL)
-        PyErr_Fetch(&exc, &val, &tb);
-    else
-        Py_DECREF(res);
+    if (res == NULL) 
+      goto end;
+    else 
+      Py_DECREF(res);
 
     res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_close, NULL);

With this patch, the close() method can be retried as appropriate until all buffered data is successfully written.
History
Date User Action Args
2015-10-25 21:46:32dabeazsetrecipients: + dabeaz
2015-10-25 21:46:32dabeazsetmessageid: <1445809592.17.0.674825265911.issue25476@psf.upfronthosting.co.za>
2015-10-25 21:46:32dabeazlinkissue25476 messages
2015-10-25 21:46:31dabeazcreate