1# Copyright (c) 2017-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA) 2# Copyright (c) 2017-2018 Centre national de la recherche scientifique (CNRS) 3# Copyright (c) 2018-2020 Simons Foundation 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http:#www.apache.org/licenses/LICENSE-2.0.txt 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17# Authors: Gregory Kramida, Olivier Parcollet, Nils Wentzell, tayral 18 19import sys, imp 20import re 21import os 22from mako.template import Template 23import importlib 24import collections 25 26# the correspondance c type -> py_type 27c_to_py_type = {'void' : 'None', 'int' : 'int', 'long' : 'int', 'double' : "float", "std::string" : "str"} 28 29# Translation for formatting of parsing converter. 30basic_types_formatting = {'double' : 'd', 'int' : 'i'} 31 32# Translate the name of the c++ type to the python type. 33# for doc signatures. 34def translate_c_type_to_py_type(t) : 35 if t in c_to_py_type : return c_to_py_type[t] 36 m = re.match('std::vector<(.*)>',t) 37 if m: return "list[%s]"%translate_c_type_to_py_type(m.group(1)) 38 # numpy, etc... 39 return t 40 41class cfunction: 42 """ 43 Representation of one overload of a C++ function or method. 44 """ 45 def __init__(self, signature, calling_pattern = None, no_self_c = False, is_constructor = False, 46 is_method = False, is_static = False, release_GIL_and_enable_signal = False, c_name = None, doc = '') : 47 """ 48 Parameters 49 ---------- 50 51 signature : string 52 signature of the function, with types, parameter names and default value 53 rtype( arg1 name1, arg2 name2 = default2, ....) 54 it can be : 55 - a string : 56 rtype (arg1 name1, arg2 name2 = default2, ....) 57 - a string : 58 rtype c_name ( arg1 name1, arg2 name2 = default2, ....) 59 - a dict [expert only] : rtype -> string , args -> list of tuples [ (c_type, variable_name, default_value)] 60 where rtype is the C++ type returned by the function. 61 62 calling_pattern : string 63 A string containing a piece of C++ code to call the C++ function. 64 This code can use : 65 - self_c : a reference to the C++ object (except for constructor, and static method). 66 - self_class : the name of the class of the C++ object (only for static method) 67 - the name of the parameters. 68 It should define a "result" variable : 69 - unless for a constructor or if the C++ return type is void. 70 - result shall be of any type from which the C++ return type is (move) constructible. 71 If calling_pattern is None, a default one is synthesized by the generator, 72 assuming the C++ function has exactly the signature given by the signature parameter of this function 73 including the c_name in it (which is then mandatory). 74 75 no_self_c : boolean. 76 Do not generate self_c reference in C++ code, in some rare calling_pattern. Avoid a compiler warning. 77 is_constructor : boolean 78 is_method : boolean 79 is_static : boolean. 80 If True, it is a static method 81 release_GIL_and_enable_signal : boolean, expert only 82 - For long functions in pure C++. 83 - If True, the GIL is released in the call of the C++ function and restored after the call. 84 - It also saves the signal handler of python and restores it after the call, 85 and enables the C++ signal_handler. 86 - This allows e.g. to intercept Ctrl-C during the long C++ function. 87 - **Requirement** : 88 The function wrapped must be pure C++, i.e. no call whatsoever to the python C API, directly or indirectly. 89 otherwise the behaviour is undefined. 90 doc : string 91 the doc string. 92 93 """ 94 #c_name : internal use only 95 self._calling_pattern = calling_pattern 96 self.is_constructor = is_constructor 97 self.no_self_c = no_self_c 98 self.doc = doc 99 self.is_method = is_method 100 self.is_static = is_static 101 self._dict_call = None 102 if is_static : assert is_method, "is_static only works with method" 103 self.release_GIL_and_enable_signal = release_GIL_and_enable_signal 104 assert isinstance(signature, str) or isinstance(signature, dict), "Signature must be a string of a dict: cf doc" 105 self.c_name = c_name # Not none for internal call only 106 ## Analyse signature. 107 self.args, self.namespace = [], '' 108 if isinstance(signature, str) : # it is a string, we analyse it to get the rtype, and args 109 signature = re.sub('operator\(\s*\)','__operator_call',signature) # temp. replacement, to make the regex easier 110 m = re.match(r"\s*(.*?)\s*\((.*)\)",signature) 111 self.rtype, args = m.group(1).strip() or None, m.group(2).strip() 112 # extract the c_name if present 113 if self.rtype : 114 spl = self.rtype.strip().rsplit(' ',1) 115 if not is_constructor and len(spl)> 1 and '>' not in spl[-1] : 116 self.rtype, c_name_fully_qualified = spl 117 self.namespace, self.c_name = c_name_fully_qualified.rsplit('::',1 ) if ':' in c_name_fully_qualified else ('', c_name_fully_qualified) 118 if self.c_name == '__operator_call' : self.c_name = "operator()" 119 if args.strip().startswith("**") : # special case : dict call 120 assert calling_pattern is None, "When ** is given as argument, no calling pattern can be provided" 121 self._dict_call = args[2:] 122 args, self.args = '','' # no argument 123 def f(): # analyse the argument, be careful that , can also be in type, like A<B,C>, so we count the < > 124 acc = '' 125 for s in args.split(',') : 126 acc += (',' if acc else '') + s.strip() 127 if acc.count('<') == acc.count('>') : 128 r, acc = acc,'' 129 yield r 130 def g(a) : 131 if '=' in a : 132 l,r = a.split('=') 133 return l.strip().rsplit(' ') + [r] 134 else : 135 return a.rsplit(' ',1) 136 #args = [ re.sub('=',' ',x).split() for x in f() if x] # list of (type, name, default) or (type, name) 137 args = [ g(x) for x in f() if x] # list of (type, name, default) or (type, name) 138 else: 139 # mostly internal use, give signature as a dict 140 self.rtype = signature.pop("rtype", None) 141 args = signature.pop('args',()) 142 self.c_name = signature.pop("c_name", '') 143 for a in args: # put back the default if there is none 144 # treat the case when the type is const T *, or T* (e.g. const char *). 145 # Need to regroup the first pieces. 146 assert len(a)>1, 'Incorrect signature %s: did you forget the name of the parameter ?'%a 147 if a[0] == 'const' : a = [' '.join(a[:2])] + list(a[2:]) 148 if a[1] == '*' : a = [' '.join(a[:2])] + list(a[2:]) 149 if len(a) == 2 : (t,n),d = a,None 150 elif len(a) == 3 : t,n,d = a 151 else : raise RuntimeError("Syntax error in overload: args = %s"%args) 152 self.args.append([t.strip(),n.strip(),d]) 153 # end analyze signature 154 155 # ensure no variable starts with __ 156 for t,n,d in self.args : 157 assert not n.startswith('__'), "Variables names starting with __ are reserved for internal use" 158 # 159 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" 160 if self.is_constructor : 161 assert self.rtype == None, "Constructor must not have a return type" 162 self.is_method = False 163 164 def _get_calling_pattern1(self) : 165 """Generation only: gets the calling_pattern or synthesize the default""" 166 if not self._dict_call: return '' 167 return """if (PySequence_Size(args)>0) {PyErr_SetString(PyExc_TypeError, "The function must be called only with named arguments"); goto error_return;} 168 if (!convertible_from_python<%s>(keywds,true)) goto error_return; 169 auto dict_transcript = convert_from_python<%s>(keywds); 170 """%(self._dict_call, self._dict_call) 171 172 def _get_calling_pattern(self) : 173 """Generation only: gets the calling_pattern or synthesize the default""" 174 if self._calling_pattern : return self._calling_pattern 175 s = "%s result = "%self.rtype if self.rtype != "void" else "" 176 self_c = "" 177 if self.is_method: 178 self_c = "self_c." if not self.is_static else "self_class::" 179 # the wrapped types are called by pointer 180 args = ",".join( n for t,n,d in self.args) 181 args = args if self._dict_call is None else "dict_transcript" 182 return "%s %s%s(%s)"%(s,self_c, (self.namespace + '::' if self.namespace else '') + self.c_name, args) 183 184 def _get_signature (self): 185 """Signature for the python doc""" 186 rtype = translate_c_type_to_py_type(self.rtype) if self.rtype else '' 187 args_rep = ", ".join(["%s %s%s"%(translate_c_type_to_py_type(t),n,r' = ' + str(d).replace('"',"'") if d else '') for t,n,d in self.args]) 188 return "({args_rep}) -> {rtype}".format(**locals()) 189 190 def _get_c_signature (self): 191 """Signature for the C++ calling errors""" 192 name = self.c_name if self.c_name else "(no C++ name)" 193 rtype = self.rtype if self.rtype else '' 194 args_rep = ", ".join(["%s %s"%(t,n) for t,n,d in self.args]) 195 return "{name}({args_rep}) -> {rtype}".format(**locals()) 196 197 def __repr__(self): 198 return "C++ function of signature : %s"%(self._get_signature()) 199 200 def _parsing_format(self) : 201 """Generation only: the formatting for the PyParse_xxx calls""" 202 def f(t) : 203 return basic_types_formatting[t] if t in basic_types_formatting else 'O&' 204 l1 = [ f(t) for t,n,d in self.args if d==None] 205 l2 = [ f(t) for t,n,d in self.args if d!=None] 206 if l2 : l2.insert(0,'|') # starts the default arguments, cf python doc 207 return ''.join(l1 + l2) 208 209 def _generate_doc(self) : 210 doc = "\n".join([ " " + x.rstrip() for x in self.doc.split('\n')]) 211 doc = doc.replace('"',"'") # the " are replaced by \"r. 212 #doc = doc.replace('"',r'\"') # the " are replaced by \"r. Does not work, makes \\" 213 if self._dict_call is not None : return doc 214 return "Signature : %s\n%s"%( self._get_signature(),doc) 215 216class pyfunction: 217 """ 218 Representation of one python function of the extension 219 It is basically : 220 - a python name 221 - a list of overload 222 - possibly some preprocessing/postprocessing python code. 223 """ 224 def __init__(self, name, arity = None, is_method = False, is_static = False, doc = ''): 225 """ 226 - name : name given in Python 227 - arity : arity of the function 228 - is_method : boolean 229 - is_static : boolean. Is is a static method 230 - doc : the doc string. 231 - overloads : a list of cfunction objects representing the various C++ overloads of the function 232 """ 233 self.py_name =name # name given in python 234 self.arity = arity 235 self.is_method = is_method # can be a method, a function... 236 self.is_static = is_static # 237 self.doc = doc 238 self.overloads = [] # List of all C++ overloads 239 self.do_implement = True # in some cases, we do not want to implement it automatically, (special methods). 240 self.is_constructor = False 241 242 def add_overload(self, **kw) : 243 self.overloads.append(cfunction(**kw)) 244 return self 245 246 def _generate_doc(self) : 247 if len(self.overloads) == 1 : #only one overload 248 s = "\n".join([f._generate_doc() for f in self.overloads]) 249 else : 250 s = "\n".join([self.doc, "\n"] + [f._generate_doc() for f in self.overloads]) 251 s=s.replace('@{','').replace('@}','') 252 return repr(s)[1:-1] # remove the ' ' made by repr 253 254 255class converter_: 256 """ 257 Representation of a simple converter for a struct 258 """ 259 def __init__(self, c_type, doc=''): 260 """ 261 Parameters 262 ---------- 263 264 c_type : string 265 C++ type to be converted. 266 """ 267 self.c_type = c_type 268 self.doc = doc 269 self.members = [] 270 271 class member_: 272 pass 273 274 def add_member(self, c_name, c_type, initializer = '', doc = ''): 275 """ 276 Add a class member 277 278 Parameters 279 ---------- 280 c_name : string 281 name of the variable in C++ 282 c_type : string 283 type of the C++ variable 284 initializer : string 285 Default value 286 doc : string 287 the doc string. 288 """ 289 m = self.member_() 290 m.c_name, m.c_type, m.initializer, m.doc = (c_name, c_type, initializer.strip(), doc) # the strip is crucial for empty string 291 self.members.append(m) 292 293 def generate(self): 294 """ Generate the C code""" 295 # generate the code for the converters 296 script_path = os.path.dirname(os.path.abspath( __file__ )) 297 tpl = Template(filename=script_path + '/mako/converters.cxx', strict_undefined=True) 298 rendered = tpl.render(c=self) 299 return rendered 300 301class class_: 302 """ 303 Representation of a wrapped type 304 """ 305 def __init__(self, py_type, c_type, c_type_absolute = None, hdf5 = False, arithmetic = None, serializable = None, 306 export = True, is_printable = False, doc = '', comparisons ='') : 307 """ 308 Parameters 309 ---------- 310 311 py_type : string 312 Name given in Python 313 c_type : string 314 C++ type to be wrapped. 315 c_type_absolute : string 316 full path of c_type, no using, no alias (need for the py_converter hpp file) 317 hdf5 : boolean 318 generate the hdf5 write/read function from C++ triqs hdf5 protocol and register them in the hdf_archive 319 arithmetic : tuple of strings 320 321 Determines the operations to be implemented. 322 - The idea is to give an abstract description of the mathematical structure to be implemented : 323 an algebra, a group, a vector space, and so on. 324 The generator will then implement all necessary functions, by calling their C++ counterparts. 325 - Possible values : 326 - ("abelian_group") : implements + and - 327 - ("vector_space", Scalar) : implements a vector_space, with scalar Scalar 328 - ("algebra", Scalar) : implements an algebra, with scalar Scalar 329 - ("algebra_with_unit", with_options..., Scalars...) : 330 implements an algebra, with: 331 - scalars Scalar... : the scalars 332 - with_options is (possibly empty) list of options : 333 - with_unit : +/- of an element with a scalar (injection of the scalar with the unit) 334 - with_unary_minus : implement unary minus 335 - "add_only" : implements only + 336 - with_inplace_operators : option to deduce the +=, -=, ... 337 operators from +,-, .. It deduces the possibles terms to put at the rhs, looking at the 338 case of the +,- operators where the lhs is of the type of self. 339 NB : The operator is mapped to the corresponding C++ operators (for some objects, this may be faster) 340 so it has to be defined in C++ as well.... 341 - .... more to be defined. 342 serializable : boolean 343 Whether and how the object is to be serialized. Possible values are : 344 - "tuple" : reduce it to a tuple of smaller objects, using the 345 boost serialization compatible template in C++, and the converters of the smaller objects. 346 - "h5" : serialize via a string, made by 347 triqs::serialize/triqs::deserialize. 348 Requires hdf5 >1.8.9. 349 - "repr" : serialize via a string produced by python repr, reconstructed by eval. 350 351 is_printable : boolean 352 If true, generate the str, repr from the C++ << stream operator 353 comparisons : string 354 a chain with all operators separated by space, e.g. "== != < >" 355 export : boolean [True] 356 if True, the class converter are exported to modules that import this module. 357 doc : string 358 the doc string. 359 """ 360 self.c_type = c_type 361 self.c_type_absolute = c_type_absolute or c_type 362 self.implement_regular_type_converter = False # Default. Overrule with add_regular_type_converter 363 self.py_type = py_type 364 c_to_py_type[self.c_type] = self.py_type # register the name translation for the doc generation 365 self.hdf5 = hdf5 366 assert serializable in [None, "h5", "tuple", "repr"] 367 self.serializable = serializable 368 self.is_printable = is_printable 369 self.comparisons = comparisons 370 self.iterator = None 371 self.doc = doc 372 self.methods = {} # a dict : string -> pyfunction for each method name 373 self.constructor = None # a pyfunction for the constructors. 374 self.members= [] # a list of _member 375 self.properties= [] # a list of _property 376 self.export = export 377 378 # If hdf5 is True, wrap the C++. 379 # We cannot generate a default implementation with error message as h5::group might not be available. 380 # FIXME Remove triqs dependence 381 if hdf5: 382 self.add_method("void __write_hdf5__(h5::group gr, std::string key)", calling_pattern = "h5_write(gr, key, self_c);", doc = "hdf5 writing") 383 384 # Init arithmetic 385 # expect a tuple : "algebra", "scalar1", "scalar2", etc... 386 self.number_protocol = {} 387 if arithmetic : 388 if not isinstance(arithmetic, tuple) : arithmetic = (arithmetic,) 389 # read the with_... option and clean them for the list 390 with_unary_minus = 'with_unary_minus' in arithmetic 391 with_unit = 'with_unit' in arithmetic 392 with_inplace_operators = 'with_inplace_operators' in arithmetic 393 arithmetic = [x for x in arithmetic if not x.startswith("with_")] 394 add = arithmetic[0] in ("algebra", "abelian_group", "vector_space", "add_only") 395 abelian_group = arithmetic[0] in ("algebra", "abelian_group", "vector_space") 396 vector_space = arithmetic[0] in ("algebra", "vector_space") 397 algebra = arithmetic[0] in ("algebra",) 398 399 if add : 400 # add 401 add = pyfunction(name ="__add__",arity = 2) 402 add.add_overload (calling_pattern = "+", signature = {'args' : [(self.c_type,'x'), (self.c_type,'y')], 'rtype' :self.c_type}) 403 self.number_protocol['add'] = add 404 405 if abelian_group : 406 #sub 407 sub = pyfunction(name ="__sub__",arity = 2) 408 sub.add_overload (calling_pattern = "-", signature = {'args' :[(self.c_type,'x'), (self.c_type,'y')], 'rtype' : self.c_type}) 409 self.number_protocol['subtract'] = sub 410 411 if vector_space : 412 # mul 413 mul = pyfunction(name ="__mul__", arity = 2) 414 for scalar in arithmetic[1:] : 415 mul.add_overload (calling_pattern = "*", signature = {'args' :[(self.c_type,'x'), (scalar,'y')], 'rtype' : self.c_type}) 416 mul.add_overload (calling_pattern = "*", signature = {'args' :[(scalar,'x'), (self.c_type,'y')], 'rtype' : self.c_type}) 417 self.number_protocol['multiply'] = mul 418 # div 419 div = pyfunction(name ="__div__", arity = 2) 420 for scalar in arithmetic[1:] : 421 div.add_overload (calling_pattern = "/", signature = {'args' :[(self.c_type,'x'), (scalar,'y')], 'rtype' : self.c_type}) 422 self.number_protocol['divide'] = div 423 self.number_protocol['true_divide'] = div 424 self.number_protocol['floor_divide'] = div 425 426 if algebra : 427 mul.add_overload (calling_pattern = "*", signature = {'args' :[(self.c_type,'x'), (self.c_type,'y')], 'rtype' : self.c_type}) 428 429 if with_unit: # Allow + and - between scalar and operator 430 assert algebra, "The with_unit option only makes sense for algebra" 431 for scal in arithmetic[1:] : 432 add = self.number_protocol['add'] 433 add.add_overload (calling_pattern = "+", signature = {'args' :[(self.c_type,'x'), (scal,'y')], 'rtype' : self.c_type}) 434 add.add_overload (calling_pattern = "+", signature = {'args' :[(scal,'x'), (self.c_type,'y')], 'rtype' : self.c_type}) 435 sub = self.number_protocol['subtract'] 436 sub.add_overload (calling_pattern = "-", signature = {'args' :[(self.c_type,'x'), (scal,'y')], 'rtype' : self.c_type}) 437 sub.add_overload (calling_pattern = "-", signature = {'args' :[(scal,'x'), (self.c_type,'y')], 'rtype' : self.c_type}) 438 439 if with_unary_minus : 440 # Allow unary - on an operator 441 neg = pyfunction(name = "__neg__", arity = 1) 442 neg.add_overload (calling_pattern = "-", signature = {'args' :[(self.c_type,'x')], 'rtype' : self.c_type}) 443 self.number_protocol['negative'] = neg 444 445 if with_inplace_operators : self.deduce_inplace_arithmetic() 446 447 def add_regular_type_converter(self): 448 self.implement_regular_type_converter = True 449 450 def deduce_inplace_arithmetic(self) : 451 """Deduce all the +=, -=, *=, /= operators from the +, -, *, / operators""" 452 def one_op(op, name, iname) : 453 if name not in self.number_protocol : return 454 impl = pyfunction(name = iname, arity = 2) 455 for overload in self.number_protocol[name].overloads : 456 x_t,y_t = overload.args[0][0], overload.args[1][0] 457 if x_t == self.c_type : # only when first the object 458 impl.add_overload (calling_pattern = op+"=", signature = {'args' : [(x_t,'x'), (y_t,'y')], 'rtype' :overload.rtype}) 459 self.number_protocol['inplace_'+name] = impl 460 one_op('+',"add","__iadd__") 461 one_op('-',"subtract","__isub__") 462 one_op('*',"multiply","__imul__") 463 one_op('/',"divide","__idiv__") 464 one_op('/',"true_divide","__idiv__") 465 one_op('/',"floor_divide","__idiv__") 466 467 def add_constructor(self, signature, calling_pattern = None, intermediate_type = None, doc = ''): 468 """ 469 Parameters 470 ---------- 471 472 signature : string 473 signature of the function, with types, parameter names and defaut value 474 rtype( arg1 name1, arg2 name2 = default2, ....) 475 signature can be : 476 - a string of 2 possible forms (i.e. c_name can be omitted) : 477 - rtype (arg1 name1, arg2 name2 = default2, ....) 478 - rtype c_name ( arg1 name1, arg2 name2 = default2, ....) 479 - a dict : rtype -> string , args -> list of tuples [ (c_type, variable_name, default_value)] 480 - rtype : the C++ type returned by the function. None for constructor 481 default_value is None when there is no default. 482 483 calling_pattern : string, expert only 484 - Pattern to rewrite the call of the c++ constructor. 485 - It is a string, argument name and defining a result of the c_type 486 e.g., the default pattern is :: 487 auto result = c_type (a,b,c) 488 489 intermediate_type : string 490 - Name of a C++ type to be used for constructing the object 491 which is then constructed as c_type { intermediate_type {....}} 492 E.g. Put a regular_type here when wrapping a view. 493 494 doc : string 495 the doc string. 496 """ 497 f = cfunction(signature, calling_pattern = calling_pattern, is_constructor = True, is_method = True, doc = doc) 498 all_args = ",".join(n for t,n,d in f.args) 499 all_args = all_args if f._dict_call is None else "convert_from_python<%s>(keywds)"%f._dict_call # call with the keywds argument 500 f._calling_pattern = '' if f._dict_call is None else "if (!convertible_from_python<%s>(keywds,true)) goto error_return;\n"%f._dict_call 501 if calling_pattern is not None : 502 f._calling_pattern, all_args = calling_pattern + ';\n', "std::move(result)" 503 if intermediate_type: 504 f._calling_pattern += "((%s *)self)->_c = new %s(%s (%s));"%(self.py_type, self.c_type, intermediate_type, all_args) 505 else : 506 f._calling_pattern += "((%s *)self)->_c = new %s (%s);"%(self.py_type, self.c_type,all_args) 507 508 if not self.constructor : 509 self.constructor = pyfunction(name = "__init__", is_method = True, doc = doc) 510 self.constructor.is_constructor = True 511 self.constructor.overloads.append(f) 512 513 def add_method(self, signature, name =None, calling_pattern = None, no_self_c = False, is_method = False, is_static = False, 514 doc = '', release_GIL_and_enable_signal = False, c_name = None): 515 """ 516 Add a C++ overload to a method of name name. 517 518 Parameters 519 ---------- 520 521 signature : string 522 signature of the function, with types, parameter names and defaut value 523 rtype( arg1 name1, arg2 name2 = default2, ....) 524 signature can be : 525 - a string of 2 possible forms (i.e. c_name can be omitted) : 526 - rtype (arg1 name1, arg2 name2 = default2, ....) 527 - rtype c_name ( arg1 name1, arg2 name2 = default2, ....) 528 - a dict : rtype -> string , args -> list of tuples [ (c_type, variable_name, default_value)] 529 - rtype : the C++ type returned by the function. None for constructor 530 default_value is None when there is no default. 531 532 name : string 533 name given in Python 534 535 c_name : string 536 name given in C++ 537 If None, the C++ name extracted from the signature is used. 538 539 calling_pattern : string 540 - Pattern to rewrite the call of the c++ function, 541 - It is a string, using self_c, argument name and defining result at the end if rtype != void 542 e.g., the default pattern is : 543 auto result = self_c.method_name(a,b,c). 544 - If None, the signature must contain c_name 545 no_self_c : boolean. 546 do not generate self_c reference in C++ code, in 547 some rare calling_pattern. Avoid a compiler warning. 548 is_method : boolean 549 is_static : boolean 550 Is is a static method 551 doc : string 552 the doc string. 553 554 release_GIL_and_enable_signal : boolean, expert only 555 - For long functions in pure C++. 556 - If True, the GIL is released in the call of the C++ function and restored after the call. 557 - It also saves the signal handler of python and restores it after the call, 558 and enables the C++ signal_handler. 559 - This allows e.g. to intercept Ctrl-C during the long C++ function. 560 - **Requirement** : 561 The function wrapped must be pure C++, i.e. no call whatsoever to the python C API, directly or indirectly. 562 otherwise the behaviour is undefined. 563 """ 564 f = cfunction(signature, calling_pattern = calling_pattern, no_self_c = no_self_c, is_constructor = False, 565 is_method = True, is_static = is_static, release_GIL_and_enable_signal = release_GIL_and_enable_signal, doc = doc, c_name = c_name or name) 566 name = name or f.c_name 567 if name not in self.methods : 568 self.methods[name] = pyfunction(name = name, is_method = True, is_static = is_static, doc = doc) 569 self.methods[name].overloads.append(f) 570 571 def add_call(self, **kw) : 572 """ 573 Add the __call__ operator. 574 575 It just calls add_method, for the operator(), with name = "__call__" 576 577 Cf add_method documentation. 578 """ 579 if 'c_name' not in kw and 'calling_pattern' not in kw : kw['c_name']= "operator()" 580 self.add_method(name = "__call__", **kw) 581 582 class _iterator: 583 def __init__(self,c_type, c_cast_type, begin, end) : 584 self.c_type, self.c_cast_type, self.begin, self.end = c_type, c_cast_type, begin, end 585 586 def add_iterator(self, c_type = "const_iterator", c_cast_type = None, begin = "std::begin", end = "std::end") : 587 """ 588 Add an iterator, wrapping a C++ iterator. 589 590 Parameters 591 ---------- 592 593 c_type : string 594 type of the C++ variable 595 c_cast_type : string 596 If not None, the result of the C++ iterator dereference if converted to the cast_type. 597 begin, end : string 598 Functions to find begin and end. 599 """ 600 self.iterator = self._iterator(c_type, c_cast_type, begin, end) 601 602 class _member: 603 def __init__(self, c_name, c_type, py_name, read_only, doc): 604 """ 605 Parameters 606 ---------- 607 608 c_name : string 609 name in C 610 c_type : string 611 type in C 612 py_name : string 613 name in Python 614 615 """ 616 self.c_name, self.c_type, self.py_name, self.doc, self.read_only = c_name, c_type, py_name or c_name, doc, read_only 617 618 def _generate_doc(self) : 619 doc = "\n".join([ " " + x.strip() for x in self.doc.split('\n')]) 620 doc = doc.replace('@{','').replace('@}','') 621 return repr(doc)[1:-1] # remove the ' ' made by repr 622 623 def add_member(self, c_name, c_type, py_name = None, read_only = False, doc = ''): 624 """ 625 Add a class member 626 627 Parameters 628 ---------- 629 c_name : string 630 name of the variable in C++ 631 c_type : string 632 type of the C++ variable 633 py_name : string 634 name of the variable in python. If None, use c_name. 635 read_only : boolean 636 is a read only parameter 637 638 doc : string 639 the doc string. 640 """ 641 self.members.append( self._member(c_name, c_type, py_name, read_only, doc)) 642 643 class _property: 644 def __init__(self, name, getter, setter, doc) : 645 self.name, self.getter, self.setter, self.doc = name, getter, setter, doc 646 647 def _generate_doc(self) : 648 doc = "\n".join([ " " + x.strip() for x in self.doc.split('\n')]) 649 doc = doc.replace('@{','').replace('@}','') 650 return repr(doc)[1:-1] # remove the ' ' made by repr 651 652 def add_property(self, getter, setter = None, name = None, doc = ''): 653 """ 654 Add a property 655 656 Parameters 657 ---------- 658 - getter : the cfunction representing the get part 659 - setter : the cfunction representing the set part or None if the property if read only 660 - name : name in python. If None, try to use the C++ name of the getter. 661 - doc : the doc string. 662 """ 663 if not isinstance(getter, str) : getter.is_method = True 664 self.properties.append( self._property(name or getter.c_name, getter, setter, doc) ) 665 666 def add_len(self, c_name = None, calling_pattern = None, doc = "Length") : 667 """ 668 Add the len operator 669 """ 670 if not c_name and not calling_pattern : c_name = "size" 671 self.add_method(name = "__len__impl", calling_pattern = calling_pattern, signature="int %s()"%c_name, doc= doc) 672 self.methods['__len__impl'].do_implement = False # do not implement automatically, the signature is special 673 674 def add_getitem(self, signature, calling_pattern = None, doc = "operator[]" ) : 675 """ 676 Add a the __getitem__ operator 677 """ 678 self.add_method(name = "__getitem__impl", calling_pattern = calling_pattern, doc = doc, signature = signature, c_name = "operator[]") 679 680 def add_setitem(self, signature, calling_pattern = None, doc = "operator[]", **d ) : 681 """ 682 Add a the __setitem__ operator 683 """ 684 self.add_method(name = "__setitem__impl", calling_pattern = calling_pattern or "self_c[i] = v", doc = doc, signature = signature, **d) 685 686 def add_method_copy(self, clone_function = "cpp2py::make_clone") : 687 """Add a method copy, that make a **deep** copy, using the clone function""" 688 self.add_method(name = "copy", calling_pattern = self.c_type + " result = %s(self_c)"%clone_function, 689 signature = self.c_type +"()", doc = "Make a copy (clone) of self") 690 691 def add_method_cpp_copy(self) : 692 """Add a method sh_copy, that make an ordinary copy in C++""" 693 self.add_method(name = "sh_copy", calling_pattern = self.c_type + " result = self_c", signature = self.c_type +"()", doc = "Make a copy of self") 694 695 def add_method_copy_from(self) : 696 """Add a copy_from, using C++ assignment""" 697 # other by pointer, it is necessarly a wrapped type 698 self.add_method(name = "copy_from", calling_pattern = " self_c = other", signature = 'void(' + self.c_type +" other)", doc = "Assignment") 699 700 def _prepare_for_generation(self) : 701 """Internal : Called just before the code generation""" 702 self.has_mapping_protocol = '__getitem__impl' in self.methods or '__len__impl' in self.methods 703 if '__setitem__impl' in self.methods and not '__getitem__impl' in self.methods : raise RuntimeError("Cannot generate a class with a setter and no getter") 704 705class module_: 706 """ 707 Representation of a module 708 """ 709 def __init__(self, full_name, doc = '', app_name = None) : 710 """ 711 Parameters 712 ---------- 713 714 full_name : string 715 complete name of the module (after install, e.g. triqs.gf) 716 717 doc : string 718 doc string 719 720 """ 721 self.full_name = full_name if app_name is None or app_name=="triqs" else app_name+"."+full_name 722 self.name = full_name.rsplit('.',1)[-1] 723 self.doc = doc 724 self.classes = {} # dict : string -> class_. Key is the Python type 725 self.converters = {} # dict : string -> converter 726 self.functions = {} # functions : dict : string -> function_. Modules functions. Key is the python name. 727 self.include_list = [] 728 self.enums = [] 729 self.using =[] 730 self.imports =[] 731 self._preamble = '' 732 733 def add_class(self, cls): 734 """ 735 Add a class into the module. 736 It should not exist in the module already. 737 """ 738 if cls.py_type in self.classes : raise IndexError("The class %s already exists"%cls.py_type) 739 self.classes[cls.py_type] = cls 740 741 def add_converter(self, conv): 742 """ 743 Add a converter into the module. 744 It should not exist in the module already. 745 """ 746 if conv.c_type in self.converters : raise IndexError("The class %s already exists"%conv.c_type) 747 self.converters[conv.c_type] = conv 748 749 def add_function(self, signature, name =None, calling_pattern = None, doc = '', release_GIL_and_enable_signal = False, c_name = None): 750 """ 751 Add a C++ overload to function of the module 752 753 Parameters 754 ---------- 755 756 signature : string 757 signature of the function, with types, parameter names and defaut value 758 rtype( arg1 name1, arg2 name2 = default2, ....) 759 signature can be : 760 - a string of 2 possible forms (i.e. c_name can be omitted) : 761 - rtype (arg1 name1, arg2 name2 = default2, ....) 762 - rtype c_name ( arg1 name1, arg2 name2 = default2, ....) 763 - a dict : rtype -> string , args -> list of tuples [ (c_type, variable_name, default_value)] 764 - rtype : the C++ type returned by the function. None for constructor 765 default_value is None when there is no default. 766 767 name : string 768 name given in Python 769 770 c_name : string 771 name given in C++ 772 If None, the C++ name extracted from the signature is used. 773 774 calling_pattern : string 775 - Pattern to rewrite the call of the c++ function, 776 - It is a string, using self_c, argument name and defining result at the end if rtype != void 777 e.g., the default pattern is : 778 auto result = self_c.method_name(a,b,c). 779 - If None, the signature must contain c_name 780 781 doc : string 782 the doc string. 783 784 release_GIL_and_enable_signal : boolean, expert only 785 - For long functions in pure C++. 786 - If True, the GIL is released in the call of the C++ function and restored after the call. 787 - It also saves the signal handler of python and restores it after the call, 788 and enables the C++ signal_handler. 789 - This allows e.g. to intercept Ctrl-C during the long C++ function. 790 - **Requirement** : 791 The function wrapped MUST be pure C++, i.e. no call whatsoever to the python C API, directly or indirectly. 792 otherwise the behaviour is undefined. 793 """ 794 f = cfunction(signature, calling_pattern = calling_pattern, release_GIL_and_enable_signal = release_GIL_and_enable_signal, doc = doc,c_name = c_name or name) 795 name = name or f.c_name 796 if name not in self.functions : 797 self.functions[name] = pyfunction(name = name, doc = doc) 798 self.functions[name].overloads.append(f) 799 800 def add_include(self, *filenames) : 801 """ 802 Add the filenames as C++ include in the generated wrapper and header. 803 """ 804 self.include_list.extend(filenames) 805 806 def add_using(self,ns) : 807 """ 808 Add the using statement into the generated wrapper (and NOT the header). 809 """ 810 self.using.append(ns) 811 812 def add_imports(self, *lst): 813 """ 814 Add a dependent import to the module. 815 """ 816 self.imports += lst 817 818 def add_preamble(self, preamble) : 819 """ 820 Add the using statement into the generated wrapper (and NOT the header). 821 """ 822 self._preamble += preamble + '\n' 823 824 class _enum: 825 def __init__(self, c_name, values, c_namespace, doc) : 826 self.c_name, self.c_namespace, self.values, self.doc = c_name, c_namespace + "::", values, doc 827 self.c_name_absolute = self.c_namespace + self.c_name 828 829 def add_enum(self, c_name, values, c_namespace ="", doc = '') : 830 """ 831 Add an enum into the module. 832 833 Parameters 834 ---------- 835 836 c_name : string 837 name in C++ 838 c_namespace: string 839 namespace of the enum 840 values : list of string 841 represents the C++ enum values 842 doc : string 843 the doc string. 844 """ 845 self.enums.append( self._enum(c_name, values, c_namespace, doc)) 846 847 def _all_args_kw_functions(self) : 848 l = [ (f, self.name, None) for f in list(self.functions.values())] 849 for c in list(self.classes.values()) : 850 l += [(m,c.py_type, c.c_type) for m in list(c.methods.values()) if m.do_implement] 851 if c.constructor : 852 l.append( (c.constructor,c.py_type, c.c_type)) 853 # Check before generation 854 for f, name, x in l: 855 n_dict_call = sum( 1 if overload._dict_call else 0 for overload in f.overloads) 856 assert n_dict_call <= 1, "At most one possible overload with ** call" 857 assert n_dict_call ==0 or len(f.overloads) == 1, ("The function %s.%s has a ** call and overloads, which is meaningless !"%(name,f.py_name)) 858 return l 859 860 def generate_code(self) : 861 """ 862 Generate the wrapper and the header. 863 The filenames are given in the sys.argv 864 if self.app_name is set, generate a copy of the py_converter 865 file with includes consistent with the installation (as opposed to the build): 866 e.g. #include "a.hpp" in py_converter.hpp becomes 867 #include <app_name/a.hpp> in py_converter.hpp.app_name.install 868 """ 869 script_path = os.path.dirname(os.path.abspath( __file__ )) 870 mako_template = script_path + '/mako/wrap.cxx' 871 wrap_file = sys.argv[1] 872 873 # prepare generation 874 for c in list(self.classes.values()) : c._prepare_for_generation() 875 876 # call mako 877 tpl = Template(filename=mako_template, strict_undefined=True) 878 rendered = tpl.render(module=self, sys_modules = sys.modules) 879 880 with open(wrap_file,'w') as f: 881 f.write(rendered) 882