mirror of
https://github.com/triqs/dft_tools
synced 2024-12-25 13:53:40 +01:00
python wrapper. Pre/postcall can now be inlined
- Add the possibility to give a function "on the fly" for the precall and postcall of a python wrapped functoin. - No change for previous code, it is a simple new feature. - changed test accordingly. See my_module_desc.py for an example.
This commit is contained in:
parent
7040e9f934
commit
3b2372c437
@ -162,44 +162,6 @@ class cfunction :
|
|||||||
doc = "\n".join([ " " + x.strip() for x in self.doc.split('\n')])
|
doc = "\n".join([ " " + x.strip() for x in self.doc.split('\n')])
|
||||||
return "Signature : %s\n%s"%( self._get_signature(),doc)
|
return "Signature : %s\n%s"%( self._get_signature(),doc)
|
||||||
|
|
||||||
class pyfunction :
|
|
||||||
"""
|
|
||||||
Representation of one python function of the extension
|
|
||||||
It is basically :
|
|
||||||
- a python name
|
|
||||||
- a list of overload
|
|
||||||
- possibly some preprocessing/postprocessing python code.
|
|
||||||
"""
|
|
||||||
def __init__(self, name, arity = None, is_method = False, is_static = False, doc = '', python_precall = None, python_postcall = None):
|
|
||||||
"""
|
|
||||||
- name : name given in Python
|
|
||||||
- arity : arity of the function
|
|
||||||
- is_method : boolean
|
|
||||||
- is_static : boolean. Is is a static method
|
|
||||||
- doc : the doc string.
|
|
||||||
- overloads : a list of cfunction objects representing the various C++ overloads of the function
|
|
||||||
- python_precall : a python function_ to be called before the call of the C++ function
|
|
||||||
The function must take F(*args, **kw) and return (args, kw)
|
|
||||||
- python_postcall : a python function_ to be called after the call of the C++ function
|
|
||||||
The function must take a python object, and return one...
|
|
||||||
"""
|
|
||||||
self.py_name =name # name given in python
|
|
||||||
self.arity = arity
|
|
||||||
self.is_method = is_method # can be a method, a function...
|
|
||||||
self.is_static = is_static #
|
|
||||||
self.doc = doc
|
|
||||||
self.python_precall, self.python_postcall = python_precall, python_postcall
|
|
||||||
self.overloads = [] # List of all C++ overloads
|
|
||||||
self.do_implement = True # in some cases, we do not want to implement it automatically, (special methods).
|
|
||||||
self.is_constructor = False
|
|
||||||
|
|
||||||
def add_overload(self, **kw) :
|
|
||||||
self.overloads.append(cfunction(**kw))
|
|
||||||
|
|
||||||
def _generate_doc(self) :
|
|
||||||
s = "\n".join([self.doc, "\n"] + [f._generate_doc() for f in self.overloads])
|
|
||||||
return repr(s)[1:-1] # remove the ' ' made by repr
|
|
||||||
|
|
||||||
class pure_pyfunction_from_module :
|
class pure_pyfunction_from_module :
|
||||||
"""
|
"""
|
||||||
Representation of one python function defined in Python code in an external module.
|
Representation of one python function defined in Python code in an external module.
|
||||||
@ -235,6 +197,46 @@ class python_function:
|
|||||||
self.code = "\n".join(['"%s\\n"'%line.rstrip().replace('"', '\\"') for line in ins.getsourcelines(self.f)[0]])
|
self.code = "\n".join(['"%s\\n"'%line.rstrip().replace('"', '\\"') for line in ins.getsourcelines(self.f)[0]])
|
||||||
self.doc = f.__doc__ # UNUSED AT THE MOMENT ??? REALLY ???
|
self.doc = f.__doc__ # UNUSED AT THE MOMENT ??? REALLY ???
|
||||||
|
|
||||||
|
class pyfunction :
|
||||||
|
"""
|
||||||
|
Representation of one python function of the extension
|
||||||
|
It is basically :
|
||||||
|
- a python name
|
||||||
|
- a list of overload
|
||||||
|
- possibly some preprocessing/postprocessing python code.
|
||||||
|
"""
|
||||||
|
def __init__(self, name, arity = None, is_method = False, is_static = False, doc = '', python_precall = None, python_postcall = None):
|
||||||
|
"""
|
||||||
|
- name : name given in Python
|
||||||
|
- arity : arity of the function
|
||||||
|
- is_method : boolean
|
||||||
|
- is_static : boolean. Is is a static method
|
||||||
|
- doc : the doc string.
|
||||||
|
- overloads : a list of cfunction objects representing the various C++ overloads of the function
|
||||||
|
- python_precall : a python function_ to be called before the call of the C++ function
|
||||||
|
The function must take F(*args, **kw) and return (args, kw)
|
||||||
|
- python_postcall : a python function_ to be called after the call of the C++ function
|
||||||
|
The function must take a python object, and return one...
|
||||||
|
"""
|
||||||
|
self.py_name =name # name given in python
|
||||||
|
self.arity = arity
|
||||||
|
self.is_method = is_method # can be a method, a function...
|
||||||
|
self.is_static = is_static #
|
||||||
|
self.doc = doc
|
||||||
|
def analyse(f):
|
||||||
|
return python_function(f.__name__, f) if callable(f) else f
|
||||||
|
self.python_precall, self.python_postcall = analyse(python_precall), analyse(python_postcall)
|
||||||
|
self.overloads = [] # List of all C++ overloads
|
||||||
|
self.do_implement = True # in some cases, we do not want to implement it automatically, (special methods).
|
||||||
|
self.is_constructor = False
|
||||||
|
|
||||||
|
def add_overload(self, **kw) :
|
||||||
|
self.overloads.append(cfunction(**kw))
|
||||||
|
|
||||||
|
def _generate_doc(self) :
|
||||||
|
s = "\n".join([self.doc, "\n"] + [f._generate_doc() for f in self.overloads])
|
||||||
|
return repr(s)[1:-1] # remove the ' ' made by repr
|
||||||
|
|
||||||
def _is_type_a_view(c_type) :
|
def _is_type_a_view(c_type) :
|
||||||
return c_type.split('<', 1)[0].endswith("_view") # A bit basic ?
|
return c_type.split('<', 1)[0].endswith("_view") # A bit basic ?
|
||||||
|
|
||||||
@ -734,7 +736,7 @@ class module_ :
|
|||||||
def _generate_wrapper_code(self, mako_template, wrap_file) :
|
def _generate_wrapper_code(self, mako_template, wrap_file) :
|
||||||
self._prepare_for_generation()
|
self._prepare_for_generation()
|
||||||
tpl = Template(filename=mako_template)
|
tpl = Template(filename=mako_template)
|
||||||
rendered = tpl.render(module=self, regular_type_if_view_else_type= _regular_type_if_view_else_type, is_type_a_view = _is_type_a_view)
|
rendered = tpl.render(module=self, regular_type_if_view_else_type= _regular_type_if_view_else_type, is_type_a_view = _is_type_a_view, python_function = python_function)
|
||||||
with open(wrap_file,'w') as f:
|
with open(wrap_file,'w') as f:
|
||||||
f.write(rendered)
|
f.write(rendered)
|
||||||
|
|
||||||
|
@ -510,13 +510,32 @@ template<typename T>
|
|||||||
|
|
||||||
%if py_meth.python_precall :
|
%if py_meth.python_precall :
|
||||||
pyref res_py; // the intermediate result of the pretreatment
|
pyref res_py; // the intermediate result of the pretreatment
|
||||||
// get the module where the function is
|
|
||||||
static pyref module = pyref::module("${py_meth.python_precall.rsplit('.',1)[0]}");
|
%if isinstance(py_meth.python_precall,python_function) :
|
||||||
if (module.is_null()) goto error_return;
|
|
||||||
// get the function (only once)
|
/// A precall function where the code is given
|
||||||
static pyref py_fnt = module.attr("${py_meth.python_precall.rsplit('.',1)[1]}");
|
static const char * _precall_code = ${py_meth.python_precall.code};
|
||||||
// call the function : return must be a tuple args, kw to replace the current args, kw
|
static pyref _precall_py_fnt, _precall_py_dict; // keep the function, contained in a specific local dict
|
||||||
res_py = PyObject_Call(py_fnt, args, keywds);
|
if (_precall_py_fnt.is_null()) { // first init
|
||||||
|
PyObject* main_module = PyImport_AddModule("__main__"); //borrowed
|
||||||
|
PyObject* global_dict = PyModule_GetDict(main_module); //borrowed
|
||||||
|
_precall_py_dict = PyDict_New(); // new ref
|
||||||
|
if (!PyRun_String(_precall_code, Py_file_input, global_dict, _precall_py_dict)) goto error_return;
|
||||||
|
_precall_py_fnt = borrowed(PyDict_GetItemString(_precall_py_dict,"${py_meth.python_precall.name}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
%else:
|
||||||
|
|
||||||
|
// get the module where the function is
|
||||||
|
static pyref module = pyref::module("${py_meth.python_precall.rsplit('.',1)[0]}");
|
||||||
|
if (module.is_null()) goto error_return;
|
||||||
|
// get the function (only once)
|
||||||
|
static pyref _precall_py_fnt = module.attr("${py_meth.python_precall.rsplit('.',1)[1]}");
|
||||||
|
// call the function : return must be a tuple args, kw to replace the current args, kw
|
||||||
|
|
||||||
|
%endif
|
||||||
|
|
||||||
|
res_py = PyObject_Call(_precall_py_fnt, args, keywds);
|
||||||
if (PyErr_Occurred()) goto error_return;
|
if (PyErr_Occurred()) goto error_return;
|
||||||
if (!PyTuple_Check(res_py) || (PyTuple_Size(res_py)!=2))
|
if (!PyTuple_Check(res_py) || (PyTuple_Size(res_py)!=2))
|
||||||
{ PyErr_SetString(PyExc_RuntimeError,"${py_meth.python_precall} must return a tuple (args,kw) "); goto error_return; }
|
{ PyErr_SetString(PyExc_RuntimeError,"${py_meth.python_precall} must return a tuple (args,kw) "); goto error_return; }
|
||||||
@ -613,10 +632,29 @@ template<typename T>
|
|||||||
|
|
||||||
post_treatment:
|
post_treatment:
|
||||||
%if py_meth.python_postcall :
|
%if py_meth.python_postcall :
|
||||||
if (py_result) { // should always be true
|
if (py_result) { // should always be true
|
||||||
|
|
||||||
|
%if isinstance(py_meth.python_postcall,python_function) :
|
||||||
|
|
||||||
|
/// A postcall function where the code is given
|
||||||
|
static const char * _postcall_code = ${py_meth.python_postcall.code};
|
||||||
|
static pyref _postcall_py_fnt, _postcall_py_dict; // keep the function, contained in a specific local dict
|
||||||
|
if (_postcall_py_fnt.is_null()) { // first init
|
||||||
|
PyObject* main_module = PyImport_AddModule("__main__"); //borrowed
|
||||||
|
PyObject* global_dict = PyModule_GetDict(main_module); //borrowed
|
||||||
|
_postcall_py_dict = PyDict_New(); // new ref
|
||||||
|
if (!PyRun_String(_postcall_code, Py_file_input, global_dict, _postcall_py_dict)) goto error_return;
|
||||||
|
_postcall_py_fnt = borrowed(PyDict_GetItemString(_postcall_py_dict,"${py_meth.python_postcall.name}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
%else:
|
||||||
|
|
||||||
static pyref module2 = pyref::module("${py_meth.python_postcall.rsplit('.',1)[0]}");
|
static pyref module2 = pyref::module("${py_meth.python_postcall.rsplit('.',1)[0]}");
|
||||||
static pyref py_fnt2 = module.attr("${py_meth.python_postcall.rsplit('.',1)[1]}");
|
static pyref _postcall_py_fnt = module2.attr("${py_meth.python_postcall.rsplit('.',1)[1]}");
|
||||||
PyObject * res_final = PyObject_CallFunctionObjArgs(py_fnt2, py_result, NULL);
|
|
||||||
|
%endif
|
||||||
|
|
||||||
|
PyObject * res_final = PyObject_CallFunctionObjArgs(_postcall_py_fnt, py_result, NULL);
|
||||||
Py_XDECREF(py_result);
|
Py_XDECREF(py_result);
|
||||||
py_result = res_final; // stealing the ref
|
py_result = res_final; // stealing the ref
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,21 @@ g.add_method(name = "m1", c_name = "m1", signature = "double (int u)", doc = "DO
|
|||||||
g.add_method(name = "m1", c_name = "m2", signature = "double (double u)", doc = "DOC of m1...")
|
g.add_method(name = "m1", c_name = "m2", signature = "double (double u)", doc = "DOC of m1...")
|
||||||
|
|
||||||
# another version of the method, with some pre/post processing written in python
|
# another version of the method, with some pre/post processing written in python
|
||||||
g.add_method(name = "m1p", c_name = "m1", signature = "double (int u, double y = 3)", doc = "DOC of mm", python_precall = "aux.ffg", python_postcall = "aux.post1")
|
|
||||||
|
def ffg( *args, **kw) :
|
||||||
|
""" my doc of ffg in module """
|
||||||
|
print "calling ffg, with :"
|
||||||
|
print args
|
||||||
|
print kw
|
||||||
|
return tuple(2*x for x in args), kw
|
||||||
|
|
||||||
|
def post1(res) :
|
||||||
|
#print "calling post1 inline"
|
||||||
|
return [res]
|
||||||
|
|
||||||
|
# First version uses the external module version, second inline
|
||||||
|
#g.add_method(name = "m1p", c_name = "m1", signature = "double (int u, double y = 3)", doc = "DOC of mm", python_precall = "aux.ffg", python_postcall = "aux.post1")
|
||||||
|
g.add_method(name = "m1p", c_name = "m1", signature = "double (int u, double y = 3)", doc = "DOC of mm", python_precall = ffg, python_postcall = post1)
|
||||||
|
|
||||||
# demo of adding a simple piece of C++ code, there is no C++ method corresponding
|
# demo of adding a simple piece of C++ code, there is no C++ method corresponding
|
||||||
g.add_method(name = "m1_x", calling_pattern = "bool result = (self_c.x >0) && (self_c.x < 10)" , signature = "bool()", doc = "A method which did not exist in C++")
|
g.add_method(name = "m1_x", calling_pattern = "bool result = (self_c.x >0) && (self_c.x < 10)" , signature = "bool()", doc = "A method which did not exist in C++")
|
||||||
@ -80,7 +94,7 @@ module.add_class(g)
|
|||||||
# various module functions....
|
# various module functions....
|
||||||
module.add_function (name = "print_a", signature = "void(A a)", doc = "DOC of print_a")
|
module.add_function (name = "print_a", signature = "void(A a)", doc = "DOC of print_a")
|
||||||
module.add_function (name = "print_err", signature = "void(A a)", doc = "DOC of print_a")
|
module.add_function (name = "print_err", signature = "void(A a)", doc = "DOC of print_a")
|
||||||
module.add_function (name = "make_vector", signature = "std::vector<int>(int size)", doc = "DOC of print_a")
|
module.add_function ("std::vector<int> make_vector(int size)", doc = "DOC of print_a")
|
||||||
module.add_function (name = "make_vector2", signature = "std::vector<std::vector<int>>(int size)", doc = "DOC ....")
|
module.add_function (name = "make_vector2", signature = "std::vector<std::vector<int>>(int size)", doc = "DOC ....")
|
||||||
module.add_function (name = "vector_x2", signature = "std::vector<int>(std::vector<int> v)", doc = "DOC of print_a")
|
module.add_function (name = "vector_x2", signature = "std::vector<int>(std::vector<int> v)", doc = "DOC of print_a")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user