diff --git a/pytriqs/gf/local/gf_desc.py b/pytriqs/gf/local/gf_desc.py index 6af43830..8b8196a3 100644 --- a/pytriqs/gf/local/gf_desc.py +++ b/pytriqs/gf/local/gf_desc.py @@ -373,7 +373,7 @@ g.add_method(name = "set_from_legendre", g.add_method(name = "fit_tail", signature = "void(tail_view known_moments, int n_moments, int n_min, int n_max, bool replace_by_fit = true)", - calling_pattern = "fit_tail(self_c, known_moments, n_moments, n_min, n_max, replace_by_fit)", + calling_pattern = "fit_tail(self_c, *known_moments, n_moments, n_min, n_max, replace_by_fit)", doc = """Set the tail by fitting""") # Pure python methods diff --git a/pytriqs/wrap_generator/CMakeLists.txt b/pytriqs/wrap_generator/CMakeLists.txt index e9550369..2c3de87a 100644 --- a/pytriqs/wrap_generator/CMakeLists.txt +++ b/pytriqs/wrap_generator/CMakeLists.txt @@ -5,10 +5,16 @@ SET(PYTHON_SOURCES ) install(FILES ${PYTHON_SOURCES} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/triqs/wrap_generator) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/clang_parser.py DESTINATION bin) # The desc_file generator from libclang ... configure_file(${CMAKE_CURRENT_SOURCE_DIR}/wrapper_desc_generator.py.in ${CMAKE_CURRENT_BINARY_DIR}/wrapper_desc_generator.py @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/wrapper_desc_generator.py DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/clang_parser.py DESTINATION bin) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/wrap_desc.mako.py DESTINATION share/triqs/wrap_generator/) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/converters.mako.hxx DESTINATION share/triqs/wrap_generator/) + +# The param generator +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/param_generator.py.in ${CMAKE_CURRENT_BINARY_DIR}/param_generator.py @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/param_generator.py DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/param.mako.cxx DESTINATION share/triqs/wrap_generator/) diff --git a/pytriqs/wrap_generator/clang_parser.py b/pytriqs/wrap_generator/clang_parser.py index 69da524e..442752d6 100644 --- a/pytriqs/wrap_generator/clang_parser.py +++ b/pytriqs/wrap_generator/clang_parser.py @@ -20,6 +20,11 @@ def process_doc (doc) : file_locations = set(()) +def decay(s) : + for tok in ['const', '&&', '&'] : + s = re.sub(tok,'',s) + return s.strip() + class member_(object): def __init__(self, cursor,ns=()): loc = cursor.location.file.name @@ -28,8 +33,15 @@ class member_(object): self.ns = ns self.name = cursor.spelling self.access = cursor.access_specifier + self.type = type_(cursor.type) self.ctype = cursor.type.spelling - + self.annotations = get_annotations(cursor) + # the declaration split in small tokens + tokens = [t.spelling for t in cursor.get_tokens()] + self.initializer = None + if '=' in tokens: + self.initializer = ''.join(tokens[tokens.index('=') + 1:tokens.index(';')]) + def namespace(self) : return "::".join(self.ns) @@ -37,6 +49,10 @@ class type_(object): def __init__(self, cursor): self.name, self.canonical_name = cursor.spelling, cursor.get_canonical().spelling + def __repr__(self) : + return "type : %s"%(self.name) + #return "type : %s %s\n"%(self.name, self.canonical_name) + class Function(object): def __init__(self, cursor, is_constructor = False, ns=() ): #, template_list =()): loc = cursor.location.file.name @@ -51,7 +67,7 @@ class Function(object): self.template_list = [] #template_list self.is_constructor = is_constructor self.is_static = cursor.is_static_method() - + self.parameter_arg = None # If exists, it is the parameter class for c in cursor.get_children(): if c.kind == clang.cindex.CursorKind.TEMPLATE_TYPE_PARAMETER : self.template_list.append(c.spelling) @@ -60,12 +76,25 @@ class Function(object): for ch in c.get_children() : # TODO : string literal do not work.. needs to use location ? useful ? if ch.kind in [clang.cindex.CursorKind.INTEGER_LITERAL, clang.cindex.CursorKind.FLOATING_LITERAL, - clang.cindex.CursorKind.CHARACTER_LITERAL, clang.cindex.CursorKind.STRING_LITERAL] : - default_value = ch.get_tokens().next().spelling + clang.cindex.CursorKind.CHARACTER_LITERAL, clang.cindex.CursorKind.STRING_LITERAL, + clang.cindex.CursorKind.UNARY_OPERATOR, clang.cindex.CursorKind.UNEXPOSED_EXPR, + clang.cindex.CursorKind.CXX_BOOL_LITERAL_EXPR ] : + #print [x.spelling for x in ch.get_tokens()] + #default_value = ch.get_tokens().next().spelling + default_value = ''.join([x.spelling for x in ch.get_tokens()][:-1]) t = type_(c.type) + + # We look if this argument is a parameter class... + if 'use_parameter_class' in self.annotations : + tt = c.type.get_declaration() # guess it is not a ref + if not tt.location.file : tt = c.type.get_pointee().get_declaration() # it is a T & + #if tt.raw_comment and 'triqs:is_parameter' in tt.raw_comment: + self.parameter_arg = Class(tt, ns) + self.params.append ( (t, c.spelling, default_value )) #else : # print " node in fun ", c.kind + if self.parameter_arg : assert len(self.params) == 1, "When using a parameter class, it must have exactly one argument" self.rtype = type_(cursor.result_type) if not is_constructor else None def namespace(self) : @@ -128,7 +157,9 @@ class Class(object): def namespace(self) : return "::".join(self.ns) - + + def canonical_name(self) : return self.namespace() + '::' + self.name + def __str__(self) : s,s2 = "class {name}:\n {doc}\n\n".format(**self.__dict__),[] for m in self.members : @@ -140,12 +171,15 @@ class Class(object): s2 = '\n'.join( [ " " + l.strip() + '\n' for l in s2 if l.strip()]) return s + s2 + def __repr__(self) : + return "Class %s"%self.name + def build_functions_and_classes(cursor, namespaces=[]): classes,functions = [],[] for c in cursor.get_children(): if (c.kind == clang.cindex.CursorKind.FUNCTION_DECL and c.location.file.name == sys.argv[1]): - functions.append( Function(c,namespaces)) + functions.append( Function(c,is_constructor = False, ns =namespaces)) elif (c.kind in [clang.cindex.CursorKind.CLASS_DECL, clang.cindex.CursorKind.STRUCT_DECL] and c.location.file.name == sys.argv[1]): classes.append( Class(c,namespaces)) @@ -163,6 +197,7 @@ def parse(filename, debug, compiler_options, where_is_libclang): clang.cindex.Config.set_library_file(where_is_libclang) index = clang.cindex.Index.create() print "Parsing the C++ file (may take a few seconds) ..." + #print filename, ['-x', 'c++'] + compiler_options translation_unit = index.parse(filename, ['-x', 'c++'] + compiler_options) print "... done. \nExtracting ..." @@ -180,9 +215,9 @@ def parse(filename, debug, compiler_options, where_is_libclang): print "... done" global file_locations - if len(file_locations) != 1 : - print file_locations - raise RuntimeError, "Multiple file location not implemented" + #if len(file_locations) != 1 : + # print file_locations + # raise RuntimeError, "Multiple file location not implemented" file_locations = list(file_locations) if debug : diff --git a/pytriqs/wrap_generator/converters.mako.hxx b/pytriqs/wrap_generator/converters.mako.hxx new file mode 100644 index 00000000..ec118564 --- /dev/null +++ b/pytriqs/wrap_generator/converters.mako.hxx @@ -0,0 +1,109 @@ +// DO NOT EDIT +// Generated automatically using libclang using the command : +// ${shell_command} +%for c in classes : +<% + def name_lmax(member_list) : + return max(len(m.name) for m in member_list) + + def name_format(name) : + f = '{:<%s}'%(name_lmax(c.members)+2) + return f.format(name) + + def name_format_q(name) : return name_format('"%s"'%name) + + def type_format(name) : + f = '{:<%s}'%(max(len(m.ctype) for m in c.members)) + return f.format(name) +%> + +// --- C++ Python converter for ${c.name} + +namespace triqs { namespace py_tools { + +template <> struct py_converter<${c.name}> { + static PyObject *c2py(${c.name} const & x) { + PyObject * d = PyDict_New(); + %for m in c.members : + PyDict_SetItemString( d, ${name_format('"%s"'%m.name)}, convert_to_python(x.${m.name})); + %endfor + return d; + } + + template static void _get_optional(PyObject *dic, const char *name, T &r, U const &init_default) { + if (PyDict_Contains(dic, pyref::string(name))) + r = convert_from_python(PyDict_GetItemString(dic, name)); + else + r = init_default; + } + + static ${c.name} py2c(PyObject *dic) { + ${c.name} res; + %for m in c.members : + %if m.initializer == None : + res.${m.name} = convert_from_python<${m.ctype}>(PyDict_GetItemString(dic, "${m.name}")); + %else: + _get_optional(dic, ${name_format_q(m.name)}, res.${name_format(m.name)}, ${m.initializer}); + %endif + %endfor + return res; + } + + template + static void _check(PyObject *dic, std::stringstream &fs, int &err, const char *name, const char *tname) { + if (!convertible_from_python(PyDict_GetItemString(dic, name), false)) + fs << "\n" << ++err << " The parameter " << name << " does not have the right type : expecting " << tname + << " in C++, but got '" << PyDict_GetItemString(dic, name)->ob_type->tp_name << "' in Python."; + } + + template + static void _check_mandatory(PyObject *dic, std::stringstream &fs, int &err, const char *name, const char *tname) { + if (!PyDict_Contains(dic, pyref::string(name))) + fs << "\n" << ++err << " Mandatory parameter " << name << " is missing."; + else _check(dic,fs,err,name,tname); + } + + template + static void _check_optional(PyObject *dic, std::stringstream &fs, int &err, const char *name, const char *tname) { + if (PyDict_Contains(dic, pyref::string(name))) _check(dic, fs, err, name, tname); + } + + static bool is_convertible(PyObject *dic, bool raise_exception) { + if (!PyDict_Check(dic)) { + if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Not a python dict");} + return false; + } + std::stringstream fs, fs2; int err=0; + +#ifndef TRIQS_ALLOW_UNUSED_PARAMETERS + std::vector ks, all_keys = {${','.join('"%s"'%m.name for m in c.members)}}; + pyref keys = PyDict_Keys(dic); + if (!convertible_from_python>(keys, true)) { + fs << "\nThe dict keys are not strings"; + goto _error; + } + ks = convert_from_python>(keys); + for (auto & k : ks) + if (std::find(all_keys.begin(), all_keys.end(), k) == all_keys.end()) + fs << "\n"<< ++err << " The parameter '" << k << "' is not recognized."; +#endif + + %for m in c.members : + %if m.initializer == None : + _check_mandatory<${type_format(m.ctype)}>(dic, fs, err, ${name_format_q(m.name)}, "${m.ctype}"); + %else: + _check_optional <${type_format(m.ctype)}>(dic, fs, err, ${name_format_q(m.name)}, "${m.ctype}"); + %endif + %endfor + if (err) goto _error; + return true; + + _error: + fs2 << "\n---- There " << (err > 1 ? "are " : "is ") << err<< " error"<<(err >1 ?"s" : "")<< " in Python -> C++ transcription for the class ${c.name}\n" < +${''.join('namespace %s {'%n for n in c.ns)} + +/// Write into HDF5 +void h5_write(h5::group fg, std::string subgroup_name, ${c.name} const &x) { + auto gr = fg.create_group(subgroup_name); + gr.write_triqs_hdf5_data_scheme("TRIQS_PARAM:${c.name}"); + %for m in c.members: + h5_write(gr, ${name_format_q(m.name)}, x.${m.name}); + %endfor +} + +/// Read from HDF5 +void h5_read(h5::group fg, std::string subgroup_name, ${c.name} &x) { + auto gr = fg.open_group(subgroup_name); + // Check the attribute or throw + auto tag_file = gr.read_triqs_hdf5_data_scheme(); + auto tag_expected = "TRIQS_PARAM:${c.name}"; + if (tag_file != tag_expected) + TRIQS_RUNTIME_ERROR << "h5_read : mismatch of the tag TRIQS_HDF5_data_scheme tag in the h5 group : found " << tag_file + << " while I expected " << tag_expected; + %for m in c.members: + h5_read(gr, ${name_format_q(m.name)}, x.${m.name}); + %endfor +} + +## close namespace +${'}'*len(c.ns)} + +%endfor diff --git a/pytriqs/wrap_generator/param_generator.py.in b/pytriqs/wrap_generator/param_generator.py.in new file mode 100644 index 00000000..80aa08c2 --- /dev/null +++ b/pytriqs/wrap_generator/param_generator.py.in @@ -0,0 +1,52 @@ +#!@PYTHON_INTERPRETER@ + +from clang_parser import parse +import sys, os +from mako.template import Template + +# the instruction that created this file +shell_command = ' '.join( [ sys.argv[0].rsplit('/')[-1]] + [x if ' ' not in x else '"%s"'%x for x in sys.argv[1:]]) + +# +print "Welcome to the C++ code generator of TRIQS, based on libclang !" + +# --- Parsing the arguments of the script and options +import argparse + +parser = argparse.ArgumentParser(description='From a regular class, generate the h5_read/h5_write, mpi, python interface for a parameter class') + +parser.add_argument('filename', help = "Name of the file") +parser.add_argument('--libclang_location', help='Location of the libclang', default = '@TRIQS_LIBCLANG_LOCATION@') +parser.add_argument('--compiler_options', nargs ='*', help='Options to pass to clang') +parser.add_argument('--includes', '-I', action='append', help='Includes to pass to clang') + +args = parser.parse_args() +args.includes = (args.includes or []) + '@TRIQS_INCLUDE_ALL@'.split(';') + +triqs_install_location = '@CMAKE_INSTALL_PREFIX@' +args.includes.insert(0, triqs_install_location + '/include') + +#------------ + +if __name__ == '__main__' : + + compiler_options = args.compiler_options or [] + + compiler_options += ['-I%s'%x for x in args.includes] + add_opts = '@TRIQS_LIBCLANG_CXX_ADDITIONAL_FLAGS@'.strip() + if add_opts: + compiler_options += add_opts.split() + + functions, classes = parse(args.filename, debug = False, compiler_options = compiler_options, where_is_libclang = args.libclang_location) + + print "Generating ..." + + tpl = Template(filename=triqs_install_location + '/share/triqs/wrap_generator/param.mako.cxx') + shell_command = ' '.join( [ sys.argv[0].rsplit('/')[-1]] + sys.argv[1:]) + rendered = tpl.render(classes = classes, functions = functions, args = args, shell_command= shell_command ) + + with open(args.filename.replace('.hpp',".hxx"), "w") as f: + f.write(rendered) + + print "... done" + diff --git a/pytriqs/wrap_generator/wrap_desc.mako.py b/pytriqs/wrap_generator/wrap_desc.mako.py index 69262139..3dcc0e9c 100644 --- a/pytriqs/wrap_generator/wrap_desc.mako.py +++ b/pytriqs/wrap_generator/wrap_desc.mako.py @@ -4,8 +4,9 @@ return ''.join([x.capitalize() for x in s.split('_')]) def decay(s) : - for tok in ['const', '&&', '&'] : + for tok in ['const ', 'const&', '&&', '&'] : s = re.sub(tok,'',s) + s = s.replace('const_view', 'view') # DISCUSS THIS return s.strip() # compute used_module_list @@ -15,12 +16,19 @@ 'triqs::utility::many_body_operator' : 'operators2', } + using_needed_for_modules = { + 'gf' : 'namespace triqs::gfs', + 'parameters' : 'namespace triqs::params', + 'operators2' : 'triqs::utility::many_body_operator', + } + used_module_list = [] def analyse(t) : + if t is None :return #global used_module_list for ns, mod in recognized_namespace_for_using.items() : if decay(t.canonical_name).startswith(ns) : - used_module_list.append(mod) + used_module_list.append(mod) for c in classes : for m in c.constructors : @@ -31,16 +39,22 @@ for p in c.proplist : analyse(p.getter.rtype) + for c in classes_of_parameters : + for m in c.members : + analyse(m.type) + for f in functions : for t,n,d in f.params : analyse(t) analyse(f.rtype) used_module_list = set(used_module_list) # makes unique + using_list = [using_needed_for_modules[m] for m in used_module_list] def cls(t) : tname = decay(t.name) if 'gf' in used_module_list: tname = re.sub('triqs::gfs::','',tname) if 'parameters' in used_module_list: tname = re.sub('triqs::params::','',tname) + tname = tname.replace(' ','') return tname def make_signature(m) : @@ -48,47 +62,74 @@ s = "({args})" if not m.is_constructor : s = cls(m.rtype) + " {name} " + s - s = s.format(args = ', '.join( ["%s %s"%(cls(t),n) + (" = %s"%d if d else "") for t,n,d in m.params]), **m.__dict__) + args = ', '.join( ["%s %s"%(cls(t),n) + (" = %s"%d if d else "") for t,n,d in m.params]) if m.parameter_arg == None else '**%s'%cls(m.params[0][0]) + s = s.format(args = args, **m.__dict__) return s.strip() %> -## -## -# Generated automatically using libclang using the command : +# Generated automatically using the command : # ${shell_command} from wrap_generator import * # The module -module = module_(full_name = "${modulename}", doc = " ") +module = module_(full_name = "${modulename}", doc = "${moduledoc}") # All the triqs C++/Python modules %for mod in used_module_list : module.use_module('${mod}') %endfor +## +## All the using +##%for ns in using_list : +##module.add_using('${ns}') +##%endfor # Add here all includes beyond what is automatically included by the triqs modules module.add_include("${args.filename}") # Add here anything to add in the C++ code at the start, e.g. namespace using module.add_preamble(""" -// using namespace XXX; +%for ns in using_list : +using ${ns}; +%endfor +%for c in classes : +%for ns in c.ns : +using namespace ${ns}; +%endfor +%endfor +%if classes_of_parameters : +#include "./converters.hxx" +%endif """) - ## %for c in classes : +<% + def doc_format(member_list) : + h= ['Parameter Name', 'Type', 'Default', 'Documentation'] + n_lmax = max(len(h[0]), max(len(m.name) for m in member_list)) + type_lmax = max(len(h[1]), max(len(m.ctype) for m in member_list)) + opt_lmax = max(len(h[2]), max(len(m.initializer) for m in member_list if m.initializer)) + doc_lmax = max(len(h[3]), max(len(m.doc) for m in member_list)) + form = " {:<%s} {:<%s} {:<%s} {:<%s}"%(n_lmax, type_lmax, opt_lmax, doc_lmax) + header = form.format(*h) + r = '\n'.join( form.format(m.name, m.ctype, m.initializer if m.initializer else '--', m.doc) for m in member_list) + return header + '\n\n' + r +%> # The class ${c.name} -g = class_( +c = class_( py_type = "${deduce_normalized_python_class_name(c.name)}", # name of the python class c_type = "${c.name}", # name of the C++ class +%if 0 : # #Hereafter several options to be selected by hand. Cf doc #has_iterator = True, #boost_serializable= True, #is_printable= True, #arithmetic = ("algebra","double") - ) +%endif +) %for m in c.members : -g.add_member(c_name = "${m.name}", +c.add_member(c_name = "${m.name}", c_type = "${m.ctype}", read_only= False, doc = """${m.doc} """) @@ -96,23 +137,22 @@ g.add_member(c_name = "${m.name}", %endfor ## %for m in [m for m in c.constructors if not m.is_template]: -g.add_constructor("${make_signature(m)}", +c.add_constructor("""${make_signature(m)}""", doc = """${m.doc} """) %endfor ## -## -%for m in [m for m in c.methods]: -g.add_method("${make_signature(m)}", +%for m in c.methods: +c.add_method("""${make_signature(m)}""", %if m.is_static : is_static = True, %endif - doc = """${m.doc} """) + doc = """${m.doc if m.parameter_arg==None else doc_format(m.parameter_arg.members) } """) %endfor ## %for p in [p for p in c.proplist]: -g.add_property(name = "${p.name}", +c.add_property(name = "${p.name}", getter = cfunction("${make_signature(p.getter)}"), %if p.setter : setter = cfunction("${make_signature(p.setter)}"), @@ -121,7 +161,7 @@ g.add_property(name = "${p.name}", %endfor ## -module.add_class(g) +module.add_class(c) %endfor ## diff --git a/pytriqs/wrap_generator/wrap_generator.py b/pytriqs/wrap_generator/wrap_generator.py index d6b73154..83d474fa 100644 --- a/pytriqs/wrap_generator/wrap_generator.py +++ b/pytriqs/wrap_generator/wrap_generator.py @@ -79,6 +79,7 @@ class cfunction : self.doc = doc self.is_method = is_method self.is_static = is_static + self._dict_call = None if is_static : assert is_method, "is_static only works with method" self.release_GIL_and_enable_signal = release_GIL_and_enable_signal assert isinstance(signature, str) or isinstance(signature, dict), "Signature must be a string of a dict: cf doc" @@ -95,7 +96,10 @@ class cfunction : if not is_constructor and len(spl)> 1 and '>' not in spl[-1] : self.rtype, self.c_name = spl if self.c_name == '__operator_call' : self.c_name = "operator()" - + if args.strip().startswith("**") : # special case : dict call + assert calling_pattern is None, "When ** is given as argument, no calling pattern can be provided" + self._dict_call = args[2:] + args, self.args = '','' # no argument def f(): # analyse the argument, be careful that , can also be in type, like A, so we count the < > acc = '' for s in args.split(',') : @@ -103,7 +107,14 @@ class cfunction : if acc.count('<') == acc.count('>') : r, acc = acc,'' yield r - args = [ re.sub('=',' ',x).split() for x in f() if x] # list of (type, name, default) or (type, name) + def g(a) : + if '=' in a : + l,r = a.split('=') + return l.strip().rsplit(' ') + [r] + else : + return a.rsplit(' ',1) + #args = [ re.sub('=',' ',x).split() for x in f() if x] # list of (type, name, default) or (type, name) + args = [ g(x) for x in f() if x] # list of (type, name, default) or (type, name) else : self.rtype = signature.pop("rtype", None) args = signature.pop('args',()) @@ -116,7 +127,7 @@ class cfunction : if len(a) == 2 : (t,n),d = a,None elif len(a) == 3 : t,n,d = a else : raise RuntimeError, "Syntax error in overload: args = %s"%args - self.args.append([t,n,d]) + self.args.append([t.strip(),n.strip(),d]) # end analyze signature assert self.c_name or self._calling_pattern or self.is_constructor, "You must specify a calling_pattern or the signature must contain the name of the function" if self.is_constructor : @@ -126,12 +137,15 @@ class cfunction : def _get_calling_pattern(self) : """Generation only: gets the calling_pattern or synthesize the default""" if self._calling_pattern : return self._calling_pattern - s= "%s result = "%self.rtype if self.rtype != "void" else "" + s1 = '' if self._dict_call is None else "if (!convertible_from_python<%s>(keywds,true)) goto error_return;\n"%self._dict_call + s = "%s result = "%self.rtype if self.rtype != "void" else "" self_c = "" if self.is_method: self_c = "self_c." if not self.is_static else "self_class::" # the wrapped types are called by pointer - return "%s %s%s(%s)"%(s,self_c, self.c_name , ",".join([ ('*' if t in module_._wrapped_types else '') + n for t,n,d in self.args])) + args = ",".join([ ('*' if t in module_._wrapped_types else '') + n for t,n,d in self.args]) + args = args if self._dict_call is None else "convert_from_python<%s>(keywds)"%self._dict_call # call with the keywds argument + return "%s %s %s%s(%s)"%(s1, s,self_c, self.c_name , args) def _get_signature (self): """Signature for the python doc""" @@ -160,6 +174,9 @@ class cfunction : def _generate_doc(self) : doc = "\n".join([ " " + x.strip() for x in self.doc.split('\n')]) + doc = doc.replace('"',"'") # the " are replaced by \"r. + #doc = doc.replace('"',r'\"') # the " are replaced by \"r. Does not work, makes \\" + if self._dict_call is not None : return doc return "Signature : %s\n%s"%( self._get_signature(),doc) class pure_pyfunction_from_module : @@ -234,7 +251,10 @@ class pyfunction : self.overloads.append(cfunction(**kw)) def _generate_doc(self) : - s = "\n".join([self.doc, "\n"] + [f._generate_doc() for f in self.overloads]) + if len(self.overloads) == 1 : #only one overload + s = "\n".join([f._generate_doc() for f in self.overloads]) + else : + 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) : @@ -417,7 +437,7 @@ class class_ : all_args = ",".join([ ('*' if t in module_._wrapped_types else '') + n for t,n,d in f.args]) f._calling_pattern = '' if calling_pattern is not None : - f._calling_pattern, all_args = calling_pattern + '\n', "std::move(result)" + f._calling_pattern, all_args = calling_pattern + ';\n', "std::move(result)" if self.c_type_is_view and build_from_regular_type_if_view : f._calling_pattern += "((%s *)self)->_c = new %s(%s (%s));"%(self.py_type, self.c_type,_regular_type_if_view_else_type(self.c_type),all_args) else : diff --git a/pytriqs/wrap_generator/wrapper.mako.cpp b/pytriqs/wrap_generator/wrapper.mako.cpp index 54f9a9f3..6b796076 100644 --- a/pytriqs/wrap_generator/wrapper.mako.cpp +++ b/pytriqs/wrap_generator/wrapper.mako.cpp @@ -555,6 +555,7 @@ template // If no overload, we avoid the err_list and let the error go through (to save some code). %for n_overload, overload in enumerate(py_meth.overloads) : {// overload ${overload._get_c_signature()} + %if not overload._dict_call : // define the variable to be filled by the parsing method // wrapped types are converted to a pointer, other converted types to a value or a view %for t,n,d in overload.args : @@ -568,7 +569,9 @@ template %endfor static char *kwlist[] = {${",".join([ '"%s"'%n for t,n,d in overload.args] + ["NULL"])}}; static const char * format = "${overload._parsing_format()}"; - if (PyArg_ParseTupleAndKeywords(args, keywds, format, kwlist ${"".join([ module._get_proper_converter(t) + ' ,&%s'%n for t,n,d in overload.args])})) { + if (PyArg_ParseTupleAndKeywords(args, keywds, format, kwlist ${"".join([ module._get_proper_converter(t) + ' ,&%s'%n for t,n,d in overload.args])})) + %endif + { %if overload.is_method and not overload.is_constructor and not overload.no_self_c and not overload.is_static : auto & self_c = convert_from_python<${self_c_type}>(self); %endif diff --git a/pytriqs/wrap_generator/wrapper_desc_generator.py.in b/pytriqs/wrap_generator/wrapper_desc_generator.py.in index e9c0828d..927b8478 100644 --- a/pytriqs/wrap_generator/wrapper_desc_generator.py.in +++ b/pytriqs/wrap_generator/wrapper_desc_generator.py.in @@ -1,24 +1,37 @@ #!@PYTHON_INTERPRETER@ -from clang_parser import parse +from clang_parser import parse, decay import sys, os from mako.template import Template +# the instruction that created this file +shell_command = ' '.join( [ sys.argv[0].rsplit('/')[-1]] + [x if ' ' not in x else '"%s"'%x for x in sys.argv[1:]]) + +# +print "Welcome to the wrapper desc file generator of TRIQS, based on libclang !" + +# macro vim to replace the old param class ... +# :'<,'>s/\s*\.add_field("\(.\{-}\)",\s*\(.\{-}\)(\(.\{-}\)),\s*"\(.\{-}\)")/\/\/\/\4\r\2 \1 = \3;\r/g + # --- Parsing the arguments of the script and options import argparse -parser = argparse.ArgumentParser(description='C++/Python wrapper desc file generator from C++ header code') +parser = argparse.ArgumentParser(description=""" +Generate the C++/Python wrapper desc file from C++ header code +""") parser.add_argument('filename', help = "Name of the file") -parser.add_argument('--modulename', help='Name of the Python module', default = '') -parser.add_argument('--libclang_location', help='Location of the libclang', default = '@TRIQS_LIBCLANG_LOCATION@') -parser.add_argument('--compiler_options', nargs ='*', help='Options to pass to clang') -parser.add_argument('--includes', '-I', action='append', help='Includes to pass to clang') +parser.add_argument('--outputname', '-o', help="Name of the xxx_desc.py file [default is same as the filename]", default = '') +parser.add_argument('--modulename', '-m', help="Name of the Python module [default ='', it will take the name of file", default = '') +parser.add_argument('--moduledoc', help="Documentation of the module", default = '') parser.add_argument('--properties', '-p', action='store_true', help="""Transforms i) every method with no arguments into read-only property ii) every method get_X into read-only property iii) every couple of methods get_X, set_X into rw property """) +parser.add_argument('--libclang_location', help='Location of the libclang', default = '@TRIQS_LIBCLANG_LOCATION@') +parser.add_argument('--compiler_options', nargs ='*', help='Options to pass to clang') +parser.add_argument('--includes', '-I', action='append', help='Includes to pass to clang') args = parser.parse_args() args.includes = (args.includes or []) + '@TRIQS_INCLUDE_ALL@'.split(';') @@ -29,6 +42,7 @@ args.includes.insert(0, triqs_install_location + '/include') #------------ modulename = args.modulename or os.path.split(args.filename)[1].split('.',1)[0] +output_name = args.outputname or os.path.split(args.filename)[1].split('.',1)[0] class property_ : def __init__ (self, **kw) : @@ -55,31 +69,50 @@ if __name__ == '__main__' : m_by_names =dict( (m.name,m) for m in cls.methods) # Find all the couples get_X, set_X for m in cls.methods : - if m.is_template or m.name.startswith('operator') or m.name in ['begin','end'] : + if m.is_template or m.name.startswith('operator') or m.name in ['begin','end'] : # or m.rtype.name=="void" : exclude.append(m) - elif m.name.startswith('get_') : - X = m.name[4:] - set_m = m_by_names.get('set_' + X, None) - if set_m and set_m.rtype == "void" and len(set_m.params_decay) ==1 : - if set_m.params_decay[0][0] == m.rtype : - cls.proplist.append(property_(name= X, doc = m.doc, getter = m, setter = set_m)) - exclude += [m,set_m] + elif len(m.params) == 0 and not m.is_static : # it is a property + X = m.name[4:] if m.name.startswith('get_') else m.name # remove the get_ if present + set_m = m_by_names.get('set_' + X, None) + if set_m and set_m.rtype.name == "void" and len(set_m.params) ==1 : + if decay(set_m.params[0][0].name) == m.rtype.name : + exclude.append(set_m) else : print "Warning :" print " in get_%s/set_%s" %(X,X) print " The type taken from set_%s is not the return type of get_%s"%(X,X) - print " I am not transforming to property" - - elif len(m.params) == 0 and not m.is_static : # it is a property not starting with get_, pure getter - cls.proplist.append(property_(name= m.name, doc = m.doc, getter = m, setter = None)) + print " Expected ",m.rtype.name + print " Got ", decay(set_m.params[0][0].name) + print " I am not adding the setter to the property" + set_m = None + cls.proplist.append(property_(name= X, doc = m.doc, getter = m, setter = set_m)) + print "Property : ", m.name, set_m.name if set_m else '' exclude.append(m) cls.methods = [m for m in cls.methods if m not in exclude] + + # all classes used as parameters needs to be converted from / to Python + classes_of_parameters = [m.parameter_arg for m in sum((c.methods for c in classes),[]) if m.parameter_arg != None] + classes_of_parameters = dict(((c.name,c) for c in classes_of_parameters)).values() # make unique + if classes_of_parameters : + print "Generating the converters for parameters classes : " + ', '.join([c.name for c in classes_of_parameters]) + # Generate the converter code for all the classes. + tpl = Template(filename=triqs_install_location + '/share/triqs/wrap_generator/converters.mako.hxx') + rendered = tpl.render(classes = classes_of_parameters, args = args, shell_command= shell_command ) + #with open(args.filename.replace('.hpp',".hxx"), "w") as f: + with open("converters.hxx", "w") as f: + f.write(rendered) + + print "... done" + + # Making the desc file tpl = Template(filename=triqs_install_location + '/share/triqs/wrap_generator/wrap_desc.mako.py') - rendered = tpl.render(classes = classes, functions = functions, modulename=modulename, args = args, shell_command= ' '.join(sys.argv) ) + rendered = tpl.render(classes = classes, functions = functions, modulename=modulename, moduledoc=args.moduledoc, args = args, + shell_command= shell_command, classes_of_parameters = classes_of_parameters ) - with open("{modulename}_desc.py".format(modulename=modulename), "w") as f: + with open("{output_name}_desc.py".format(output_name=output_name), "w") as f: f.write(rendered) - - print "... done" + + print "... done" + diff --git a/triqs/gfs/local/fit_tail.hpp b/triqs/gfs/local/fit_tail.hpp index 5b54dde6..724f1805 100644 --- a/triqs/gfs/local/fit_tail.hpp +++ b/triqs/gfs/local/fit_tail.hpp @@ -53,4 +53,4 @@ namespace triqs { namespace gfs { int n_max, bool replace_by_fit = false) ; void fit_tail(gf_view gf, tail_view known_moments, int n_moments, int n_min, int n_max, bool replace_by_fit = false) ; -}}} // namespace +}} // namespace diff --git a/triqs/python_tools/wrapper_tools.hpp b/triqs/python_tools/wrapper_tools.hpp index aafd71ef..56b73a5c 100644 --- a/triqs/python_tools/wrapper_tools.hpp +++ b/triqs/python_tools/wrapper_tools.hpp @@ -516,6 +516,10 @@ template struct py_converter struct py_converter>> : +py_converter>> {}; + // Converter for scalar_valued gf : reinterpreted as 1x1 matrix template struct py_converter>{