Index: Objects/fileobject.c =================================================================== --- Objects/fileobject.c (revision 67917) +++ Objects/fileobject.c (working copy) @@ -2515,12 +2515,21 @@ return NULL; } univ_newline = ((PyFileObject *)fobj)->f_univ_newline; - if ( !univ_newline ) + if ( !univ_newline ) { + /* Issue #1706039: Support continued reading from a file even after + * EOF was hit. + */ + clearerr(stream); return fgets(buf, n, stream); + } newlinetypes = ((PyFileObject *)fobj)->f_newlinetypes; skipnextlf = ((PyFileObject *)fobj)->f_skipnextlf; } FLOCKFILE(stream); + /* Issue #1706039: Support continued reading from a file even after + * EOF was hit. + */ + clearerr(stream); c = 'x'; /* Shut up gcc warning */ while (--n > 0 && (c = GETC(stream)) != EOF ) { if (skipnextlf ) { @@ -2603,6 +2612,10 @@ errno = ENXIO; /* What can you do... */ return 0; } + /* Issue #1706039: Support continued reading from a file even after + * EOF was hit. + */ + clearerr(stream); if (!f->f_univ_newline) return fread(buf, 1, n, stream); newlinetypes = f->f_newlinetypes; Index: Lib/test/test_file.py =================================================================== --- Lib/test/test_file.py (revision 67917) +++ Lib/test/test_file.py (working copy) @@ -547,7 +547,48 @@ finally: sys.stdout = save_stdout + 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.