]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-141863: use `bytearray.take_bytes` in asyncio streams for better performance ...
authorCody Maloney <cmaloney@users.noreply.github.com>
Mon, 24 Nov 2025 15:36:53 +0000 (07:36 -0800)
committerGitHub <noreply@github.com>
Mon, 24 Nov 2025 15:36:53 +0000 (21:06 +0530)
Lib/asyncio/streams.py
Misc/NEWS.d/next/Library/2025-11-22-16-33-48.gh-issue-141863.4PLhnv.rst [new file with mode: 0644]

index 59e22f523a85c5f4e7039de22cd19eb3980ab5a9..d2db1a930c2ad2bac29600e4f874cf4204f128eb 100644 (file)
@@ -667,8 +667,7 @@ class StreamReader:
             # adds data which makes separator be found. That's why we check for
             # EOF *after* inspecting the buffer.
             if self._eof:
-                chunk = bytes(self._buffer)
-                self._buffer.clear()
+                chunk = self._buffer.take_bytes()
                 raise exceptions.IncompleteReadError(chunk, None)
 
             # _wait_for_data() will resume reading if stream was paused.
@@ -678,10 +677,9 @@ class StreamReader:
             raise exceptions.LimitOverrunError(
                 'Separator is found, but chunk is longer than limit', match_start)
 
-        chunk = self._buffer[:match_end]
-        del self._buffer[:match_end]
+        chunk = self._buffer.take_bytes(match_end)
         self._maybe_resume_transport()
-        return bytes(chunk)
+        return chunk
 
     async def read(self, n=-1):
         """Read up to `n` bytes from the stream.
@@ -716,20 +714,16 @@ class StreamReader:
             # collect everything in self._buffer, but that would
             # deadlock if the subprocess sends more than self.limit
             # bytes.  So just call self.read(self._limit) until EOF.
-            blocks = []
-            while True:
-                block = await self.read(self._limit)
-                if not block:
-                    break
-                blocks.append(block)
-            return b''.join(blocks)
+            joined = bytearray()
+            while block := await self.read(self._limit):
+                joined += block
+            return joined.take_bytes()
 
         if not self._buffer and not self._eof:
             await self._wait_for_data('read')
 
         # This will work right even if buffer is less than n bytes
-        data = bytes(memoryview(self._buffer)[:n])
-        del self._buffer[:n]
+        data = self._buffer.take_bytes(min(len(self._buffer), n))
 
         self._maybe_resume_transport()
         return data
@@ -760,18 +754,12 @@ class StreamReader:
 
         while len(self._buffer) < n:
             if self._eof:
-                incomplete = bytes(self._buffer)
-                self._buffer.clear()
+                incomplete = self._buffer.take_bytes()
                 raise exceptions.IncompleteReadError(incomplete, n)
 
             await self._wait_for_data('readexactly')
 
-        if len(self._buffer) == n:
-            data = bytes(self._buffer)
-            self._buffer.clear()
-        else:
-            data = bytes(memoryview(self._buffer)[:n])
-            del self._buffer[:n]
+        data = self._buffer.take_bytes(n)
         self._maybe_resume_transport()
         return data
 
diff --git a/Misc/NEWS.d/next/Library/2025-11-22-16-33-48.gh-issue-141863.4PLhnv.rst b/Misc/NEWS.d/next/Library/2025-11-22-16-33-48.gh-issue-141863.4PLhnv.rst
new file mode 100644 (file)
index 0000000..910ab98
--- /dev/null
@@ -0,0 +1,2 @@
+Update :ref:`asyncio-streams` to use :func:`bytearray.take_bytes` for a over
+10% performance improvement on pyperformance asyncio_tcp benchmark.