summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/lib/libstdtypes.tex4
-rw-r--r--Misc/NEWS3
-rw-r--r--Objects/fileobject.c33
3 files changed, 36 insertions, 4 deletions
diff --git a/Doc/lib/libstdtypes.tex b/Doc/lib/libstdtypes.tex
index a104d85..6dee0c9 100644
--- a/Doc/lib/libstdtypes.tex
+++ b/Doc/lib/libstdtypes.tex
@@ -1205,7 +1205,9 @@ flush the read-ahead buffer.
certain files, like ttys, it makes sense to continue reading after
an \EOF{} is hit.) Note that this method may call the underlying
C function \cfunction{fread()} more than once in an effort to
- acquire as close to \var{size} bytes as possible.
+ acquire as close to \var{size} bytes as possible. Also note that
+ when in non-blocking mode, less data than what was requested may
+ be returned, even if no \var{size} parameter was given.
\end{methoddesc}
\begin{methoddesc}[file]{readline}{\optional{size}}
diff --git a/Misc/NEWS b/Misc/NEWS
index 772ae95..3c7bf75 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -335,6 +335,9 @@ Core and builtins
general left to right evaluation order rule. Now {f1(): f2()} will
evaluate f1 first.
+- Fixed bug #521782: when a file was in non-blocking mode, file.read()
+ could silently lose data or wrongly throw an unknown error.
+
Extension modules
-----------------
diff --git a/Objects/fileobject.c b/Objects/fileobject.c
index 86620ff..612cefd 100644
--- a/Objects/fileobject.c
+++ b/Objects/fileobject.c
@@ -741,6 +741,20 @@ new_buffersize(PyFileObject *f, size_t currentsize)
return currentsize + SMALLCHUNK;
}
+#if defined(EWOULDBLOCK) && defined(EAGAIN) && EWOULDBLOCK != EAGAIN
+#define BLOCKED_ERRNO(x) ((x) == EWOULDBLOCK || (x) == EAGAIN)
+#else
+#ifdef EWOULDBLOCK
+#define BLOCKED_ERRNO(x) ((x) == EWOULDBLOCK)
+#else
+#ifdef EAGAIN
+#define BLOCKED_ERRNO(x) ((x) == EAGAIN)
+#else
+#define BLOCKED_ERRNO(x) 0
+#endif
+#endif
+#endif
+
static PyObject *
file_read(PyFileObject *f, PyObject *args)
{
@@ -774,18 +788,29 @@ file_read(PyFileObject *f, PyObject *args)
if (chunksize == 0) {
if (!ferror(f->f_fp))
break;
- PyErr_SetFromErrno(PyExc_IOError);
clearerr(f->f_fp);
+ /* When in non-blocking mode, data shouldn't
+ * be discarded if a blocking signal was
+ * received. That will also happen if
+ * chunksize != 0, but bytesread < buffersize. */
+ if (bytesread > 0 && BLOCKED_ERRNO(errno))
+ break;
+ PyErr_SetFromErrno(PyExc_IOError);
Py_DECREF(v);
return NULL;
}
bytesread += chunksize;
- if (bytesread < buffersize)
+ if (bytesread < buffersize) {
+ clearerr(f->f_fp);
break;
+ }
if (bytesrequested < 0) {
buffersize = new_buffersize(f, buffersize);
if (_PyString_Resize(&v, buffersize) < 0)
return NULL;
+ } else {
+ assert(bytesread == bytesrequested);
+ break;
}
}
if (bytesread != buffersize)
@@ -1518,7 +1543,9 @@ PyDoc_STRVAR(readline_doc,
PyDoc_STRVAR(read_doc,
"read([size]) -> read at most size bytes, returned as a string.\n"
"\n"
-"If the size argument is negative or omitted, read until EOF is reached.");
+"If the size argument is negative or omitted, read until EOF is reached.\n"
+"Notice that when in non-blocking mode, less data than what was requested\n"
+"may be returned, even if no size parameter was given.");
PyDoc_STRVAR(write_doc,
"write(str) -> None. Write string str to file.\n"