From 741f33224bd69e733da9b7cf9227b5dd93616c20 Mon Sep 17 00:00:00 2001 From: esadomer <54475303+esadomer@users.noreply.github.com> Date: Tue, 13 Oct 2026 19:51:13 +0300 Subject: [PATCH] gh-87034: Keep SpooledTemporaryFile iterators valid across rollover --- Lib/tempfile.py | 2 +- Lib/test/test_tempfile.py | 35 +++++++++++++++++++ ...6-10-13-21-00-00.gh-issue-87034.sPlItR.rst | 2 ++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-10-13-21-00-00.gh-issue-87034.sPlItR.rst diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 82c1ee353117e94..94daa9ee3383300 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -838,7 +838,7 @@ def __exit__(self, exc, value, tb): # file protocol def __iter__(self): - return self._file.__iter__() + return super().__iter__() def __del__(self): if not self.closed: diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 2f7b8798daa8c95..bb8b238be7a8fc7 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1230,6 +1230,41 @@ def test_is_iobase(self): # SpooledTemporaryFile should implement io.IOBase self.assertIsInstance(self.do_create(), io.IOBase) + def test_iteration_rollover(self): + for mode, data in [('w+b', b'first\nsecond\nthird\n'), + ('w+', 'first\nsecond\nthird\n')]: + with self.subTest(mode=mode): + kwargs = {} if 'b' in mode else {'encoding': 'utf-8', + 'newline': '\n'} + with tempfile.SpooledTemporaryFile(mode=mode, **kwargs) as f: + f.write(data) + f.seek(0) + iterator = iter(f) + self.assertEqual(next(iterator), data[:6]) + f.rollover() + self.assertEqual(next(iterator), data[6:13]) + self.assertEqual(f.tell(), 13) + self.assertEqual(f.read(), data[13:]) + self.assertRaises(StopIteration, next, iterator) + + def test_iterator_identity(self): + for mode in ('w+b', 'w+'): + with self.subTest(mode=mode): + with tempfile.SpooledTemporaryFile(mode=mode) as f: + self.assertIs(iter(f), f) + f.rollover() + self.assertIs(iter(f), f) + + def test_iteration_closed(self): + for mode in ('w+b', 'w+'): + for rolled in (False, True): + with self.subTest(mode=mode, rolled=rolled): + f = tempfile.SpooledTemporaryFile(mode=mode) + if rolled: + f.rollover() + f.close() + self.assertRaises(ValueError, iter, f) + def test_iobase_interface(self): # SpooledTemporaryFile should implement the io.IOBase interface. # Ensure it has all the required methods and properties. diff --git a/Misc/NEWS.d/next/Library/2026-10-13-21-00-00.gh-issue-87034.sPlItR.rst b/Misc/NEWS.d/next/Library/2026-10-13-21-00-00.gh-issue-87034.sPlItR.rst new file mode 100644 index 000000000000000..6f387bb7795897f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-13-21-00-00.gh-issue-87034.sPlItR.rst @@ -0,0 +1,2 @@ +Fix iteration over :class:`tempfile.SpooledTemporaryFile` when the file rolls +over to disk during iteration.