https://github.com/python/cpython/commit/92d8672bf5847a70155587f979645e9f0bff5b28
commit: 92d8672bf5847a70155587f979645e9f0bff5b28
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-07-29T07:03:09Z
summary:

[3.14] gh-153531: fix thread safety of setting func.__doc__ and func.__module__ 
(GH-154851) (#154857)

gh-153531: fix thread safety of setting func.__doc__ and func.__module__ 
(GH-154851)
(cherry picked from commit 9ccd5bb81edde823fb5fdbd51287f4a0ddfcd149)

Co-authored-by: Kumar Aditya <[email protected]>

files:
M Lib/test/test_free_threading/test_functions.py
M Objects/funcobject.c

diff --git a/Lib/test/test_free_threading/test_functions.py 
b/Lib/test/test_free_threading/test_functions.py
index 65a90a17bd5b8d..7a59638d27dc9d 100644
--- a/Lib/test/test_free_threading/test_functions.py
+++ b/Lib/test/test_free_threading/test_functions.py
@@ -58,6 +58,12 @@ def test_annotate(self):
     def test_type_params(self):
         self.stress_attribute("__type_params__", lambda: (random_string(),))
 
+    def test_doc(self):
+        self.stress_attribute("__doc__", random_string)
+
+    def test_module(self):
+        self.stress_attribute("__module__", random_string)
+
     def test_annotations_and_annotate(self):
         # The __annotations__ and __annotate__ setters clear each other.
         def target(): pass
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index 624d7503fa8aa0..b59a493e6234cb 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -644,9 +644,7 @@ PyFunction_SetAnnotations(PyObject *op, PyObject 
*annotations)
 
 static PyMemberDef func_memberlist[] = {
     {"__closure__",   _Py_T_OBJECT,     OFF(func_closure), Py_READONLY},
-    {"__doc__",       _Py_T_OBJECT,     OFF(func_doc), 0},
     {"__globals__",   _Py_T_OBJECT,     OFF(func_globals), Py_READONLY},
-    {"__module__",    _Py_T_OBJECT,     OFF(func_module), 0},
     {"__builtins__",  _Py_T_OBJECT,     OFF(func_builtins), Py_READONLY},
     {NULL}  /* Sentinel */
 };
@@ -777,6 +775,56 @@ func_set_qualname(PyObject *self, PyObject *value, void 
*Py_UNUSED(ignored))
     return 0;
 }
 
+static PyObject *
+func_get_doc(PyObject *self, void *Py_UNUSED(ignored))
+{
+    PyFunctionObject *op = _PyFunction_CAST(self);
+    PyObject *doc = op->func_doc;
+    if (doc == NULL) {
+        doc = Py_None;
+    }
+    return Py_NewRef(doc);
+}
+
+static int
+func_set_doc(PyObject *self, PyObject *value, void *Py_UNUSED(ignored))
+{
+    /* Legal to del f.__doc__ or to set it to any object. */
+    PyFunctionObject *op = _PyFunction_CAST(self);
+    PyInterpreterState *interp = _PyInterpreterState_GET();
+    _PyEval_StopTheWorld(interp);
+    PyObject *old_doc = op->func_doc;
+    op->func_doc = Py_XNewRef(value);
+    _PyEval_StartTheWorld(interp);
+    Py_XDECREF(old_doc);
+    return 0;
+}
+
+static PyObject *
+func_get_module(PyObject *self, void *Py_UNUSED(ignored))
+{
+    PyFunctionObject *op = _PyFunction_CAST(self);
+    PyObject *module = op->func_module;
+    if (module == NULL) {
+        module = Py_None;
+    }
+    return Py_NewRef(module);
+}
+
+static int
+func_set_module(PyObject *self, PyObject *value, void *Py_UNUSED(ignored))
+{
+    /* Legal to del f.__module__ or to set it to any object. */
+    PyFunctionObject *op = _PyFunction_CAST(self);
+    PyInterpreterState *interp = _PyInterpreterState_GET();
+    _PyEval_StopTheWorld(interp);
+    PyObject *old_module = op->func_module;
+    op->func_module = Py_XNewRef(value);
+    _PyEval_StartTheWorld(interp);
+    Py_XDECREF(old_module);
+    return 0;
+}
+
 static PyObject *
 func_get_defaults(PyObject *self, void *Py_UNUSED(ignored))
 {
@@ -906,24 +954,12 @@ function___annotate___set_impl(PyFunctionObject *self, 
PyObject *value)
         return -1;
     }
     if (Py_IsNone(value)) {
-        PyInterpreterState *interp = _PyInterpreterState_GET();
-        _PyEval_StopTheWorld(interp);
-        PyObject *old_annotate = self->func_annotate;
-        self->func_annotate = Py_NewRef(value);
-        _PyEval_StartTheWorld(interp);
-        Py_XDECREF(old_annotate);
+        Py_XSETREF(self->func_annotate, Py_NewRef(value));
         return 0;
     }
     else if (PyCallable_Check(value)) {
-        PyInterpreterState *interp = _PyInterpreterState_GET();
-        _PyEval_StopTheWorld(interp);
-        PyObject *old_annotate = self->func_annotate;
-        self->func_annotate = Py_NewRef(value);
-        PyObject *old_annotations = self->func_annotations;
-        self->func_annotations = NULL;
-        _PyEval_StartTheWorld(interp);
-        Py_XDECREF(old_annotate);
-        Py_XDECREF(old_annotations);
+        Py_XSETREF(self->func_annotate, Py_NewRef(value));
+        Py_CLEAR(self->func_annotations);
         return 0;
     }
     else {
@@ -976,15 +1012,8 @@ function___annotations___set_impl(PyFunctionObject *self, 
PyObject *value)
             "__annotations__ must be set to a dict object");
         return -1;
     }
-    PyInterpreterState *interp = _PyInterpreterState_GET();
-    _PyEval_StopTheWorld(interp);
-    PyObject *old_annotations = self->func_annotations;
-    self->func_annotations = Py_XNewRef(value);
-    PyObject *old_annotate = self->func_annotate;
-    self->func_annotate = NULL;
-    _PyEval_StartTheWorld(interp);
-    Py_XDECREF(old_annotations);
-    Py_XDECREF(old_annotate);
+    Py_XSETREF(self->func_annotations, Py_XNewRef(value));
+    Py_CLEAR(self->func_annotate);
     return 0;
 }
 
@@ -1025,12 +1054,7 @@ function___type_params___set_impl(PyFunctionObject 
*self, PyObject *value)
                         "__type_params__ must be set to a tuple");
         return -1;
     }
-    PyInterpreterState *interp = _PyInterpreterState_GET();
-    _PyEval_StopTheWorld(interp);
-    PyObject *old_typeparams = self->func_typeparams;
-    self->func_typeparams = Py_NewRef(value);
-    _PyEval_StartTheWorld(interp);
-    Py_XDECREF(old_typeparams);
+    Py_XSETREF(self->func_typeparams, Py_NewRef(value));
     return 0;
 }
 
@@ -1052,6 +1076,8 @@ static PyGetSetDef func_getsetlist[] = {
     FUNCTION___ANNOTATIONS___GETSETDEF
     FUNCTION___ANNOTATE___GETSETDEF
     {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
+    {"__doc__", func_get_doc, func_set_doc},
+    {"__module__", func_get_module, func_set_module},
     {"__name__", func_get_name, func_set_name},
     {"__qualname__", func_get_qualname, func_set_qualname},
     FUNCTION___TYPE_PARAMS___GETSETDEF

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to