Skip to content

Commit cb67b44

Browse files
authored
gh-127371 Avoid unbounded growth SpooledTempfile.writelines (GH-127372)
1 parent 691354c commit cb67b44

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
@@ -868,10 +868,14 @@ def write(self, s):
868868
return rv
869869

870870
def writelines(self, iterable):
871-
file = self._file
872-
rv = file.writelines(iterable)
873-
self._check(file)
874-
return rv
871+
if self._max_size == 0 or self._rolled:
872+
return self._file.writelines(iterable)
873+
874+
it = iter(iterable)
875+
for line in it:
876+
self.write(line)
877+
if self._rolled:
878+
return self._file.writelines(it)
875879

876880
def detach(self):
877881
return self._file.detach()

Lib/test/test_tempfile.py

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

1287+
def test_writelines_rollover(self):
1288+
# Verify writelines rolls over before exhausting the iterator
1289+
f = self.do_create(max_size=2)
1290+
1291+
def it():
1292+
yield b'xy'
1293+
self.assertFalse(f._rolled)
1294+
yield b'z'
1295+
self.assertTrue(f._rolled)
1296+
1297+
f.writelines(it())
1298+
pos = f.seek(0)
1299+
self.assertEqual(pos, 0)
1300+
buf = f.read()
1301+
self.assertEqual(buf, b'xyz')
1302+
1303+
def test_writelines_fast_path(self):
1304+
f = self.do_create(max_size=2)
1305+
f.write(b'abc')
1306+
self.assertTrue(f._rolled)
1307+
1308+
f.writelines([b'd', b'e', b'f'])
1309+
pos = f.seek(0)
1310+
self.assertEqual(pos, 0)
1311+
buf = f.read()
1312+
self.assertEqual(buf, b'abcdef')
1313+
1314+
12871315
def test_writelines_sequential(self):
12881316
# A SpooledTemporaryFile should hold exactly max_size bytes, and roll
12891317
# 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)