2014-05-08 22:15:15 +02:00
import sys
import re
import os
from mako . template import Template
import importlib
2014-05-30 19:21:38 +02:00
# the xxx_desc.py will always be called by cmake command, with the proper arguments
# analyse sys.argv right now : we need it early for the use_module
2014-05-31 11:38:40 +02:00
# The mako files for the wrapper and the header, and the file to write (_target)
2014-05-30 19:21:38 +02:00
wrapper_mako , wrapper_target , header_mako , header_target = sys . argv [ 1 : 5 ]
2014-05-31 11:38:40 +02:00
# Directories where to find the headers generated by other modules
2014-05-30 19:21:38 +02:00
module_path_list = sys . argv [ 5 : ]
2014-05-08 22:15:15 +02:00
# the correspondance c type -> py_type
c_to_py_type = { ' void ' : ' None ' , ' int ' : ' int ' , ' long ' : ' int ' , ' double ' : " float " , " std::string " : " str " }
2014-05-31 11:38:40 +02:00
# Translation for formatting of parsing converter.
2014-05-08 22:15:15 +02:00
basic_types_formatting = { ' double ' : ' d ' , ' int ' : ' i ' }
2014-05-31 11:38:40 +02:00
# Translate the name of the c++ type to the python type.
# for doc signatures.
2014-05-08 22:15:15 +02:00
def translate_c_type_to_py_type ( t ) :
if t in c_to_py_type : return c_to_py_type [ t ]
m = re . match ( ' std::vector<(.*)> ' , t )
if m : return " list[ %s ] " % translate_c_type_to_py_type ( m . group ( 1 ) )
# numpy, etc...
return t
class cfunction :
"""
2014-05-31 11:38:40 +02:00
Representation of one overload of a C + + function or method .
"""
def __init__ ( self , signature , calling_pattern = None , no_self_c = False , is_constructor = False ,
is_method = False , is_static = False , release_GIL_and_enable_signal = False , c_name = None , doc = ' ' ) :
"""
- signature :
signature of the function , with types , parameter names and defaut value
rtype ( arg1 name1 , arg2 name2 = default2 , . . . . )
it can be :
- a string :
rtype ( arg1 name1 , arg2 name2 = default2 , . . . . )
- a string :
rtype c_name ( arg1 name1 , arg2 name2 = default2 , . . . . )
- a dict [ expert only ] : rtype - > string , args - > list of tuples [ ( c_type , variable_name , default_value ) ]
where rtype is the C + + type returned by the function .
- calling_pattern :
A string containing a piece of C + + code to call the C + + function .
This code can use :
- self_c : a reference to the C + + object ( except for constructor , and static method ) .
- self_class : the name of the class of the C + + object ( only for static method )
- the name of the parameters .
It should define a " result " variable :
- unless for a constructor or if the C + + return type is void .
- result shall be of any type from which the C + + return type is ( move ) constructible .
If calling_pattern is None , a default one is synthesized by the generator ,
assuming the C + + function has exactly the signature given by the signature parameter of this function
including the c_name in it ( which is then mandatory ) .
- no_self_c : boolean . do not generate self_c reference in C + + code , in some rare calling_pattern . Avoid a compiler warning .
2014-05-08 22:15:15 +02:00
- is_constructor : boolean
2014-05-31 11:38:40 +02:00
- is_method : boolean
- is_static : boolean . If True , it is a static method
2014-05-26 09:34:22 +02:00
- release_GIL_and_enable_signal [ expert only ] :
2014-05-31 11:38:40 +02:00
- For long functions in pure C + + .
- If True , the GIL is released in the call of the C + + function and restored after the call .
- It also saves the signal handler of python and restores it after the call ,
and enables the C + + triqs signal_handler .
- This allows e . g . to intercept Ctrl - C during the long C + + function .
- * * Requirement * * :
The function wrapped must be pure C + + , i . e . no call whatsoever to the python C API , directly or indirectly .
otherwise the behaviour is undefined .
- doc : the doc string .
- c_name : Internal use only .
"""
self . _calling_pattern = calling_pattern
self . is_constructor = is_constructor
self . no_self_c = no_self_c
2014-05-08 22:15:15 +02:00
self . doc = doc
self . is_method = is_method
2014-05-30 11:11:28 +02:00
self . is_static = is_static
2014-05-31 11:38:40 +02:00
if is_static : assert is_method , " is_static only works with method "
2014-05-26 09:34:22 +02:00
self . release_GIL_and_enable_signal = release_GIL_and_enable_signal
2014-05-31 11:38:40 +02:00
assert isinstance ( signature , str ) or isinstance ( signature , dict ) , " Signature must be a string of a dict: cf doc "
self . c_name = c_name # Not none for internal call only
## Analyse signature.
2014-05-08 22:15:15 +02:00
self . args = [ ]
2014-05-31 11:38:40 +02:00
if isinstance ( signature , str ) : # it is a string, we analyse it to get the rtype, and args
signature = re . sub ( ' operator \ ( \ s* \ ) ' , ' __operator_call ' , signature ) # temp. replacement, to make the regex easier
2014-05-08 22:15:15 +02:00
m = re . match ( r " \ s*(.*?) \ s* \ ((.*) \ ) " , signature )
self . rtype , args = m . group ( 1 ) . strip ( ) or None , m . group ( 2 ) . strip ( )
2014-05-31 11:38:40 +02:00
# extract the c_name if present
if self . rtype :
spl = self . rtype . strip ( ) . rsplit ( ' ' , 1 )
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() "
2014-05-08 22:15:15 +02:00
def f ( ) : # analyse the argument, be careful that , can also be in type, like A<B,C>, so we count the < >
acc = ' '
for s in args . split ( ' , ' ) :
2014-05-29 21:34:46 +02:00
acc + = ( ' , ' if acc else ' ' ) + s . strip ( )
2014-05-08 22:15:15 +02:00
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)
2014-05-31 11:38:40 +02:00
else :
self . rtype = signature . pop ( " rtype " , None )
args = signature . pop ( ' args ' , ( ) )
self . c_name = signature . pop ( " c_name " , ' ' )
2014-05-08 22:15:15 +02:00
for a in args : # put back the default if there is none
2014-05-31 11:38:40 +02:00
# treat the case when the type is const T *, or T* (e.g. const char *).
# Need to regroup the first pieces.
if a [ 0 ] == ' const ' : a = [ ' ' . join ( a [ : 2 ] ) ] + list ( a [ 2 : ] )
if a [ 1 ] == ' * ' : a = [ ' ' . join ( a [ : 2 ] ) ] + list ( a [ 2 : ] )
2014-05-08 22:15:15 +02:00
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 ] )
2014-05-31 11:38:40 +02:00
# 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 "
2014-05-08 22:15:15 +02:00
if self . is_constructor :
assert self . rtype == None , " Constructor must not have a return type "
self . is_method = False
2014-05-31 11:38:40 +02:00
def _get_calling_pattern ( self ) :
""" Generation only: gets the calling_pattern or synthesize the default """
2014-05-08 22:15:15 +02:00
if self . _calling_pattern : return self . _calling_pattern
s = " %s result = " % self . rtype if self . rtype != " void " else " "
2014-05-30 11:11:28 +02:00
self_c = " "
2014-05-31 11:38:40 +02:00
if self . is_method :
2014-05-30 11:11:28 +02:00
self_c = " self_c. " if not self . is_static else " self_class:: "
2014-05-31 11:38:40 +02:00
# 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 ] ) )
2014-05-08 22:15:15 +02:00
2014-05-31 11:38:40 +02:00
def _get_signature ( self ) :
2014-05-08 22:15:15 +02:00
""" Signature for the python doc """
rtype = translate_c_type_to_py_type ( self . rtype ) if self . rtype else ' '
args_rep = " , " . join ( [ " %s %s %s " % ( translate_c_type_to_py_type ( t ) , n , ' = ' + str ( d ) if d else ' ' ) for t , n , d in self . args ] )
return " ( {args_rep} ) -> {rtype} " . format ( * * locals ( ) )
2014-05-31 11:38:40 +02:00
def _get_c_signature ( self ) :
2014-05-08 22:15:15 +02:00
""" Signature for the C++ calling errors """
name = self . c_name if self . c_name else " (no C++ name) "
rtype = self . rtype if self . rtype else ' '
args_rep = " , " . join ( [ " %s %s " % ( t , n ) for t , n , d in self . args ] )
return " {name} ( {args_rep} ) -> {rtype} " . format ( * * locals ( ) )
def __repr__ ( self ) :
2014-05-31 11:38:40 +02:00
return " C++ function of signature : %s " % ( self . _get_signature ( ) )
2014-05-08 22:15:15 +02:00
2014-05-31 11:38:40 +02:00
def _parsing_format ( self ) :
""" Generation only: the formatting for the PyParse_xxx calls """
def f ( t ) :
2014-05-08 22:15:15 +02:00
return basic_types_formatting [ t ] if t in basic_types_formatting else ' O& '
l1 = [ f ( t ) for t , n , d in self . args if d == None ]
l2 = [ f ( t ) for t , n , d in self . args if d != None ]
2014-05-31 11:38:40 +02:00
if l2 : l2 . insert ( 0 , ' | ' ) # starts the default arguments, cf python doc
return ' ' . join ( l1 + l2 )
def _generate_doc ( self ) :
2014-05-08 22:15:15 +02:00
doc = " \n " . join ( [ " " + x . strip ( ) for x in self . doc . split ( ' \n ' ) ] )
2014-05-31 11:38:40 +02:00
return " Signature : %s \n %s " % ( self . _get_signature ( ) , doc )
2014-05-08 22:15:15 +02:00
class pyfunction :
"""
Representation of one python function of the extension
2014-05-31 11:38:40 +02:00
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
2014-05-08 22:15:15 +02:00
- 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 . . .
2014-05-31 11:38:40 +02:00
"""
self . py_name = name # name given in python
2014-05-08 22:15:15 +02:00
self . arity = arity
self . is_method = is_method # can be a method, a function...
2014-05-31 11:38:40 +02:00
self . is_static = is_static #
self . doc = doc
2014-05-08 22:15:15 +02:00
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 ) )
2014-05-31 11:38:40 +02:00
def _generate_doc ( self ) :
s = " \n " . join ( [ self . doc , " \n " ] + [ f . _generate_doc ( ) for f in self . overloads ] )
2014-05-08 22:15:15 +02:00
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 .
Will be use to make a pure python method of an object , or or a module .
Data :
2014-05-31 11:38:40 +02:00
- name : name given in Python
2014-05-08 22:15:15 +02:00
- doc : the doc string .
- module : module path to the function [ pure python only ]
"""
2014-05-31 11:38:40 +02:00
def __init__ ( self , name , module ) :
2014-05-08 22:15:15 +02:00
""" """
2014-05-31 11:38:40 +02:00
self . py_name , self . module = name , module
try :
2014-05-08 22:15:15 +02:00
m = __import__ ( module . rsplit ( ' . ' ) [ - 1 ] )
2014-05-31 11:38:40 +02:00
f = getattr ( m , name )
2014-05-08 22:15:15 +02:00
self . doc = f . __doc__ # get the doc and check the function can be loaded.
except :
2014-05-31 11:38:40 +02:00
print " I can not import the function %s from the module %s " % ( name , module )
2014-05-08 22:15:15 +02:00
raise
2014-05-31 11:38:40 +02:00
def _generate_doc ( self ) :
2014-05-08 22:15:15 +02:00
return self . doc
2014-05-31 11:38:40 +02:00
class python_function :
2014-05-08 22:15:15 +02:00
"""
A python function , given as a function .
Its code gets analysed and will be put into the C + + wrapper , to avoid import .
"""
2014-05-31 11:38:40 +02:00
def __init__ ( self , name , f ) :
2014-05-08 22:15:15 +02:00
""" """
self . name , self . f = name , f
import inspect as ins
self . code = " \n " . join ( [ ' " %s \\ n " ' % line . rstrip ( ) . replace ( ' " ' , ' \\ " ' ) for line in ins . getsourcelines ( self . f ) [ 0 ] ] )
2014-05-31 11:38:40 +02:00
self . doc = f . __doc__ # UNUSED AT THE MOMENT ??? REALLY ???
2014-05-08 22:15:15 +02:00
2014-05-31 11:38:40 +02:00
def _is_type_a_view ( c_type ) :
2014-05-08 22:15:15 +02:00
return c_type . split ( ' < ' , 1 ) [ 0 ] . endswith ( " _view " ) # A bit basic ?
2014-05-31 11:38:40 +02:00
def _regular_type_if_view_else_type ( c_type ) :
return " typename %s ::regular_type " % c_type if _is_type_a_view ( c_type ) else c_type
2014-05-08 22:15:15 +02:00
class class_ :
"""
Representation of a wrapped type
"""
hidden_python_function = { } # global dict of the python function to add to the module, hidden for the user, for precompute and so on
2014-05-31 11:38:40 +02:00
def __init__ ( self , py_type , c_type , c_type_absolute = None , hdf5 = False , arithmetic = None , serializable = None , is_printable = False , doc = ' ' ) :
"""
- py_type : Name given in Python
- c_type : C + + type to be wrapped .
- c_type_absolute : full path of c_type , no using , no alias ( need for the py_converter hpp file )
- hdf5 : generate the hdf5 write / read function from C + + triqs hdf5 protocol and register them in the hdf_archive
- arithmetic : determines the operations to be implemented .
- The idea is to give an abstract description of the mathematical structure to be implemented :
an algebra , a group , a vector space , and so on .
The generator will then implement all necessary functions , by calling their C + + counterparts .
- Possible values :
- ( " abelian_group " ) : implements + and -
- ( " vector_space " , Scalar ) : implements a vector_space , with scalar Scalar
- ( " algebra " , Scalar ) : implements an algebra , with scalar Scalar
- ( " algebra_with_unit " , with_options . . . , Scalars . . . ) :
implements an algebra , with :
- scalars Scalar . . . : the scalars
- with_options is ( possibly empty ) list of options :
- with_unit : + / - of an element with a scalar ( injection of the scalar with the unit )
- with_unary_minus : implement unary minus
- " add_only " : implements only +
- . . . . more to be defined .
- serializable : Whether and how the object is to be serialized . Possible values are :
- " tuple " : reduce it to a tuple of smaller objects , using the
boost serialization compatible template in C + + , and the converters of the smaller objects .
- " via_string " : serialize via a string , made by
triqs : : serialize / triqs : : deserialize
On modern hdf5 ( > 1.8 .9 ) it uses hdf5 to make the string , on older version it will use boost serialization
( which generates a very heavy code , sometimes can x2 the code size of the wrapper , just for this function ! ) .
- is_printable = If true , generate the str , repr from the C + + << stream operator
- doc : the doc string .
"""
2014-05-08 22:15:15 +02:00
self . c_type = c_type
2014-05-20 17:45:16 +02:00
self . c_type_absolute = c_type_absolute or c_type
2014-05-31 11:38:40 +02:00
self . c_type_is_view = _is_type_a_view ( c_type )
2014-05-08 22:15:15 +02:00
self . implement_regular_type_converter = self . c_type_is_view # by default, it will also make the converter of the associated regular type
2014-05-31 11:38:40 +02:00
if self . c_type_is_view :
2014-05-20 17:45:16 +02:00
self . regular_type = ' typename ' + self . c_type + ' ::regular_type '
self . regular_type_absolute = ' typename ' + self . c_type_absolute + ' ::regular_type '
2014-05-08 22:15:15 +02:00
self . py_type = py_type
c_to_py_type [ self . c_type ] = self . py_type # register the name translation for the doc generation
self . hdf5 = hdf5
2014-05-31 11:38:40 +02:00
assert serializable in [ None , " via_string " , " tuple " ]
2014-05-08 22:15:15 +02:00
self . serializable = serializable
self . is_printable = is_printable
self . iterator = None
2014-05-31 11:38:40 +02:00
self . doc = doc
self . methods = { } # a dict : string -> pyfunction for each method name
2014-05-08 22:15:15 +02:00
self . pure_python_methods = { }
2014-05-31 11:38:40 +02:00
self . constructor = None # a pyfunction for the constructors.
self . members = [ ] # a list of _member
self . properties = [ ] # a list of _property
2014-05-08 22:15:15 +02:00
# Init arithmetic
# expect a tuple : "algebra", "scalar1", "scalar2", etc...
self . number_protocol = { }
if arithmetic :
2014-05-30 13:36:47 +02:00
if not isinstance ( arithmetic , tuple ) : arithmetic = ( arithmetic , )
2014-05-31 11:38:40 +02:00
# read the with_... option and clean them for the list
with_unary_minus = ' with_unary_minus ' in arithmetic
with_unit = ' with_unit ' in arithmetic
arithmetic = [ x for x in arithmetic if not x . startswith ( " with_ " ) ]
2014-05-16 19:35:48 +02:00
add = arithmetic [ 0 ] in ( " algebra " , " abelian_group " , " vector_space " , " only_add " )
2014-05-31 11:38:40 +02:00
abelian_group = arithmetic [ 0 ] in ( " algebra " , " abelian_group " , " vector_space " )
2014-05-08 22:15:15 +02:00
vector_space = arithmetic [ 0 ] in ( " algebra " , " vector_space " )
2014-05-31 11:38:40 +02:00
algebra = arithmetic [ 0 ] in ( " algebra " , )
2014-05-16 19:35:48 +02:00
if add :
2014-05-08 22:15:15 +02:00
# add
2014-05-31 11:38:40 +02:00
add = pyfunction ( name = " __add__ " , arity = 2 )
add . add_overload ( calling_pattern = " + " , signature = { ' args ' : [ ( self . c_type , ' x ' ) , ( self . c_type , ' y ' ) ] , ' rtype ' : self . c_type } )
2014-05-08 22:15:15 +02:00
self . number_protocol [ ' add ' ] = add
2014-05-31 11:38:40 +02:00
2014-05-16 19:35:48 +02:00
if abelian_group :
2014-05-08 22:15:15 +02:00
#sub
2014-05-31 11:38:40 +02:00
sub = pyfunction ( name = " __sub__ " , arity = 2 )
sub . add_overload ( calling_pattern = " - " , signature = { ' args ' : [ ( self . c_type , ' x ' ) , ( self . c_type , ' y ' ) ] , ' rtype ' : self . c_type } )
2014-05-08 22:15:15 +02:00
self . number_protocol [ ' subtract ' ] = sub
2014-05-31 11:38:40 +02:00
2014-05-08 22:15:15 +02:00
if vector_space :
# mul
2014-05-31 11:38:40 +02:00
mul = pyfunction ( name = " __mul__ " , arity = 2 )
2014-05-08 22:15:15 +02:00
for scalar in arithmetic [ 1 : ] :
2014-05-31 11:38:40 +02:00
mul . add_overload ( calling_pattern = " * " , signature = { ' args ' : [ ( self . c_type , ' x ' ) , ( scalar , ' y ' ) ] , ' rtype ' : self . c_type } )
mul . add_overload ( calling_pattern = " * " , signature = { ' args ' : [ ( scalar , ' x ' ) , ( self . c_type , ' y ' ) ] , ' rtype ' : self . c_type } )
2014-05-08 22:15:15 +02:00
self . number_protocol [ ' multiply ' ] = mul
# div
2014-05-31 11:38:40 +02:00
div = pyfunction ( name = " __div__ " , arity = 2 )
2014-05-08 22:15:15 +02:00
for scalar in arithmetic [ 1 : ] :
2014-05-31 11:38:40 +02:00
div . add_overload ( calling_pattern = " / " , signature = { ' args ' : [ ( self . c_type , ' x ' ) , ( scalar , ' y ' ) ] , ' rtype ' : self . c_type } )
2014-05-08 22:15:15 +02:00
self . number_protocol [ ' divide ' ] = div
2014-05-31 11:38:40 +02:00
2014-05-08 22:15:15 +02:00
if algebra :
2014-05-31 11:38:40 +02:00
mul . add_overload ( calling_pattern = " * " , signature = { ' args ' : [ ( self . c_type , ' x ' ) , ( self . c_type , ' y ' ) ] , ' rtype ' : self . c_type } )
if with_unit : # Allow + and - between scalar and operator
assert algebra , " The with_unit option only makes sense for algebra "
for scal in arithmetic [ 1 : ] :
add = self . number_protocol [ ' add ' ]
add . add_overload ( calling_pattern = " + " , signature = { ' args ' : [ ( self . c_type , ' x ' ) , ( scal , ' y ' ) ] , ' rtype ' : self . c_type } )
add . add_overload ( calling_pattern = " + " , signature = { ' args ' : [ ( scal , ' x ' ) , ( self . c_type , ' y ' ) ] , ' rtype ' : self . c_type } )
sub = self . number_protocol [ ' subtract ' ]
sub . add_overload ( calling_pattern = " - " , signature = { ' args ' : [ ( self . c_type , ' x ' ) , ( scal , ' y ' ) ] , ' rtype ' : self . c_type } )
sub . add_overload ( calling_pattern = " - " , signature = { ' args ' : [ ( scal , ' x ' ) , ( self . c_type , ' y ' ) ] , ' rtype ' : self . c_type } )
if with_unary_minus :
# Allow unary - on an operator
neg = pyfunction ( name = " __neg__ " , arity = 1 )
neg . add_overload ( calling_pattern = " - " , signature = { ' args ' : [ ( self . c_type , ' x ' ) ] , ' rtype ' : self . c_type } )
self . number_protocol [ ' negative ' ] = neg
def add_constructor ( self , signature , calling_pattern = None , python_precall = None , python_postcall = None , build_from_regular_type_if_view = True , doc = ' ' ) :
"""
- signature : signature of the function , with types , parameter names and defaut value
rtype ( arg1 name1 , arg2 name2 = default2 , . . . . )
signature can be :
- a string of 2 possible forms ( i . e . c_name can be omitted ) :
- rtype ( arg1 name1 , arg2 name2 = default2 , . . . . )
- rtype c_name ( arg1 name1 , arg2 name2 = default2 , . . . . )
- a dict : rtype - > string , args - > list of tuples [ ( c_type , variable_name , default_value ) ]
- rtype : the C + + type returned by the function . None for constructor
default_value is None when there is no default .
- calling_pattern [ expert only ] :
- Pattern to rewrite the call of the c + + constructor .
- It is a string , argument name and defining a result of the c_type
e . g . , the default pattern is : :
auto result = c_type ( a , b , c )
- build_from_regular_type_if_view : boolean .
- If True , and the type is a view , the wrapper calls the C + + constructor * of the corresponding regular type * .
- If False , it simply calls the constructor the C + + type .
- This allows to construct object which are wrapped by view ( like gf e . g . ) , by calling the
constructor of the regular type , much simpler , than the view .
- python_precall :
- A string of the type " module.function_name "
where function_name is a python function to be called before the call of the C + + function .
- It must take F ( * args , * * kw ) and return ( args , kw )
- python_postcall :
- A string of the type " module.function_name "
where function_name is a python function to be called after the call of the C + + function .
- The function must take a python object , and return one . . .
- doc : the doc string .
"""
f = cfunction ( signature , calling_pattern = calling_pattern , is_constructor = True , is_method = True , doc = doc )
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) "
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 :
f . _calling_pattern + = " (( %s *)self)->_c = new %s ( %s ); " % ( self . py_type , self . c_type , all_args )
2014-05-08 22:15:15 +02:00
2014-05-31 11:38:40 +02:00
if not self . constructor :
self . constructor = pyfunction ( name = " __init__ " , is_method = True , doc = doc , python_precall = python_precall , python_postcall = python_postcall )
self . constructor . is_constructor = True
self . constructor . overloads . append ( f )
def add_method ( self , signature , name = None , calling_pattern = None , no_self_c = False , is_method = False , is_static = False ,
python_precall = None , python_postcall = None , doc = ' ' , release_GIL_and_enable_signal = False , c_name = None ) :
2014-05-08 22:15:15 +02:00
"""
2014-05-31 11:38:40 +02:00
Add a C + + overload to a method of name name .
- signature : signature of the function , with types , parameter names and defaut value
rtype ( arg1 name1 , arg2 name2 = default2 , . . . . )
signature can be :
- a string of 2 possible forms ( i . e . c_name can be omitted ) :
- rtype ( arg1 name1 , arg2 name2 = default2 , . . . . )
- rtype c_name ( arg1 name1 , arg2 name2 = default2 , . . . . )
- a dict : rtype - > string , args - > list of tuples [ ( c_type , variable_name , default_value ) ]
- rtype : the C + + type returned by the function . None for constructor
default_value is None when there is no default .
- name : name given in Python
If None , the C + + name extracted from the signature is used .
- calling_pattern :
- Pattern to rewrite the call of the c + + function ,
- It is a string , using self_c , argument name and defining result at the end if rtype != void
e . g . , the default pattern is :
auto result = self_c . method_name ( a , b , c ) .
- If None , the signature must contain c_name
- no_self_c : boolean . do not generate self_c reference in C + + code , in
some rare calling_pattern . Avoid a compiler warning .
- is_method : boolean
- is_static : boolean . Is is a static method
- python_precall :
- A string of the type " module.function_name "
where function_name is a python function to be called before the call of the C + + function .
- It must take F ( * args , * * kw ) and return ( args , kw )
- python_postcall :
- A string of the type " module.function_name "
where function_name is a python function to be called after the call of the C + + function .
- The function must take a python object , and return one . . .
- doc : the doc string .
- release_GIL_and_enable_signal [ expert only ] :
- For long functions in pure C + + .
- If True , the GIL is released in the call of the C + + function and restored after the call .
- It also saves the signal handler of python and restores it after the call ,
and enables the C + + triqs signal_handler .
- This allows e . g . to intercept Ctrl - C during the long C + + function .
- * * Requirement * * :
The function wrapped must be pure C + + , i . e . no call whatsoever to the python C API , directly or indirectly .
otherwise the behaviour is undefined .
"""
f = cfunction ( signature , calling_pattern = calling_pattern , no_self_c = no_self_c , is_constructor = False ,
is_method = True , is_static = is_static , release_GIL_and_enable_signal = release_GIL_and_enable_signal , doc = doc , c_name = c_name )
name = name or f . c_name
if name not in self . methods :
self . methods [ name ] = pyfunction ( name = name , is_method = True , is_static = is_static , doc = doc , python_precall = python_precall , python_postcall = python_postcall )
self . methods [ name ] . overloads . append ( f )
def add_call ( self , * * kw ) :
2014-05-08 22:15:15 +02:00
"""
2014-05-31 11:38:40 +02:00
Add the __call__ operator .
2014-05-08 22:15:15 +02:00
2014-05-31 11:38:40 +02:00
It just call add_method , for the operator ( ) , with name = " __call__ "
2014-05-08 22:15:15 +02:00
2014-05-31 11:38:40 +02:00
Cf add_method documentation .
"""
if ' c_name ' not in kw and ' calling_pattern ' not in kw : kw [ ' c_name ' ] = " operator() "
self . add_method ( name = " __call__ " , * * kw )
class _iterator :
def __init__ ( self , c_type , c_cast_type , begin , end ) :
self . c_type , self . c_cast_type , self . begin , self . end = c_type , c_cast_type , begin , end
def add_iterator ( self , c_type = " const_iterator " , c_cast_type = None , begin = " std::begin " , end = " std::end " ) :
"""
Add an iterator , wrapping a C + + iterator .
Parameters :
- c_type : type of the C + + variable
- c_cast_type : If not None , the result of the C + + iterator dereference if converted to the cast_type .
- begin , end :
"""
self . iterator = self . _iterator ( c_type , c_cast_type , begin , end )
def add_pure_python_method ( self , f , rename = None ) :
2014-05-08 22:15:15 +02:00
"""
Add a method name ( or an overload of method name ) .
2014-05-31 11:38:40 +02:00
f can be :
2014-05-08 22:15:15 +02:00
- a string module1 . module2 . fnt_name
2014-05-31 11:38:40 +02:00
- a function in python
2014-05-08 22:15:15 +02:00
"""
2014-05-31 11:38:40 +02:00
def process_doc ( doc ) :
2014-05-08 22:15:15 +02:00
return doc . replace ( ' \n ' , ' \\ n ' ) if doc else ' '
2014-05-31 11:38:40 +02:00
if type ( f ) == type ( ' ' ) :
2014-05-08 22:15:15 +02:00
module , name = f . rsplit ( ' . ' , 1 )
2014-05-31 11:38:40 +02:00
try :
2014-05-08 22:15:15 +02:00
m = __import__ ( module . rsplit ( ' . ' ) [ - 1 ] )
doc = m . __dict__ [ name ] . __doc__
2014-05-31 11:38:40 +02:00
except :
2014-05-08 22:15:15 +02:00
raise
2014-05-31 11:38:40 +02:00
self . pure_python_methods [ rename or name ] = pure_pyfunction_from_module ( name = name , module = module ) , ' module ' , process_doc ( doc )
2014-05-08 22:15:15 +02:00
elif callable ( f ) :
2014-05-31 11:38:40 +02:00
assert name == None
2014-05-08 22:15:15 +02:00
self . hidden_python_function [ f . __name__ ] = f
self . pure_python_methods [ f . __name__ ] = f . __name__ , ' inline ' , process_doc ( f . __doc__ )
else : raise ValueError , " argument f must be callable or a string "
2014-05-31 11:38:40 +02:00
class _member :
def __init__ ( self , c_name , c_type , py_name , read_only , doc ) :
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
2014-05-08 22:15:15 +02:00
2014-05-31 11:38:40 +02:00
def add_member ( self , c_name , c_type , py_name = None , read_only = False , doc = ' ' ) :
2014-05-08 22:15:15 +02:00
"""
Add a class member
2014-05-31 11:38:40 +02:00
Parameters :
- c_name : name of the variable in C + +
- c_type : type of the C + + variable
- py_name : name of the variable in python . If None , use c_name .
- read_only : bool
- doc : the doc string .
2014-05-08 22:15:15 +02:00
"""
2014-05-31 11:38:40 +02:00
self . members . append ( self . _member ( c_name , c_type , py_name , read_only , doc ) )
class _property :
def __init__ ( self , name , getter , setter , doc ) :
self . name , self . getter , self . setter , self . doc = name , getter , setter , doc
2014-05-08 22:15:15 +02:00
def add_property ( self , getter , setter = None , name = None , doc = ' ' ) :
"""
Add a property
2014-05-31 11:38:40 +02:00
Parameters :
- getter : the cfunction representing the get part
- setter : the cfunction representing the set part or None if the property if read only
- name : name in python . If None , try to use the C + + name of the getter .
- doc : the doc string .
2014-05-08 22:15:15 +02:00
"""
if not isinstance ( getter , str ) : getter . is_method = True
2014-05-31 11:38:40 +02:00
self . properties . append ( self . _property ( name or getter . c_name , getter , setter , doc ) )
2014-05-08 22:15:15 +02:00
def add_len ( self , c_name = None , calling_pattern = None , doc = " Length " ) :
"""
Add the len operator
"""
if not c_name and not calling_pattern : c_name = " size "
2014-05-31 11:38:40 +02:00
self . add_method ( name = " __len__impl " , calling_pattern = calling_pattern , signature = " int %s () " % c_name , doc = doc )
2014-05-08 22:15:15 +02:00
self . methods [ ' __len__impl ' ] . do_implement = False # do not implement automatically, the signature is special
2014-05-31 11:38:40 +02:00
def add_getitem ( self , signature , calling_pattern = None , doc = " operator[] " ) :
2014-05-08 22:15:15 +02:00
"""
Add a the __getitem__ operator
"""
2014-05-31 11:38:40 +02:00
self . add_method ( name = " __getitem__impl " , calling_pattern = calling_pattern , doc = doc , signature = signature , c_name = " operator[] " )
2014-05-08 22:15:15 +02:00
def add_setitem ( self , signature , calling_pattern = None , doc = " operator[] " , * * d ) :
"""
Add a the __setitem__ operator
"""
2014-05-31 11:38:40 +02:00
self . add_method ( name = " __setitem__impl " , calling_pattern = calling_pattern or " self_c[i] = v " , doc = doc , signature = signature , * * d )
2014-05-08 22:15:15 +02:00
def add_method_copy ( self ) :
2014-05-31 11:38:40 +02:00
""" Add a method copy, that make a **deep** copy, using triqs::make_clone """
self . add_method ( name = " copy " , calling_pattern = self . c_type + " result = triqs::make_clone(self_c) " , signature = self . c_type + " () " , doc = " Make a copy (clone) of self " )
2014-05-08 22:15:15 +02:00
def add_method_copy_from ( self ) :
""" Add a copy_from, using C++ assignment """
# other by pointer, it is necessarly a wrapped type
2014-05-31 11:38:40 +02:00
self . add_method ( name = " copy_from " , calling_pattern = " self_c = *other " , signature = ' void( ' + self . c_type + " other) " , doc = " Assignment " )
2014-05-08 22:15:15 +02:00
2014-05-31 11:38:40 +02:00
def _prepare_for_generation ( self ) :
""" Internal : Called just before the code generation """
self . has_mapping_protocol = ' __getitem__impl ' in self . methods or ' __len__impl ' in self . methods
if ' __setitem__impl ' in self . methods and not ' __getitem__impl ' in self . methods : raise RuntimeError , " Can not generate a class with a setter and no getter "
2014-05-30 13:36:47 +02:00
2014-05-08 22:15:15 +02:00
class module_ :
"""
Representation of a module
"""
2014-05-31 11:38:40 +02:00
_wrapped_types = [ ]
def __init__ ( self , full_name , doc = ' ' ) :
"""
- full_name = complete name of the module ( after install , e . g . pytriqs . gf . local . gf
- doc : doc string
"""
self . full_name = full_name
self . name = full_name . rsplit ( ' . ' , 1 ) [ - 1 ]
self . doc = doc
self . classes = { } # dict : string -> class_. Key is the Python type
self . functions = { } # functions : dict : string -> function_. Modules functions. Key is the python name.
self . include_list = [ ]
self . enums = [ ]
self . using = [ ]
self . python_functions = { }
self . hidden_python_functions = { }
self . module_path_list = [ ]
2014-05-30 19:21:38 +02:00
2014-05-08 22:15:15 +02:00
def add_class ( self , cls ) :
2014-05-31 11:38:40 +02:00
"""
Add a class into the module .
It should not exist in the module already .
"""
2014-05-08 22:15:15 +02:00
if cls . py_type in self . classes : raise IndexError , " The class %s already exists " % cls . py_type
self . classes [ cls . py_type ] = cls
2014-05-31 11:38:40 +02:00
self . _wrapped_types + = [ cls . c_type , cls . c_type_absolute ] # we can call is by its name or its absolute name
def add_function ( self , signature , name = None , calling_pattern = None , python_precall = None , python_postcall = None , doc = ' ' , release_GIL_and_enable_signal = False ) :
"""
Add a C + + overload to function of the module
- signature : signature of the function , with types , parameter names and defaut value
rtype ( arg1 name1 , arg2 name2 = default2 , . . . . )
signature can be :
- a string of 2 possible forms ( i . e . c_name can be omitted ) :
- rtype ( arg1 name1 , arg2 name2 = default2 , . . . . )
- rtype c_name ( arg1 name1 , arg2 name2 = default2 , . . . . )
- a dict : rtype - > string , args - > list of tuples [ ( c_type , variable_name , default_value ) ]
- rtype : the C + + type returned by the function . None for constructor
default_value is None when there is no default .
- name : name given in Python
If None , the C + + name extracted from the signature is used .
- calling_pattern :
- Pattern to rewrite the call of the c + + function ,
- It is a string , using self_c , argument name and defining result at the end if rtype != void
e . g . , the default pattern is :
auto result = self_c . method_name ( a , b , c ) .
- If None , the signature must contain c_name
- python_precall :
- A string of the type " module.function_name "
where function_name is a python function to be called before the call of the C + + function .
- It must take F ( * args , * * kw ) and return ( args , kw )
- python_postcall :
- A string of the type " module.function_name "
where function_name is a python function to be called after the call of the C + + function .
- The function must take a python object , and return one . . .
- doc : the doc string .
- release_GIL_and_enable_signal [ expert only ] :
- For long functions in pure C + + .
- If True , the GIL is released in the call of the C + + function and restored after the call .
- It also saves the signal handler of python and restores it after the call ,
and enables the C + + triqs signal_handler .
- This allows e . g . to intercept Ctrl - C during the long C + + function .
- * * Requirement * * :
The function wrapped must be pure C + + , i . e . no call whatsoever to the python C API , directly or indirectly .
otherwise the behaviour is undefined .
"""
f = cfunction ( signature , calling_pattern = calling_pattern , release_GIL_and_enable_signal = release_GIL_and_enable_signal , doc = doc , c_name = name )
name = name or f . c_name
if name not in self . functions :
self . functions [ name ] = pyfunction ( name = name , doc = doc , python_precall = python_precall , python_postcall = python_postcall )
self . functions [ name ] . overloads . append ( f )
def add_python_function ( self , f , name = None , hidden = False ) :
2014-05-08 22:15:15 +02:00
assert callable ( f )
2014-05-31 11:38:40 +02:00
if not hidden :
2014-05-08 22:15:15 +02:00
self . python_functions [ name or f . __name__ ] = python_function ( name or f . __name__ , f )
2014-05-31 11:38:40 +02:00
else :
2014-05-08 22:15:15 +02:00
self . hidden_python_functions [ name or f . __name__ ] = python_function ( name or f . __name__ , f )
2014-05-31 11:38:40 +02:00
def add_include ( self , * filenames ) :
"""
Add the filenames as C + + include in the generated wrapper and header .
"""
self . include_list . extend ( filenames )
def add_using ( self , ns ) :
"""
Add the using statement into the generated wrapper ( and NOT the header ) .
"""
self . using . append ( ns )
def use_module ( self , modulename ) :
"""
From the name of the module :
- add the header file generated for this module to the C + + include list
- read this file , extract the list of _wrapped_types , and add it to the wrapped_type list .
2014-05-30 19:21:38 +02:00
"""
f = None
2014-05-31 11:38:40 +02:00
for path in module_path_list :
2014-05-30 19:21:38 +02:00
hppfile = path + ' / ' + modulename + ' .hpp '
2014-05-31 11:38:40 +02:00
if os . path . exists ( hppfile ) :
2014-05-30 19:21:38 +02:00
f = open ( hppfile , ' r ' )
break
if not f : raise RuntimeError , " Can not find the module %s . \n ... module_path_list = %s " % ( modulename , self . module_path_list )
2014-05-31 11:38:40 +02:00
while f . readline ( ) . strip ( ) != " // WrappedTypeList " :
2014-05-30 19:21:38 +02:00
pass
l = f . readline ( ) [ 3 : ] # // strip "// "
2014-05-31 11:38:40 +02:00
self . _wrapped_types + = eval ( l )
2014-05-30 19:21:38 +02:00
self . add_include ( hppfile )
print " Loading triqs wrapped module %s " % modulename
print " ... found C++ header file %s " % hppfile
print " ... found wrapped types %s " % l
2014-05-31 11:38:40 +02:00
class _enum :
def __init__ ( self , c_name , values , c_namespace , doc ) :
self . c_name , self . c_namespace , self . values , self . doc = c_name , c_namespace + " :: " , values , doc
self . c_name_absolute = self . c_namespace + self . c_name
2014-05-08 22:15:15 +02:00
2014-05-31 11:38:40 +02:00
def add_enum ( self , c_name , values , c_namespace = " " , doc = ' ' ) :
"""
Add an enum into the module .
Parameters :
- c_name : name in C + +
- c_namespace : namespace of the enum
- values : list of string representing the C + + enum values
- doc : the doc string .
"""
self . enums . append ( self . _enum ( c_name , values , c_namespace , doc ) )
2014-05-08 22:15:15 +02:00
2014-05-31 11:38:40 +02:00
def _get_proper_converter ( self , t ) :
2014-05-08 22:15:15 +02:00
if t in basic_types_formatting : return ' '
2014-05-31 11:38:40 +02:00
if t in self . _wrapped_types : return ' ,converter_for_parser_wrapped_type< ' + t + ' > '
2014-05-08 22:15:15 +02:00
if t . split ( ' < ' , 1 ) [ 0 ] . endswith ( " _view " ) : return ' ,converter_for_parser_view_type< ' + t + ' > '
return ' ,converter_for_parser_non_wrapped_type< ' + t + ' > '
2014-05-31 11:38:40 +02:00
def _all_args_kw_functions ( self ) :
l = [ ( f , self . name , None ) for f in self . functions . values ( ) ]
for c in self . classes . values ( ) :
2014-05-08 22:15:15 +02:00
l + = [ ( m , c . py_type , c . c_type ) for m in c . methods . values ( ) if m . do_implement ]
2014-05-31 11:38:40 +02:00
if c . constructor :
2014-05-08 22:15:15 +02:00
l . append ( ( c . constructor , c . py_type , c . c_type ) )
return l
2014-05-31 11:38:40 +02:00
def _prepare_for_generation ( self ) :
for c in self . classes . values ( ) : c . _prepare_for_generation ( )
for n , f in class_ . hidden_python_function . items ( ) :
2014-05-08 22:15:15 +02:00
self . add_python_function ( f , name = n , hidden = True )
2014-05-31 11:38:40 +02:00
def _generate_wrapper_code ( self , mako_template , wrap_file ) :
self . _prepare_for_generation ( )
2014-05-08 22:15:15 +02:00
tpl = Template ( filename = mako_template )
2014-05-31 11:38:40 +02:00
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 )
2014-05-08 22:15:15 +02:00
with open ( wrap_file , ' w ' ) as f :
f . write ( rendered )
2014-05-16 19:35:48 +02:00
2014-05-31 11:38:40 +02:00
def _generate_py_converter_header ( self , mako_template , wrap_file ) :
self . _prepare_for_generation ( )
2014-05-16 19:35:48 +02:00
tpl = Template ( filename = mako_template )
rendered = tpl . render ( module = self )
with open ( wrap_file , ' w ' ) as f :
f . write ( rendered )
2014-05-31 11:38:40 +02:00
def generate_code ( self ) :
"""
Generate the wrapper and the header .
The filenames are given in the sys . argv
"""
self . _generate_wrapper_code ( mako_template = wrapper_mako , wrap_file = wrapper_target )
self . _generate_py_converter_header ( mako_template = header_mako , wrap_file = header_target )
2014-05-30 19:21:38 +02:00