[Python-checkins] [3.13] gh-129643: fix thread safety of `PyList_SetItem` (#129644) (#129677)

2025-02-05 Thread kumaraditya303
https://github.com/python/cpython/commit/b0810916235ee01d0d3df565288e28ffc45875d3
commit: b0810916235ee01d0d3df565288e28ffc45875d3
branch: 3.13
author: Kumar Aditya 
committer: kumaraditya303 
date: 2025-02-05T08:09:37Z
summary:

[3.13] gh-129643: fix thread safety of `PyList_SetItem` (#129644) (#129677)

gh-129643: fix thread safety of `PyList_SetItem` (#129644)

files:
A Misc/NEWS.d/next/Core and 
Builtins/2025-02-04-12-42-40.gh-issue-129643.K24Zow.rst
M Objects/listobject.c

diff --git a/Misc/NEWS.d/next/Core and 
Builtins/2025-02-04-12-42-40.gh-issue-129643.K24Zow.rst b/Misc/NEWS.d/next/Core 
and Builtins/2025-02-04-12-42-40.gh-issue-129643.K24Zow.rst
new file mode 100644
index 00..27dd3b7f652aca
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and 
Builtins/2025-02-04-12-42-40.gh-issue-129643.K24Zow.rst 
@@ -0,0 +1 @@
+Fix thread safety of :c:func:`PyList_SetItem` in free-threading builds. Patch 
by Kumar Aditya.
diff --git a/Objects/listobject.c b/Objects/listobject.c
index 31ec8d5e05cf50..d581fc8b0b290c 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -451,7 +451,6 @@ int
 PyList_SetItem(PyObject *op, Py_ssize_t i,
PyObject *newitem)
 {
-PyObject **p;
 if (!PyList_Check(op)) {
 Py_XDECREF(newitem);
 PyErr_BadInternalCall();
@@ -467,8 +466,9 @@ PyList_SetItem(PyObject *op, Py_ssize_t i,
 ret = -1;
 goto end;
 }
-p = self->ob_item + i;
-Py_XSETREF(*p, newitem);
+PyObject *tmp = self->ob_item[i];
+FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item[i], newitem);
+Py_XDECREF(tmp);
 ret = 0;
 end:;
 Py_END_CRITICAL_SECTION();

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] gh-128911: Use PyImport_ImportModuleAttr() function (#129657)

2025-02-05 Thread vstinner
https://github.com/python/cpython/commit/dc804ffb2f7cfaf60916b36f3d5cac9c00e4f1ea
commit: dc804ffb2f7cfaf60916b36f3d5cac9c00e4f1ea
branch: main
author: Victor Stinner 
committer: vstinner 
date: 2025-02-05T11:03:58+01:00
summary:

gh-128911: Use PyImport_ImportModuleAttr() function (#129657)

* Replace PyImport_ImportModule() + PyObject_GetAttr() with
  PyImport_ImportModuleAttr().
* Replace PyImport_ImportModule() + PyObject_GetAttrString() with
  PyImport_ImportModuleAttrString().

files:
M Modules/_ctypes/callbacks.c
M Modules/_ctypes/stgdict.c
M Modules/_testcapi/code.c
M Modules/_testcapimodule.c
M Modules/main.c
M Python/crossinterp.c
M Python/pythonrun.c

diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c
index b84bd25af8ec2c..6dd6f6ec56d008 100644
--- a/Modules/_ctypes/callbacks.c
+++ b/Modules/_ctypes/callbacks.c
@@ -551,31 +551,19 @@ STDAPI DllGetClassObject(REFCLSID rclsid,
 
 long Call_CanUnloadNow(void)
 {
-PyObject *mod, *func, *result;
-long retval;
-
-mod = PyImport_ImportModule("ctypes");
-if (!mod) {
-/*  OutputDebugString("Could not import ctypes"); */
-/* We assume that this error can only occur when shutting
-   down, so we silently ignore it */
-PyErr_Clear();
-return E_FAIL;
-}
-/* Other errors cannot be raised, but are printed to stderr */
-func = PyObject_GetAttrString(mod, "DllCanUnloadNow");
-Py_DECREF(mod);
+PyObject *func = PyImport_ImportModuleAttrString("ctypes",
+ "DllCanUnloadNow");
 if (!func) {
 goto error;
 }
 
-result = _PyObject_CallNoArgs(func);
+PyObject *result = _PyObject_CallNoArgs(func);
 Py_DECREF(func);
 if (!result) {
 goto error;
 }
 
-retval = PyLong_AsLong(result);
+long retval = PyLong_AsLong(result);
 if (PyErr_Occurred()) {
 Py_DECREF(result);
 goto error;
diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c
index d63a46a3bc23d2..05239d85c44d2c 100644
--- a/Modules/_ctypes/stgdict.c
+++ b/Modules/_ctypes/stgdict.c
@@ -258,7 +258,7 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject 
*fields, int isStruct
 }
 
 PyObject *layout_func = PyImport_ImportModuleAttrString("ctypes._layout",
-  "get_layout");
+"get_layout");
 if (!layout_func) {
 goto error;
 }
diff --git a/Modules/_testcapi/code.c b/Modules/_testcapi/code.c
index c0193489b6f340..94f752c9726189 100644
--- a/Modules/_testcapi/code.c
+++ b/Modules/_testcapi/code.c
@@ -47,7 +47,6 @@ static PyObject *
 test_code_extra(PyObject* self, PyObject *Py_UNUSED(callable))
 {
 PyObject *result = NULL;
-PyObject *test_module = NULL;
 PyObject *test_func = NULL;
 
 // Get or initialize interpreter-specific code object storage index
@@ -62,11 +61,8 @@ test_code_extra(PyObject* self, PyObject 
*Py_UNUSED(callable))
 
 // Get a function to test with
 // This can be any Python function. Use `test.test_misc.testfunction`.
-test_module = PyImport_ImportModule("test.test_capi.test_misc");
-if (!test_module) {
-goto finally;
-}
-test_func = PyObject_GetAttrString(test_module, "testfunction");
+test_func = PyImport_ImportModuleAttrString("test.test_capi.test_misc",
+"testfunction");
 if (!test_func) {
 goto finally;
 }
@@ -102,7 +98,6 @@ test_code_extra(PyObject* self, PyObject 
*Py_UNUSED(callable))
 }
 result = Py_NewRef(Py_None);
 finally:
-Py_XDECREF(test_module);
 Py_XDECREF(test_func);
 return result;
 }
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 09e74fd3cf20af..c84646ccf03fa7 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -1057,15 +1057,10 @@ test_pep3118_obsolete_write_locks(PyObject* self, 
PyObject *Py_UNUSED(ignored))
 if (ret != -1 || match == 0)
 goto error;
 
-PyObject *mod_io = PyImport_ImportModule("_io");
-if (mod_io == NULL) {
-return NULL;
-}
-
 /* bytesiobuf_getbuffer() */
-PyTypeObject *type = (PyTypeObject *)PyObject_GetAttrString(
-mod_io, "_BytesIOBuffer");
-Py_DECREF(mod_io);
+PyTypeObject *type = (PyTypeObject *)PyImport_ImportModuleAttrString(
+"_io",
+"_BytesIOBuffer");
 if (type == NULL) {
 return NULL;
 }
diff --git a/Modules/main.c b/Modules/main.c
index 5bb1de2d04d30c..f8a2438cdd0d93 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -314,25 +314,19 @@ pymain_start_pyrepl_no_main(void)
 static int
 pymain_run_module(const wchar_t *modname, int set_argv0)
 {
-PyObject *module, *runpy, *runmodule, *runargs, *result;
+PyObject *module, *runmodule, *runargs, *result;
 if (PySys_Audit("cpython.run_module", "u", modname) 

[Python-checkins] gh-129354: Use PyErr_FormatUnraisable() function (#129523)

2025-02-05 Thread vstinner
https://github.com/python/cpython/commit/a25042e6d2b71651e93ff90c361ad763c0368872
commit: a25042e6d2b71651e93ff90c361ad763c0368872
branch: main
author: Victor Stinner 
committer: vstinner 
date: 2025-02-05T10:31:59Z
summary:

gh-129354: Use PyErr_FormatUnraisable() function (#129523)

Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable().

Update tests:

* test_coroutines
* test_exceptions
* test_generators
* test_struct

files:
M Lib/test/test_coroutines.py
M Lib/test/test_exceptions.py
M Lib/test/test_generators.py
M Lib/test/test_struct.py
M Objects/genobject.c
M Objects/typeobject.c
M Python/_warnings.c

diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index 840043d5271224..5566c9803d43ed 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -2136,8 +2136,10 @@ async def func(): pass
 coro = None
 support.gc_collect()
 
+self.assertEqual(cm.unraisable.err_msg,
+ f"Exception ignored while finalizing "
+ f"coroutine {coro_repr}")
 self.assertIn("was never awaited", str(cm.unraisable.exc_value))
-self.assertEqual(repr(cm.unraisable.object), coro_repr)
 
 def test_for_assign_raising_stop_async_iteration(self):
 class BadTarget:
@@ -2411,10 +2413,13 @@ async def corofn():
 coro_repr = repr(coro)
 
 # clear reference to the coroutine without awaiting for it
+coro_repr = repr(coro)
 del coro
 support.gc_collect()
 
-self.assertEqual(repr(cm.unraisable.object), coro_repr)
+self.assertEqual(cm.unraisable.err_msg,
+ f"Exception ignored while finalizing "
+ f"coroutine {coro_repr}")
 self.assertEqual(cm.unraisable.exc_type, ZeroDivisionError)
 
 del warnings._warn_unawaited_coroutine
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index 2d324827451b54..3838eb5b27c9e6 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -1678,10 +1678,13 @@ def __del__(self):
 
 obj = BrokenDel()
 with support.catch_unraisable_exception() as cm:
+obj_repr = repr(type(obj).__del__)
 del obj
 
 gc_collect()  # For PyPy or other GCs.
-self.assertEqual(cm.unraisable.object, BrokenDel.__del__)
+self.assertEqual(cm.unraisable.err_msg,
+ f"Exception ignored while calling "
+ f"deallocator {obj_repr}")
 self.assertIsNotNone(cm.unraisable.exc_traceback)
 
 def test_unhandled(self):
diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py
index b6985054c33d10..bf4b88cd9c4450 100644
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -2664,14 +2664,18 @@ def printsolution(self, x):
 >>> with support.catch_unraisable_exception() as cm:
 ... g = f()
 ... next(g)
+... gen_repr = repr(g)
 ... del g
 ...
+... cm.unraisable.err_msg == (f'Exception ignored while closing '
+...   f'generator {gen_repr}')
 ... cm.unraisable.exc_type == RuntimeError
 ... "generator ignored GeneratorExit" in str(cm.unraisable.exc_value)
 ... cm.unraisable.exc_traceback is not None
 True
 True
 True
+True
 
 And errors thrown during closing should propagate:
 
@@ -2776,10 +2780,12 @@ def printsolution(self, x):
 ... invoke("del failed")
 ...
 >>> with support.catch_unraisable_exception() as cm:
-... l = Leaker()
-... del l
+... leaker = Leaker()
+... del_repr = repr(type(leaker).__del__)
+... del leaker
 ...
-... cm.unraisable.object == Leaker.__del__
+... cm.unraisable.err_msg == (f'Exception ignored while '
+...   f'calling deallocator {del_repr}')
 ... cm.unraisable.exc_type == RuntimeError
 ... str(cm.unraisable.exc_value) == "del failed"
 ... cm.unraisable.exc_traceback is not None
diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py
index 5fee9fbb92acf4..b99391e482ff70 100644
--- a/Lib/test/test_struct.py
+++ b/Lib/test/test_struct.py
@@ -694,7 +694,7 @@ def __del__(self):
 rc, stdout, stderr = assert_python_ok("-c", code)
 self.assertEqual(rc, 0)
 self.assertEqual(stdout.rstrip(), b"")
-self.assertIn(b"Exception ignored in:", stderr)
+self.assertIn(b"Exception ignored while calling deallocator", stderr)
 self.assertIn(b"C.__del__", stderr)
 
 def test__struct_reference_cycle_cleaned_up(self):
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 73bbf86588c457..79aed8571c35e7 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -97,8 +97,10 @@ _PyGen_Finalize(PyObject *self)
 
 PyObject *res = PyObject_CallOneArg(finalizer, self);
   

[Python-checkins] [3.13] gh-129660: Do not use test_embed in PGO profile builds (#129662)

2025-02-05 Thread vstinner
https://github.com/python/cpython/commit/19ceb342b5cc9d9da51dfb6d11dab078c3d43ff1
commit: 19ceb342b5cc9d9da51dfb6d11dab078c3d43ff1
branch: 3.13
author: neonene <53406459+neon...@users.noreply.github.com>
committer: vstinner 
date: 2025-02-05T12:22:30+01:00
summary:

[3.13] gh-129660: Do not use test_embed in PGO profile builds (#129662)

files:
A Misc/NEWS.d/next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst
M Lib/test/libregrtest/pgo.py

diff --git a/Lib/test/libregrtest/pgo.py b/Lib/test/libregrtest/pgo.py
index f762345c88cde3..04803ddf64453c 100644
--- a/Lib/test/libregrtest/pgo.py
+++ b/Lib/test/libregrtest/pgo.py
@@ -19,7 +19,6 @@
 'test_datetime',
 'test_decimal',
 'test_difflib',
-'test_embed',
 'test_float',
 'test_fstring',
 'test_functools',
diff --git 
a/Misc/NEWS.d/next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst 
b/Misc/NEWS.d/next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst
new file mode 100644
index 00..945f91be63809a
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst
@@ -0,0 +1,2 @@
+Drop ``test_embed`` from PGO training, whose contribution in recent
+versions is considered to be ignorable.

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] [3.12] gh-129660: Do not use test_embed in PGO profile builds (GH-129662) (#129685)

2025-02-05 Thread vstinner
https://github.com/python/cpython/commit/62fafd867d0beb879a6bb3689706ae7fc1a889b1
commit: 62fafd867d0beb879a6bb3689706ae7fc1a889b1
branch: 3.12
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: vstinner 
date: 2025-02-05T11:39:58Z
summary:

[3.12] gh-129660: Do not use test_embed in PGO profile builds (GH-129662) 
(#129685)

[3.13] gh-129660: Do not use test_embed in PGO profile builds (GH-129662)
(cherry picked from commit 19ceb342b5cc9d9da51dfb6d11dab078c3d43ff1)

Co-authored-by: neonene <53406459+neon...@users.noreply.github.com>

files:
A Misc/NEWS.d/next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst
M Lib/test/libregrtest/pgo.py

diff --git a/Lib/test/libregrtest/pgo.py b/Lib/test/libregrtest/pgo.py
index cabbba73d5eff5..fe4d2fcf24bd5e 100644
--- a/Lib/test/libregrtest/pgo.py
+++ b/Lib/test/libregrtest/pgo.py
@@ -19,7 +19,6 @@
 'test_datetime',
 'test_decimal',
 'test_difflib',
-'test_embed',
 'test_float',
 'test_fstring',
 'test_functools',
diff --git 
a/Misc/NEWS.d/next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst 
b/Misc/NEWS.d/next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst
new file mode 100644
index 00..945f91be63809a
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst
@@ -0,0 +1,2 @@
+Drop ``test_embed`` from PGO training, whose contribution in recent
+versions is considered to be ignorable.

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] Convert change detection to a Python script (#129627)

2025-02-05 Thread AA-Turner
https://github.com/python/cpython/commit/7d9a22f50923309955a2caf7d57013f224071e6e
commit: 7d9a22f50923309955a2caf7d57013f224071e6e
branch: main
author: Adam Turner <9087854+aa-tur...@users.noreply.github.com>
committer: AA-Turner <9087854+aa-tur...@users.noreply.github.com>
date: 2025-02-05T16:39:42Z
summary:

Convert change detection to a Python script (#129627)

Co-authored-by: Hugo van Kemenade <1324225+hug...@users.noreply.github.com>
Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) 


files:
A .github/workflows/reusable-context.yml
A Tools/build/compute-changes.py
D .github/workflows/reusable-change-detection.yml
M .github/workflows/build.yml

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c10c5b4aa46ffb..dc2f4858be6e8c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -22,25 +22,25 @@ env:
   FORCE_COLOR: 1
 
 jobs:
-  check_source:
+  build-context:
 name: Change detection
 # To use boolean outputs from this job, parse them as JSON.
 # Here's some examples:
 #
-#   if: fromJSON(needs.check_source.outputs.run-docs)
+#   if: fromJSON(needs.build-context.outputs.run-docs)
 #
 #   ${{
-#fromJSON(needs.check_source.outputs.run_tests)
+#fromJSON(needs.build-context.outputs.run-tests)
 #&& 'truthy-branch'
 #|| 'falsy-branch'
 #   }}
 #
-uses: ./.github/workflows/reusable-change-detection.yml
+uses: ./.github/workflows/reusable-context.yml
 
   check-docs:
 name: Docs
-needs: check_source
-if: fromJSON(needs.check_source.outputs.run-docs)
+needs: build-context
+if: fromJSON(needs.build-context.outputs.run-docs)
 uses: ./.github/workflows/reusable-docs.yml
 
   check_autoconf_regen:
@@ -51,8 +51,8 @@ jobs:
 container:
   image: ghcr.io/python/autoconf:2025.01.02.12581854023
 timeout-minutes: 60
-needs: check_source
-if: needs.check_source.outputs.run_tests == 'true'
+needs: build-context
+if: needs.build-context.outputs.run-tests == 'true'
 steps:
   - name: Install Git
 run: |
@@ -94,8 +94,8 @@ jobs:
 # reproducible: to get the same tools versions (autoconf, aclocal, ...)
 runs-on: ubuntu-24.04
 timeout-minutes: 60
-needs: check_source
-if: needs.check_source.outputs.run_tests == 'true'
+needs: build-context
+if: needs.build-context.outputs.run-tests == 'true'
 steps:
   - uses: actions/checkout@v4
 with:
@@ -110,7 +110,7 @@ jobs:
 with:
   path: config.cache
   # Include env.pythonLocation in key to avoid changes in environment 
when setup-python updates Python
-  key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ 
needs.check_source.outputs.config_hash }}-${{ env.pythonLocation }}
+  key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ 
needs.build-context.outputs.config-hash }}-${{ env.pythonLocation }}
   - name: Install Dependencies
 run: sudo ./.github/workflows/posix-deps-apt.sh
   - name: Add ccache to PATH
@@ -153,8 +153,8 @@ jobs:
 name: >-
   Windows
   ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
-needs: check_source
-if: fromJSON(needs.check_source.outputs.run_tests)
+needs: build-context
+if: fromJSON(needs.build-context.outputs.run-tests)
 strategy:
   fail-fast: false
   matrix:
@@ -184,8 +184,8 @@ jobs:
   build_windows_msi:
 name: >-  # ${{ '' } is a hack to nest jobs under the same sidebar category
   Windows MSI${{ '' }}
-needs: check_source
-if: fromJSON(needs.check_source.outputs.run-win-msi)
+needs: build-context
+if: fromJSON(needs.build-context.outputs.run-windows-msi)
 strategy:
   matrix:
 arch:
@@ -200,8 +200,8 @@ jobs:
 name: >-
   macOS
   ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
-needs: check_source
-if: needs.check_source.outputs.run_tests == 'true'
+needs: build-context
+if: needs.build-context.outputs.run-tests == 'true'
 strategy:
   fail-fast: false
   matrix:
@@ -226,7 +226,7 @@ jobs:
   free-threading: true
 uses: ./.github/workflows/reusable-macos.yml
 with:
-  config_hash: ${{ needs.check_source.outputs.config_hash }}
+  config_hash: ${{ needs.build-context.outputs.config-hash }}
   free-threading: ${{ matrix.free-threading }}
   os: ${{ matrix.os }}
 
@@ -235,8 +235,8 @@ jobs:
   Ubuntu
   ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
   ${{ fromJSON(matrix.bolt) && '(bolt)' || '' }}
-needs: check_source
-if: needs.check_source.outputs.run_tests == 'true'
+needs: build-context
+if: needs.build-context.outputs.run-tests == 'true'
 strategy:
   matrix:
 bolt:
@@ -257,7 +257,7 @@ jobs:
   bolt: true
 uses: ./.github/workflows/reusable-ubun

[Python-checkins] gh-69001: Replace maintainer email in IDLE credits (#129588)

2025-02-05 Thread terryjreedy
https://github.com/python/cpython/commit/76e018294801ab95f30756c702b63bf6b4c23310
commit: 76e018294801ab95f30756c702b63bf6b4c23310
branch: main
author: Stan Ulbrych <89152624+stanfromirel...@users.noreply.github.com>
committer: terryjreedy 
date: 2025-02-06T01:44:50-05:00
summary:

gh-69001: Replace maintainer email in IDLE credits (#129588)

Instead, anyone requesting credit should submit a PR with contribution summary.
(Also fix typo in existing name.)

files:
M Lib/idlelib/CREDITS.txt

diff --git a/Lib/idlelib/CREDITS.txt b/Lib/idlelib/CREDITS.txt
index 4a42af586a4a9e..bea3ba7c20de22 100644
--- a/Lib/idlelib/CREDITS.txt
+++ b/Lib/idlelib/CREDITS.txt
@@ -33,15 +33,15 @@ Major contributors since 2005:
 
 - 2005: Tal Einat
 - 2010: Terry Jan Reedy (current maintainer)
-- 2013: Roger Serwys
+- 2013: Roger Serwy
 - 2014: Saimadhav Heblikar
 - 2015: Mark Roseman
 - 2017: Louie Lu, Cheryl Sabella, and Serhiy Storchaka
 
 For additional details refer to NEWS.txt and Changelog.
 
-Please contact the IDLE maintainer (k...@shore.net) to have yourself included
-here if you are one of those we missed!
+If we missed you, feel free to submit a PR with a summary of
+contributions (for instance, at least 5 merged PRs).
 
 
 

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] [3.13] gh-69001: Replace maintainer email in IDLE credits (GH-129588) (#129713)

2025-02-05 Thread terryjreedy
https://github.com/python/cpython/commit/825c6e4dd7804b8b4c65a7477e694c2642360823
commit: 825c6e4dd7804b8b4c65a7477e694c2642360823
branch: 3.13
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: terryjreedy 
date: 2025-02-06T07:37:50Z
summary:

[3.13] gh-69001: Replace maintainer email in IDLE credits (GH-129588) (#129713)

Instead, anyone requesting credit should submit a PR with contribution summary.
(Also fix typo in existing name.)
(cherry picked from commit 76e018294801ab95f30756c702b63bf6b4c23310)

Co-authored-by: Stan Ulbrych <89152624+stanfromirel...@users.noreply.github.com>

files:
M Lib/idlelib/CREDITS.txt

diff --git a/Lib/idlelib/CREDITS.txt b/Lib/idlelib/CREDITS.txt
index 4a42af586a4a9e..bea3ba7c20de22 100644
--- a/Lib/idlelib/CREDITS.txt
+++ b/Lib/idlelib/CREDITS.txt
@@ -33,15 +33,15 @@ Major contributors since 2005:
 
 - 2005: Tal Einat
 - 2010: Terry Jan Reedy (current maintainer)
-- 2013: Roger Serwys
+- 2013: Roger Serwy
 - 2014: Saimadhav Heblikar
 - 2015: Mark Roseman
 - 2017: Louie Lu, Cheryl Sabella, and Serhiy Storchaka
 
 For additional details refer to NEWS.txt and Changelog.
 
-Please contact the IDLE maintainer (k...@shore.net) to have yourself included
-here if you are one of those we missed!
+If we missed you, feel free to submit a PR with a summary of
+contributions (for instance, at least 5 merged PRs).
 
 
 

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] gh-129354: Cleanup test_coroutines (#129684)

2025-02-05 Thread vstinner
https://github.com/python/cpython/commit/e5c3b7e34974dcd6d7f6a1a50030bf7fbce38e74
commit: e5c3b7e34974dcd6d7f6a1a50030bf7fbce38e74
branch: main
author: Victor Stinner 
committer: vstinner 
date: 2025-02-05T11:43:46Z
summary:

gh-129354: Cleanup test_coroutines (#129684)

Remove unused variables.

files:
M Lib/test/test_coroutines.py

diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index 5566c9803d43ed..ae3cd3555002ef 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -735,7 +735,7 @@ async def func(): pass
 
 def test_func_12(self):
 async def g():
-i = me.send(None)
+me.send(None)
 await foo
 me = g()
 with self.assertRaisesRegex(ValueError,
@@ -2283,7 +2283,7 @@ async def __aexit__(self, exc_type, exc_val, exc_tb):
 buffer.append(exc_type.__name__)
 
 async def f():
-async with CM() as c:
+async with CM():
 await asyncio.sleep(0.01)
 raise MyException
 buffer.append('unreachable')
@@ -2375,7 +2375,7 @@ def check(depth, msg):
 
 orig_depth = sys.get_coroutine_origin_tracking_depth()
 try:
-msg = check(0, f"coroutine '{corofn.__qualname__}' was never 
awaited")
+check(0, f"coroutine '{corofn.__qualname__}' was never awaited")
 check(1, "".join([
 f"coroutine '{corofn.__qualname__}' was never awaited\n",
 "Coroutine created at (most recent call last)\n",
@@ -2413,7 +2413,6 @@ async def corofn():
 coro_repr = repr(coro)
 
 # clear reference to the coroutine without awaiting for it
-coro_repr = repr(coro)
 del coro
 support.gc_collect()
 

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] gh-127833: Docs: Add a `grammar-snippet` directive & replace `productionlist` (GH-127835)

2025-02-05 Thread encukou
https://github.com/python/cpython/commit/58a4357e29a15135e6fd99f320c60f8ea0472d27
commit: 58a4357e29a15135e6fd99f320c60f8ea0472d27
branch: main
author: Petr Viktorin 
committer: encukou 
date: 2025-02-05T16:12:23+01:00
summary:

gh-127833: Docs: Add a `grammar-snippet` directive & replace `productionlist` 
(GH-127835)

As a first step toward aligning the grammar documentation with Python's actual
grammar, this overrides the ReST `productionlist` directive to:
- use `:` instead of the `::=` symbol
- add syntax highlighting for strings (using a Pygments highlighting class)

All links and link targets should be preserved. (Unfortunately, this reaches
into some Sphinx internals; I don't see a better way to do exactly what
Sphinx does.)

This also adds a new directive, `grammar-snippet`, which formats the snippet
almost exactly like what's in the source, modulo syntax highlighting and
keeping the backtick character to mark links to other rules.
This will allow formatting the snippets as in the grammar file
(file:///home/encukou/dev/cpython/Doc/build/html/reference/grammar.html).

The new directive is applied to two simple rules in toplevel_components.rst

-

Co-authored-by: Blaise Pabon 
Co-authored-by: William Ferreira 
Co-authored-by: bswck 
Co-authored-by: Adam Turner <9087854+aa-tur...@users.noreply.github.com>

files:
A Doc/tools/extensions/grammar_snippet.py
M Doc/conf.py
M Doc/reference/toplevel_components.rst

diff --git a/Doc/conf.py b/Doc/conf.py
index 94af54084ee338..a4e0c628649018 100644
--- a/Doc/conf.py
+++ b/Doc/conf.py
@@ -27,6 +27,7 @@
 'c_annotations',
 'changes',
 'glossary_search',
+'grammar_snippet',
 'lexers',
 'misc_news',
 'pydoc_topics',
diff --git a/Doc/reference/toplevel_components.rst 
b/Doc/reference/toplevel_components.rst
index dd3d3d6878e289..f155fafbe4d738 100644
--- a/Doc/reference/toplevel_components.rst
+++ b/Doc/reference/toplevel_components.rst
@@ -66,7 +66,9 @@ File input
 
 All input read from non-interactive files has the same form:
 
-.. productionlist:: python-grammar
+.. grammar-snippet::
+   :group: python-grammar
+
file_input: (NEWLINE | `statement`)*
 
 This syntax is used in the following situations:
@@ -85,7 +87,9 @@ Interactive input
 
 Input in interactive mode is parsed using the following grammar:
 
-.. productionlist:: python-grammar
+.. grammar-snippet::
+   :group: python-grammar
+
interactive_input: [`stmt_list`] NEWLINE | `compound_stmt` NEWLINE
 
 Note that a (top-level) compound statement must be followed by a blank line in
diff --git a/Doc/tools/extensions/grammar_snippet.py 
b/Doc/tools/extensions/grammar_snippet.py
new file mode 100644
index 00..03c7e7ce2f4228
--- /dev/null
+++ b/Doc/tools/extensions/grammar_snippet.py
@@ -0,0 +1,219 @@
+"""Support for documenting Python's grammar."""
+
+from __future__ import annotations
+
+import re
+from typing import TYPE_CHECKING
+
+from docutils import nodes
+from docutils.parsers.rst import directives
+from sphinx import addnodes
+from sphinx.domains.std import token_xrefs
+from sphinx.util.docutils import SphinxDirective
+from sphinx.util.nodes import make_id
+
+if TYPE_CHECKING:
+from collections.abc import Sequence
+from typing import Any
+
+from docutils.nodes import Node
+from sphinx.application import Sphinx
+from sphinx.util.typing import ExtensionMetadata
+
+
+class snippet_string_node(nodes.inline):  # noqa: N801 (snake_case is fine)
+"""Node for a string literal in a grammar snippet."""
+
+def __init__(
+self,
+rawsource: str = '',
+text: str = '',
+*children: Node,
+**attributes: Any,
+) -> None:
+super().__init__(rawsource, text, *children, **attributes)
+# Use the Pygments highlight class for `Literal.String.Other`
+self['classes'].append('sx')
+
+
+class GrammarSnippetBase(SphinxDirective):
+"""Common functionality for GrammarSnippetDirective & 
CompatProductionList."""
+
+# The option/argument handling is left to the individual classes.
+
+def make_grammar_snippet(
+self, options: dict[str, Any], content: Sequence[str]
+) -> list[nodes.paragraph]:
+"""Create a literal block from options & content."""
+
+group_name = options['group']
+
+# Docutils elements have a `rawsource` attribute that is supposed to be
+# set to the original ReST source.
+# Sphinx does the following with it:
+# - if it's empty, set it to `self.astext()`
+# - if it matches `self.astext()` when generating the output,
+#   apply syntax highlighting (which is based on the plain-text content
+#   and thus discards internal formatting, like references).
+# To get around this, we set it to this non-empty string:
+rawsource = 'You should not see this.'
+
+literal = nodes.literal_block(
+rawsource,
+'',
+classes=['highlight'],
+  

[Python-checkins] [3.12] gh-69001: Replace maintainer email in IDLE credits (GH-129588) (#129714)

2025-02-05 Thread terryjreedy
https://github.com/python/cpython/commit/e7fb38186e3547a8e4cf1941fab47ee9784d227a
commit: e7fb38186e3547a8e4cf1941fab47ee9784d227a
branch: 3.12
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: terryjreedy 
date: 2025-02-06T07:03:21Z
summary:

[3.12] gh-69001: Replace maintainer email in IDLE credits (GH-129588) (#129714)

Instead, anyone requesting credit should submit a PR with contribution summary.
(Also fix typo in existing name.)
(cherry picked from commit 76e018294801ab95f30756c702b63bf6b4c23310)

Co-authored-by: Stan Ulbrych <89152624+stanfromirel...@users.noreply.github.com>

files:
M Lib/idlelib/CREDITS.txt

diff --git a/Lib/idlelib/CREDITS.txt b/Lib/idlelib/CREDITS.txt
index 4a42af586a4a9e..bea3ba7c20de22 100644
--- a/Lib/idlelib/CREDITS.txt
+++ b/Lib/idlelib/CREDITS.txt
@@ -33,15 +33,15 @@ Major contributors since 2005:
 
 - 2005: Tal Einat
 - 2010: Terry Jan Reedy (current maintainer)
-- 2013: Roger Serwys
+- 2013: Roger Serwy
 - 2014: Saimadhav Heblikar
 - 2015: Mark Roseman
 - 2017: Louie Lu, Cheryl Sabella, and Serhiy Storchaka
 
 For additional details refer to NEWS.txt and Changelog.
 
-Please contact the IDLE maintainer (k...@shore.net) to have yourself included
-here if you are one of those we missed!
+If we missed you, feel free to submit a PR with a summary of
+contributions (for instance, at least 5 merged PRs).
 
 
 

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] gh-86206: Change IDLE splash line (#129698)

2025-02-05 Thread terryjreedy
https://github.com/python/cpython/commit/d83a8a26f5e321b26bec59f5fd47c9c46c16ab12
commit: d83a8a26f5e321b26bec59f5fd47c9c46c16ab12
branch: main
author: Stan Ulbrych <89152624+stanfromirel...@users.noreply.github.com>
committer: terryjreedy 
date: 2025-02-06T07:55:25Z
summary:

gh-86206: Change IDLE splash line (#129698)

Change splash line

Co-authored-by: Terry Jan Reedy 

files:
M Lib/idlelib/pyshell.py

diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py
index 66fbbd4a97b7af..295d06e4a5f017 100755
--- a/Lib/idlelib/pyshell.py
+++ b/Lib/idlelib/pyshell.py
@@ -1133,8 +1133,7 @@ def ispythonsource(self, filename):
 def short_title(self):
 return self.shell_title
 
-COPYRIGHT = \
-  'Type "help", "copyright", "credits" or "license()" for more 
information.'
+SPLASHLINE = 'Enter "help" below or click "Help" above for more 
information.'
 
 def begin(self):
 self.text.mark_set("iomark", "insert")
@@ -1153,7 +1152,7 @@ def begin(self):
 sys.displayhook = rpc.displayhook
 
 self.write("Python %s on %s\n%s\n%s" %
-   (sys.version, sys.platform, self.COPYRIGHT, nosub))
+   (sys.version, sys.platform, self.SPLASHLINE, nosub))
 self.text.focus_force()
 self.showprompt()
 # User code should use separate default Tk root window

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] [3.13] gh-128772: Fix pydoc for methods with __module__ is None (GH-129177) (GH-129653)

2025-02-05 Thread serhiy-storchaka
https://github.com/python/cpython/commit/7eba097137a047d1ddbd6e052979b5babf264954
commit: 7eba097137a047d1ddbd6e052979b5babf264954
branch: 3.13
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: serhiy-storchaka 
date: 2025-02-05T14:42:51Z
summary:

[3.13] gh-128772: Fix pydoc for methods with __module__ is None (GH-129177) 
(GH-129653)

(cherry picked from commit 979d76620990e6f8d68fa63e0ae0db1ec5b4d14c)

Co-authored-by: Serhiy Storchaka 

files:
A Lib/test/test_pydoc/module_none.py
A Misc/NEWS.d/next/Library/2025-01-22-13-29-06.gh-issue-128772.6YrxYM.rst
M Lib/pydoc.py
M Lib/test/test_pydoc/test_pydoc.py

diff --git a/Lib/pydoc.py b/Lib/pydoc.py
index 1a527b2c307b68..591d7bc8f865cf 100755
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -242,7 +242,7 @@ def parentname(object, modname):
 if necessary) or module."""
 if '.' in object.__qualname__:
 name = object.__qualname__.rpartition('.')[0]
-if object.__module__ != modname:
+if object.__module__ != modname and object.__module__ is not None:
 return object.__module__ + '.' + name
 else:
 return name
diff --git a/Lib/test/test_pydoc/module_none.py 
b/Lib/test/test_pydoc/module_none.py
new file mode 100644
index 00..ebb50fc86e2cf7
--- /dev/null
+++ b/Lib/test/test_pydoc/module_none.py
@@ -0,0 +1,8 @@
+def func():
+pass
+func.__module__ = None
+
+class A:
+def method(self):
+pass
+method.__module__ = None
diff --git a/Lib/test/test_pydoc/test_pydoc.py 
b/Lib/test/test_pydoc/test_pydoc.py
index 9cc2252e29367d..00ec3cdb3d49e5 100644
--- a/Lib/test/test_pydoc/test_pydoc.py
+++ b/Lib/test/test_pydoc/test_pydoc.py
@@ -1875,6 +1875,11 @@ def a_fn_with_https_link():
 html
 )
 
+def test_module_none(self):
+# Issue #128772
+from test.test_pydoc import module_none
+pydoc.render_doc(module_none)
+
 
 class PydocFodderTest(unittest.TestCase):
 def tearDown(self):
diff --git 
a/Misc/NEWS.d/next/Library/2025-01-22-13-29-06.gh-issue-128772.6YrxYM.rst 
b/Misc/NEWS.d/next/Library/2025-01-22-13-29-06.gh-issue-128772.6YrxYM.rst
new file mode 100644
index 00..53d6b3ccaffda8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-01-22-13-29-06.gh-issue-128772.6YrxYM.rst
@@ -0,0 +1,2 @@
+Fix :mod:`pydoc` for methods with the ``__module__`` attribute equal to
+``None``.

___
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com


[Python-checkins] gh-129559: Add `bytearray.resize()` (GH-129560)

2025-02-05 Thread gpshead
https://github.com/python/cpython/commit/5fb019fc29a90e722aff20a9522bf588351358cd
commit: 5fb019fc29a90e722aff20a9522bf588351358cd
branch: main
author: Cody Maloney 
committer: gpshead 
date: 2025-02-05T11:33:17-08:00
summary:

gh-129559: Add `bytearray.resize()` (GH-129560)

Add bytearray.resize() which wraps PyByteArray_Resize.

Make negative size passed to resize exception/error rather than crash in 
optimized builds.

files:
A Misc/NEWS.d/next/Library/2025-02-01-14-55-33.gh-issue-129559.hQCeAz.rst
M Doc/c-api/bytearray.rst
M Doc/library/stdtypes.rst
M Lib/test/test_bytes.py
M Lib/test/test_capi/test_bytearray.py
M Objects/bytearrayobject.c
M Objects/clinic/bytearrayobject.c.h

diff --git a/Doc/c-api/bytearray.rst b/Doc/c-api/bytearray.rst
index 9045689a6be567..15295096a710c8 100644
--- a/Doc/c-api/bytearray.rst
+++ b/Doc/c-api/bytearray.rst
@@ -74,6 +74,11 @@ Direct API functions
 .. c:function:: int PyByteArray_Resize(PyObject *bytearray, Py_ssize_t len)
 
Resize the internal buffer of *bytearray* to *len*.
+   Failure is a ``-1`` return with an exception set.
+
+   .. versionchanged:: next
+  A negative *len* will now result in an exception being set and -1 
returned.
+
 
 Macros
 ^^
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index 6050784264707b..4a15e27f82a160 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -2841,6 +2841,38 @@ objects.
  optional *sep* and *bytes_per_sep* parameters to insert separators
  between bytes in the hex output.
 
+   .. method:: resize(size)
+
+  Resize the :class:`bytearray` to contain *size* bytes. *size* must be
+  greater than or equal to 0.
+
+  If the :class:`bytearray` needs to shrink, bytes beyond *size* are 
truncated.
+
+  If the :class:`bytearray` needs to grow, all new bytes, those beyond 
*size*,
+  will be set to null bytes.
+
+
+  This is equivalent to:
+
+  >>> def resize(ba, size):
+  ... if len(ba) > size:
+  ... del ba[size:]
+  ... else:
+  ... ba += b'\0' * (size - len(ba))
+
+  Examples:
+
+  >>> shrink = bytearray(b'abc')
+  >>> shrink.resize(1)
+  >>> (shrink, len(shrink))
+  (bytearray(b'a'), 1)
+  >>> grow = bytearray(b'abc')
+  >>> grow.resize(5)
+  >>> (grow, len(grow))
+  (bytearray(b'abc\x00\x00'), 5)
+
+  .. versionadded:: next
+
 Since bytearray objects are sequences of integers (akin to a list), for a
 bytearray object *b*, ``b[0]`` will be an integer, while ``b[0:1]`` will be
 a bytearray object of length 1.  (This contrasts with text strings, where
diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py
index 7bb1ab38aa4fdf..18d619eb6239a1 100644
--- a/Lib/test/test_bytes.py
+++ b/Lib/test/test_bytes.py
@@ -1359,6 +1359,44 @@ def by(s):
 b = by("Hello, world")
 self.assertEqual(re.findall(br"\w+", b), [by("Hello"), by("world")])
 
+def test_resize(self):
+ba = bytearray(b'abcdef')
+self.assertIsNone(ba.resize(3))
+self.assertEqual(ba, bytearray(b'abc'))
+
+self.assertIsNone(ba.resize(10))
+self.assertEqual(len(ba), 10)
+# Bytes beyond set values must be cleared.
+self.assertEqual(ba, bytearray(b'abc\0\0\0\0\0\0\0'))
+
+ba[3:10] = b'defghij'
+self.assertEqual(ba, bytearray(b'abcdefghij'))
+
+self.assertIsNone(ba.resize(2 ** 20))
+self.assertEqual(len(ba), 2**20)
+self.assertEqual(ba, bytearray(b'abcdefghij' + b'\0' * (2 ** 20 - 10)))
+
+self.assertIsNone(ba.resize(0))
+self.assertEqual(ba, bytearray())
+
+self.assertIsNone(ba.resize(10))
+self.assertEqual(ba, bytearray(b'\0' * 10))
+
+# Subclass
+ba = ByteArraySubclass(b'abcdef')
+self.assertIsNone(ba.resize(3))
+self.assertEqual(ba, bytearray(b'abc'))
+
+# Check arguments
+self.assertRaises(TypeError, bytearray().resize)
+self.assertRaises(TypeError, bytearray().resize, (10, 10))
+
+self.assertRaises(ValueError, bytearray().resize, -1)
+self.assertRaises(ValueError, bytearray().resize, -200)
+self.assertRaises(MemoryError, bytearray().resize, sys.maxsize)
+self.assertRaises(MemoryError, bytearray(1000).resize, sys.maxsize)
+
+
 def test_setitem(self):
 def setitem_as_mapping(b, i, val):
 b[i] = val
@@ -1715,17 +1753,18 @@ def test_resize_forbidden(self):
 # if it wouldn't reallocate the underlying buffer.
 # Furthermore, no destructive changes to the buffer may be applied
 # before raising the error.
-b = bytearray(range(10))
+b = bytearray(10)
 v = memoryview(b)
-def resize(n):
+def manual_resize(n):
 b[1:-1] = range(n + 1, 2*n - 1)
-resize(10)
+b.resize(10)
 orig = b[:]
-self.assertRaises(BufferError, resize, 11)
+self.assertRaises(B

[Python-checkins] gh-129201: Use prefetch in GC mark alive phase. (gh-129203)

2025-02-05 Thread nascheme
https://github.com/python/cpython/commit/cdcacec79f7a216c3c988baa4dc31ce4e76c97ac
commit: cdcacec79f7a216c3c988baa4dc31ce4e76c97ac
branch: main
author: Neil Schemenauer 
committer: nascheme 
date: 2025-02-05T11:38:30-08:00
summary:

gh-129201: Use prefetch in GC mark alive phase. (gh-129203)

For the free-threaded version of the cyclic GC, restructure the "mark alive" 
phase to use software prefetch instructions.  This gives a speedup in most 
cases when the number of objects is large enough.  The prefetching is enabled 
conditionally based on the number of long-lived objects the GC finds.

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-01-22-14-22-34.gh-issue-129201.wiZzEb.rst
M Python/gc_free_threading.c

diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-22-14-22-34.gh-issue-129201.wiZzEb.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-22-14-22-34.gh-issue-129201.wiZzEb.rst
new file mode 100644
index 00..26737330716181
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-22-14-22-34.gh-issue-129201.wiZzEb.rst
@@ -0,0 +1,5 @@
+The free-threaded version of the cyclic garbage collector has been optimized to
+conditionally use CPU prefetch instructions during the collection.  This can
+reduce collection times by making it more likely that data is in the CPU cache
+when it is needed.  The prefetch instructions are enabled if the number of
+long-lived objects (objects surviving a full collection) exceeds a threshold.
diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c
index 5d264e407e1cc8..10c76a67979884 100644
--- a/Python/gc_free_threading.c
+++ b/Python/gc_free_threading.c
@@ -21,6 +21,9 @@
 // enable the "mark alive" pass of GC
 #define GC_ENABLE_MARK_ALIVE 1
 
+// if true, enable the use of "prefetch" CPU instructions
+#define GC_ENABLE_PREFETCH_INSTRUCTIONS 1
+
 // include additional roots in "mark alive" pass
 #define GC_MARK_ALIVE_EXTRA_ROOTS 1
 
@@ -472,13 +475,193 @@ gc_maybe_untrack(PyObject *op)
 }
 
 #ifdef GC_ENABLE_MARK_ALIVE
+
+// prefetch buffer and stack //
+
+// The buffer is a circular FIFO queue of PyObject pointers.  We take
+// care to not dereference these pointers until they are taken out of
+// the buffer.  A prefetch CPU instruction is issued when a pointer is
+// put into the buffer.  If all is working as expected, there will be
+// enough time between the enqueue and dequeue so that the needed memory
+// for the object, most importantly ob_gc_bits and ob_type words, will
+// already be in the CPU cache.
+#define BUFFER_SIZE 256
+#define BUFFER_HI 16
+#define BUFFER_LO 8
+#define BUFFER_MASK (BUFFER_SIZE - 1)
+
+// the buffer size must be an exact power of two
+static_assert(BUFFER_SIZE > 0 && !(BUFFER_SIZE & BUFFER_MASK),
+  "Invalid BUFFER_SIZE, must be power of 2");
+// the code below assumes these relationships are true
+static_assert(BUFFER_HI < BUFFER_SIZE &&
+  BUFFER_LO < BUFFER_HI &&
+  BUFFER_LO > 0,
+  "Invalid prefetch buffer level settings.");
+
+// Prefetch intructions will fetch the line of data from memory that
+// contains the byte specified with the source operand to a location in
+// the cache hierarchy specified by a locality hint.  The instruction
+// is only a hint and the CPU is free to ignore it.  Instructions and
+// behaviour are CPU specific but the definitions of locality hints
+// below are mostly consistent.
+//
+// * T0 (temporal data) prefetch data into all levels of the cache hierarchy.
+//
+// * T1 (temporal data with respect to first level cache) prefetch data into
+//   level 2 cache and higher.
+//
+// * T2 (temporal data with respect to second level cache) prefetch data into
+//   level 3 cache and higher, or an implementation-specific choice.
+//
+// * NTA (non-temporal data with respect to all cache levels) prefetch data 
into
+//   non-temporal cache structure and into a location close to the processor,
+//   minimizing cache pollution.
+
+#if defined(__GNUC__) || defined(__clang__)
+#define PREFETCH_T0(ptr)  __builtin_prefetch(ptr, 0, 3)
+#define PREFETCH_T1(ptr)  __builtin_prefetch(ptr, 0, 2)
+#define PREFETCH_T2(ptr)  __builtin_prefetch(ptr, 0, 1)
+#define PREFETCH_NTA(ptr)  __builtin_prefetch(ptr, 0, 0)
+#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) && 
!defined(_M_ARM64EC)
+#include 
+#define PREFETCH_T0(ptr)  _mm_prefetch((const char*)(ptr), _MM_HINT_T0)
+#define PREFETCH_T1(ptr)  _mm_prefetch((const char*)(ptr), _MM_HINT_T1)
+#define PREFETCH_T2(ptr)  _mm_prefetch((const char*)(ptr), _MM_HINT_T2)
+#define PREFETCH_NTA(ptr)  _mm_prefetch((const char*)(ptr), _MM_HINT_NTA)
+#elif defined (__aarch64__)
+#define PREFETCH_T0(ptr)  \
+do { __asm__ __volatile__("prfm pldl1keep, %0" ::"Q"(*(ptr))); } while 
(0)
+#define PREFETCH_T1(ptr)  \
+do { __asm__ __volatile__("prfm pldl2keep, %0" ::"Q"(*(ptr))); } while 
(0)
+#define PREFETCH_