Index: Objects/fileobject.c =================================================================== --- Objects/fileobject.c (revision 55015) +++ Objects/fileobject.c (working copy) @@ -871,16 +871,16 @@ buffersize - bytesread, f->f_fp, (PyObject *)f); Py_END_ALLOW_THREADS if (chunksize == 0) { - if (!ferror(f->f_fp)) + if (!PyErr_ExceptionMatches(PyExc_IOError)) break; - 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)) + if (bytesread > 0 && BLOCKED_ERRNO(errno)) { + PyErr_Clear(); break; - PyErr_SetFromErrno(PyExc_IOError); + } Py_DECREF(v); return NULL; } @@ -927,10 +927,8 @@ (PyObject *)f); Py_END_ALLOW_THREADS if (nnow == 0) { - if (!ferror(f->f_fp)) + if (!PyErr_ExceptionMatches(PyExc_IOError)) break; - PyErr_SetFromErrno(PyExc_IOError); - clearerr(f->f_fp); return NULL; } ndone += nnow; @@ -1422,10 +1420,8 @@ } if (nread == 0) { sizehint = 0; - if (!ferror(f->f_fp)) + if (!PyErr_ExceptionMatches(PyExc_IOError)) break; - PyErr_SetFromErrno(PyExc_IOError); - clearerr(f->f_fp); error: Py_DECREF(list); list = NULL; @@ -1873,9 +1869,7 @@ f->f_buf, bufsize, f->f_fp, (PyObject *)f); Py_END_ALLOW_THREADS if (chunksize == 0) { - if (ferror(f->f_fp)) { - PyErr_SetFromErrno(PyExc_IOError); - clearerr(f->f_fp); + if (PyErr_ExceptionMatches(PyExc_IOError)) { drop_readahead(f); return -1; } @@ -2426,6 +2420,7 @@ char *dst = buf; PyFileObject *f = (PyFileObject *)fobj; int newlinetypes, skipnextlf; + size_t nread; assert(buf != NULL); assert(stream != NULL); @@ -2434,22 +2429,40 @@ errno = ENXIO; /* What can you do... */ return 0; } - if (!f->f_univ_newline) - return fread(buf, 1, n, stream); + if (!f->f_univ_newline) { + nread = fread(buf, 1, n, stream); + if (nread == 0) { + if (ferror(stream)) { + clearerr(stream); + PyErr_SetFromErrno(PyExc_IOError); + return 0; + } else if (feof(stream)) { + clearerr(stream); + } + } + return nread; + } newlinetypes = f->f_newlinetypes; skipnextlf = f->f_skipnextlf; /* Invariant: n is the number of bytes remaining to be filled * in the buffer. */ while (n) { - size_t nread; int shortread; char *src = dst; nread = fread(dst, 1, n, stream); assert(nread <= n); - if (nread == 0) + if (nread == 0) { + if (ferror(stream)) { + clearerr(stream); + PyErr_SetFromErrno(PyExc_IOError); + return 0; + } else if (feof(stream)) { + clearerr(stream); + } break; + } n -= nread; /* assuming 1 byte out for each in; will adjust */ shortread = n != 0; /* true iff EOF or error */ Index: Lib/test/test_file.py =================================================================== --- Lib/test/test_file.py (revision 55015) +++ Lib/test/test_file.py (working copy) @@ -322,7 +322,48 @@ finally: os.unlink(TESTFN) + def testReadAfterEOF(self): + # Verify read works after hitting EOF + # Prepare the testfile + teststring = "spam" + bag = open(TESTFN, "w") + bag.write(teststring) + bag.close() + + # And buf for readinto + buf = array("c", " "*len(teststring)) + + # Test for appropriate errors mixing read* and iteration + methods = [("readline", ()), ("read",()), ("readlines", ()), + ("readinto", (buf,))] + + for attr in 'r', 'rU': + for methodname, args in methods: + f = open(TESTFN, "rU") + f.seek(0, 2) + meth = getattr(f, methodname) + meth(*args) # hits EOF + try: + # Writing the same file with another file descriptor + append = open(TESTFN, "a+") + append.write(teststring) + append.flush() + append.close() + try: + meth = getattr(f, methodname) + if methodname == 'readlines': + self.failUnlessEqual(meth(*args), [teststring]) + elif methodname == 'readinto': + meth(*args) + self.failUnlessEqual(buf.tostring(), teststring) + else: + self.failUnlessEqual(meth(*args), teststring) + except ValueError: + self.fail("read* failed after hitting EOF") + finally: + f.close() + def test_main(): # Historically, these tests have been sloppy about removing TESTFN. # So get rid of it no matter what.