return self.read(size)
def write(self, b):
- if self.closed:
- raise ValueError("write to closed file")
if isinstance(b, str):
raise TypeError("can't write str to binary stream")
with memoryview(b) as view:
n = view.nbytes # Size of any bytes-like object
+ if self.closed:
+ raise ValueError("write to closed file")
if n == 0:
return 0
self.ioclass(initial_bytes=buf)
self.assertRaises(TypeError, self.ioclass, buf, foo=None)
+ def test_write_concurrent_close(self):
+ class B:
+ def __buffer__(self, flags):
+ memio.close()
+ return memoryview(b"A")
+
+ memio = self.ioclass()
+ self.assertRaises(ValueError, memio.write, B())
+
+ # Prevent crashes when memio.write() or memio.writelines()
+ # concurrently mutates (e.g., closes or exports) 'memio'.
+ # See: https://github.com/python/cpython/issues/143378.
+
+ def test_writelines_concurrent_close(self):
+ class B:
+ def __buffer__(self, flags):
+ memio.close()
+ return memoryview(b"A")
+
+ memio = self.ioclass()
+ self.assertRaises(ValueError, memio.writelines, [B()])
+
+ def test_write_concurrent_export(self):
+ class B:
+ buf = None
+ def __buffer__(self, flags):
+ self.buf = memio.getbuffer()
+ return memoryview(b"A")
+
+ memio = self.ioclass()
+ self.assertRaises(BufferError, memio.write, B())
+
+ def test_writelines_concurrent_export(self):
+ class B:
+ buf = None
+ def __buffer__(self, flags):
+ self.buf = memio.getbuffer()
+ return memoryview(b"A")
+
+ memio = self.ioclass()
+ self.assertRaises(BufferError, memio.writelines, [B()])
+
class TextIOTestMixin:
{
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
- if (check_closed(self)) {
- return -1;
- }
- if (check_exports(self)) {
- return -1;
- }
-
Py_buffer buf;
+ Py_ssize_t len;
if (PyObject_GetBuffer(b, &buf, PyBUF_CONTIG_RO) < 0) {
return -1;
}
- Py_ssize_t len = buf.len;
+
+ if (check_closed(self) || check_exports(self)) {
+ len = -1;
+ goto done;
+ }
+
+ len = buf.len;
if (len == 0) {
goto done;
}