Skip to content

Commit 39f7b06

Browse files
[3.13] gh-127371 Avoid unbounded growth SpooledTempfile.writelines (GH-127372) (#130886)
gh-127371 Avoid unbounded growth SpooledTempfile.writelines (GH-127372) (cherry picked from commit cb67b44) Co-authored-by: Bert Peters <[email protected]>
1 parent ab74967 commit 39f7b06

File tree

3 files changed

+39
-4
lines changed

3 files changed

+39
-4
lines changed

Lib/tempfile.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -848,10 +848,14 @@ def write(self, s):
848848
return rv
849849

850850
def writelines(self, iterable):
851-
file = self._file
852-
rv = file.writelines(iterable)
853-
self._check(file)
854-
return rv
851+
if self._max_size == 0 or self._rolled:
852+
return self._file.writelines(iterable)
853+
854+
it = iter(iterable)
855+
for line in it:
856+
self.write(line)
857+
if self._rolled:
858+
return self._file.writelines(it)
855859

856860
def detach(self):
857861
return self._file.detach()

Lib/test/test_tempfile.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,34 @@ def test_writelines(self):
12881288
buf = f.read()
12891289
self.assertEqual(buf, b'xyz')
12901290

1291+
def test_writelines_rollover(self):
1292+
# Verify writelines rolls over before exhausting the iterator
1293+
f = self.do_create(max_size=2)
1294+
1295+
def it():
1296+
yield b'xy'
1297+
self.assertFalse(f._rolled)
1298+
yield b'z'
1299+
self.assertTrue(f._rolled)
1300+
1301+
f.writelines(it())
1302+
pos = f.seek(0)
1303+
self.assertEqual(pos, 0)
1304+
buf = f.read()
1305+
self.assertEqual(buf, b'xyz')
1306+
1307+
def test_writelines_fast_path(self):
1308+
f = self.do_create(max_size=2)
1309+
f.write(b'abc')
1310+
self.assertTrue(f._rolled)
1311+
1312+
f.writelines([b'd', b'e', b'f'])
1313+
pos = f.seek(0)
1314+
self.assertEqual(pos, 0)
1315+
buf = f.read()
1316+
self.assertEqual(buf, b'abcdef')
1317+
1318+
12911319
def test_writelines_sequential(self):
12921320
# A SpooledTemporaryFile should hold exactly max_size bytes, and roll
12931321
# over afterward
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Avoid unbounded buffering for :meth:`!tempfile.SpooledTemporaryFile.writelines`.
2+
Previously, disk spillover was only checked after the lines iterator had been
3+
exhausted. This is now done after each line is written.

0 commit comments

Comments
 (0)