diff options
author | Pearu Peterson <pearu.peterson@gmail.com> | 2006-09-16 05:56:18 +0000 |
---|---|---|
committer | Pearu Peterson <pearu.peterson@gmail.com> | 2006-09-16 05:56:18 +0000 |
commit | 617b46e97017ca4614d04090db5c7d999d1be422 (patch) | |
tree | 31fe8bd3b9a28b01d233eceae83735631789ee9b /numpy/f2py/lib/python_wrapper.py | |
parent | 8b6db6e885dec95dda7bbe2001d95bdee63e6cf1 (diff) | |
download | numpy-617b46e97017ca4614d04090db5c7d999d1be422.tar.gz |
4G f2py: first working example.
Diffstat (limited to 'numpy/f2py/lib/python_wrapper.py')
-rw-r--r-- | numpy/f2py/lib/python_wrapper.py | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/numpy/f2py/lib/python_wrapper.py b/numpy/f2py/lib/python_wrapper.py new file mode 100644 index 000000000..f31e6293b --- /dev/null +++ b/numpy/f2py/lib/python_wrapper.py @@ -0,0 +1,549 @@ + +__all__ = ['TypeWrapper'] + +import re +import os +import sys + +from block_statements import * +#from typedecl_statements import intrinsic_type_spec, Character +from utils import CHAR_BIT + +class WrapperBase: + + + def __init__(self): + self.srcdir = os.path.join(os.path.dirname(__file__),'src') + return + def warning(self, message): + print >> sys.stderr, message + def info(self, message): + print >> sys.stderr, message + + def get_resource_content(self, name, ext): + if name.startswith('pyobj_to_'): + body = self.generate_pyobj_to_ctype_c(name[9:]) + if body is not None: return body + generator_mth_name = 'generate_' + name + ext.replace('.','_') + generator_mth = getattr(self, generator_mth_name, lambda : None) + body = generator_mth() + if body is not None: + return body + fn = os.path.join(self.srcdir,name+ext) + if os.path.isfile(fn): + f = open(fn,'r') + body = f.read() + f.close() + return body + self.warning('No such file: %r' % (fn)) + return + + def get_dependencies(self, code): + l = [] + for uses in re.findall(r'(?<=depends:)([,\w\s.]+)', code, re.I): + for use in uses.split(','): + use = use.strip() + if not use: continue + l.append(use) + return l + + def apply_attributes(self, template): + """ + Apply instance attributes to template string. + + Replace rules for attributes: + _list - will be joined with newline + _clist - _list will be joined with comma + _elist - _list will be joined + ..+.. - attributes will be added + [..] - will be evaluated + """ + replace_names = set(re.findall(r'[ ]*%\(.*?\)s', template)) + d = {} + for name in replace_names: + tab = ' ' * (len(name)-len(name.lstrip())) + name = name.lstrip()[2:-2] + names = name.split('+') + joinsymbol = '\n' + attrs = None + for n in names: + realname = n.strip() + if n.endswith('_clist'): + joinsymbol = ', ' + realname = realname[:-6] + '_list' + elif n.endswith('_elist'): + joinsymbol = '' + realname = realname[:-6] + '_list' + if hasattr(self, realname): + attr = getattr(self, realname) + elif realname.startswith('['): + attr = eval(realname) + else: + self.warning('Undefined %r attribute: %r' % (self.__class__.__name__, realname)) + continue + if attrs is None: + attrs = attr + else: + attrs += attr + if isinstance(attrs, list): + attrs = joinsymbol.join(attrs) + d[name] = str(attrs).replace('\n','\n'+tab) + return template % d + +class PythonWrapperModule(WrapperBase): + + main_template = '''\ +#ifdef __cplusplus +extern \"C\" { +#endif +#include "Python.h" + +#define PY_ARRAY_UNIQUE_SYMBOL PyArray_API +#include "numpy/arrayobject.h" + +%(include_list)s +%(cppmacro_list)s +%(typedef_list)s +%(objdecl_list)s +%(extern_list)s +%(c_function_list)s +%(capi_function_list)s +static PyObject *f2py_module; +static PyMethodDef f2py_module_methods[] = { + %(module_method_list)s + {NULL,NULL,0,NULL} +}; +PyMODINIT_FUNC init%(modulename)s(void) { + f2py_module = Py_InitModule("%(modulename)s", f2py_module_methods); + %(initialize_interface_list)s + if (PyErr_Occurred()) { + PyErr_SetString(PyExc_ImportError, "can\'t initialize module %(modulename)s"); + return; + } +} +#ifdef __cplusplus +} +#endif +''' + + main_fortran_template = '''\ +%(fortran_code_list)s +''' + def __init__(self, modulename): + WrapperBase.__init__(self) + self.modulename = modulename + self.include_list = [] + self.typedef_list = [] + self.cppmacro_list = [] + self.objdecl_list = [] + self.c_function_list = [] + self.extern_list = [] + self.capi_function_list = [] + self.module_method_list = [] + self.initialize_interface_list = [] + self.fortran_code_list = [] + + self.defined_types = [] + self.defined_macros = [] + self.defined_c_functions = [] + self.defined_typedefs = [] + return + + def add(self, block): + if isinstance(block, BeginSource): + for name, subblock in block.a.external_subprogram.items(): + self.add(subblock) + elif isinstance(block, (Subroutine, Function)): + self.info('Generating interface for %s' % (block.name)) + f = PythonCAPIFunction(self, block) + f.fill() + else: + raise NotImplementedError,`block.__class__.__name__` + return + + def c_code(self): + return self.apply_attributes(self.main_template) + def fortran_code(self): + return self.apply_attributes(self.main_fortran_template) + + def add_c_function(self, name): + if name not in self.defined_c_functions: + body = self.get_resource_content(name,'.c') + if body is None: + self.warning('Failed to get C function %r content.' % (name)) + return + for d in self.get_dependencies(body): + if d.endswith('.cpp'): + self.add_cppmacro(d[:-4]) + elif d.endswith('.c'): + self.add_c_function(d[:-2]) + else: + self.warning('Unknown dependence: %r.' % (d)) + self.defined_c_functions.append(name) + self.c_function_list.append(body) + return + + def add_cppmacro(self, name): + if name not in self.defined_macros: + body = self.get_resource_content(name,'.cpp') + if body is None: + self.warning('Failed to get CPP macro %r content.' % (name)) + return + for d in self.get_dependencies(body): + if d.endswith('.cpp'): + self.add_cppmacro(d[:-4]) + elif d.endswith('.c'): + self.add_c_function(d[:-2]) + else: + self.warning('Unknown dependence: %r.' % (d)) + self.defined_macros.append(name) + self.cppmacro_list.append(body) + return + + def add_type(self, typedecl): + typewrap = TypeDecl(self, typedecl) + typename = typewrap.typename + if typename not in self.defined_types: + self.defined_types.append(typename) + typewrap.fill() + return typename + + def add_typedef(self, name, code): + if name not in self.defined_typedefs: + self.typedef_list.append(code) + self.defined_types.append(name) + return + + def add_include(self, include): + if include not in self.include_list: + self.include_list.append(include) + return + + def add_subroutine(self, block): + f = PythonCAPIFunction(self, block) + f.fill() + return + + def generate_pyobj_to_ctype_c(self, ctype): + if ctype.startswith('npy_int'): + ctype_bits = int(ctype[7:]) + return ''' +/* depends: pyobj_to_long.c, pyobj_to_npy_longlong.c */ +#if NPY_BITSOF_LONG == %(ctype_bits)s +#define pyobj_to_%(ctype)s pyobj_to_long +#else +#if NPY_BITSOF_LONG > %(ctype_bits)s +static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) { + long tmp; + if (pyobj_to_long(obj,&tmp)) { + *value = (%(ctype)s)tmp; + return 1; + } + return 0; +} +#else +static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) { + npy_longlong tmp; + if (pyobj_to_npy_longlong(obj,&tmp)) { + *value = (%(ctype)s)tmp; + return 1; + } + return 0; +} +#endif +#endif +''' % (locals()) + elif ctype.startswith('npy_float'): + ctype_bits = int(ctype[9:]) + return ''' +/* depends: pyobj_to_double.c */ +#if NPY_BITSOF_DOUBLE == %(ctype_bits)s +#define pyobj_to_%(ctype)s pyobj_to_double +#else +#if NPY_BITSOF_DOUBLE > %(ctype_bits)s +static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) { + double tmp; + if (pyobj_to_double(obj,&tmp)) { + *value = (%(ctype)s)tmp; + return 1; + } + return 0; +} +#else +#error, "NOTIMPLEMENTED pyobj_to_%(ctype)s" +#endif +#endif +''' % (locals()) + elif ctype.startswith('npy_complex'): + ctype_bits = int(ctype[11:]) + cfloat_bits = ctype_bits/2 + return ''' +/* depends: pyobj_to_Py_complex.c */ +#if NPY_BITSOF_DOUBLE >= %(cfloat_bits)s +static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) { + Py_complex c; + if (pyobj_to_Py_complex(obj,&c)) { + (*value).real = (npy_float%(cfloat_bits)s)c.real; + (*value).imag = (npy_float%(cfloat_bits)s)c.imag; + return 1; + } + return 0; +} +#else +#error, "NOTIMPLEMENTED pyobj_to_%(ctype)s" +#endif +''' % (locals()) + elif ctype.startswith('f2py_string'): + ctype_bits = int(ctype[11:]) + ctype_bytes = ctype_bits / CHAR_BIT + self.add_typedef('f2py_string','typedef char * f2py_string;') + self.add_typedef(ctype,'typedef struct { char data[%s]; } %s;' % (ctype_bytes,ctype)) + self.add_include('#include <string.h>') + return ''' +/* depends: pyobj_to_string_len.c */ +static int pyobj_to_%(ctype)s(PyObject *obj, %(ctype)s* value) { + return pyobj_to_string_len(obj, (f2py_string*)value, %(ctype_bytes)s); +} +''' % (locals()) + +class PythonCAPIFunction(WrapperBase): + capi_function_template = ''' +static char f2py_doc_%(function_name)s[] = "%(function_doc)s"; +static PyObject* f2py_%(function_name)s(PyObject *capi_self, PyObject *capi_args, PyObject *capi_keywds) { + PyObject * volatile capi_buildvalue = NULL; + volatile int f2py_success = 1; + %(decl_list)s + static char *capi_kwlist[] = {%(keyword_clist+optkw_clist+extrakw_clist+["NULL"])s}; + if (!PyArg_ParseTupleAndKeywords(capi_args,capi_keywds, + "%(pyarg_format_elist)s", + %(["capi_kwlist"]+pyarg_obj_clist)s)) + return NULL; + %(frompyobj_list)s + %(call_list)s + f2py_success = !PyErr_Occurred(); + if (f2py_success) { + %(pyobjfrom_list)s + capi_buildvalue = Py_BuildValue(%(buildvalue_clist)s); + %(clean_pyobjfrom_list)s + } + %(clean_frompyobj_list)s + return capi_buildvalue; +} +''' + + pymethoddef_template = '''\ +{"%(function_name)s", (PyCFunction)f2py_%(function_name)s, METH_VARARGS | METH_KEYWORDS, f2py_doc_%(function_name)s},\ +''' + + cppmacro_template = '''\ +#define %(function_name)s_f F_FUNC(%(function_name)s,%(FUNCTION_NAME)s) +''' + + extdef_template = '''\ +extern void %(function_name)s_f();\ +''' + + def __init__(self, parent, block): + WrapperBase.__init__(self) + self.parent = parent + self.block = block + self.function_name = block.name + self.FUNCTION_NAME = self.function_name.upper() + self.function_doc = '' + self.args_list = block.args + self.decl_list = [] + self.keyword_list = [] + self.optkw_list = [] + self.extrakw_list = [] + self.frompyobj_list = [] + self.call_list = [] + self.pyobjfrom_list = [] + self.buildvalue_list = [] + self.clean_pyobjfrom_list = [] + self.clean_frompyobj_list = [] + self.pyarg_format_list = [] + self.pyarg_obj_list = [] + return + + def fill(self): + for argname in self.args_list: + var = self.block.a.variables[argname] + argwrap = ArgumentWrapper(self, var) + argwrap.fill() + self.call_list.append('%s_f(%s);' % (self.function_name, ', '.join(['&'+a for a in self.args_list]))) + if not self.buildvalue_list: + self.buildvalue_list.append('""') + self.parent.capi_function_list.append(self.apply_attributes(self.capi_function_template)) + self.parent.module_method_list.append(self.apply_attributes(self.pymethoddef_template)) + self.parent.extern_list.append(self.apply_attributes(self.extdef_template)) + self.parent.add_cppmacro('F_FUNC') + self.parent.cppmacro_list.append(self.apply_attributes(self.cppmacro_template)) + return + +class ArgumentWrapper(WrapperBase): + + objdecl_template = '%(ctype)s %(name)s;' + pyarg_obj_template = '\npyobj_to_%(ctype)s, &%(name)s' + + def __init__(self, parent, variable): + WrapperBase.__init__(self) + self.parent = parent + self.grand_parent = parent.parent + self.variable = variable + self.typedecl = variable.typedecl + self.name = variable.name + self.ctype = self.typedecl.get_c_type() + + def fill(self): + typename = self.grand_parent.add_type(self.typedecl) + self.parent.decl_list.append(self.apply_attributes(self.objdecl_template)) + + self.parent.pyarg_obj_list.append(self.apply_attributes(self.pyarg_obj_template)) + self.parent.pyarg_format_list.append('O&') + self.parent.keyword_list.append('"%s"' % (self.name)) + + self.grand_parent.add_c_function('pyobj_to_%s' % (self.ctype)) + return + +class TypeDecl(WrapperBase): + cppmacro_template = '''\ +#define initialize_%(typename)s_interface F_FUNC(initialize_%(typename)s_interface_f,INITIALIZE_%(TYPENAME)s_INTERFACE_F)\ +''' + typedef_template = '''\ +typedef struct { char data[%(byte_size)s] } %(ctype)s; +typedef %(ctype)s (*create_%(typename)s_functype)(void); +typedef void (*initialize_%(typename)s_interface_functype)(create_%(typename)s_functype);\ +''' + objdecl_template = '''\ +static create_%(typename)s_functype create_%(typename)s_object; +''' + funcdef_template = '''\ +static void initialize_%(typename)s_interface_c(create_%(typename)s_functype create_object_f) { + create_%(typename)s_object = create_object_f; +} +''' + extdef_template = '''\ +extern void initialize_%(typename)s_interface(initialize_%(typename)s_interface_functype);\ +''' + initcall_template = '''\ +initialize_%(typename)s_interface(initialize_%(typename)s_interface_c);\ +''' + fortran_code_template = '''\ + function create_%(typename)s_object_f() result (obj) + %(typedecl)s obj +! %(initexpr)s + end + subroutine initialize_%(typename)s_interface_f(init_c) + external create_%(typename)s_object_f + call init_c(create_%(typename)s_object_f) + end +''' + + def __init__(self, parent, typedecl): + WrapperBase.__init__(self) + self.parent = parent + self.typedecl = typedecl.astypedecl() + self.ctype = self.typedecl.get_c_type() + self.byte_size = self.typedecl.get_byte_size() + self.typename = self.typedecl.name.lower() + self.TYPENAME = self.typedecl.name.upper() + self.initexpr = self.typedecl.assign_expression('obj',self.typedecl.get_zero_value()) + return + + def fill(self): + ctype =self.typedecl.get_c_type() + if ctype.startswith('npy_'): + pass + elif ctype.startswith('f2py_string'): + pass + else: + self.parent.typedef_list.append(self.apply_attributes(self.typedef_template)) + self.parent.objdecl_list.append(self.apply_attributes(self.objdecl_template)) + self.parent.c_function_list.append(self.apply_attributes(self.funcdef_template)) + self.parent.extern_list.append(self.apply_attributes(self.extdef_template)) + self.parent.initialize_interface_list.append(self.apply_attributes(self.initcall_template)) + self.parent.fortran_code_list.append(self.apply_attributes(self.fortran_code_template)) + self.parent.add_cppmacro('F_FUNC') + self.parent.cppmacro_list.append(self.apply_attributes(self.cppmacro_template)) + return + + + + +if __name__ == '__main__': + from utils import str2stmt, get_char_bit + + stmt = str2stmt(""" + module rat + integer :: i + type rational + integer n + integer*8 d + end type rational + end module rat + subroutine foo(a) + use rat + type(rational) a + end + """) + #stmt = stmt.content[-1].content[1] + #print stmt + #wrapgen = TypeWrapper(stmt) + #print wrapgen.fortran_code() + #print wrapgen.c_code() + + foo_code = """! -*- f90 -*- + module rat + type rational + integer d,n + end type rational + end module rat + subroutine foo(a,b) + use rat + integer a + character*5 b + type(rational) c + print*,'a=',a,b,c + end +""" + + wm = PythonWrapperModule('foo') + wm.add(str2stmt(foo_code)) + #wm.add_fortran_code(foo_code) + #wm.add_subroutine(str2stmt(foo_code)) + #print wm.c_code() + + c_code = wm.c_code() + f_code = wm.fortran_code() + + f = open('foomodule.c','w') + f.write(c_code) + f.close() + f = open('foo.f','w') + f.write(foo_code) + f.close() + f = open('foo_wrap.f','w') + f.write(f_code) + f.close() + f = open('foo_setup.py','w') + f.write('''\ +def configuration(parent_package='',top_path=None): + from numpy.distutils.misc_util import Configuration + config = Configuration('foopack',parent_package,top_path) + config.add_library('foolib', + sources = ['foo.f','foo_wrap.f']) + config.add_extension('foo', + sources=['foomodule.c'], + libraries = ['foolib'], + ) + return config +if __name__ == '__main__': + from numpy.distutils.core import setup + setup(configuration=configuration) +''') + f.close() + print get_char_bit() + os.system('python foo_setup.py config_fc --fcompiler=gnu95 build build_ext --inplace') + import foo + print dir(foo) + foo.foo(2,"abcdefg") |