Skip to content

Commit 2cfcaf5

Browse files
authored
gh-98999: Raise ValueError in _pyio on closed buffers (gh-99009)
1 parent 26720ff commit 2cfcaf5

File tree

3 files changed

+33
-12
lines changed

3 files changed

+33
-12
lines changed

Lib/_pyio.py

+5
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,7 @@ def peek(self, size=0):
11291129
do at most one raw read to satisfy it. We never return more
11301130
than self.buffer_size.
11311131
"""
1132+
self._checkClosed("peek of closed file")
11321133
with self._read_lock:
11331134
return self._peek_unlocked(size)
11341135

@@ -1147,6 +1148,7 @@ def read1(self, size=-1):
11471148
"""Reads up to size bytes, with at most one read() system call."""
11481149
# Returns up to size bytes. If at least one byte is buffered, we
11491150
# only return buffered bytes. Otherwise, we do one raw read.
1151+
self._checkClosed("read of closed file")
11501152
if size < 0:
11511153
size = self.buffer_size
11521154
if size == 0:
@@ -1164,6 +1166,8 @@ def read1(self, size=-1):
11641166
def _readinto(self, buf, read1):
11651167
"""Read data into *buf* with at most one system call."""
11661168

1169+
self._checkClosed("readinto of closed file")
1170+
11671171
# Need to create a memoryview object of type 'b', otherwise
11681172
# we may not be able to assign bytes to it, and slicing it
11691173
# would create a new object.
@@ -1213,6 +1217,7 @@ def tell(self):
12131217
def seek(self, pos, whence=0):
12141218
if whence not in valid_seek_flags:
12151219
raise ValueError("invalid whence value")
1220+
self._checkClosed("seek of closed file")
12161221
with self._read_lock:
12171222
if whence == 1:
12181223
pos -= len(self._read_buf) - self._read_pos

Lib/test/test_io.py

+26-12
Original file line numberDiff line numberDiff line change
@@ -1531,11 +1531,25 @@ def test_no_extraneous_read(self):
15311531

15321532
def test_read_on_closed(self):
15331533
# Issue #23796
1534-
b = io.BufferedReader(io.BytesIO(b"12"))
1534+
b = self.BufferedReader(self.BytesIO(b"12"))
15351535
b.read(1)
15361536
b.close()
1537-
self.assertRaises(ValueError, b.peek)
1538-
self.assertRaises(ValueError, b.read1, 1)
1537+
with self.subTest('peek'):
1538+
self.assertRaises(ValueError, b.peek)
1539+
with self.subTest('read1'):
1540+
self.assertRaises(ValueError, b.read1, 1)
1541+
with self.subTest('read'):
1542+
self.assertRaises(ValueError, b.read)
1543+
with self.subTest('readinto'):
1544+
self.assertRaises(ValueError, b.readinto, bytearray())
1545+
with self.subTest('readinto1'):
1546+
self.assertRaises(ValueError, b.readinto1, bytearray())
1547+
with self.subTest('flush'):
1548+
self.assertRaises(ValueError, b.flush)
1549+
with self.subTest('truncate'):
1550+
self.assertRaises(ValueError, b.truncate)
1551+
with self.subTest('seek'):
1552+
self.assertRaises(ValueError, b.seek, 0)
15391553

15401554
def test_truncate_on_read_only(self):
15411555
rawio = self.MockFileIO(b"abc")
@@ -1593,18 +1607,18 @@ def test_garbage_collection(self):
15931607
def test_args_error(self):
15941608
# Issue #17275
15951609
with self.assertRaisesRegex(TypeError, "BufferedReader"):
1596-
self.tp(io.BytesIO(), 1024, 1024, 1024)
1610+
self.tp(self.BytesIO(), 1024, 1024, 1024)
15971611

15981612
def test_bad_readinto_value(self):
1599-
rawio = io.BufferedReader(io.BytesIO(b"12"))
1613+
rawio = self.tp(self.BytesIO(b"12"))
16001614
rawio.readinto = lambda buf: -1
16011615
bufio = self.tp(rawio)
16021616
with self.assertRaises(OSError) as cm:
16031617
bufio.readline()
16041618
self.assertIsNone(cm.exception.__cause__)
16051619

16061620
def test_bad_readinto_type(self):
1607-
rawio = io.BufferedReader(io.BytesIO(b"12"))
1621+
rawio = self.tp(self.BytesIO(b"12"))
16081622
rawio.readinto = lambda buf: b''
16091623
bufio = self.tp(rawio)
16101624
with self.assertRaises(OSError) as cm:
@@ -1747,7 +1761,7 @@ def test_write_non_blocking(self):
17471761
self.assertTrue(s.startswith(b"01234567A"), s)
17481762

17491763
def test_write_and_rewind(self):
1750-
raw = io.BytesIO()
1764+
raw = self.BytesIO()
17511765
bufio = self.tp(raw, 4)
17521766
self.assertEqual(bufio.write(b"abcdef"), 6)
17531767
self.assertEqual(bufio.tell(), 6)
@@ -1957,7 +1971,7 @@ def test_garbage_collection(self):
19571971
def test_args_error(self):
19581972
# Issue #17275
19591973
with self.assertRaisesRegex(TypeError, "BufferedWriter"):
1960-
self.tp(io.BytesIO(), 1024, 1024, 1024)
1974+
self.tp(self.BytesIO(), 1024, 1024, 1024)
19611975

19621976

19631977
class PyBufferedWriterTest(BufferedWriterTest):
@@ -2433,7 +2447,7 @@ def test_garbage_collection(self):
24332447
def test_args_error(self):
24342448
# Issue #17275
24352449
with self.assertRaisesRegex(TypeError, "BufferedRandom"):
2436-
self.tp(io.BytesIO(), 1024, 1024, 1024)
2450+
self.tp(self.BytesIO(), 1024, 1024, 1024)
24372451

24382452

24392453
class PyBufferedRandomTest(BufferedRandomTest):
@@ -3465,7 +3479,7 @@ def test_illegal_encoder(self):
34653479
# encode() is invalid shouldn't cause an assertion failure.
34663480
rot13 = codecs.lookup("rot13")
34673481
with support.swap_attr(rot13, '_is_text_encoding', True):
3468-
t = io.TextIOWrapper(io.BytesIO(b'foo'), encoding="rot13")
3482+
t = self.TextIOWrapper(self.BytesIO(b'foo'), encoding="rot13")
34693483
self.assertRaises(TypeError, t.write, 'bar')
34703484

34713485
def test_illegal_decoder(self):
@@ -3571,7 +3585,7 @@ def seekable(self): return True
35713585
t = self.TextIOWrapper(F(), encoding='utf-8')
35723586

35733587
def test_reconfigure_locale(self):
3574-
wrapper = io.TextIOWrapper(io.BytesIO(b"test"))
3588+
wrapper = self.TextIOWrapper(self.BytesIO(b"test"))
35753589
wrapper.reconfigure(encoding="locale")
35763590

35773591
def test_reconfigure_encoding_read(self):
@@ -3741,7 +3755,7 @@ def test_garbage_collection(self):
37413755
# all data to disk.
37423756
# The Python version has __del__, so it ends in gc.garbage instead.
37433757
with warnings_helper.check_warnings(('', ResourceWarning)):
3744-
rawio = io.FileIO(os_helper.TESTFN, "wb")
3758+
rawio = self.FileIO(os_helper.TESTFN, "wb")
37453759
b = self.BufferedWriter(rawio)
37463760
t = self.TextIOWrapper(b, encoding="ascii")
37473761
t.write("456def")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Now :mod:`_pyio` is consistent with :mod:`_io` in raising ``ValueError``
2+
when executing methods over closed buffers.

0 commit comments

Comments
 (0)