Skip to content

Commit 0f0eb97

Browse files
generatedunixname1734921407115435meta-codesync[bot]
authored andcommitted
Import CPython 3.14.5+ stable branch (2026-05-24)
Summary: Imported python/cpython `3.14.5+` from upstream rev [`5676bfc`](https://www.github.com/python/cpython/commit/5676bfcf859529b9ddd9a74b3199f2d932992458) (committed 2026-05-24 07:42:21+00:00). # Commit Info Base: (`3.14.5+`) - [`2dd91d2`](https://www.github.com/python/cpython/commit/2dd91d2b92a6c74d78cd3385ede328190cd8eaa9) (commit date: 2026-05-22 14:28:43+00:00) Imported: (`3.14.5+`) - [`5676bfc`](https://www.github.com/python/cpython/commit/5676bfcf859529b9ddd9a74b3199f2d932992458) (commit date: 2026-05-24 07:42:21+00:00) # Noteworthy file changes - Low-signal files (1 added, 1 removed) (NEWS.d, docs, .github) Complete list of added/removed files: https://www.internalfb.com/intern/everpaste/?color=0&handle=GJICnyNP_NB_62EHAD4iNJNqnGJTbr0LAAAz Reviewed By: itamaro Differential Revision: D106240554 fbshipit-source-id: 5f92d83756f3f4184d83c645cc67703774bc90be
1 parent c1afdf4 commit 0f0eb97

9 files changed

Lines changed: 55 additions & 33 deletions

File tree

Doc/library/threading.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ since it is impossible to detect the termination of alien threads.
512512
This constructor should always be called with keyword arguments. Arguments
513513
are:
514514

515-
*group* should be ``None``; reserved for future extension when a
515+
*group* must be ``None`` as it is reserved for future extension when a
516516
:class:`!ThreadGroup` class is implemented.
517517

518518
*target* is the callable object to be invoked by the :meth:`run` method.

Lib/asyncio/coroutines.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ def _is_debug_mode():
1818

1919

2020
def iscoroutinefunction(func):
21-
import warnings
2221
"""Return True if func is a decorated coroutine function."""
22+
import warnings
2323
warnings._deprecated("asyncio.iscoroutinefunction",
2424
f"{warnings._DEPRECATED_MSG}; "
2525
"use inspect.iscoroutinefunction() instead",

Lib/test/test_type_cache.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
""" Tests for the internal type cache in CPython. """
2+
import collections.abc
23
import dis
34
import unittest
45
import warnings
@@ -114,6 +115,25 @@ class HolderSub(Holder):
114115
Holder.set_value()
115116
HolderSub.value
116117

118+
def test_abc_register_invalidates_subclass_versions(self):
119+
class Parent:
120+
pass
121+
122+
class Child(Parent):
123+
pass
124+
125+
type_assign_version(Parent)
126+
type_assign_version(Child)
127+
parent_version = type_get_version(Parent)
128+
child_version = type_get_version(Child)
129+
if parent_version == 0 or child_version == 0:
130+
self.skipTest("Could not assign valid type versions")
131+
132+
collections.abc.Mapping.register(Parent)
133+
134+
self.assertEqual(type_get_version(Parent), 0)
135+
self.assertEqual(type_get_version(Child), 0)
136+
117137
@support.cpython_only
118138
class TypeCacheWithSpecializationTests(unittest.TestCase):
119139
def tearDown(self):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix ``abc.register()`` so it invalidates type version tags for registered classes.

Misc/NEWS.d/next/Library/2026-05-18-15-30-34.gh-issue-146452.RM0EVJ.rst

Lines changed: 0 additions & 2 deletions
This file was deleted.

Modules/_io/bufferedio.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,7 +1497,9 @@ buffered_iternext(PyObject *op)
14971497
tp == state->PyBufferedRandom_Type)
14981498
{
14991499
/* Skip method call overhead for speed */
1500+
Py_BEGIN_CRITICAL_SECTION(self);
15001501
line = _buffered_readline(self, -1);
1502+
Py_END_CRITICAL_SECTION();
15011503
}
15021504
else {
15031505
line = PyObject_CallMethodNoArgs((PyObject *)self,

Modules/_pickle.c

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3351,12 +3351,9 @@ batch_dict(PickleState *state, PicklerObject *self, PyObject *iter, PyObject *or
33513351
* Returns 0 on success, -1 on error.
33523352
*
33533353
* Note that this currently doesn't work for protocol 0.
3354-
3355-
* gh-146452: Wrap the dict iteration in a critical sections to prevent
3356-
* concurrent mutation from invalidating PyDict_Next() iteration state.
33573354
*/
33583355
static int
3359-
batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj)
3356+
batch_dict_exact_impl(PickleState *state, PicklerObject *self, PyObject *obj)
33603357
{
33613358
PyObject *key = NULL, *value = NULL;
33623359
int i;
@@ -3373,19 +3370,9 @@ batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj)
33733370

33743371
/* Special-case len(d) == 1 to save space. */
33753372
if (dict_size == 1) {
3376-
int next;
3377-
Py_BEGIN_CRITICAL_SECTION(obj);
3378-
next = PyDict_Next(obj, &ppos, &key, &value);
3379-
if (next) {
3380-
Py_INCREF(key);
3381-
Py_INCREF(value);
3382-
}
3383-
Py_END_CRITICAL_SECTION();
3384-
if (!next) {
3385-
PyErr_SetString(PyExc_RuntimeError,
3386-
"dictionary changed size during iteration");
3387-
goto error;
3388-
}
3373+
PyDict_Next(obj, &ppos, &key, &value);
3374+
Py_INCREF(key);
3375+
Py_INCREF(value);
33893376
if (save(state, self, key, 0) < 0) {
33903377
goto error;
33913378
}
@@ -3405,18 +3392,9 @@ batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj)
34053392
i = 0;
34063393
if (_Pickler_Write(self, &mark_op, 1) < 0)
34073394
return -1;
3408-
int next;
3409-
while (1) {
3410-
Py_BEGIN_CRITICAL_SECTION(obj);
3411-
next = PyDict_Next(obj, &ppos, &key, &value);
3412-
if (next) {
3413-
Py_INCREF(key);
3414-
Py_INCREF(value);
3415-
}
3416-
Py_END_CRITICAL_SECTION();
3417-
if (!next) {
3418-
break;
3419-
}
3395+
while (PyDict_Next(obj, &ppos, &key, &value)) {
3396+
Py_INCREF(key);
3397+
Py_INCREF(value);
34203398
if (save(state, self, key, 0) < 0) {
34213399
goto error;
34223400
}
@@ -3446,6 +3424,18 @@ batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj)
34463424
return -1;
34473425
}
34483426

3427+
/* gh-146452: Wrap the dict iteration in a critical section to prevent
3428+
concurrent mutation from invalidating PyDict_Next() iteration state. */
3429+
static int
3430+
batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj)
3431+
{
3432+
int ret;
3433+
Py_BEGIN_CRITICAL_SECTION(obj);
3434+
ret = batch_dict_exact_impl(state, self, obj);
3435+
Py_END_CRITICAL_SECTION();
3436+
return ret;
3437+
}
3438+
34493439
static int
34503440
save_dict(PickleState *state, PicklerObject *self, PyObject *obj)
34513441
{

Objects/typeobject.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6074,6 +6074,15 @@ void
60746074
_PyType_SetFlagsRecursive(PyTypeObject *self, unsigned long mask, unsigned long flags)
60756075
{
60766076
BEGIN_TYPE_LOCK();
6077+
/* Ideally, changing flags and invalidating the old version tag would happen
6078+
in one step. In 3.14, invalidate first while holding TYPE_LOCK so readers
6079+
cannot assign a fresh tag from stale flags. Immutable types are skipped by
6080+
set_flags_recursive(). */
6081+
if (!PyType_HasFeature(self, Py_TPFLAGS_IMMUTABLETYPE) &&
6082+
(self->tp_flags & mask) != flags)
6083+
{
6084+
type_modified_unlocked(self);
6085+
}
60776086
set_flags_recursive(self, mask, flags);
60786087
END_TYPE_LOCK();
60796088
}

Python/hamt.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@ hamt_node_bitmap_assoc(PyHamtNode_Bitmap *self,
701701

702702
PyHamtNode_Bitmap *ret = hamt_node_bitmap_clone(self);
703703
if (ret == NULL) {
704+
Py_DECREF(sub_node);
704705
return NULL;
705706
}
706707
Py_SETREF(ret->b_array[val_idx], (PyObject*)sub_node);
@@ -993,6 +994,7 @@ hamt_node_bitmap_without(PyHamtNode_Bitmap *self,
993994

994995
PyHamtNode_Bitmap *clone = hamt_node_bitmap_clone(self);
995996
if (clone == NULL) {
997+
Py_DECREF(sub_node);
996998
return W_ERROR;
997999
}
9981000

0 commit comments

Comments
 (0)