Skip to content

Commit 1a262e5

Browse files
vepadulanosiliataider
authored andcommitted
[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'}} ```
1 parent 6afd6e8 commit 1a262e5

File tree

4 files changed

+156
-39
lines changed

4 files changed

+156
-39
lines changed

bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,70 @@ PyObject* CPyCppyy::CPPMethod::GetSignature(bool fa)
10551055
return CPyCppyy_PyText_FromString(GetSignatureString(fa).c_str());
10561056
}
10571057

1058+
/**
1059+
* @brief Returns a tuple with the names of the input parameters of this method.
1060+
*
1061+
* For example given a function with prototype:
1062+
*
1063+
* double foo(int a, float b, double c)
1064+
*
1065+
* this function returns:
1066+
*
1067+
* ('a', 'b', 'c')
1068+
*/
1069+
PyObject *CPyCppyy::CPPMethod::GetSignatureNames()
1070+
{
1071+
// Build a tuple of the argument names for this signature.
1072+
int argcount = GetMaxArgs();
1073+
PyObject *signature_names = PyTuple_New(argcount);
1074+
1075+
for (int iarg = 0; iarg < argcount; ++iarg) {
1076+
const std::string &argname_cpp = Cppyy::GetMethodArgName(fMethod, iarg);
1077+
PyObject *argname_py = CPyCppyy_PyText_FromString(argname_cpp.c_str());
1078+
PyTuple_SET_ITEM(signature_names, iarg, argname_py);
1079+
}
1080+
1081+
return signature_names;
1082+
}
1083+
1084+
/**
1085+
* @brief Returns a dictionary with the types of the signature of this method.
1086+
*
1087+
* This dictionary will store both the return type and the input parameter
1088+
* types of this method, respectively with keys "return_type" and
1089+
* "input_types", for example given a function with prototype:
1090+
*
1091+
* double foo(int a, float b, double c)
1092+
*
1093+
* this function returns:
1094+
*
1095+
* {'input_types': ('int', 'float', 'double'), 'return_type': 'double'}
1096+
*/
1097+
PyObject *CPyCppyy::CPPMethod::GetSignatureTypes()
1098+
{
1099+
1100+
PyObject *signature_types_dict = PyDict_New();
1101+
1102+
// Insert the return type first
1103+
std::string return_type = GetReturnTypeName();
1104+
PyObject *return_type_py = CPyCppyy_PyText_FromString(return_type.c_str());
1105+
PyDict_SetItem(signature_types_dict, CPyCppyy_PyText_FromString("return_type"), return_type_py);
1106+
1107+
// Build a tuple of the argument types for this signature.
1108+
int argcount = GetMaxArgs();
1109+
PyObject *parameter_types = PyTuple_New(argcount);
1110+
1111+
for (int iarg = 0; iarg < argcount; ++iarg) {
1112+
const std::string &argtype_cpp = Cppyy::GetMethodArgType(fMethod, iarg);
1113+
PyObject *argtype_py = CPyCppyy_PyText_FromString(argtype_cpp.c_str());
1114+
PyTuple_SET_ITEM(parameter_types, iarg, argtype_py);
1115+
}
1116+
1117+
PyDict_SetItem(signature_types_dict, CPyCppyy_PyText_FromString("input_types"), parameter_types);
1118+
1119+
return signature_types_dict;
1120+
}
1121+
10581122
//----------------------------------------------------------------------------
10591123
std::string CPyCppyy::CPPMethod::GetReturnTypeName()
10601124
{

bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ class CPPMethod : public PyCallable {
5151

5252
public:
5353
PyObject* GetSignature(bool show_formalargs = true) override;
54+
PyObject *GetSignatureNames() override;
55+
PyObject *GetSignatureTypes() override;
5456
PyObject* GetPrototype(bool show_formalargs = true) override;
5557
PyObject* GetTypeName() override;
5658
PyObject* Reflex(Cppyy::Reflex::RequestId_t request,

bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx

Lines changed: 88 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ class TPythonCallback : public PyCallable {
6262
PyObject* GetSignature(bool /*show_formalargs*/ = true) override {
6363
return CPyCppyy_PyText_FromString("*args, **kwargs");
6464
}
65+
PyObject *GetSignatureNames() override { return PyTuple_New(0); }
66+
PyObject *GetSignatureTypes() override { return PyTuple_New(0); }
6567
PyObject* GetPrototype(bool /*show_formalargs*/ = true) override {
6668
return CPyCppyy_PyText_FromString("<callback>");
6769
}
@@ -285,6 +287,54 @@ static int mp_doc_set(CPPOverload* pymeth, PyObject *val, void *)
285287
return 0;
286288
}
287289

290+
/**
291+
* @brief Returns a dictionary with the input parameter names for all overloads.
292+
*
293+
* This dictionary may look like:
294+
*
295+
* {'double ::foo(int a, float b, double c)': ('a', 'b', 'c'),
296+
* 'float ::foo(float b)': ('b',),
297+
* 'int ::foo(int a)': ('a',),
298+
* 'int ::foo(int a, float b)': ('a', 'b')}
299+
*/
300+
static PyObject *mp_func_overloads_names(CPPOverload *pymeth)
301+
{
302+
303+
const CPPOverload::Methods_t &methods = pymeth->fMethodInfo->fMethods;
304+
305+
PyObject *overloads_names_dict = PyDict_New();
306+
307+
for (PyCallable *method : methods) {
308+
PyDict_SetItem(overloads_names_dict, method->GetPrototype(), method->GetSignatureNames());
309+
}
310+
311+
return overloads_names_dict;
312+
}
313+
314+
/**
315+
* @brief Returns a dictionary with the types of all overloads.
316+
*
317+
* This dictionary may look like:
318+
*
319+
* {'double ::foo(int a, float b, double c)': {'input_types': ('int', 'float', 'double'), 'return_type': 'double'},
320+
* 'float ::foo(float b)': {'input_types': ('float',), 'return_type': 'float'},
321+
* 'int ::foo(int a)': {'input_types': ('int',), 'return_type': 'int'},
322+
* 'int ::foo(int a, float b)': {'input_types': ('int', 'float'), 'return_type': 'int'}}
323+
*/
324+
static PyObject *mp_func_overloads_types(CPPOverload *pymeth)
325+
{
326+
327+
const CPPOverload::Methods_t &methods = pymeth->fMethodInfo->fMethods;
328+
329+
PyObject *overloads_types_dict = PyDict_New();
330+
331+
for (PyCallable *method : methods) {
332+
PyDict_SetItem(overloads_types_dict, method->GetPrototype(), method->GetSignatureTypes());
333+
}
334+
335+
return overloads_types_dict;
336+
}
337+
288338
//----------------------------------------------------------------------------
289339
static PyObject* mp_meth_func(CPPOverload* pymeth, void*)
290340
{
@@ -565,44 +615,43 @@ static PyObject* mp_getcppname(CPPOverload* pymeth, void*)
565615

566616
//----------------------------------------------------------------------------
567617
static PyGetSetDef mp_getset[] = {
568-
{(char*)"__name__", (getter)mp_name, nullptr, nullptr, nullptr},
569-
{(char*)"__module__", (getter)mp_module, nullptr, nullptr, nullptr},
570-
{(char*)"__doc__", (getter)mp_doc, (setter)mp_doc_set, nullptr, nullptr},
571-
572-
// to be more python-like, where these are duplicated as well; to actually
573-
// derive from the python method or function type is too memory-expensive,
574-
// given that most of the members of those types would not be used
575-
{(char*)"im_func", (getter)mp_meth_func, nullptr, nullptr, nullptr},
576-
{(char*)"im_self", (getter)mp_meth_self, nullptr, nullptr, nullptr},
577-
{(char*)"im_class", (getter)mp_meth_class, nullptr, nullptr, nullptr},
578-
579-
{(char*)"func_closure", (getter)mp_func_closure, nullptr, nullptr, nullptr},
580-
{(char*)"func_code", (getter)mp_func_code, nullptr, nullptr, nullptr},
581-
{(char*)"func_defaults", (getter)mp_func_defaults, nullptr, nullptr, nullptr},
582-
{(char*)"func_globals", (getter)mp_func_globals, nullptr, nullptr, nullptr},
583-
{(char*)"func_doc", (getter)mp_doc, (setter)mp_doc_set, nullptr, nullptr},
584-
{(char*)"func_name", (getter)mp_name, nullptr, nullptr, nullptr},
585-
586-
587-
// flags to control behavior
588-
{(char*)"__creates__", (getter)mp_getcreates, (setter)mp_setcreates,
589-
(char*)"For ownership rules of result: if true, objects are python-owned", nullptr},
590-
{(char*)"__mempolicy__", (getter)mp_getmempolicy, (setter)mp_setmempolicy,
591-
(char*)"For argument ownership rules: like global, either heuristic or strict", nullptr},
592-
{(char*)"__set_lifeline__", (getter)mp_getlifeline, (setter)mp_setlifeline,
593-
(char*)"If true, set a lifeline from the return value onto self", nullptr},
594-
{(char*)"__release_gil__", (getter)mp_getthreaded, (setter)mp_setthreaded,
595-
(char*)"If true, releases GIL on call into C++", nullptr},
596-
{(char*)"__useffi__", (getter)mp_getuseffi, (setter)mp_setuseffi,
597-
(char*)"not implemented", nullptr},
598-
{(char*)"__sig2exc__", (getter)mp_getsig2exc, (setter)mp_setsig2exc,
599-
(char*)"If true, turn signals into Python exceptions", nullptr},
600-
601-
// basic reflection information
602-
{(char*)"__cpp_name__", (getter)mp_getcppname, nullptr, nullptr, nullptr},
603-
604-
{(char*)nullptr, nullptr, nullptr, nullptr, nullptr}
605-
};
618+
{(char *)"__name__", (getter)mp_name, nullptr, nullptr, nullptr},
619+
{(char *)"__module__", (getter)mp_module, nullptr, nullptr, nullptr},
620+
{(char *)"__doc__", (getter)mp_doc, (setter)mp_doc_set, nullptr, nullptr},
621+
622+
// to be more python-like, where these are duplicated as well; to actually
623+
// derive from the python method or function type is too memory-expensive,
624+
// given that most of the members of those types would not be used
625+
{(char *)"im_func", (getter)mp_meth_func, nullptr, nullptr, nullptr},
626+
{(char *)"im_self", (getter)mp_meth_self, nullptr, nullptr, nullptr},
627+
{(char *)"im_class", (getter)mp_meth_class, nullptr, nullptr, nullptr},
628+
629+
{(char *)"func_closure", (getter)mp_func_closure, nullptr, nullptr, nullptr},
630+
{(char *)"func_code", (getter)mp_func_code, nullptr, nullptr, nullptr},
631+
{(char *)"func_defaults", (getter)mp_func_defaults, nullptr, nullptr, nullptr},
632+
{(char *)"func_globals", (getter)mp_func_globals, nullptr, nullptr, nullptr},
633+
{(char *)"func_doc", (getter)mp_doc, (setter)mp_doc_set, nullptr, nullptr},
634+
{(char *)"func_name", (getter)mp_name, nullptr, nullptr, nullptr},
635+
{(char *)"func_overloads_types", (getter)mp_func_overloads_types, nullptr, nullptr, nullptr},
636+
{(char *)"func_overloads_names", (getter)mp_func_overloads_names, nullptr, nullptr, nullptr},
637+
638+
// flags to control behavior
639+
{(char *)"__creates__", (getter)mp_getcreates, (setter)mp_setcreates,
640+
(char *)"For ownership rules of result: if true, objects are python-owned", nullptr},
641+
{(char *)"__mempolicy__", (getter)mp_getmempolicy, (setter)mp_setmempolicy,
642+
(char *)"For argument ownership rules: like global, either heuristic or strict", nullptr},
643+
{(char *)"__set_lifeline__", (getter)mp_getlifeline, (setter)mp_setlifeline,
644+
(char *)"If true, set a lifeline from the return value onto self", nullptr},
645+
{(char *)"__release_gil__", (getter)mp_getthreaded, (setter)mp_setthreaded,
646+
(char *)"If true, releases GIL on call into C++", nullptr},
647+
{(char *)"__useffi__", (getter)mp_getuseffi, (setter)mp_setuseffi, (char *)"not implemented", nullptr},
648+
{(char *)"__sig2exc__", (getter)mp_getsig2exc, (setter)mp_setsig2exc,
649+
(char *)"If true, turn signals into Python exceptions", nullptr},
650+
651+
// basic reflection information
652+
{(char *)"__cpp_name__", (getter)mp_getcppname, nullptr, nullptr, nullptr},
653+
654+
{(char *)nullptr, nullptr, nullptr, nullptr, nullptr}};
606655

607656
//= CPyCppyy method proxy function behavior ==================================
608657
#if PY_VERSION_HEX >= 0x03080000
@@ -1250,4 +1299,4 @@ CPyCppyy::CPPOverload::MethodInfo_t::~MethodInfo_t()
12501299
Py_XDECREF(fDoc);
12511300
}
12521301

1253-
// TODO: something like PyMethod_Fini to clear up the free_list
1302+
// TODO: something like PyMethod_Fini to clear up the free_list

bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class PyCallable {
1818

1919
public:
2020
virtual PyObject* GetSignature(bool show_formalargs = true) = 0;
21+
virtual PyObject *GetSignatureNames() = 0;
22+
virtual PyObject *GetSignatureTypes() = 0;
2123
virtual PyObject* GetPrototype(bool show_formalargs = true) = 0;
2224
virtual PyObject* GetTypeName() { return GetPrototype(false); }
2325
virtual PyObject* GetDocString() { return GetPrototype(); }

0 commit comments

Comments
 (0)