From 6c500375d673fdf11833e9cf385d4f125367774c Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Fri, 7 Nov 2025 12:26:04 -0800 Subject: [PATCH 1/2] gh-85524: Raise "UnsupportedOperation" on FileIO.readall io.UnsupportedOperation is a subclass of OSError and recommended by io.IOBase for this case; matches other read methods on io.FileIO. --- Lib/test/test_io/test_general.py | 1 + .../2025-11-07-12-25-46.gh-issue-85524.9SWFIC.rst | 3 +++ Modules/_io/clinic/fileio.c.h | 14 +++++++++----- Modules/_io/fileio.c | 13 ++++++++++--- 4 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-11-07-12-25-46.gh-issue-85524.9SWFIC.rst diff --git a/Lib/test/test_io/test_general.py b/Lib/test/test_io/test_general.py index a1cdd6876c2892..f0677b01ea5ce1 100644 --- a/Lib/test/test_io/test_general.py +++ b/Lib/test/test_io/test_general.py @@ -125,6 +125,7 @@ def test_invalid_operations(self): self.assertRaises(exc, fp.readline) with self.open(os_helper.TESTFN, "wb", buffering=0) as fp: self.assertRaises(exc, fp.read) + self.assertRaises(exc, fp.readall) self.assertRaises(exc, fp.readline) with self.open(os_helper.TESTFN, "rb", buffering=0) as fp: self.assertRaises(exc, fp.write, b"blah") diff --git a/Misc/NEWS.d/next/Library/2025-11-07-12-25-46.gh-issue-85524.9SWFIC.rst b/Misc/NEWS.d/next/Library/2025-11-07-12-25-46.gh-issue-85524.9SWFIC.rst new file mode 100644 index 00000000000000..a00204287cef4d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-11-07-12-25-46.gh-issue-85524.9SWFIC.rst @@ -0,0 +1,3 @@ +Update :meth:`io.FileIO.readall` to follow :class:`io.IOBase` guidelines and +raise :exc:`io.UnsupportedOperation`, a subclass of :exc:`OSError`, when a +file is in "w" mode rather than :exc:`OSError` diff --git a/Modules/_io/clinic/fileio.c.h b/Modules/_io/clinic/fileio.c.h index 04870b1c890361..96c31ce8d6f415 100644 --- a/Modules/_io/clinic/fileio.c.h +++ b/Modules/_io/clinic/fileio.c.h @@ -277,15 +277,19 @@ PyDoc_STRVAR(_io_FileIO_readall__doc__, "data is available (EAGAIN is returned before bytes are read) returns None."); #define _IO_FILEIO_READALL_METHODDEF \ - {"readall", (PyCFunction)_io_FileIO_readall, METH_NOARGS, _io_FileIO_readall__doc__}, + {"readall", _PyCFunction_CAST(_io_FileIO_readall), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io_FileIO_readall__doc__}, static PyObject * -_io_FileIO_readall_impl(fileio *self); +_io_FileIO_readall_impl(fileio *self, PyTypeObject *cls); static PyObject * -_io_FileIO_readall(PyObject *self, PyObject *Py_UNUSED(ignored)) +_io_FileIO_readall(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { - return _io_FileIO_readall_impl((fileio *)self); + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "readall() takes no arguments"); + return NULL; + } + return _io_FileIO_readall_impl((fileio *)self, cls); } PyDoc_STRVAR(_io_FileIO_read__doc__, @@ -543,4 +547,4 @@ _io_FileIO_isatty(PyObject *self, PyObject *Py_UNUSED(ignored)) #ifndef _IO_FILEIO_TRUNCATE_METHODDEF #define _IO_FILEIO_TRUNCATE_METHODDEF #endif /* !defined(_IO_FILEIO_TRUNCATE_METHODDEF) */ -/*[clinic end generated code: output=1902fac9e39358aa input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2e48f3df2f189170 input=a9049054013a1b77]*/ diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index b84c1bd3e22c18..3e9b5892c95145 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -728,6 +728,9 @@ new_buffersize(fileio *self, size_t currentsize) @permit_long_docstring_body _io.FileIO.readall + cls: defining_class + / + Read all data from the file, returned as bytes. Reads until either there is an error or read() returns size 0 (indicates EOF). @@ -738,8 +741,8 @@ data is available (EAGAIN is returned before bytes are read) returns None. [clinic start generated code]*/ static PyObject * -_io_FileIO_readall_impl(fileio *self) -/*[clinic end generated code: output=faa0292b213b4022 input=10d8b2ec403302dc]*/ +_io_FileIO_readall_impl(fileio *self, PyTypeObject *cls) +/*[clinic end generated code: output=d546737ec895c462 input=cecda40bf9961299]*/ { Py_off_t pos, end; PyBytesWriter *writer; @@ -750,6 +753,10 @@ _io_FileIO_readall_impl(fileio *self) if (self->fd < 0) { return err_closed(); } + if (!self->readable) { + _PyIO_State *state = get_io_state_by_cls(cls); + return err_mode(state, "reading"); + } if (self->stat_atopen != NULL && self->stat_atopen->st_size < _PY_READ_MAX) { end = (Py_off_t)self->stat_atopen->st_size; @@ -873,7 +880,7 @@ _io_FileIO_read_impl(fileio *self, PyTypeObject *cls, Py_ssize_t size) } if (size < 0) - return _io_FileIO_readall_impl(self); + return _io_FileIO_readall_impl(self, cls); if (size > _PY_READ_MAX) { size = _PY_READ_MAX; From df53d48aab78742c4601e17c590f5a155d4616ce Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Fri, 7 Nov 2025 12:34:36 -0800 Subject: [PATCH 2/2] Fix doc reference --- .../Library/2025-11-07-12-25-46.gh-issue-85524.9SWFIC.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2025-11-07-12-25-46.gh-issue-85524.9SWFIC.rst b/Misc/NEWS.d/next/Library/2025-11-07-12-25-46.gh-issue-85524.9SWFIC.rst index a00204287cef4d..3e4fd1a5897b04 100644 --- a/Misc/NEWS.d/next/Library/2025-11-07-12-25-46.gh-issue-85524.9SWFIC.rst +++ b/Misc/NEWS.d/next/Library/2025-11-07-12-25-46.gh-issue-85524.9SWFIC.rst @@ -1,3 +1,3 @@ -Update :meth:`io.FileIO.readall` to follow :class:`io.IOBase` guidelines and -raise :exc:`io.UnsupportedOperation`, a subclass of :exc:`OSError`, when a -file is in "w" mode rather than :exc:`OSError` +Update ``io.FileIO.readall``, an implementation of :meth:`io.RawIOBase.readall`, +to follow :class:`io.IOBase` guidelines and raise :exc:`io.UnsupportedOperation` +when a file is in "w" mode rather than :exc:`OSError`