diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx index 9efac995acdd7..ff417468bf180 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx @@ -1055,6 +1055,70 @@ PyObject* CPyCppyy::CPPMethod::GetSignature(bool fa) return CPyCppyy_PyText_FromString(GetSignatureString(fa).c_str()); } +/** + * @brief Returns a tuple with the names of the input parameters of this method. + * + * For example given a function with prototype: + * + * double foo(int a, float b, double c) + * + * this function returns: + * + * ('a', 'b', 'c') + */ +PyObject *CPyCppyy::CPPMethod::GetSignatureNames() +{ + // Build a tuple of the argument names for this signature. + int argcount = GetMaxArgs(); + PyObject *signature_names = PyTuple_New(argcount); + + for (int iarg = 0; iarg < argcount; ++iarg) { + const std::string &argname_cpp = Cppyy::GetMethodArgName(fMethod, iarg); + PyObject *argname_py = CPyCppyy_PyText_FromString(argname_cpp.c_str()); + PyTuple_SET_ITEM(signature_names, iarg, argname_py); + } + + return signature_names; +} + +/** + * @brief Returns a dictionary with the types of the signature of this method. + * + * This dictionary will store both the return type and the input parameter + * types of this method, respectively with keys "return_type" and + * "input_types", for example given a function with prototype: + * + * double foo(int a, float b, double c) + * + * this function returns: + * + * {'input_types': ('int', 'float', 'double'), 'return_type': 'double'} + */ +PyObject *CPyCppyy::CPPMethod::GetSignatureTypes() +{ + + PyObject *signature_types_dict = PyDict_New(); + + // Insert the return type first + std::string return_type = GetReturnTypeName(); + PyObject *return_type_py = CPyCppyy_PyText_FromString(return_type.c_str()); + PyDict_SetItem(signature_types_dict, CPyCppyy_PyText_FromString("return_type"), return_type_py); + + // Build a tuple of the argument types for this signature. + int argcount = GetMaxArgs(); + PyObject *parameter_types = PyTuple_New(argcount); + + for (int iarg = 0; iarg < argcount; ++iarg) { + const std::string &argtype_cpp = Cppyy::GetMethodArgType(fMethod, iarg); + PyObject *argtype_py = CPyCppyy_PyText_FromString(argtype_cpp.c_str()); + PyTuple_SET_ITEM(parameter_types, iarg, argtype_py); + } + + PyDict_SetItem(signature_types_dict, CPyCppyy_PyText_FromString("input_types"), parameter_types); + + return signature_types_dict; +} + //---------------------------------------------------------------------------- std::string CPyCppyy::CPPMethod::GetReturnTypeName() { diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h b/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h index 88e49621ef629..e9558f5c6869b 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h @@ -51,6 +51,8 @@ class CPPMethod : public PyCallable { public: PyObject* GetSignature(bool show_formalargs = true) override; + PyObject* GetSignatureNames() override; + PyObject* GetSignatureTypes() override; PyObject* GetPrototype(bool show_formalargs = true) override; PyObject* GetTypeName() override; PyObject* Reflex(Cppyy::Reflex::RequestId_t request, diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx index d6aa4a747b3a3..9d67f42c8224e 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx @@ -62,6 +62,12 @@ class TPythonCallback : public PyCallable { PyObject* GetSignature(bool /*show_formalargs*/ = true) override { return CPyCppyy_PyText_FromString("*args, **kwargs"); } + PyObject* GetSignatureNames() override { + return PyTuple_New(0); + } + PyObject* GetSignatureTypes() override { + return PyTuple_New(0); + } PyObject* GetPrototype(bool /*show_formalargs*/ = true) override { return CPyCppyy_PyText_FromString(""); } @@ -285,6 +291,54 @@ static int mp_doc_set(CPPOverload* pymeth, PyObject *val, void *) return 0; } +/** + * @brief Returns a dictionary with the input parameter names for all overloads. + * + * This dictionary may look like: + * + * {'double ::foo(int a, float b, double c)': ('a', 'b', 'c'), + * 'float ::foo(float b)': ('b',), + * 'int ::foo(int a)': ('a',), + * 'int ::foo(int a, float b)': ('a', 'b')} + */ +static PyObject *mp_func_overloads_names(CPPOverload *pymeth) +{ + + const CPPOverload::Methods_t &methods = pymeth->fMethodInfo->fMethods; + + PyObject *overloads_names_dict = PyDict_New(); + + for (PyCallable *method : methods) { + PyDict_SetItem(overloads_names_dict, method->GetPrototype(), method->GetSignatureNames()); + } + + return overloads_names_dict; +} + +/** + * @brief Returns a dictionary with the types of all overloads. + * + * This dictionary may look like: + * + * {'double ::foo(int a, float b, double c)': {'input_types': ('int', 'float', 'double'), 'return_type': 'double'}, + * 'float ::foo(float b)': {'input_types': ('float',), 'return_type': 'float'}, + * 'int ::foo(int a)': {'input_types': ('int',), 'return_type': 'int'}, + * 'int ::foo(int a, float b)': {'input_types': ('int', 'float'), 'return_type': 'int'}} + */ +static PyObject *mp_func_overloads_types(CPPOverload *pymeth) +{ + + const CPPOverload::Methods_t &methods = pymeth->fMethodInfo->fMethods; + + PyObject *overloads_types_dict = PyDict_New(); + + for (PyCallable *method : methods) { + PyDict_SetItem(overloads_types_dict, method->GetPrototype(), method->GetSignatureTypes()); + } + + return overloads_types_dict; +} + //---------------------------------------------------------------------------- static PyObject* mp_meth_func(CPPOverload* pymeth, void*) { @@ -582,6 +636,8 @@ static PyGetSetDef mp_getset[] = { {(char*)"func_globals", (getter)mp_func_globals, nullptr, nullptr, nullptr}, {(char*)"func_doc", (getter)mp_doc, (setter)mp_doc_set, nullptr, nullptr}, {(char*)"func_name", (getter)mp_name, nullptr, nullptr, nullptr}, + {(char*)"func_overloads_types", (getter)mp_func_overloads_types, nullptr, nullptr, nullptr}, + {(char*)"func_overloads_names", (getter)mp_func_overloads_names, nullptr, nullptr, nullptr}, // flags to control behavior @@ -1250,4 +1306,4 @@ CPyCppyy::CPPOverload::MethodInfo_t::~MethodInfo_t() Py_XDECREF(fDoc); } -// TODO: something like PyMethod_Fini to clear up the free_list +// TODO: something like PyMethod_Fini to clear up the free_list \ No newline at end of file diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h b/bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h index 62ce3aa1af693..d2a072c5b389e 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h @@ -18,6 +18,8 @@ class PyCallable { public: virtual PyObject* GetSignature(bool show_formalargs = true) = 0; + virtual PyObject* GetSignatureNames() = 0; + virtual PyObject* GetSignatureTypes() = 0; virtual PyObject* GetPrototype(bool show_formalargs = true) = 0; virtual PyObject* GetTypeName() { return GetPrototype(false); } virtual PyObject* GetDocString() { return GetPrototype(); } diff --git a/bindings/pyroot/cppyy/patches/get-names-and-types-of-all-overloads-signature.patch b/bindings/pyroot/cppyy/patches/get-names-and-types-of-all-overloads-signature.patch new file mode 100644 index 0000000000000..58c04ac4f6265 --- /dev/null +++ b/bindings/pyroot/cppyy/patches/get-names-and-types-of-all-overloads-signature.patch @@ -0,0 +1,239 @@ +From a11104f7c6dd357e75bdf18ce91b493ef673d2b2 Mon Sep 17 00:00:00 2001 +From: Vincenzo Eduardo Padulano +Date: Mon, 18 Oct 2021 19:03:19 +0200 +Subject: [PATCH] [PyROOT] Get names and types of all overloads' signature + +Expose two new attributes of a CPPOverload object on the Python side, +namely `func_overloads_names` and `func_overloads_types`. The first +returns a dictionary with all the input parameter names for all the +overloads, the second returns a dictionary with the types of the return +and input parameters for all the overloads. An example: + +```python +import ROOT +from pprint import pprint + +ROOT.gInterpreter.Declare(""" + int foo(int a, float b); + int foo(int a); + float foo(float b); + double foo(int a, float b, double c); +""") + +pprint(ROOT.foo.func_overloads_names) +pprint(ROOT.foo.func_overloads_types) +``` +Output: +``` +{'double ::foo(int a, float b, double c)': ('a', 'b', 'c'), + 'float ::foo(float b)': ('b',), + 'int ::foo(int a)': ('a',), + 'int ::foo(int a, float b)': ('a', 'b')} +{'double ::foo(int a, float b, double c)': {'input_types': ('int', + 'float', + 'double'), + 'return_type': 'double'}, + 'float ::foo(float b)': {'input_types': ('float',), 'return_type': 'float'}, + 'int ::foo(int a)': {'input_types': ('int',), 'return_type': 'int'}, + 'int ::foo(int a, float b)': {'input_types': ('int', 'float'), + 'return_type': 'int'}} +``` +--- + .../pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx | 64 +++++++++++++++++++ + .../pyroot/cppyy/CPyCppyy/src/CPPMethod.h | 2 + + .../pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx | 58 ++++++++++++++++- + .../pyroot/cppyy/CPyCppyy/src/PyCallable.h | 2 + + 4 files changed, 125 insertions(+), 1 deletion(-) + +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx +index 9efac995ac..ff417468bf 100644 +--- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx ++++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx +@@ -1055,6 +1055,70 @@ PyObject* CPyCppyy::CPPMethod::GetSignature(bool fa) + return CPyCppyy_PyText_FromString(GetSignatureString(fa).c_str()); + } + ++/** ++ * @brief Returns a tuple with the names of the input parameters of this method. ++ * ++ * For example given a function with prototype: ++ * ++ * double foo(int a, float b, double c) ++ * ++ * this function returns: ++ * ++ * ('a', 'b', 'c') ++ */ ++PyObject *CPyCppyy::CPPMethod::GetSignatureNames() ++{ ++ // Build a tuple of the argument names for this signature. ++ int argcount = GetMaxArgs(); ++ PyObject *signature_names = PyTuple_New(argcount); ++ ++ for (int iarg = 0; iarg < argcount; ++iarg) { ++ const std::string &argname_cpp = Cppyy::GetMethodArgName(fMethod, iarg); ++ PyObject *argname_py = CPyCppyy_PyText_FromString(argname_cpp.c_str()); ++ PyTuple_SET_ITEM(signature_names, iarg, argname_py); ++ } ++ ++ return signature_names; ++} ++ ++/** ++ * @brief Returns a dictionary with the types of the signature of this method. ++ * ++ * This dictionary will store both the return type and the input parameter ++ * types of this method, respectively with keys "return_type" and ++ * "input_types", for example given a function with prototype: ++ * ++ * double foo(int a, float b, double c) ++ * ++ * this function returns: ++ * ++ * {'input_types': ('int', 'float', 'double'), 'return_type': 'double'} ++ */ ++PyObject *CPyCppyy::CPPMethod::GetSignatureTypes() ++{ ++ ++ PyObject *signature_types_dict = PyDict_New(); ++ ++ // Insert the return type first ++ std::string return_type = GetReturnTypeName(); ++ PyObject *return_type_py = CPyCppyy_PyText_FromString(return_type.c_str()); ++ PyDict_SetItem(signature_types_dict, CPyCppyy_PyText_FromString("return_type"), return_type_py); ++ ++ // Build a tuple of the argument types for this signature. ++ int argcount = GetMaxArgs(); ++ PyObject *parameter_types = PyTuple_New(argcount); ++ ++ for (int iarg = 0; iarg < argcount; ++iarg) { ++ const std::string &argtype_cpp = Cppyy::GetMethodArgType(fMethod, iarg); ++ PyObject *argtype_py = CPyCppyy_PyText_FromString(argtype_cpp.c_str()); ++ PyTuple_SET_ITEM(parameter_types, iarg, argtype_py); ++ } ++ ++ PyDict_SetItem(signature_types_dict, CPyCppyy_PyText_FromString("input_types"), parameter_types); ++ ++ return signature_types_dict; ++} ++ + //---------------------------------------------------------------------------- + std::string CPyCppyy::CPPMethod::GetReturnTypeName() + { +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h b/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h +index 88e49621ef..e9558f5c68 100644 +--- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h ++++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h +@@ -51,6 +51,8 @@ public: + + public: + PyObject* GetSignature(bool show_formalargs = true) override; ++ PyObject* GetSignatureNames() override; ++ PyObject* GetSignatureTypes() override; + PyObject* GetPrototype(bool show_formalargs = true) override; + PyObject* GetTypeName() override; + PyObject* Reflex(Cppyy::Reflex::RequestId_t request, +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx +index d6aa4a747b..9d67f42c82 100644 +--- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx ++++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx +@@ -62,6 +62,12 @@ public: + PyObject* GetSignature(bool /*show_formalargs*/ = true) override { + return CPyCppyy_PyText_FromString("*args, **kwargs"); + } ++ PyObject* GetSignatureNames() override { ++ return PyTuple_New(0); ++ } ++ PyObject* GetSignatureTypes() override { ++ return PyTuple_New(0); ++ } + PyObject* GetPrototype(bool /*show_formalargs*/ = true) override { + return CPyCppyy_PyText_FromString(""); + } +@@ -285,6 +291,54 @@ static int mp_doc_set(CPPOverload* pymeth, PyObject *val, void *) + return 0; + } + ++/** ++ * @brief Returns a dictionary with the input parameter names for all overloads. ++ * ++ * This dictionary may look like: ++ * ++ * {'double ::foo(int a, float b, double c)': ('a', 'b', 'c'), ++ * 'float ::foo(float b)': ('b',), ++ * 'int ::foo(int a)': ('a',), ++ * 'int ::foo(int a, float b)': ('a', 'b')} ++ */ ++static PyObject *mp_func_overloads_names(CPPOverload *pymeth) ++{ ++ ++ const CPPOverload::Methods_t &methods = pymeth->fMethodInfo->fMethods; ++ ++ PyObject *overloads_names_dict = PyDict_New(); ++ ++ for (PyCallable *method : methods) { ++ PyDict_SetItem(overloads_names_dict, method->GetPrototype(), method->GetSignatureNames()); ++ } ++ ++ return overloads_names_dict; ++} ++ ++/** ++ * @brief Returns a dictionary with the types of all overloads. ++ * ++ * This dictionary may look like: ++ * ++ * {'double ::foo(int a, float b, double c)': {'input_types': ('int', 'float', 'double'), 'return_type': 'double'}, ++ * 'float ::foo(float b)': {'input_types': ('float',), 'return_type': 'float'}, ++ * 'int ::foo(int a)': {'input_types': ('int',), 'return_type': 'int'}, ++ * 'int ::foo(int a, float b)': {'input_types': ('int', 'float'), 'return_type': 'int'}} ++ */ ++static PyObject *mp_func_overloads_types(CPPOverload *pymeth) ++{ ++ ++ const CPPOverload::Methods_t &methods = pymeth->fMethodInfo->fMethods; ++ ++ PyObject *overloads_types_dict = PyDict_New(); ++ ++ for (PyCallable *method : methods) { ++ PyDict_SetItem(overloads_types_dict, method->GetPrototype(), method->GetSignatureTypes()); ++ } ++ ++ return overloads_types_dict; ++} ++ + //---------------------------------------------------------------------------- + static PyObject* mp_meth_func(CPPOverload* pymeth, void*) + { +@@ -582,6 +636,8 @@ static PyGetSetDef mp_getset[] = { + {(char*)"func_globals", (getter)mp_func_globals, nullptr, nullptr, nullptr}, + {(char*)"func_doc", (getter)mp_doc, (setter)mp_doc_set, nullptr, nullptr}, + {(char*)"func_name", (getter)mp_name, nullptr, nullptr, nullptr}, ++ {(char*)"func_overloads_types", (getter)mp_func_overloads_types, nullptr, nullptr, nullptr}, ++ {(char*)"func_overloads_names", (getter)mp_func_overloads_names, nullptr, nullptr, nullptr}, + + + // flags to control behavior +@@ -1250,4 +1306,4 @@ CPyCppyy::CPPOverload::MethodInfo_t::~MethodInfo_t() + Py_XDECREF(fDoc); + } + +-// TODO: something like PyMethod_Fini to clear up the free_list ++// TODO: something like PyMethod_Fini to clear up the free_list +\ No newline at end of file +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h b/bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h +index 62ce3aa1af..d2a072c5b3 100644 +--- a/bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h ++++ b/bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h +@@ -18,6 +18,8 @@ public: + + public: + virtual PyObject* GetSignature(bool show_formalargs = true) = 0; ++ virtual PyObject* GetSignatureNames() = 0; ++ virtual PyObject* GetSignatureTypes() = 0; + virtual PyObject* GetPrototype(bool show_formalargs = true) = 0; + virtual PyObject* GetTypeName() { return GetPrototype(false); } + virtual PyObject* GetDocString() { return GetPrototype(); } +-- +2.49.0 +