From 2144b1c1cc625e58fe96691f1cad433f8b98eb66 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Wed, 14 Apr 2021 10:46:22 -0500 Subject: [PATCH 1/2] DOC: Ensure that we add documentation also as to the dict for types This ensures that `help(np.dtype)` produces a result. I am not exactly sure why it picks up `__doc__` from the dict instead of `tp_doc` right now. It probably is due to the combination of inheritance and the fact that the dict always includes `None` and gets preference during inheritance. (That probably makes a lot of sense to not inherit the `type` docstring by default.) Modifying the dictionary directly is not really good style, either, but hopefully works. Closes gh-18740 --- numpy/core/src/multiarray/compiled_base.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c index de793f87c156..485f87f0013a 100644 --- a/numpy/core/src/multiarray/compiled_base.c +++ b/numpy/core/src/multiarray/compiled_base.c @@ -1425,9 +1425,26 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) PyCFunctionObject *new = (PyCFunctionObject *)obj; _ADDDOC(new->m_ml->ml_doc, new->m_ml->ml_name); } - else if (Py_TYPE(obj) == &PyType_Type) { + else if (PyObject_TypeCheck(obj, &PyType_Type)) { + /* + * We add it to both `tp_doc` and `__doc__` here. Note that in theory + * `tp_doc` extracts the signature line, but we currently do not use + * it. It may make sense to only add it as `__doc__` and + * `__text_signature__` to the dict in the future. + * The dictionary path is only necessary for heaptypes (currently not + * used) and metaclasses. + * If `__doc__` as stored in `to_dict` is None, we assume this was + * filled in by `PyType_Read()` and should also be replaced. + */ PyTypeObject *new = (PyTypeObject *)obj; _ADDDOC(new->tp_doc, new->tp_name); + if (new->tp_dict != NULL && PyDict_CheckExact(new->tp_dict) && + PyDict_GetItemString(new->tp_dict, "__doc__") == Py_None) { + /* Warning: Modifying `tp_dict` is not generally safe! */ + if (PyDict_SetItemString(new->tp_dict, "__doc__", str) < 0) { + return NULL; + } + } } else if (Py_TYPE(obj) == &PyMemberDescr_Type) { PyMemberDescrObject *new = (PyMemberDescrObject *)obj; From 8f0c4b045d75e709ee1beeb5de33d9f61028f3ec Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Tue, 5 Oct 2021 09:24:48 -0500 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Warren Weckesser --- numpy/core/src/multiarray/compiled_base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c index 485f87f0013a..6b76e570f8e6 100644 --- a/numpy/core/src/multiarray/compiled_base.c +++ b/numpy/core/src/multiarray/compiled_base.c @@ -1433,8 +1433,8 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) * `__text_signature__` to the dict in the future. * The dictionary path is only necessary for heaptypes (currently not * used) and metaclasses. - * If `__doc__` as stored in `to_dict` is None, we assume this was - * filled in by `PyType_Read()` and should also be replaced. + * If `__doc__` as stored in `tp_dict` is None, we assume this was + * filled in by `PyType_Ready()` and should also be replaced. */ PyTypeObject *new = (PyTypeObject *)obj; _ADDDOC(new->tp_doc, new->tp_name);