Skip to content

Commit 1e1bc77

Browse files
committed
Make func.__doc__ a property that defers to func.__code__.co_doc
(Unless set explicitly)
1 parent 68fe26c commit 1e1bc77

File tree

1 file changed

+34
-17
lines changed

1 file changed

+34
-17
lines changed

Objects/funcobject.c

+34-17
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "Python.h"
55
#include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals()
6+
#include "pycore_code.h" // Hydration
67
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
78
#include "pycore_pyerrors.h" // _PyErr_Occurred()
89
#include "structmember.h" // PyMemberDef
@@ -31,20 +32,6 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
3132
assert(qualname != NULL);
3233
Py_INCREF(qualname);
3334

34-
PyObject *consts = code_obj->co_consts;
35-
assert(consts == NULL || PyTuple_Check(consts));
36-
PyObject *doc;
37-
if (consts != NULL && PyTuple_Size(consts) >= 1) {
38-
doc = PyTuple_GetItem(consts, 0);
39-
if (!PyUnicode_Check(doc)) {
40-
doc = Py_None;
41-
}
42-
}
43-
else {
44-
doc = Py_None;
45-
}
46-
Py_INCREF(doc);
47-
4835
// __module__: Use globals['__name__'] if it exists, or NULL.
4936
_Py_IDENTIFIER(__name__);
5037
PyObject *module = _PyDict_GetItemIdWithError(globals, &PyId___name__);
@@ -75,7 +62,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
7562
op->func_defaults = NULL; // No default positional arguments
7663
op->func_kwdefaults = NULL; // No default keyword arguments
7764
op->func_closure = NULL;
78-
op->func_doc = doc;
65+
op->func_doc = NULL;
7966
op->func_dict = NULL;
8067
op->func_weakreflist = NULL;
8168
op->func_module = module;
@@ -90,7 +77,6 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
9077
Py_DECREF(code_obj);
9178
Py_DECREF(name);
9279
Py_DECREF(qualname);
93-
Py_DECREF(doc);
9480
Py_XDECREF(module);
9581
Py_XDECREF(builtins);
9682
return NULL;
@@ -279,7 +265,6 @@ PyFunction_SetAnnotations(PyObject *op, PyObject *annotations)
279265

280266
static PyMemberDef func_memberlist[] = {
281267
{"__closure__", T_OBJECT, OFF(func_closure), READONLY},
282-
{"__doc__", T_OBJECT, OFF(func_doc), 0},
283268
{"__globals__", T_OBJECT, OFF(func_globals), READONLY},
284269
{"__module__", T_OBJECT, OFF(func_module), 0},
285270
{"__builtins__", T_OBJECT, OFF(func_builtins), READONLY},
@@ -510,6 +495,37 @@ func_set_annotations(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(igno
510495
return 0;
511496
}
512497

498+
static PyObject *
499+
func_get_doc(PyFunctionObject *op, void *Py_UNUSED(ignored))
500+
{
501+
PyObject *doc = op->func_doc;
502+
if (doc == NULL) {
503+
PyCodeObject *code = (PyCodeObject *)op->func_code;
504+
if (!_PyCode_IsHydrated(code)) {
505+
if (_PyCode_Hydrate(code) == NULL) {
506+
return NULL;
507+
}
508+
}
509+
if (PyTuple_CheckExact(code->co_consts)
510+
&& PyTuple_GET_SIZE(code->co_consts) >= 1) {
511+
doc = PyTuple_GET_ITEM(code->co_consts, 0);
512+
}
513+
}
514+
if (doc == NULL) {
515+
doc = Py_None;
516+
}
517+
Py_INCREF(doc);
518+
return doc;
519+
}
520+
521+
static int
522+
func_set_doc(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
523+
{
524+
Py_INCREF(value);
525+
Py_XSETREF(op->func_doc, value);
526+
return 0;
527+
}
528+
513529
static PyGetSetDef func_getsetlist[] = {
514530
{"__code__", (getter)func_get_code, (setter)func_set_code},
515531
{"__defaults__", (getter)func_get_defaults,
@@ -521,6 +537,7 @@ static PyGetSetDef func_getsetlist[] = {
521537
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
522538
{"__name__", (getter)func_get_name, (setter)func_set_name},
523539
{"__qualname__", (getter)func_get_qualname, (setter)func_set_qualname},
540+
{"__doc__", (getter)func_get_doc, (setter)func_set_doc},
524541
{NULL} /* Sentinel */
525542
};
526543

0 commit comments

Comments
 (0)