Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Lib/_pyio.py
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,8 @@ def readinto1(self, b):
def _readinto(self, b, read1):
if not isinstance(b, memoryview):
b = memoryview(b)
if b.readonly:
raise TypeError("readinto() argument must be read-write bytes-like object")
Comment thread
picnixz marked this conversation as resolved.
b = b.cast('B')

if read1:
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_io/test_bufferedio.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from test import support
from test.support import check_sanitizer, os_helper, threading_helper
from .utils import byteslike, CTestCase, PyTestCase
from .utils import check_readinto_readonly_buffer


import io # C implementation.
Expand Down Expand Up @@ -313,6 +314,10 @@ def test_readinto(self):
self.assertEqual(bufio.readinto(b), 1)
self.assertEqual(b, b"cb")

def test_readinto_readonly_buffer(self):
check_readinto_readonly_buffer(
self, lambda data: self.tp(self.BytesIO(data)), prefill=True)

def test_readinto1(self):
buffer_size = 10
rawio = self.MockRawIO((b"abc", b"de", b"fgh", b"jkl"))
Expand Down
4 changes: 4 additions & 0 deletions Lib/test/test_io/test_memoryio.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import unittest
from test import support
from .utils import check_readinto_readonly_buffer

import gc
import io
Expand Down Expand Up @@ -568,6 +569,9 @@ def test_readinto(self):
memio.readinto(b)
self.assertEqual(b, b"")

def test_readinto_readonly_buffer(self):
check_readinto_readonly_buffer(self, self.ioclass, empty_and_eof=True)

def test_relative_seek(self):
buf = self.buftype("1234567890")
memio = self.ioclass(buf)
Expand Down
25 changes: 25 additions & 0 deletions Lib/test/test_io/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import threading
import time
import unittest
from itertools import product

import io # C implementation of io
import _pyio as pyio # Python implementation of io
Expand All @@ -26,6 +27,30 @@ def byteslike(*pos, **kw):
return obj


def check_readinto_readonly_buffer(testcase, stream_factory, *,
empty_and_eof=False, prefill=False):
data = b"abcdef"
buffers = [b"xxx", memoryview(b"xxx")]
positions = [0, 4]
if empty_and_eof:
buffers.extend([b"", memoryview(b"")])
positions.append(10)
methods = ("readinto", "readinto1")
prefilled = (False, True) if prefill else (False,)
for method, buffer, position, buffered in product(
methods, buffers, positions, prefilled):
with testcase.subTest(method=method, buffer=buffer, position=position,
buffered=buffered):
with stream_factory(data) as stream:
stream.seek(position)
if buffered:
stream.peek(1)
op = getattr(stream, method)
testcase.assertRaises(TypeError, op, buffer)
testcase.assertEqual(stream.tell(), position)
testcase.assertEqual(stream.read(), data[position:])


class MockRawIOWithoutRead:
"""A RawIO implementation without read(), so as to exercise the default
RawIO.read() which calls readinto()."""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The pure Python implementation of :meth:`io.BufferedIOBase.readinto` and
:meth:`~io.BufferedIOBase.readinto1` now rejects read-only buffers before
reading input, without changing the stream position.
Loading