From 61cdfc5baf43a8ffa42184dbc213900b73a3d143 Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Mon, 12 Aug 2024 11:43:44 +0200 Subject: [PATCH 01/22] Add unittest to handle non-blocking read scenarios. - Introduced `test_read_non_blocking` to ensure proper handling of non-blocking reads using `os.pipe()`. - The test verifies that a `BlockingIOError` is raised when attempting to read from a non-blocking pipe with no data. - Updated code to raise `BlockingIOError` with a specific message when `read()` does not return bytes. --- Lib/test/test_io.py | 16 ++++++++++++++++ Modules/_io/textio.c | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 1ca3edac8c8dc9..6fd610c159ba3f 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3919,6 +3919,22 @@ def test_issue35928(self): f.write(res) self.assertEqual(res + f.readline(), 'foo\nbar\n') + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_read_non_blocking(self): + import os + r, w = os.pipe() + try: + os.set_blocking(r, False) + with open(r, 'r') as textfile: + r = None + # Nothing has been written so a non-blocking read raises a BlockingIOError exception. + with self.assertRaises(BlockingIOError): + textfile.read() + finally: + if r is not None: + os.close(r) + os.close(w) + class MemviewBytesIO(io.BytesIO): '''A BytesIO object whose read method returns memoryviews diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index c162d8106ec1fd..9685c3b38881ed 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1992,6 +1992,12 @@ _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n) if (bytes == NULL) goto fail; + if (bytes == Py_None){ + PyErr_SetString(PyExc_BlockingIOError, "read() should return bytes"); + return NULL; + + } + _PyIO_State *state = self->state; if (Py_IS_TYPE(self->decoder, state->PyIncrementalNewlineDecoder_Type)) decoded = _PyIncrementalNewlineDecoder_decode(self->decoder, From 5123ff6bba92005f6a3ca4e4a06161a4ae25732d Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 10:15:20 +0000 Subject: [PATCH 02/22] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst diff --git a/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst b/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst new file mode 100644 index 00000000000000..fcdde3c6801907 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst @@ -0,0 +1 @@ +In _io_TextIOWrapper_read_impl, raise BlockingIOError when read() from a non-blocking pipe does not return bytes. From 7a9037f2cb503cc6531323fc9f44a5cfb5c4b4ba Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Thu, 15 Aug 2024 10:56:23 +0200 Subject: [PATCH 03/22] Update documentation for Text I/O and Buffered Streams to include handling of BlockingIOError in non-blocking I/O scenarios --- Doc/library/io.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 748c49968f505c..4d14a0d5382655 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -64,6 +64,13 @@ In-memory text streams are also available as :class:`StringIO` objects:: f = io.StringIO("some initial text data") +.. note:: + If you are working with a non-blocking stream, be aware that operations on text I/O + objects may raise a `BlockingIOError`. This occurs when the underlying stream is + in non-blocking mode and a read operation cannot be completed immediately, potentially + leading to a `BlockingIOError`. To handle this, ensure your code properly catches and + manages such exceptions when working with non-blocking streams. + The text stream API is described in detail in the documentation of :class:`TextIOBase`. @@ -770,6 +777,11 @@ than raw I/O does. Read and return *size* bytes, or if *size* is not given or negative, until EOF or if the read call would block in non-blocking mode. + .. note:: + When the underlying raw stream is non-blocking, a `BlockingIOError` + may be raised if the read operation cannot be completed immediately. + Ensure proper exception handling in such cases. + .. method:: read1(size=-1, /) Read and return up to *size* bytes with only one call on the raw stream. @@ -779,6 +791,10 @@ than raw I/O does. .. versionchanged:: 3.7 The *size* argument is now optional. + .. note:: + When the underlying raw stream is non-blocking, a `BlockingIOError` + may be raised if the read operation cannot be completed immediately. + Ensure proper exception handling in such cases. .. class:: BufferedWriter(raw, buffer_size=DEFAULT_BUFFER_SIZE) @@ -1007,6 +1023,10 @@ Text I/O .. versionchanged:: 3.10 The *encoding* argument now supports the ``"locale"`` dummy encoding name. + .. note:: + If the underlying raw stream is non-blocking, the `read()` method may raise a + `BlockingIOError` if no data is available immediately. + :class:`TextIOWrapper` provides these data attributes and methods in addition to those from :class:`TextIOBase` and :class:`IOBase`: From c31b0c76d5493761758cd08dbb727a6316f21a85 Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Thu, 15 Aug 2024 16:41:52 +0200 Subject: [PATCH 04/22] Fix double backticks for inline literals. --- Doc/library/io.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 4d14a0d5382655..f19fb49f6132c0 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -66,9 +66,9 @@ In-memory text streams are also available as :class:`StringIO` objects:: .. note:: If you are working with a non-blocking stream, be aware that operations on text I/O - objects may raise a `BlockingIOError`. This occurs when the underlying stream is + objects may raise a ``BlockingIOError``. This occurs when the underlying stream is in non-blocking mode and a read operation cannot be completed immediately, potentially - leading to a `BlockingIOError`. To handle this, ensure your code properly catches and + leading to a `BlockingIOError``. To handle this, ensure your code properly catches and manages such exceptions when working with non-blocking streams. The text stream API is described in detail in the documentation of @@ -778,7 +778,7 @@ than raw I/O does. EOF or if the read call would block in non-blocking mode. .. note:: - When the underlying raw stream is non-blocking, a `BlockingIOError` + When the underlying raw stream is non-blocking, a ``BlockingIOError`` may be raised if the read operation cannot be completed immediately. Ensure proper exception handling in such cases. @@ -792,7 +792,7 @@ than raw I/O does. The *size* argument is now optional. .. note:: - When the underlying raw stream is non-blocking, a `BlockingIOError` + When the underlying raw stream is non-blocking, a ``BlockingIOError`` may be raised if the read operation cannot be completed immediately. Ensure proper exception handling in such cases. @@ -1024,8 +1024,8 @@ Text I/O The *encoding* argument now supports the ``"locale"`` dummy encoding name. .. note:: - If the underlying raw stream is non-blocking, the `read()` method may raise a - `BlockingIOError` if no data is available immediately. + If the underlying raw stream is non-blocking, the ``read()`` method may raise a + ``BlockingIOError`` if no data is available immediately. :class:`TextIOWrapper` provides these data attributes and methods in addition to those from :class:`TextIOBase` and :class:`IOBase`: From ade7db087dc0393008f6c0ba5ab99d65ecd9992f Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Thu, 15 Aug 2024 16:50:40 +0200 Subject: [PATCH 05/22] Add a more meaningful message for the BlockingIOError. --- Modules/_io/textio.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 9685c3b38881ed..d1f18c4c458e44 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1993,7 +1993,10 @@ _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n) goto fail; if (bytes == Py_None){ - PyErr_SetString(PyExc_BlockingIOError, "read() should return bytes"); + PyErr_SetString(PyExc_BlockingIOError, "Unexpected None encountered. This may be due to non-blocking I/O " + "or an issue with the underlying I/O implementation. Please refer to " + "the documentation or ensure that the I/O operation is properly " + "configured."); return NULL; } From 0953ad0da990d0d5484783800672594bc4b84250 Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Thu, 15 Aug 2024 16:57:04 +0200 Subject: [PATCH 06/22] Updated the TextIOWrapper test to use self.io.open() instead of open() to ensure the test runs under both _io and _pyio implementations. Also, explicitly specified 'rt' mode in the open() call for clarity, as the test is focused on TextIO behavior. --- Lib/test/test_io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 6fd610c159ba3f..dadda3f4ee7947 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3925,7 +3925,7 @@ def test_read_non_blocking(self): r, w = os.pipe() try: os.set_blocking(r, False) - with open(r, 'r') as textfile: + with self.io.open(r, 'rt') as textfile: r = None # Nothing has been written so a non-blocking read raises a BlockingIOError exception. with self.assertRaises(BlockingIOError): From 100c4cbda6372150c13779d32aea206ed1651f7b Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Thu, 15 Aug 2024 22:21:56 +0200 Subject: [PATCH 07/22] - Raise `BlockingIOError` in `TextIOWrapper.read()` in `_pyio.py` when `buffer.read()` returns zero bytes. - Fix double backticks in the documentation. --- Doc/library/io.rst | 2 +- Lib/_pyio.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index f19fb49f6132c0..0188adc5b0c2aa 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -68,7 +68,7 @@ In-memory text streams are also available as :class:`StringIO` objects:: If you are working with a non-blocking stream, be aware that operations on text I/O objects may raise a ``BlockingIOError``. This occurs when the underlying stream is in non-blocking mode and a read operation cannot be completed immediately, potentially - leading to a `BlockingIOError``. To handle this, ensure your code properly catches and + leading to a ``BlockingIOError``. To handle this, ensure your code properly catches and manages such exceptions when working with non-blocking streams. The text stream API is described in detail in the documentation of diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 75b5ad1b1a47d2..28b5f7f0c04615 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -2523,9 +2523,14 @@ def read(self, size=None): size = size_index() decoder = self._decoder or self._get_decoder() if size < 0: + + b = self.buffer.read() + if b is None: + raise BlockingIOError + # Read everything. result = (self._get_decoded_chars() + - decoder.decode(self.buffer.read(), final=True)) + decoder.decode(b, final=True)) if self._snapshot is not None: self._set_decoded_chars('') self._snapshot = None From bf5cc2bc66c05c29ba61da4e6d338ef82a4db267 Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Sat, 17 Aug 2024 19:05:25 +0200 Subject: [PATCH 08/22] Update NEWS entry to include references to _io and _pyio for BlockingIOError change Revised the NEWS entry in `MISC/NEWS.d/next/C_API` to specify that the recent change affecting the `BlockingIOError` now applies to both `_io.TextIOWrapper.read()` and `_pyio.TextIOWrapper.read()`. --- .../next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst b/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst index fcdde3c6801907..5e880a04b4d084 100644 --- a/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst +++ b/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst @@ -1 +1 @@ -In _io_TextIOWrapper_read_impl, raise BlockingIOError when read() from a non-blocking pipe does not return bytes. +Reading text from a non-blocking stream with `.read()` may now raise a `BlockingIOError` if the operation cannot immediately return bytes. From fc02b20345c922e926c5517035441d6d4919eeea Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Sat, 17 Aug 2024 19:17:45 +0200 Subject: [PATCH 09/22] Align documentation and code with Python style practices - Adapted documentation to reflect common Python style practices. - Refactored code to use a clearer variable name. --- Doc/library/io.rst | 14 ++++++-------- Lib/_pyio.py | 6 +++--- Modules/_io/textio.c | 4 +--- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 0188adc5b0c2aa..62604a9baa2360 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -65,11 +65,10 @@ In-memory text streams are also available as :class:`StringIO` objects:: f = io.StringIO("some initial text data") .. note:: - If you are working with a non-blocking stream, be aware that operations on text I/O - objects may raise a ``BlockingIOError``. This occurs when the underlying stream is - in non-blocking mode and a read operation cannot be completed immediately, potentially - leading to a ``BlockingIOError``. To handle this, ensure your code properly catches and - manages such exceptions when working with non-blocking streams. + When working with a non-blocking stream, be aware that operations on text I/O objects + might raise a :exc:`BlockingIOError` if the stream cannot perform a read operation + immediately. + The text stream API is described in detail in the documentation of :class:`TextIOBase`. @@ -778,9 +777,8 @@ than raw I/O does. EOF or if the read call would block in non-blocking mode. .. note:: - When the underlying raw stream is non-blocking, a ``BlockingIOError`` - may be raised if the read operation cannot be completed immediately. - Ensure proper exception handling in such cases. + When the underlying raw stream is non-blocking, a :exc:`BlockingIOError` + may be raised if a read operation cannot be completed immediately. .. method:: read1(size=-1, /) diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 28b5f7f0c04615..161e15ad93e2c6 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -2524,13 +2524,13 @@ def read(self, size=None): decoder = self._decoder or self._get_decoder() if size < 0: - b = self.buffer.read() - if b is None: + chunk = self.buffer.read() + if chunk is None: raise BlockingIOError # Read everything. result = (self._get_decoded_chars() + - decoder.decode(b, final=True)) + decoder.decode(chunk, final=True)) if self._snapshot is not None: self._set_decoded_chars('') self._snapshot = None diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index d1f18c4c458e44..62e0d3a4fcebf6 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1994,9 +1994,7 @@ _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n) if (bytes == Py_None){ PyErr_SetString(PyExc_BlockingIOError, "Unexpected None encountered. This may be due to non-blocking I/O " - "or an issue with the underlying I/O implementation. Please refer to " - "the documentation or ensure that the I/O operation is properly " - "configured."); + "or an issue with the underlying I/O implementation."); return NULL; } From c2d14e3aa61f8dec323e8a429073a45346543bad Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Sat, 17 Aug 2024 19:30:44 +0200 Subject: [PATCH 10/22] Fix news message for sphinx-lint --- .../next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst b/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst index 5e880a04b4d084..569d34d8d687d6 100644 --- a/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst +++ b/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst @@ -1 +1 @@ -Reading text from a non-blocking stream with `.read()` may now raise a `BlockingIOError` if the operation cannot immediately return bytes. +Reading text from a non-blocking stream with :meth:`.read()` may now raise a :exc:`BlockingIOError` if the operation cannot immediately return bytes. From a735fff590b02a3b05708690cdae13b711b30734 Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Sat, 17 Aug 2024 19:45:32 +0200 Subject: [PATCH 11/22] Fix news message for sphinx-lint --- .../next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst b/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst index 569d34d8d687d6..c4da39e7f1d8e6 100644 --- a/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst +++ b/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst @@ -1 +1 @@ -Reading text from a non-blocking stream with :meth:`.read()` may now raise a :exc:`BlockingIOError` if the operation cannot immediately return bytes. +Reading text from a non-blocking stream with `read` may now raise a :exc:`BlockingIOError` if the operation cannot immediately return bytes. From 5f1cbae1a2300a587c74a0bc603c2251dd3b3d35 Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Sat, 17 Aug 2024 19:48:42 +0200 Subject: [PATCH 12/22] Fix news message for sphinx-lint --- .../next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst b/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst index c4da39e7f1d8e6..9d6b2e0c565623 100644 --- a/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst +++ b/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst @@ -1 +1 @@ -Reading text from a non-blocking stream with `read` may now raise a :exc:`BlockingIOError` if the operation cannot immediately return bytes. +Reading text from a non-blocking stream with ``read`` may now raise a :exc:`BlockingIOError` if the operation cannot immediately return bytes. From cabdf39c25984ac07f1d8b540549e3a3f78bf5ed Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Sat, 17 Aug 2024 20:02:58 +0200 Subject: [PATCH 13/22] Make notes more consistent --- Doc/library/io.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 62604a9baa2360..5808a5761f6837 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -790,9 +790,8 @@ than raw I/O does. The *size* argument is now optional. .. note:: - When the underlying raw stream is non-blocking, a ``BlockingIOError`` - may be raised if the read operation cannot be completed immediately. - Ensure proper exception handling in such cases. + When the underlying raw stream is non-blocking, a :exc:`BlockingIOError` + may be raised if a read operation cannot be completed immediately. .. class:: BufferedWriter(raw, buffer_size=DEFAULT_BUFFER_SIZE) @@ -1022,8 +1021,8 @@ Text I/O The *encoding* argument now supports the ``"locale"`` dummy encoding name. .. note:: - If the underlying raw stream is non-blocking, the ``read()`` method may raise a - ``BlockingIOError`` if no data is available immediately. + When the underlying raw stream is non-blocking, a :exc:`BlockingIOError` + may be raised if a read operation cannot be completed immediately. :class:`TextIOWrapper` provides these data attributes and methods in addition to those from :class:`TextIOBase` and :class:`IOBase`: From 8286a0ad06fc8589be4127430a781d1fb1958132 Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Thu, 19 Sep 2024 11:22:34 +0200 Subject: [PATCH 14/22] Align BlockingIOError message in _pyio.py with the C implementation in textio.c --- Lib/_pyio.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 161e15ad93e2c6..9be775a2ac4b75 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -2526,7 +2526,8 @@ def read(self, size=None): chunk = self.buffer.read() if chunk is None: - raise BlockingIOError + raise BlockingIOError("Unexpected None encountered. This may be due to non-blocking I/O or an issue " + "with the underlying I/O implementation.") # Read everything. result = (self._get_decoded_chars() + From 86903970f9ac83025e71f5f842ea26beee6e7cf6 Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Wed, 27 Nov 2024 11:37:00 +0100 Subject: [PATCH 15/22] Add blank line after note. --- Doc/library/io.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 5808a5761f6837..2924da935e2c86 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -65,6 +65,7 @@ In-memory text streams are also available as :class:`StringIO` objects:: f = io.StringIO("some initial text data") .. note:: + When working with a non-blocking stream, be aware that operations on text I/O objects might raise a :exc:`BlockingIOError` if the stream cannot perform a read operation immediately. From 2f7fbb267bfa71a270d37118617a9a75b95913c7 Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Wed, 27 Nov 2024 11:38:02 +0100 Subject: [PATCH 16/22] Remove unnecessary blank lines. --- Lib/_pyio.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 9be775a2ac4b75..cdf6465eab3057 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -2523,12 +2523,10 @@ def read(self, size=None): size = size_index() decoder = self._decoder or self._get_decoder() if size < 0: - chunk = self.buffer.read() if chunk is None: raise BlockingIOError("Unexpected None encountered. This may be due to non-blocking I/O or an issue " "with the underlying I/O implementation.") - # Read everything. result = (self._get_decoded_chars() + decoder.decode(chunk, final=True)) From dbd807dbf7fccbe4174382a7eb97be2fda667454 Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Wed, 27 Nov 2024 11:48:40 +0100 Subject: [PATCH 17/22] Keep the error message simple. --- Doc/library/io.rst | 4 ++-- Lib/_pyio.py | 3 +-- Modules/_io/textio.c | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 2924da935e2c86..5b09e94113a5da 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -66,8 +66,8 @@ In-memory text streams are also available as :class:`StringIO` objects:: .. note:: - When working with a non-blocking stream, be aware that operations on text I/O objects - might raise a :exc:`BlockingIOError` if the stream cannot perform a read operation + When working with a non-blocking stream, be aware that read operations on text I/O objects + might raise a :exc:`BlockingIOError` if the stream cannot perform the operation immediately. diff --git a/Lib/_pyio.py b/Lib/_pyio.py index cdf6465eab3057..3d88b176c31ed7 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -2525,8 +2525,7 @@ def read(self, size=None): if size < 0: chunk = self.buffer.read() if chunk is None: - raise BlockingIOError("Unexpected None encountered. This may be due to non-blocking I/O or an issue " - "with the underlying I/O implementation.") + raise BlockingIOError("Read returned None.") # Read everything. result = (self._get_decoded_chars() + decoder.decode(chunk, final=True)) diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 62e0d3a4fcebf6..73f1b451292694 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1993,8 +1993,7 @@ _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n) goto fail; if (bytes == Py_None){ - PyErr_SetString(PyExc_BlockingIOError, "Unexpected None encountered. This may be due to non-blocking I/O " - "or an issue with the underlying I/O implementation."); + PyErr_SetString(PyExc_BlockingIOError, "Read returned None."); return NULL; } From 1ac9096d6080c36c180df652edf9bd542d000c14 Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Wed, 27 Nov 2024 11:49:49 +0100 Subject: [PATCH 18/22] Release the reference before throwing the exception. --- Modules/_io/textio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 73f1b451292694..4eedb71db702aa 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1993,6 +1993,7 @@ _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n) goto fail; if (bytes == Py_None){ + Py_DECREF(bytes); PyErr_SetString(PyExc_BlockingIOError, "Read returned None."); return NULL; From 7d05cc3be99655bf133092d3fffc7f2f6cd75e8c Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Wed, 27 Nov 2024 11:57:10 +0100 Subject: [PATCH 19/22] Add blank line after notes. --- Doc/library/io.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 5b09e94113a5da..638e729be6e46a 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -778,6 +778,7 @@ than raw I/O does. EOF or if the read call would block in non-blocking mode. .. note:: + When the underlying raw stream is non-blocking, a :exc:`BlockingIOError` may be raised if a read operation cannot be completed immediately. @@ -791,6 +792,7 @@ than raw I/O does. The *size* argument is now optional. .. note:: + When the underlying raw stream is non-blocking, a :exc:`BlockingIOError` may be raised if a read operation cannot be completed immediately. @@ -1022,6 +1024,7 @@ Text I/O The *encoding* argument now supports the ``"locale"`` dummy encoding name. .. note:: + When the underlying raw stream is non-blocking, a :exc:`BlockingIOError` may be raised if a read operation cannot be completed immediately. From 9f1c5f4763bcc26df6c0cd5abafc13c229f5d691 Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Wed, 27 Nov 2024 12:52:28 +0100 Subject: [PATCH 20/22] Remove unnecessary blank lines. --- Doc/library/io.rst | 1 - Modules/_io/textio.c | 1 - 2 files changed, 2 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 638e729be6e46a..d6a1c57272292c 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -70,7 +70,6 @@ In-memory text streams are also available as :class:`StringIO` objects:: might raise a :exc:`BlockingIOError` if the stream cannot perform the operation immediately. - The text stream API is described in detail in the documentation of :class:`TextIOBase`. diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 4eedb71db702aa..60a526bef20619 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1996,7 +1996,6 @@ _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n) Py_DECREF(bytes); PyErr_SetString(PyExc_BlockingIOError, "Read returned None."); return NULL; - } _PyIO_State *state = self->state; From 317d40252a0c40aaaaefa4d05e67a5d7496e39bd Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Fri, 29 Nov 2024 12:42:34 +0100 Subject: [PATCH 21/22] Added what's new entry. Added entry in ACKS. --- Doc/whatsnew/3.14.rst | 6 ++++++ Misc/ACKS | 1 + 2 files changed, 7 insertions(+) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 3f53f6b940027b..2cbc95e825452f 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -117,6 +117,12 @@ Added support for converting any objects that have the :meth:`!as_integer_ratio` method to a :class:`~fractions.Fraction`. (Contributed by Serhiy Storchaka in :gh:`82017`.) +io +___ + +* Reading text from a non-blocking stream with ``read`` may now raise a :exc:`BlockingIOError` if the operation cannot immediately return bytes. +(Contributed by Giovanni Siragusa in :gh:`109523`.) + json ---- diff --git a/Misc/ACKS b/Misc/ACKS index b031eb7c11f73f..fa19f9bc15f208 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1726,6 +1726,7 @@ Ng Pheng Siong Yann Sionneau George Sipe J. Sipprell +Giovanni Siragusa Ngalim Siregar Kragen Sitaker Kaartic Sivaraam From 8e4e661567c501eae6a5d0902a045a12e9f5c56c Mon Sep 17 00:00:00 2001 From: Giovanni Siragusa Date: Fri, 29 Nov 2024 13:15:41 +0100 Subject: [PATCH 22/22] Update Doc/whatsnew/3.14.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix missing indents and other syntax issues. Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/whatsnew/3.14.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 2cbc95e825452f..7ba6ad7ea7bf28 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -117,11 +117,14 @@ Added support for converting any objects that have the :meth:`!as_integer_ratio` method to a :class:`~fractions.Fraction`. (Contributed by Serhiy Storchaka in :gh:`82017`.) + io -___ +-- + +* Reading text from a non-blocking stream with ``read`` may now raise a + :exc:`BlockingIOError` if the operation cannot immediately return bytes. + (Contributed by Giovanni Siragusa in :gh:`109523`.) -* Reading text from a non-blocking stream with ``read`` may now raise a :exc:`BlockingIOError` if the operation cannot immediately return bytes. -(Contributed by Giovanni Siragusa in :gh:`109523`.) json ----