aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken2
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2019-01-13 17:56:00 +0100
committerChristian Tismer <tismer@stackless.com>2019-01-15 13:33:08 +0000
commitb7707a51337cd7982d298041fa3db2ed225bfc54 (patch)
treed264067b87e9a466d275e73843921ef8f966045a /sources/shiboken2
parentaef6a443a241756e9dfaff192b69d51eaa235ef6 (diff)
Support help() using the Signature Module
The signature module will be used to generate automated documentation by using the function signatures as docstrings. This functionality should be low-hanging fruit. Actually, it was a bit tricky to get this working. The crucial point was to use PyType_Modified(). The function works fine on methods. Supporting types needs some more effort. It is not clear why the __signature__ attribute can be added, but the change to __doc__ is not recognized. May be related to the absence of Py_TPFLAGS_HAVE_VERSION_TAG ? This will be addressed another time. Task-number: PYSIDE-908 Change-Id: If8faa87927899f4c072d42b91eafd8f7658c6abc Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'sources/shiboken2')
-rw-r--r--sources/shiboken2/libshiboken/pep384impl.cpp19
-rw-r--r--sources/shiboken2/libshiboken/pep384impl.h2
-rw-r--r--sources/shiboken2/libshiboken/signature.cpp129
-rw-r--r--sources/shiboken2/shibokenmodule/support/signature/errorhandler.py16
-rw-r--r--sources/shiboken2/shibokenmodule/support/signature/loader.py4
5 files changed, 148 insertions, 22 deletions
diff --git a/sources/shiboken2/libshiboken/pep384impl.cpp b/sources/shiboken2/libshiboken/pep384impl.cpp
index 7cca03c84..7b333f4ff 100644
--- a/sources/shiboken2/libshiboken/pep384impl.cpp
+++ b/sources/shiboken2/libshiboken/pep384impl.cpp
@@ -77,17 +77,22 @@ static struct PyMethodDef probe_methoddef[] = {
{0}
};
+static PyGetSetDef probe_getseters[] = {
+ {0} /* Sentinel */
+};
+
#define probe_tp_call make_dummy(1)
#define probe_tp_str make_dummy(2)
#define probe_tp_traverse make_dummy(3)
#define probe_tp_clear make_dummy(4)
#define probe_tp_methods probe_methoddef
-#define probe_tp_descr_get make_dummy(6)
-#define probe_tp_init make_dummy(7)
-#define probe_tp_alloc make_dummy(8)
-#define probe_tp_new make_dummy(9)
-#define probe_tp_free make_dummy(10)
-#define probe_tp_is_gc make_dummy(11)
+#define probe_tp_getset probe_getseters
+#define probe_tp_descr_get make_dummy(7)
+#define probe_tp_init make_dummy(8)
+#define probe_tp_alloc make_dummy(9)
+#define probe_tp_new make_dummy(10)
+#define probe_tp_free make_dummy(11)
+#define probe_tp_is_gc make_dummy(12)
#define probe_tp_name "type.probe"
#define probe_tp_basicsize make_dummy_int(42)
@@ -98,6 +103,7 @@ static PyType_Slot typeprobe_slots[] = {
{Py_tp_traverse, probe_tp_traverse},
{Py_tp_clear, probe_tp_clear},
{Py_tp_methods, probe_tp_methods},
+ {Py_tp_getset, probe_tp_getset},
{Py_tp_descr_get, probe_tp_descr_get},
{Py_tp_init, probe_tp_init},
{Py_tp_alloc, probe_tp_alloc},
@@ -138,6 +144,7 @@ check_PyTypeObject_valid(void)
|| probe_tp_clear != check->tp_clear
|| probe_tp_weakrefoffset != typetype->tp_weaklistoffset
|| probe_tp_methods != check->tp_methods
+ || probe_tp_getset != check->tp_getset
|| probe_tp_base != typetype->tp_base
|| !PyDict_Check(check->tp_dict)
|| !PyDict_GetItemString(check->tp_dict, "dummy")
diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h
index bfa8f38a7..d883677ce 100644
--- a/sources/shiboken2/libshiboken/pep384impl.h
+++ b/sources/shiboken2/libshiboken/pep384impl.h
@@ -113,7 +113,7 @@ typedef struct _typeobject {
void *X26; // iternextfunc tp_iternext;
struct PyMethodDef *tp_methods;
void *X28; // struct PyMemberDef *tp_members;
- void *X29; // struct PyGetSetDef *tp_getset;
+ struct PyGetSetDef *tp_getset;
struct _typeobject *tp_base;
PyObject *tp_dict;
descrgetfunc tp_descr_get;
diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp
index a8874e2e0..da9c56c62 100644
--- a/sources/shiboken2/libshiboken/signature.cpp
+++ b/sources/shiboken2/libshiboken/signature.cpp
@@ -75,6 +75,7 @@ typedef struct safe_globals_struc {
PyObject *sigparse_func;
PyObject *createsig_func;
PyObject *seterror_argument_func;
+ PyObject *make_helptext_func;
} safe_globals_struc, *safe_globals;
static safe_globals pyside_globals = 0;
@@ -136,13 +137,15 @@ _get_class_of_cf(PyObject *ob_cf)
// This must be an overloaded function that we handled special.
Shiboken::AutoDecRef special(Py_BuildValue("(Os)", ob_cf, "overload"));
selftype = PyDict_GetItem(pyside_globals->map_dict, special);
+ if (selftype == nullptr) {
+ // This is probably a module function. We will return type(None).
+ selftype = Py_None;
+ }
}
}
- assert(selftype);
PyObject *typemod = (PyType_Check(selftype) || PyModule_Check(selftype))
? selftype : (PyObject *)Py_TYPE(selftype);
- // do we support module functions?
Py_INCREF(typemod);
return typemod;
}
@@ -514,6 +517,9 @@ init_phase_2(safe_globals_struc *p, PyMethodDef *methods)
p->seterror_argument_func = PyObject_GetAttrString(p->helper_module, "seterror_argument");
if (p->seterror_argument_func == NULL)
goto error;
+ p->make_helptext_func = PyObject_GetAttrString(p->helper_module, "make_helptext");
+ if (p->make_helptext_func == NULL)
+ goto error;
return 0;
error:
@@ -524,20 +530,50 @@ error:
}
static int
-add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp)
+_fixup_getset(PyTypeObject *type, const char *name, PyGetSetDef *new_gsp)
{
+ PyGetSetDef *gsp = type->tp_getset;
+ if (gsp != nullptr) {
+ for (; gsp->name != NULL; gsp++) {
+ if (strcmp(gsp->name, name) == 0) {
+ new_gsp->set = gsp->set;
+ new_gsp->doc = gsp->doc;
+ new_gsp->closure = gsp->closure;
+ return 1;
+ }
+ }
+ }
+ // staticmethod has just a __doc__ in the class
+ assert(strcmp(type->tp_name, "staticmethod") == 0);
+ return 0;
+}
+
+static int
+add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp, PyObject **old_descr)
+{
+ /*
+ * This function is used to assign a new __signature__ attribute,
+ * and also to override a __doc__ attribute.
+ */
assert(PyType_Check(type));
PyType_Ready(type);
PyObject *dict = type->tp_dict;
for (; gsp->name != NULL; gsp++) {
- if (PyDict_GetItemString(dict, gsp->name))
- continue;
+ PyObject *have_descr = PyDict_GetItemString(dict, gsp->name);
+ if (have_descr != nullptr) {
+ assert(strcmp(gsp->name, "__doc__") == 0);
+ Py_INCREF(have_descr);
+ *old_descr = have_descr;
+ if (!_fixup_getset(type, gsp->name, gsp))
+ continue;
+ }
Shiboken::AutoDecRef descr(PyDescr_NewGetSet(type, gsp));
if (descr.isNull())
return -1;
if (PyDict_SetItemString(dict, gsp->name, descr) < 0)
return -1;
}
+ PyType_Modified(type);
return 0;
}
@@ -553,28 +589,91 @@ add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp)
// Please note that in fact we are modifying 'type', the metaclass of all
// objects, because we add new functionality.
//
+// Addendum 2019-01-12: We now also compute a docstring from the signature.
+//
+
+// keep the original __doc__ functions
+static PyObject *old_cf_doc_descr = 0;
+static PyObject *old_sm_doc_descr = 0;
+static PyObject *old_md_doc_descr = 0;
+static PyObject *old_tp_doc_descr = 0;
+static PyObject *old_wd_doc_descr = 0;
+
+static int handle_doc_in_progress = 0;
+
+static PyObject *
+handle_doc(PyObject *ob, PyObject *old_descr)
+{
+ init_module_1();
+ init_module_2();
+ Shiboken::AutoDecRef ob_type(GetClassOfFunc(ob));
+ PyTypeObject *type = reinterpret_cast<PyTypeObject *>(ob_type.object());
+ if (handle_doc_in_progress || strncmp(type->tp_name, "PySide2.", 8) != 0)
+ return PyObject_CallMethod(old_descr, const_cast<char *>("__get__"), const_cast<char *>("(O)"), ob);
+ handle_doc_in_progress++;
+ PyObject *res = PyObject_CallFunction(
+ pyside_globals->make_helptext_func,
+ const_cast<char *>("(O)"), ob);
+ handle_doc_in_progress--;
+ if (res == nullptr) {
+ PyErr_Print();
+ Py_FatalError("handle_doc did not receive a result");
+ }
+ return res;
+}
+
+static PyObject *
+pyside_cf_get___doc__(PyObject *cf) {
+ return handle_doc(cf, old_cf_doc_descr);
+}
+
+static PyObject *
+pyside_sm_get___doc__(PyObject *sm) {
+ return handle_doc(sm, old_sm_doc_descr);
+}
+
+static PyObject *
+pyside_md_get___doc__(PyObject *md) {
+ return handle_doc(md, old_md_doc_descr);
+}
+
+static PyObject *
+pyside_tp_get___doc__(PyObject *tp) {
+ return handle_doc(tp, old_tp_doc_descr);
+}
+
+static PyObject *
+pyside_wd_get___doc__(PyObject *wd) {
+ return handle_doc(wd, old_wd_doc_descr);
+}
+
static PyGetSetDef new_PyCFunction_getsets[] = {
- {(char *) "__signature__", (getter)pyside_cf_get___signature__},
+ {const_cast<char *>("__signature__"), (getter)pyside_cf_get___signature__},
+ {const_cast<char *>("__doc__"), (getter)pyside_cf_get___doc__},
{0}
};
static PyGetSetDef new_PyStaticMethod_getsets[] = {
- {(char *) "__signature__", (getter)pyside_sm_get___signature__},
+ {const_cast<char *>("__signature__"), (getter)pyside_sm_get___signature__},
+ {const_cast<char *>("__doc__"), (getter)pyside_sm_get___doc__},
{0}
};
static PyGetSetDef new_PyMethodDescr_getsets[] = {
- {(char *) "__signature__", (getter)pyside_md_get___signature__},
+ {const_cast<char *>("__signature__"), (getter)pyside_md_get___signature__},
+ {const_cast<char *>("__doc__"), (getter)pyside_md_get___doc__},
{0}
};
static PyGetSetDef new_PyType_getsets[] = {
- {(char *) "__signature__", (getter)pyside_tp_get___signature__},
+ {const_cast<char *>("__signature__"), (getter)pyside_tp_get___signature__},
+ {const_cast<char *>("__doc__"), (getter)pyside_tp_get___doc__},
{0}
};
static PyGetSetDef new_PyWrapperDescr_getsets[] = {
- {(char *) "__signature__", (getter)pyside_wd_get___signature__},
+ {const_cast<char *>("__signature__"), (getter)pyside_wd_get___signature__},
+ {const_cast<char *>("__doc__"), (getter)pyside_wd_get___doc__},
{0}
};
@@ -655,11 +754,11 @@ PySide_PatchTypes(void)
Shiboken::AutoDecRef wd(PyObject_GetAttrString((PyObject *)Py_TYPE(Py_True), "__add__")); // wrapper-descriptor
if (md.isNull() || wd.isNull()
|| PyType_Ready(Py_TYPE(md)) < 0
- || add_more_getsets(PepMethodDescr_TypePtr, new_PyMethodDescr_getsets) < 0
- || add_more_getsets(&PyCFunction_Type, new_PyCFunction_getsets) < 0
- || add_more_getsets(PepStaticMethod_TypePtr, new_PyStaticMethod_getsets) < 0
- || add_more_getsets(&PyType_Type, new_PyType_getsets) < 0
- || add_more_getsets(Py_TYPE(wd), new_PyWrapperDescr_getsets) < 0
+ || add_more_getsets(PepMethodDescr_TypePtr, new_PyMethodDescr_getsets, &old_md_doc_descr) < 0
+ || add_more_getsets(&PyCFunction_Type, new_PyCFunction_getsets, &old_cf_doc_descr) < 0
+ || add_more_getsets(PepStaticMethod_TypePtr, new_PyStaticMethod_getsets, &old_sm_doc_descr) < 0
+ || add_more_getsets(&PyType_Type, new_PyType_getsets, &old_tp_doc_descr) < 0
+ || add_more_getsets(Py_TYPE(wd), new_PyWrapperDescr_getsets, &old_wd_doc_descr) < 0
)
return -1;
#ifndef _WIN32
diff --git a/sources/shiboken2/shibokenmodule/support/signature/errorhandler.py b/sources/shiboken2/shibokenmodule/support/signature/errorhandler.py
index 902bb05af..df24234e3 100644
--- a/sources/shiboken2/shibokenmodule/support/signature/errorhandler.py
+++ b/sources/shiboken2/shibokenmodule/support/signature/errorhandler.py
@@ -121,4 +121,20 @@ def seterror_argument(args, func_name):
# We don't raise the error here, to avoid the loader in the traceback.
return TypeError, msg
+
+def make_helptext(func):
+ existing_doc = func.__doc__
+ sigs = get_signature(func)
+ if not sigs:
+ return existing_doc
+ if type(sigs) != list:
+ sigs = [sigs]
+ try:
+ func_name = func.__name__
+ except AttribureError:
+ func_name = func.__func__.__name__
+ sigtext = "\n".join(func_name + str(sig) for sig in sigs)
+ msg = sigtext + "\n\n" + existing_doc if existing_doc else sigtext
+ return msg
+
# end of file
diff --git a/sources/shiboken2/shibokenmodule/support/signature/loader.py b/sources/shiboken2/shibokenmodule/support/signature/loader.py
index be30483fe..8c7739d9a 100644
--- a/sources/shiboken2/shibokenmodule/support/signature/loader.py
+++ b/sources/shiboken2/shibokenmodule/support/signature/loader.py
@@ -201,4 +201,8 @@ def create_signature(props, key):
def seterror_argument(args, func_name):
return errorhandler.seterror_argument(args, func_name)
+# name used in signature.cpp
+def make_helptext(func):
+ return errorhandler.make_helptext(func)
+
# end of file