Files
Neil Schemenauer 010bebbf9f gh-159136: Avoid stop-the-world when setting function name attributes (gh-159269)
Setting __name__, __qualname__, __doc__ or __module__ on a function
stopped the world in the free-threaded build since gh-154826 and
gh-154851, making functools.wraps() orders of magnitude slower with
multiple threads.

Use the per-object critical section and _PyObject_XSetRefDelayed()
instead, as gen_set_name() already does.  Use acquire loads in the
readers and hold strong references across error reporting, tracing, and
eval-frame recording that may execute Python or detach the thread.  The
__code__, __defaults__, __kwdefaults__ and __closure__ setters still
stop the world to clear func_version.

Document synchronization requirements for direct field access and the
lifetime of borrowed function attributes.
2026-10-16 20:24:12 -07:00

160 lines
3.9 KiB
C

#include "parts.h"
#include "util.h"
static PyObject *
function_get_code(PyObject *self, PyObject *func)
{
PyObject *code = PyFunction_GetCode(func);
if (code != NULL) {
return Py_NewRef(code);
} else {
return NULL;
}
}
static PyObject *
function_get_globals(PyObject *self, PyObject *func)
{
PyObject *globals = PyFunction_GetGlobals(func);
if (globals != NULL) {
return Py_NewRef(globals);
} else {
return NULL;
}
}
static PyObject *
function_get_module(PyObject *self, PyObject *func)
{
PyObject *module = PyFunction_GetModule(func);
if (module != NULL) {
return Py_NewRef(module);
} else {
return NULL;
}
}
static PyObject *
function_get_module_macro(PyObject *self, PyObject *func)
{
return Py_XNewRef(PyFunction_GET_MODULE(func));
}
static PyObject *
function_get_defaults(PyObject *self, PyObject *func)
{
PyObject *defaults = PyFunction_GetDefaults(func);
if (defaults != NULL) {
return Py_NewRef(defaults);
} else if (PyErr_Occurred()) {
return NULL;
} else {
Py_RETURN_NONE; // This can happen when `defaults` are set to `None`
}
}
static PyObject *
function_set_defaults(PyObject *self, PyObject *args)
{
PyObject *func = NULL, *defaults = NULL;
if (!PyArg_ParseTuple(args, "OO", &func, &defaults)) {
return NULL;
}
int result = PyFunction_SetDefaults(func, defaults);
if (result == -1)
return NULL;
Py_RETURN_NONE;
}
static PyObject *
function_get_kw_defaults(PyObject *self, PyObject *func)
{
PyObject *defaults = PyFunction_GetKwDefaults(func);
if (defaults != NULL) {
return Py_NewRef(defaults);
} else if (PyErr_Occurred()) {
return NULL;
} else {
Py_RETURN_NONE; // This can happen when `kwdefaults` are set to `None`
}
}
static PyObject *
function_set_kw_defaults(PyObject *self, PyObject *args)
{
PyObject *func = NULL, *defaults = NULL;
if (!PyArg_ParseTuple(args, "OO", &func, &defaults)) {
return NULL;
}
int result = PyFunction_SetKwDefaults(func, defaults);
if (result == -1)
return NULL;
Py_RETURN_NONE;
}
static PyObject *
function_get_closure(PyObject *self, PyObject *func)
{
PyObject *closure = PyFunction_GetClosure(func);
if (closure != NULL) {
return Py_NewRef(closure);
} else if (PyErr_Occurred()) {
return NULL;
} else {
Py_RETURN_NONE; // This can happen when `closure` is set to `None`
}
}
static PyObject *
function_set_closure(PyObject *self, PyObject *args)
{
PyObject *func = NULL, *closure = NULL;
if (!PyArg_ParseTuple(args, "OO", &func, &closure)) {
return NULL;
}
int result = PyFunction_SetClosure(func, closure);
if (result == -1) {
return NULL;
}
Py_RETURN_NONE;
}
static PyObject *
function_get_annotations(PyObject *self, PyObject *func)
{
return Py_XNewRef(PyFunction_GetAnnotations(func));
}
static PyMethodDef test_methods[] = {
{"function_get_code", function_get_code, METH_O, NULL},
{"function_get_globals", function_get_globals, METH_O, NULL},
{"function_get_module", function_get_module, METH_O, NULL},
{"function_get_module_macro", function_get_module_macro, METH_O, NULL},
{"function_get_defaults", function_get_defaults, METH_O, NULL},
{"function_set_defaults", function_set_defaults, METH_VARARGS, NULL},
{"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL},
{"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
{"function_get_closure", function_get_closure, METH_O, NULL},
{"function_set_closure", function_set_closure, METH_VARARGS, NULL},
{"function_get_annotations", function_get_annotations, METH_O, NULL},
{NULL},
};
int
_PyTestCapi_Init_Function(PyObject *m)
{
return PyModule_AddFunctions(m, test_methods);
}