Thread View
j: Next unread message
k: Previous unread message
j a: Jump to all threads
j l: Jump to MailingList overview
https://github.com/python/cpython/commit/0e57600bb7064225a91eb490cf8121b90d…
commit: 0e57600bb7064225a91eb490cf8121b90d0c7cc7
branch: 3.13
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: picnixz <10796600+picnixz(a)users.noreply.github.com>
date: 2026-03-01T00:20:20+01:00
summary:
[3.13] gh-145269: simplify bisect.bisect doc example (GH-145270) (#145368)
gh-145269: simplify bisect.bisect doc example (GH-145270)
---------
(cherry picked from commit fdb4b3527f356a84bc00ca32516181016400e567)
Co-authored-by: Nathan Goldbaum <nathan.goldbaum(a)gmail.com>
Co-authored-by: Pieter Eendebak <pieter.eendebak(a)gmail.com>
files:
M Doc/library/bisect.rst
M Lib/test/test_bisect.py
diff --git a/Doc/library/bisect.rst b/Doc/library/bisect.rst
index d5ec4212c1f9f4..96d8b54e7d2bbd 100644
--- a/Doc/library/bisect.rst
+++ b/Doc/library/bisect.rst
@@ -203,9 +203,9 @@ example uses :py:func:`~bisect.bisect` to look up a letter grade for an exam sco
based on a set of ordered numeric breakpoints: 90 and up is an 'A', 80 to 89 is
a 'B', and so on::
- >>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
- ... i = bisect(breakpoints, score)
- ... return grades[i]
+ >>> def grade(score)
+ ... i = bisect([60, 70, 80, 90], score)
+ ... return "FDCBA"[i]
...
>>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
['F', 'A', 'C', 'C', 'B', 'A', 'A']
diff --git a/Lib/test/test_bisect.py b/Lib/test/test_bisect.py
index 97204d4cad3871..a7e1f533ff2adc 100644
--- a/Lib/test/test_bisect.py
+++ b/Lib/test/test_bisect.py
@@ -391,9 +391,9 @@ class TestErrorHandlingC(TestErrorHandling, unittest.TestCase):
class TestDocExample:
def test_grades(self):
- def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
- i = self.module.bisect(breakpoints, score)
- return grades[i]
+ def grade(score):
+ i = self.module.bisect([60, 70, 80, 90], score)
+ return "FDCBA"[i]
result = [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
self.assertEqual(result, ['F', 'A', 'C', 'C', 'B', 'A', 'A'])
https://github.com/python/cpython/commit/a1ec7467874207957519bb53e16efdaa7e…
commit: a1ec7467874207957519bb53e16efdaa7e03cb5b
branch: main
author: Zachary Ware <zach(a)python.org>
committer: zware <zachary.ware(a)gmail.com>
date: 2026-02-28T22:26:47Z
summary:
gh-144551: Update iOS builds to use OpenSSL 3.5.5 (GH-145372)
files:
M Apple/__main__.py
diff --git a/Apple/__main__.py b/Apple/__main__.py
index 253bdfaab5520c..3261f368a88fc0 100644
--- a/Apple/__main__.py
+++ b/Apple/__main__.py
@@ -316,7 +316,7 @@ def unpack_deps(
for name_ver in [
"BZip2-1.0.8-2",
"libFFI-3.4.7-2",
- "OpenSSL-3.0.19-1",
+ "OpenSSL-3.5.5-1",
"XZ-5.6.4-2",
"mpdecimal-4.0.0-2",
"zstd-1.5.7-1",
https://github.com/python/cpython/commit/50c2e23f69943a6f70d98c4d0bcf1ac37b…
commit: 50c2e23f69943a6f70d98c4d0bcf1ac37bcaf0d3
branch: main
author: Zachary Ware <zach(a)python.org>
committer: zware <zachary.ware(a)gmail.com>
date: 2026-02-28T16:14:46-06:00
summary:
gh-144551: Update Android builds to use OpenSSL 3.5.5 (GH-145371)
files:
M Android/android.py
diff --git a/Android/android.py b/Android/android.py
index 0a894a958a0165..b644be9cc64c7a 100755
--- a/Android/android.py
+++ b/Android/android.py
@@ -208,7 +208,7 @@ def make_build_python(context):
def unpack_deps(host, prefix_dir):
os.chdir(prefix_dir)
deps_url = "https://github.com/beeware/cpython-android-source-deps/releases/download "
- for name_ver in ["bzip2-1.0.8-3", "libffi-3.4.4-3", "openssl-3.0.19-1",
+ for name_ver in ["bzip2-1.0.8-3", "libffi-3.4.4-3", "openssl-3.5.5-0",
"sqlite-3.50.4-0", "xz-5.4.6-1", "zstd-1.5.7-1"]:
filename = f"{name_ver}-{host}.tar.gz"
download(f"{deps_url}/{name_ver}/{filename}")
https://github.com/python/cpython/commit/f1446d39221b8fc0eb8224d91a020d5c8b…
commit: f1446d39221b8fc0eb8224d91a020d5c8bc1cd70
branch: main
author: Zachary Ware <zach(a)python.org>
committer: zware <zachary.ware(a)gmail.com>
date: 2026-02-28T21:12:05Z
summary:
Destroy the turtle window after its doctests finish (GH-125294)
files:
M Doc/library/turtle.rst
diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst
index 234042c661f51a..20c659756fe1c1 100644
--- a/Doc/library/turtle.rst
+++ b/Doc/library/turtle.rst
@@ -16,6 +16,9 @@
import os
os.remove("my_drawing.ps")
+ # Destroy the turtle window after tests are complete
+ # Imported via star import in testsetup
+ bye()
--------------
https://github.com/python/cpython/commit/ef41f73611d413f81c5f3698398820fbf0…
commit: ef41f73611d413f81c5f3698398820fbf044c9b2
branch: main
author: Thomas Kowalski <thom.kowa(a)gmail.com>
committer: hugovk <1324225+hugovk(a)users.noreply.github.com>
date: 2026-02-28T22:37:15+02:00
summary:
gh-145349: Do not install `ccache` (#145350)
files:
M .github/workflows/build.yml
M .github/workflows/posix-deps-apt.sh
M .github/workflows/reusable-san.yml
M .github/workflows/reusable-ubuntu.yml
M .github/workflows/reusable-wasi.yml
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d777f35ac208fd..91235c0309d29f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -111,8 +111,6 @@ jobs:
run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
- name: Install dependencies
run: sudo ./.github/workflows/posix-deps-apt.sh
- - name: Add ccache to PATH
- run: echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV"
- name: Configure CPython
run: |
# Build Python with the libpython dynamic library
@@ -299,9 +297,6 @@ jobs:
- name: Install OpenSSL
if: steps.cache-openssl.outputs.cache-hit != 'true'
run: python3 Tools/ssl/multissltests.py --steps=library --base-directory "$MULTISSL_DIR" --openssl "$OPENSSL_VER" --system Linux
- - name: Add ccache to PATH
- run: |
- echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV"
- name: Configure CPython
run: ./configure CFLAGS="-fdiagnostics-format=json" --config-cache --enable-slower-safety --with-pydebug --with-openssl="$OPENSSL_DIR"
- name: Build CPython
@@ -356,9 +351,6 @@ jobs:
--base-directory "$MULTISSL_DIR" \
--awslc ${{ matrix.awslc_ver }} \
--system Linux
- - name: Add ccache to PATH
- run: |
- echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV"
- name: Configure CPython
run: |
./configure CFLAGS="-fdiagnostics-format=json" \
@@ -459,9 +451,6 @@ jobs:
- name: Install OpenSSL
if: steps.cache-openssl.outputs.cache-hit != 'true'
run: python3 Tools/ssl/multissltests.py --steps=library --base-directory "$MULTISSL_DIR" --openssl "$OPENSSL_VER" --system Linux
- - name: Add ccache to PATH
- run: |
- echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV"
- name: Setup directory envs for out-of-tree builds
run: |
echo "CPYTHON_RO_SRCDIR=$(realpath -m "${GITHUB_WORKSPACE}"/../cpython-ro-srcdir)" >> "$GITHUB_ENV"
@@ -577,9 +566,6 @@ jobs:
- name: Install OpenSSL
if: steps.cache-openssl.outputs.cache-hit != 'true'
run: python3 Tools/ssl/multissltests.py --steps=library --base-directory "$MULTISSL_DIR" --openssl "$OPENSSL_VER" --system Linux
- - name: Add ccache to PATH
- run: |
- echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV"
- name: Configure CPython
run: ./configure --config-cache --with-address-sanitizer --without-pymalloc --with-openssl="$OPENSSL_DIR"
- name: Build CPython
diff --git a/.github/workflows/posix-deps-apt.sh b/.github/workflows/posix-deps-apt.sh
index 0b64367e6c4562..a2fac7c66db1d9 100755
--- a/.github/workflows/posix-deps-apt.sh
+++ b/.github/workflows/posix-deps-apt.sh
@@ -4,7 +4,6 @@ apt-get update
apt-get -yq install \
build-essential \
pkg-config \
- ccache \
cmake \
gdb \
lcov \
diff --git a/.github/workflows/reusable-san.yml b/.github/workflows/reusable-san.yml
index 49876cf49260d9..b70f9b4b0d6259 100644
--- a/.github/workflows/reusable-san.yml
+++ b/.github/workflows/reusable-san.yml
@@ -66,9 +66,6 @@ jobs:
env:
SANITIZER: ${{ inputs.sanitizer }}
SAN_LOG_OPTION: log_path=${{ github.workspace }}/san_log
- - name: Add ccache to PATH
- run: |
- echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV"
- name: Configure CPython
run: >-
./configure
diff --git a/.github/workflows/reusable-ubuntu.yml b/.github/workflows/reusable-ubuntu.yml
index 4bb4f535acb360..9032ac016e4810 100644
--- a/.github/workflows/reusable-ubuntu.yml
+++ b/.github/workflows/reusable-ubuntu.yml
@@ -63,9 +63,6 @@ jobs:
- name: Install OpenSSL
if: steps.cache-openssl.outputs.cache-hit != 'true'
run: python3 Tools/ssl/multissltests.py --steps=library --base-directory "$MULTISSL_DIR" --openssl "$OPENSSL_VER" --system Linux
- - name: Add ccache to PATH
- run: |
- echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV"
- name: Setup directory envs for out-of-tree builds
run: |
echo "CPYTHON_RO_SRCDIR=$(realpath -m "${GITHUB_WORKSPACE}"/../cpython-ro-srcdir)" >> "$GITHUB_ENV"
diff --git a/.github/workflows/reusable-wasi.yml b/.github/workflows/reusable-wasi.yml
index 68c5ef14cfe212..fb62f0d5164e07 100644
--- a/.github/workflows/reusable-wasi.yml
+++ b/.github/workflows/reusable-wasi.yml
@@ -38,8 +38,6 @@ jobs:
mkdir "${WASI_SDK_PATH}" && \
curl -s -S --location "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_S… " | \
tar --strip-components 1 --directory "${WASI_SDK_PATH}" --extract --gunzip
- - name: "Add ccache to PATH"
- run: echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV"
- name: "Install Python"
uses: actions/setup-python@v6
with:
https://github.com/python/cpython/commit/4152bbb773149fa0df0a6afc594ca372da…
commit: 4152bbb773149fa0df0a6afc594ca372da6f5f86
branch: 3.14
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: picnixz <10796600+picnixz(a)users.noreply.github.com>
date: 2026-02-28T19:28:14Z
summary:
[3.14] gh-145269: simplify bisect.bisect doc example (GH-145270) (#145367)
gh-145269: simplify bisect.bisect doc example (GH-145270)
---------
(cherry picked from commit fdb4b3527f356a84bc00ca32516181016400e567)
Co-authored-by: Nathan Goldbaum <nathan.goldbaum(a)gmail.com>
Co-authored-by: Pieter Eendebak <pieter.eendebak(a)gmail.com>
files:
M Doc/library/bisect.rst
M Lib/test/test_bisect.py
diff --git a/Doc/library/bisect.rst b/Doc/library/bisect.rst
index 3efa3999171646..ecc8d69a2b39ca 100644
--- a/Doc/library/bisect.rst
+++ b/Doc/library/bisect.rst
@@ -203,9 +203,9 @@ example uses :py:func:`~bisect.bisect` to look up a letter grade for an exam sco
based on a set of ordered numeric breakpoints: 90 and up is an 'A', 80 to 89 is
a 'B', and so on::
- >>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
- ... i = bisect(breakpoints, score)
- ... return grades[i]
+ >>> def grade(score)
+ ... i = bisect([60, 70, 80, 90], score)
+ ... return "FDCBA"[i]
...
>>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
['F', 'A', 'C', 'C', 'B', 'A', 'A']
diff --git a/Lib/test/test_bisect.py b/Lib/test/test_bisect.py
index 97204d4cad3871..a7e1f533ff2adc 100644
--- a/Lib/test/test_bisect.py
+++ b/Lib/test/test_bisect.py
@@ -391,9 +391,9 @@ class TestErrorHandlingC(TestErrorHandling, unittest.TestCase):
class TestDocExample:
def test_grades(self):
- def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
- i = self.module.bisect(breakpoints, score)
- return grades[i]
+ def grade(score):
+ i = self.module.bisect([60, 70, 80, 90], score)
+ return "FDCBA"[i]
result = [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
self.assertEqual(result, ['F', 'A', 'C', 'C', 'B', 'A', 'A'])
https://github.com/python/cpython/commit/06d319e4a3c73e9bf7b98c78393ea3976e…
commit: 06d319e4a3c73e9bf7b98c78393ea3976eb644f4
branch: 3.13
author: Stan Ulbrych <89152624+StanFromIreland(a)users.noreply.github.com>
committer: gpshead <68491+gpshead(a)users.noreply.github.com>
date: 2026-02-28T19:05:52Z
summary:
[3.13] Move news entry (#145366)
Move news entry
files:
A Misc/NEWS.d/next/Core_and_Builtins/2026-02-13-12-00-00.gh-issue-144759.d3qYpe.rst
D Misc/NEWS.d/next/Core and Builtins/2026-02-13-12-00-00.gh-issue-144759.d3qYpe.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/2026-02-13-12-00-00.gh-issue-144759.d3qYpe.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-13-12-00-00.gh-issue-144759.d3qYpe.rst
similarity index 100%
rename from Misc/NEWS.d/next/Core and Builtins/2026-02-13-12-00-00.gh-issue-144759.d3qYpe.rst
rename to Misc/NEWS.d/next/Core_and_Builtins/2026-02-13-12-00-00.gh-issue-144759.d3qYpe.rst
https://github.com/python/cpython/commit/aa0aa314addfec922fb243605c0a636700…
commit: aa0aa314addfec922fb243605c0a636700dc4966
branch: 3.13
author: Kumar Aditya <kumaraditya(a)python.org>
committer: kumaraditya303 <kumaraditya(a)python.org>
date: 2026-03-01T00:23:27+05:30
summary:
[3.13] gh-142352: Fix asyncio start_tls() to transfer buffered data from StreamReader (GH-142354) (#145364)
[3.13] gh-142352: Fix `asyncio` `start_tls()` to transfer buffered data from StreamReader (GH-142354)
(cherry picked from commit 0598f4a8999b96409e0a2bf9c480afc76a876860)
Co-authored-by: Maksym Kasimov <39828623+kasimov-maxim(a)users.noreply.github.com>
files:
A Misc/NEWS.d/next/Library/2025-12-06-16-14-18.gh-issue-142352.pW5HLX88.rst
M Lib/asyncio/base_events.py
M Lib/test/test_asyncio/test_streams.py
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index 356fc3d7913ed3..84cad10636feef 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -1348,6 +1348,17 @@ async def start_tls(self, transport, protocol, sslcontext, *,
# have a chance to get called before "ssl_protocol.connection_made()".
transport.pause_reading()
+ # gh-142352: move buffered StreamReader data to SSLProtocol
+ if server_side:
+ from .streams import StreamReaderProtocol
+ if isinstance(protocol, StreamReaderProtocol):
+ stream_reader = getattr(protocol, '_stream_reader', None)
+ if stream_reader is not None:
+ buffer = stream_reader._buffer
+ if buffer:
+ ssl_protocol._incoming.write(buffer)
+ buffer.clear()
+
transport.set_protocol(ssl_protocol)
conmade_cb = self.call_soon(ssl_protocol.connection_made, transport)
resume_cb = self.call_soon(transport.resume_reading)
diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py
index 3040ca55fae0e9..f688c12649e543 100644
--- a/Lib/test/test_asyncio/test_streams.py
+++ b/Lib/test/test_asyncio/test_streams.py
@@ -868,6 +868,48 @@ def test_read_all_from_pipe_reader(self):
data = self.loop.run_until_complete(reader.read(-1))
self.assertEqual(data, b'data')
+ @unittest.skipIf(ssl is None, 'No ssl module')
+ def test_start_tls_buffered_data(self):
+ # gh-142352: test start_tls() with buffered data
+
+ async def server_handler(client_reader, client_writer):
+ # Wait for TLS ClientHello to be buffered before start_tls().
+ await client_reader._wait_for_data('test_start_tls_buffered_data'),
+ self.assertTrue(client_reader._buffer)
+ await client_writer.start_tls(test_utils.simple_server_sslcontext())
+
+ line = await client_reader.readline()
+ self.assertEqual(line, b"ping\n")
+ client_writer.write(b"pong\n")
+ await client_writer.drain()
+ client_writer.close()
+ await client_writer.wait_closed()
+
+ async def client(addr):
+ reader, writer = await asyncio.open_connection(*addr)
+ await writer.start_tls(test_utils.simple_client_sslcontext())
+
+ writer.write(b"ping\n")
+ await writer.drain()
+ line = await reader.readline()
+ self.assertEqual(line, b"pong\n")
+ writer.close()
+ await writer.wait_closed()
+
+ async def run_test():
+ server = await asyncio.start_server(
+ server_handler, socket_helper.HOSTv4, 0)
+ server_addr = server.sockets[0].getsockname()
+
+ await client(server_addr)
+ server.close()
+ await server.wait_closed()
+
+ messages = []
+ self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx))
+ self.loop.run_until_complete(run_test())
+ self.assertEqual(messages, [])
+
def test_streamreader_constructor_without_loop(self):
with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
asyncio.StreamReader()
diff --git a/Misc/NEWS.d/next/Library/2025-12-06-16-14-18.gh-issue-142352.pW5HLX88.rst b/Misc/NEWS.d/next/Library/2025-12-06-16-14-18.gh-issue-142352.pW5HLX88.rst
new file mode 100644
index 00000000000000..13e38b118175b4
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-12-06-16-14-18.gh-issue-142352.pW5HLX88.rst
@@ -0,0 +1,4 @@
+Fix :meth:`asyncio.StreamWriter.start_tls` to transfer buffered data from
+:class:`~asyncio.StreamReader` to the SSL layer, preventing data loss when
+upgrading a connection to TLS mid-stream (e.g., when implementing PROXY
+protocol support).
https://github.com/python/cpython/commit/7f9c3695dfb3e2405ef44869ba06412f4a…
commit: 7f9c3695dfb3e2405ef44869ba06412f4ae5c00a
branch: 3.13
author: Stan Ulbrych <89152624+StanFromIreland(a)users.noreply.github.com>
committer: gpshead <68491+gpshead(a)users.noreply.github.com>
date: 2026-02-28T10:48:12-08:00
summary:
[3.13] Move NEWS entries out of directories with spaces (GH-145357)
Re-do on updated branch
files:
A Misc/NEWS.d/next/C_API/README.rst
A Misc/NEWS.d/next/Core_and_Builtins/2023-07-26-00-03-00.gh-issue-80667.N7Dh8B.rst
A Misc/NEWS.d/next/Core_and_Builtins/2026-01-10-10-58-36.gh-issue-143650.k8mR4x.rst
A Misc/NEWS.d/next/Core_and_Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst
A Misc/NEWS.d/next/Core_and_Builtins/2026-02-13-18-30-59.gh-issue-144766.JGu3x3.rst
A Misc/NEWS.d/next/Core_and_Builtins/2026-02-26-21-36-00.gh-issue-145234.w0mQ9n.rst
A Misc/NEWS.d/next/Core_and_Builtins/README.rst
D Misc/NEWS.d/next/C API/README.rst
D Misc/NEWS.d/next/Core and Builtins/2023-07-26-00-03-00.gh-issue-80667.N7Dh8B.rst
D Misc/NEWS.d/next/Core and Builtins/2026-01-10-10-58-36.gh-issue-143650.k8mR4x.rst
D Misc/NEWS.d/next/Core and Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst
D Misc/NEWS.d/next/Core and Builtins/2026-02-13-18-30-59.gh-issue-144766.JGu3x3.rst
D Misc/NEWS.d/next/Core and Builtins/2026-02-26-21-36-00.gh-issue-145234.w0mQ9n.rst
D Misc/NEWS.d/next/Core and Builtins/README.rst
M .pre-commit-config.yaml
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 33c0ab8645f13c..fa49526ad51ad3 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -49,6 +49,22 @@ repos:
types: [python]
exclude: ^Tools/c-analyzer/cpython/_parser.py
+ - repo: local
+ hooks:
+ - id: blurb-no-space-c-api
+ name: Check C API news entries
+ language: fail
+ entry: Space found in path, move to Misc/NEWS.d/next/C_API/
+ files: Misc/NEWS.d/next/C API/20.*.rst
+
+ - repo: local
+ hooks:
+ - id: blurb-no-space-core-and-builtins
+ name: Check Core and Builtins news entries
+ language: fail
+ entry: Space found in path, move to Misc/NEWS.d/next/Core_and_Builtins/
+ files: Misc/NEWS.d/next/Core and Builtins/20.*.rst
+
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
diff --git a/Misc/NEWS.d/next/C API/README.rst b/Misc/NEWS.d/next/C_API/README.rst
similarity index 100%
rename from Misc/NEWS.d/next/C API/README.rst
rename to Misc/NEWS.d/next/C_API/README.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-26-00-03-00.gh-issue-80667.N7Dh8B.rst b/Misc/NEWS.d/next/Core_and_Builtins/2023-07-26-00-03-00.gh-issue-80667.N7Dh8B.rst
similarity index 100%
rename from Misc/NEWS.d/next/Core and Builtins/2023-07-26-00-03-00.gh-issue-80667.N7Dh8B.rst
rename to Misc/NEWS.d/next/Core_and_Builtins/2023-07-26-00-03-00.gh-issue-80667.N7Dh8B.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/2026-01-10-10-58-36.gh-issue-143650.k8mR4x.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-10-10-58-36.gh-issue-143650.k8mR4x.rst
similarity index 100%
rename from Misc/NEWS.d/next/Core and Builtins/2026-01-10-10-58-36.gh-issue-143650.k8mR4x.rst
rename to Misc/NEWS.d/next/Core_and_Builtins/2026-01-10-10-58-36.gh-issue-143650.k8mR4x.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst
similarity index 100%
rename from Misc/NEWS.d/next/Core and Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst
rename to Misc/NEWS.d/next/Core_and_Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/2026-02-13-18-30-59.gh-issue-144766.JGu3x3.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-13-18-30-59.gh-issue-144766.JGu3x3.rst
similarity index 100%
rename from Misc/NEWS.d/next/Core and Builtins/2026-02-13-18-30-59.gh-issue-144766.JGu3x3.rst
rename to Misc/NEWS.d/next/Core_and_Builtins/2026-02-13-18-30-59.gh-issue-144766.JGu3x3.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/2026-02-26-21-36-00.gh-issue-145234.w0mQ9n.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-26-21-36-00.gh-issue-145234.w0mQ9n.rst
similarity index 100%
rename from Misc/NEWS.d/next/Core and Builtins/2026-02-26-21-36-00.gh-issue-145234.w0mQ9n.rst
rename to Misc/NEWS.d/next/Core_and_Builtins/2026-02-26-21-36-00.gh-issue-145234.w0mQ9n.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/README.rst b/Misc/NEWS.d/next/Core_and_Builtins/README.rst
similarity index 100%
rename from Misc/NEWS.d/next/Core and Builtins/README.rst
rename to Misc/NEWS.d/next/Core_and_Builtins/README.rst
https://github.com/python/cpython/commit/fc2a6cf0d3129d71dfaed3a9cb62c41af1…
commit: fc2a6cf0d3129d71dfaed3a9cb62c41af1cb6f5f
branch: 3.14
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: kumaraditya303 <kumaraditya(a)python.org>
date: 2026-02-28T18:43:43Z
summary:
[3.14] gh-142352: Fix `asyncio` `start_tls()` to transfer buffered data from StreamReader (GH-142354) (#145363)
gh-142352: Fix `asyncio` `start_tls()` to transfer buffered data from StreamReader (GH-142354)
(cherry picked from commit 0598f4a8999b96409e0a2bf9c480afc76a876860)
Co-authored-by: Kumar Aditya <kumaraditya(a)python.org>
Co-authored-by: Maksym Kasimov <39828623+kasimov-maxim(a)users.noreply.github.com>
files:
A Misc/NEWS.d/next/Library/2025-12-06-16-14-18.gh-issue-142352.pW5HLX88.rst
M Lib/asyncio/base_events.py
M Lib/test/test_asyncio/test_streams.py
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index 8cbb71f708537f..b83b84181fd24d 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -1345,6 +1345,17 @@ async def start_tls(self, transport, protocol, sslcontext, *,
# have a chance to get called before "ssl_protocol.connection_made()".
transport.pause_reading()
+ # gh-142352: move buffered StreamReader data to SSLProtocol
+ if server_side:
+ from .streams import StreamReaderProtocol
+ if isinstance(protocol, StreamReaderProtocol):
+ stream_reader = getattr(protocol, '_stream_reader', None)
+ if stream_reader is not None:
+ buffer = stream_reader._buffer
+ if buffer:
+ ssl_protocol._incoming.write(buffer)
+ buffer.clear()
+
transport.set_protocol(ssl_protocol)
conmade_cb = self.call_soon(ssl_protocol.connection_made, transport)
resume_cb = self.call_soon(transport.resume_reading)
diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py
index f93ee54abc6469..cae8c7c6f7c94c 100644
--- a/Lib/test/test_asyncio/test_streams.py
+++ b/Lib/test/test_asyncio/test_streams.py
@@ -819,6 +819,48 @@ async def client(addr):
self.assertEqual(msg1, b"hello world 1!\n")
self.assertEqual(msg2, b"hello world 2!\n")
+ @unittest.skipIf(ssl is None, 'No ssl module')
+ def test_start_tls_buffered_data(self):
+ # gh-142352: test start_tls() with buffered data
+
+ async def server_handler(client_reader, client_writer):
+ # Wait for TLS ClientHello to be buffered before start_tls().
+ await client_reader._wait_for_data('test_start_tls_buffered_data'),
+ self.assertTrue(client_reader._buffer)
+ await client_writer.start_tls(test_utils.simple_server_sslcontext())
+
+ line = await client_reader.readline()
+ self.assertEqual(line, b"ping\n")
+ client_writer.write(b"pong\n")
+ await client_writer.drain()
+ client_writer.close()
+ await client_writer.wait_closed()
+
+ async def client(addr):
+ reader, writer = await asyncio.open_connection(*addr)
+ await writer.start_tls(test_utils.simple_client_sslcontext())
+
+ writer.write(b"ping\n")
+ await writer.drain()
+ line = await reader.readline()
+ self.assertEqual(line, b"pong\n")
+ writer.close()
+ await writer.wait_closed()
+
+ async def run_test():
+ server = await asyncio.start_server(
+ server_handler, socket_helper.HOSTv4, 0)
+ server_addr = server.sockets[0].getsockname()
+
+ await client(server_addr)
+ server.close()
+ await server.wait_closed()
+
+ messages = []
+ self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx))
+ self.loop.run_until_complete(run_test())
+ self.assertEqual(messages, [])
+
def test_streamreader_constructor_without_loop(self):
with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
asyncio.StreamReader()
diff --git a/Misc/NEWS.d/next/Library/2025-12-06-16-14-18.gh-issue-142352.pW5HLX88.rst b/Misc/NEWS.d/next/Library/2025-12-06-16-14-18.gh-issue-142352.pW5HLX88.rst
new file mode 100644
index 00000000000000..13e38b118175b4
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-12-06-16-14-18.gh-issue-142352.pW5HLX88.rst
@@ -0,0 +1,4 @@
+Fix :meth:`asyncio.StreamWriter.start_tls` to transfer buffered data from
+:class:`~asyncio.StreamReader` to the SSL layer, preventing data loss when
+upgrading a connection to TLS mid-stream (e.g., when implementing PROXY
+protocol support).