mirror of
https://github.com/triqs/dft_tools
synced 2024-12-25 05:43: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')])
|
||||
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 :
|
||||
"""
|
||||
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.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) :
|
||||
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) :
|
||||
self._prepare_for_generation()
|
||||
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:
|
||||
f.write(rendered)
|
||||
|
||||
|
@ -510,13 +510,32 @@ template<typename T>
|
||||
|
||||
%if py_meth.python_precall :
|
||||
pyref res_py; // the intermediate result of the pretreatment
|
||||
|
||||
%if isinstance(py_meth.python_precall,python_function) :
|
||||
|
||||
/// A precall function where the code is given
|
||||
static const char * _precall_code = ${py_meth.python_precall.code};
|
||||
static pyref _precall_py_fnt, _precall_py_dict; // keep the function, contained in a specific local dict
|
||||
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 py_fnt = module.attr("${py_meth.python_precall.rsplit('.',1)[1]}");
|
||||
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
|
||||
res_py = PyObject_Call(py_fnt, args, keywds);
|
||||
|
||||
%endif
|
||||
|
||||
res_py = PyObject_Call(_precall_py_fnt, args, keywds);
|
||||
if (PyErr_Occurred()) goto error_return;
|
||||
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; }
|
||||
@ -614,9 +633,28 @@ template<typename T>
|
||||
post_treatment:
|
||||
%if py_meth.python_postcall :
|
||||
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 py_fnt2 = module.attr("${py_meth.python_postcall.rsplit('.',1)[1]}");
|
||||
PyObject * res_final = PyObject_CallFunctionObjArgs(py_fnt2, py_result, NULL);
|
||||
static pyref _postcall_py_fnt = module2.attr("${py_meth.python_postcall.rsplit('.',1)[1]}");
|
||||
|
||||
%endif
|
||||
|
||||
PyObject * res_final = PyObject_CallFunctionObjArgs(_postcall_py_fnt, py_result, NULL);
|
||||
Py_XDECREF(py_result);
|
||||
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...")
|
||||
|
||||
# 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
|
||||
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....
|
||||
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 = "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 = "vector_x2", signature = "std::vector<int>(std::vector<int> v)", doc = "DOC of print_a")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user