__all__ = ['PySource', 'PyCFunction', 'PyCModule', 'PyCTypeSpec', 'PyCArgument', 'PyCReturn'] import os import sys from base import Component from utils import * from c_support import * class PySource(FileSource): template_py_header = '''\ #!/usr/bin/env python # This file %(path)r is generated using ExtGen tool # from NumPy version %(numpy_version)s. # ExtGen is developed by Pearu Peterson . # For more information see http://www.scipy.org/ExtGen/ .''' container_options = dict( Content = dict(default='', prefix = template_py_header + '\n', suffix = '\n', use_indent=True) ) pass class PyCModule(CSource): """ >>> m = PyCModule('PyCModule_test', title='This is first line.\\nSecond line.', description='This is a module.\\nYes, it is.') >>> mod = m.build() >>> print mod.__doc__ #doctest: +ELLIPSIS This module 'PyCModule_test' is generated with ExtGen from NumPy version ... This is first line. Second line. This is a module. Yes, it is. """ template = CSource.template_c_header + ''' #ifdef __cplusplus extern \"C\" { #endif #include "Python.h" %(CHeader)s %(CTypeDef)s %(CProto)s %(CDefinition)s %(CAPIDefinition)s %(CDeclaration)s %(PyCModuleCDeclaration)s %(CMainProgram)s #ifdef __cplusplus } #endif ''' container_options = CSource.container_options.copy() container_options.update(CAPIDefinition=container_options['CDefinition'], PyCModuleCDeclaration=dict(default='', ignore_empty_content=True), ) component_container_map = dict( PyCModuleInitFunction = 'CMainProgram', PyCModuleCDeclaration = 'PyCModuleCDeclaration', PyCFunction = 'CAPIDefinition', ) def initialize(self, pyname, *components, **options): self.pyname = pyname self.title = options.pop('title', None) self.description = options.pop('description', None) self = CSource.initialize(self, '%smodule.c' % (pyname), **options) self.need_numpy_support = False self.cdecl = PyCModuleCDeclaration(pyname) self += self.cdecl self.main = PyCModuleInitFunction(pyname) self += self.main map(self.add, components) return self def update_parent(self, parent): if isinstance(parent, Component.SetupPy): self.update_SetupPy(parent) def update_SetupPy(self, parent): parent.setup_py += self.evaluate(' config.add_extension(%(pyname)r, sources = ["%(extmodulesrc)s"])', extmodulesrc = self.path) parent.init_py += 'import %s' % (self.pyname) def finalize(self): if self.need_numpy_support: self.add(CCode(''' #define PY_ARRAY_UNIQUE_SYMBOL PyArray_API #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" '''), 'CHeader') self.main.add(CCode(''' import_array(); if (PyErr_Occurred()) { PyErr_SetString(PyExc_ImportError, "failed to load NumPy array module."); goto capi_error; } '''),'CBody') CSource.finalize(self) def build(self, build_dir=None, clean_at_exit=None): """ build(build_dir=None, clean_at_exit=None) A convenience function to build, import, an return an extension module object. """ if build_dir is None: import tempfile import time packagename = 'extgen_' + str(hex(int(time.time()*10000000)))[2:] build_dir = os.path.join(tempfile.gettempdir(), packagename) clean_at_exit = True setup = Component.SetupPy(build_dir) setup += self s,o = setup.execute('build_ext','--inplace') if s: self.info('return status=%s' % (s)) self.info(o) raise RuntimeError('failed to build extension module %r,'\ ' the build is located in %r directory'\ % (self.pyname, build_dir)) if clean_at_exit: import atexit import shutil atexit.register(lambda d=build_dir: shutil.rmtree(d)) self.info('directory %r will be removed at exit from python.' % (build_dir)) sys.path.insert(0, os.path.dirname(build_dir)) packagename = os.path.basename(build_dir) try: p = __import__(packagename) m = getattr(p, self.pyname) except: del sys.path[0] raise else: del sys.path[0] return m class PyCModuleCDeclaration(Component): template = '''\ static PyObject* extgen_module; static PyMethodDef extgen_module_methods[] = { %(PyMethodDef)s {NULL,NULL,0,NULL} }; static char extgen_module_doc[] = "This module %(pyname)r is generated with ExtGen from NumPy version %(numpy_version)s." %(Title)s %(Description)s %(FunctionSignature)s ;''' container_options = dict( PyMethodDef = dict(suffix=',', skip_suffix_when_empty=True,separator=',\n', default='', use_indent=True, ignore_empty_content=True), FunctionSignature = dict(prefix='"\\n\\n:Functions:\\n"\n" ', skip_prefix_when_empty=True, use_indent=True, ignore_empty_content=True, default='', separator = '"\n" ', suffix='"', skip_suffix_when_empty=True, ), Title = dict(default='',prefix='"\\n\\n',suffix='"',separator='\\n"\n"', skip_prefix_when_empty=True, skip_suffix_when_empty=True, use_firstline_indent=True, replace_map={'\n':'\\n'}), Description = dict(default='',prefix='"\\n\\n"\n"', suffix='"',separator='\\n"\n"', skip_prefix_when_empty=True, skip_suffix_when_empty=True, use_firstline_indent=True, replace_map={'\n':'\\n'}), ) default_component_class_name = 'Line' def initialize(self, pyname): self.pyname = pyname return self def update_parent(self, parent): if isinstance(parent, PyCModule): self.update_PyCModule(parent) def update_PyCModule(self, parent): if parent.title: self.add(parent.title, 'Title') if parent.description: self.add(parent.description, 'Description') class PyCModuleInitFunction(CFunction): """ >>> f = PyCModuleInitFunction('test_PyCModuleInitFunction') >>> print f.generate() PyMODINIT_FUNC inittest_PyCModuleInitFunction(void) { PyObject* extgen_module_dict = NULL; PyObject* extgen_str_obj = NULL; extgen_module = Py_InitModule(\"test_PyCModuleInitFunction\", extgen_module_methods); if ((extgen_module_dict = PyModule_GetDict(extgen_module))==NULL) goto capi_error; if ((extgen_str_obj = PyString_FromString(extgen_module_doc))==NULL) goto capi_error; PyDict_SetItemString(extgen_module_dict, \"__doc__\", extgen_str_obj); Py_DECREF(extgen_str_obj); if ((extgen_str_obj = PyString_FromString(\"restructuredtext\"))==NULL) goto capi_error; PyDict_SetItemString(extgen_module_dict, \"__docformat__\", extgen_str_obj); Py_DECREF(extgen_str_obj); return; capi_error: if (!PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, \"failed to initialize 'test_PyCModuleInitFunction' module.\"); } return; } """ template = '''\ %(CSpecifier)s %(CTypeSpec)s %(name)s(void) { PyObject* extgen_module_dict = NULL; PyObject* extgen_str_obj = NULL; %(CDeclaration)s extgen_module = Py_InitModule("%(pyname)s", extgen_module_methods); if ((extgen_module_dict = PyModule_GetDict(extgen_module))==NULL) goto capi_error; if ((extgen_str_obj = PyString_FromString(extgen_module_doc))==NULL) goto capi_error; PyDict_SetItemString(extgen_module_dict, "__doc__", extgen_str_obj); Py_DECREF(extgen_str_obj); if ((extgen_str_obj = PyString_FromString("restructuredtext"))==NULL) goto capi_error; PyDict_SetItemString(extgen_module_dict, "__docformat__", extgen_str_obj); Py_DECREF(extgen_str_obj); %(CBody)s return; capi_error: if (!PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, "failed to initialize %(pyname)r module."); } return; }''' def initialize(self, pyname, *components, **options): self.pyname = pyname self.title = options.pop('title', None) self.description = options.pop('description', None) self = CFunction.initialize(self, 'init'+pyname, 'PyMODINIT_FUNC', *components, **options) return self #helper classes for PyCFunction class KWListBase(Word): parent_container_options = dict(separator=', ', suffix=', ', skip_suffix_when_empty=True) class ReqKWList(KWListBase): pass class OptKWList(KWListBase): pass class ExtKWList(KWListBase): pass class ArgBase(Word): parent_container_options = dict(separator=', ') class ReqArg(ArgBase): pass class OptArg(ArgBase): pass class ExtArg(ArgBase): pass class RetArg(ArgBase): parent_container_options = dict(separator=', ', prefix='(', suffix=')', default = 'None', skip_prefix_when_empty=True, skip_suffix_when_empty=True, skip_prefix_suffix_when_single=True) class OptExtArg(ArgBase): parent_container_options = dict(separator=', ', prefix=' [, ', skip_prefix_when_empty=True, suffix=']', skip_suffix_when_empty=True) class ArgDocBase(Word): parent_container_options = dict(default='', prefix='"\\n\\nArguments:\\n"\n" ', separator='\\n"\n" ', suffix='"', skip_prefix_when_empty=True, skip_suffix_when_empty=True, use_firstline_indent=True, replace_map={'\n':'\\n'}) class ReqArgDoc(ArgDocBase): parent_container_options = ArgDocBase.parent_container_options.copy() parent_container_options.update(prefix='"\\n\\n:Parameters:\\n"\n" ') class OptArgDoc(ArgDocBase): parent_container_options = ArgDocBase.parent_container_options.copy() parent_container_options.update(prefix='"\\n\\n:Optional parameters:\\n"\n" ') class ExtArgDoc(ArgDocBase): parent_container_options = ArgDocBase.parent_container_options.copy() parent_container_options.update(prefix='"\\n\\n:Extra parameters:\\n"\n" ') class RetArgDoc(ArgDocBase): parent_container_options = ArgDocBase.parent_container_options.copy() parent_container_options.update(prefix='"\\n\\n:Returns:\\n"\n" ', default='"\\n\\n:Returns:\\n None"') class ArgFmtBase(Word): parent_container_options = dict(separator='') class ReqArgFmt(ArgFmtBase): pass class OptArgFmt(ArgFmtBase): pass class ExtArgFmt(ArgFmtBase): pass class RetArgFmt(ArgFmtBase): pass class OptExtArgFmt(ArgFmtBase): parent_container_options = dict(separator='', prefix='|', skip_prefix_when_empty=True) class ArgObjBase(Word): parent_container_options = dict(separator=', ', prefix=', ', skip_prefix_when_empty=True) class ReqArgObj(ArgObjBase): pass class OptArgObj(ArgObjBase): pass class ExtArgObj(ArgObjBase): pass class RetArgObj(ArgObjBase): pass class FunctionSignature(Component): template = '%(name)s(%(ReqArg)s%(OptExtArg)s) -> %(RetArg)s' parent_container_options = dict() container_options = dict( ReqArg = ReqArg.parent_container_options, OptArg = OptArg.parent_container_options, ExtArg = ExtArg.parent_container_options, RetArg = RetArg.parent_container_options, OptExtArg = OptExtArg.parent_container_options, ) def initialize(self, name, *components, **options): self.name = name map(self.add, components) return self def update_containers(self): self.container_OptExtArg += self.container_OptArg + self.container_ExtArg class PyCFunction(CFunction): """ >>> from __init__ import * >>> f = PyCFunction('foo') >>> print f.generate() static char pyc_function_foo_doc[] = \" foo() -> None\" \"\\n\\n:Returns:\\n None\" ; static PyObject* pyc_function_foo(PyObject *pyc_self, PyObject *pyc_args, PyObject *pyc_keywds) { PyObject * volatile pyc_buildvalue = NULL; volatile int capi_success = 1; static char *capi_kwlist[] = {NULL}; if (PyArg_ParseTupleAndKeywords(pyc_args, pyc_keywds,"", capi_kwlist)) { capi_success = !PyErr_Occurred(); if (capi_success) { pyc_buildvalue = Py_BuildValue(""); } } return pyc_buildvalue; } >>> f = PyCFunction('foo', title=' Function title.\\nSecond line.', description=' This is a function.\\n2nd line.') >>> e = PyCModule('PyCFunction_test', f) >>> mod = e.build() >>> print mod.foo.__doc__ foo() -> None Function title. Second line. This is a function. 2nd line. :Returns: None """ template = '''\ static char %(name)s_doc[] = " %(FunctionSignature)s" %(Title)s %(Description)s %(ReqArgDoc)s %(RetArgDoc)s %(OptArgDoc)s %(ExtArgDoc)s ; static PyObject* %(name)s(PyObject *pyc_self, PyObject *pyc_args, PyObject *pyc_keywds) { PyObject * volatile pyc_buildvalue = NULL; volatile int capi_success = 1; %(CDeclaration)s static char *capi_kwlist[] = {%(ReqKWList)s%(OptKWList)s%(ExtKWList)sNULL}; if (PyArg_ParseTupleAndKeywords(pyc_args, pyc_keywds,"%(ReqArgFmt)s%(OptExtArgFmt)s", capi_kwlist%(ReqArgObj)s%(OptArgObj)s%(ExtArgObj)s)) { %(FromPyObj)s %(CBody)s capi_success = !PyErr_Occurred(); if (capi_success) { %(PyObjFrom)s pyc_buildvalue = Py_BuildValue("%(RetArgFmt)s"%(RetArgObj)s); %(CleanPyObjFrom)s } %(CleanCBody)s %(CleanFromPyObj)s } return pyc_buildvalue; }''' container_options = CFunction.container_options.copy() container_options.update(\ TMP = dict(), ReqArg = ReqArg.parent_container_options, OptArg = OptArg.parent_container_options, ExtArg = ExtArg.parent_container_options, RetArg = RetArg.parent_container_options, FunctionSignature = FunctionSignature.parent_container_options, OptExtArg = OptExtArg.parent_container_options, Title = dict(default='',prefix='"\\n\\n',suffix='"',separator='\\n"\n"', skip_prefix_when_empty=True, skip_suffix_when_empty=True, use_firstline_indent=True, replace_map={'\n':'\\n'}), Description = dict(default='',prefix='"\\n\\n"\n"', suffix='"',separator='\\n"\n"', skip_prefix_when_empty=True, skip_suffix_when_empty=True, use_firstline_indent=True, replace_map={'\n':'\\n'}), ReqArgDoc = ReqArgDoc.parent_container_options, OptArgDoc = OptArgDoc.parent_container_options, ExtArgDoc = ExtArgDoc.parent_container_options, RetArgDoc = RetArgDoc.parent_container_options, ReqKWList = ReqKWList.parent_container_options, OptKWList = OptKWList.parent_container_options, ExtKWList = ExtKWList.parent_container_options, ReqArgFmt = ReqArgFmt.parent_container_options, OptArgFmt = OptArgFmt.parent_container_options, ExtArgFmt = ExtArgFmt.parent_container_options, OptExtArgFmt = OptExtArgFmt.ExtArgFmt.parent_container_options, RetArgFmt = ExtArgFmt.parent_container_options, ReqArgObj = ReqArgObj.parent_container_options, OptArgObj = OptArgObj.parent_container_options, ExtArgObj = ExtArgObj.parent_container_options, RetArgObj = RetArgObj.parent_container_options, FromPyObj = CCode.parent_container_options, PyObjFrom = CCode.parent_container_options, CleanPyObjFrom = dict(default='', reverse=True, use_indent=True, ignore_empty_content=True), CleanCBody = dict(default='', reverse=True, use_indent=True, ignore_empty_content=True), CleanFromPyObj = dict(default='', reverse=True, use_indent=True, ignore_empty_content=True), ) default_component_class_name = 'CCode' component_container_map = CFunction.component_container_map.copy() component_container_map.update( PyCArgument = 'TMP', CCode = 'CBody', ) def initialize(self, pyname, *components, **options): self.pyname = pyname self.title = options.pop('title', None) self.description = options.pop('description', None) self = CFunction.initialize(self, 'pyc_function_'+pyname, 'PyObject*', **options) self.signature = FunctionSignature(pyname) self += self.signature if self.title: self.add(self.title, 'Title') if self.description: self.add(self.description, 'Description') map(self.add, components) return self def __repr__(self): return '%s(%s)' % (self.__class__.__name__, ', '.join(map(repr,[self.pyname]+[c for (c,l) in self.components]))) def update_parent(self, parent): if isinstance(parent, PyCModule): self.update_PyCModule(parent) def update_PyCModule(self, parent): t = ' {"%(pyname)s", (PyCFunction)%(name)s, METH_VARARGS | METH_KEYWORDS, %(name)s_doc}' parent.cdecl.add(self.evaluate(t),'PyMethodDef') parent.cdecl.add(self.signature,'FunctionSignature') def update_containers(self): self.container_OptExtArg += self.container_OptArg + self.container_ExtArg self.container_OptExtArgFmt += self.container_OptArgFmt + self.container_ExtArgFmt # resolve dependencies sorted_arguments = [] sorted_names = [] comp_map = {} dep_map = {} for (c,l) in self.components: if not isinstance(c, Component.PyCArgument): continue d = [n for n in c.depends if n not in sorted_names] if not d: sorted_arguments.append((c,l)) sorted_names.append(c.name) else: comp_map[c.name] = (c,l) dep_map[c.name] = d while dep_map: dep_map_copy = dep_map.copy() for name, deps in dep_map.items(): d = [n for n in deps if n in dep_map] if not d: sorted_arguments.append(comp_map[name]) del dep_map[name] else: dep_map[name] = d if dep_map_copy==dep_map: self.warnign('%s: detected cyclic dependencies in %r, incorrect behavior is expected.\n'\ % (self.provides, dep_map)) sorted_arguments += dep_map.values() break for c, l in sorted_arguments: old_parent = c.parent c.parent = self c.ctype.set_converters(c) c.parent = old_parent class PyCArgument(Component): """ >>> from __init__ import * >>> a = PyCArgument('a') >>> print a PyCArgument('a', PyCTypeSpec('object')) >>> print a.generate() a >>> f = PyCFunction('foo') >>> f += a >>> f += PyCArgument('b') >>> m = PyCModule('PyCArgument_test') >>> m += f >>> #print m.generate() >>> mod = m.build() >>> print mod.__doc__ #doctest: +ELLIPSIS This module 'PyCArgument_test' is generated with ExtGen from NumPy version ... :Functions: foo(a, b) -> None """ container_options = dict( TMP = dict() ) component_container_map = dict( PyCTypeSpec = 'TMP' ) template = '%(name)s' def initialize(self, name, ctype = object, *components, **options): self.input_intent = options.pop('input_intent','required') # 'optional', 'extra', 'hide' self.output_intent = options.pop('output_intent','hide') # 'return' self.input_title = options.pop('input_title', None) self.output_title = options.pop('output_title', None) self.input_description = options.pop('input_description', None) self.output_description = options.pop('output_description', None) self.depends = options.pop('depends', []) title = options.pop('title', None) description = options.pop('description', None) if title is not None: if self.input_intent!='hide': if self.input_title is None: self.input_title = title elif self.output_intent!='hide': if self.output_title is None: self.output_title = title if description is not None: if self.input_intent!='hide': if self.input_description is None: self.input_description = description elif self.output_intent!='hide': if self.output_description is None: self.output_description = description if options: self.warning('%s unused options: %s\n' % (self.__class__.__name__, options)) self.name = name self.ctype = ctype = PyCTypeSpec(ctype) self += ctype self.cvar = name self.pycvar = None self.retpycvar = None retfmt = ctype.get_pyret_fmt(self) if isinstance(ctype, PyCTypeSpec): if retfmt and retfmt in 'SON': if self.output_intent == 'return': if self.input_intent=='hide': self.retpycvar = name else: self.pycvar = name self.retpycvar = name + '_return' elif self.input_intent!='hide': self.pycvar = name else: self.pycvar = name self.retpycvar = name else: self.pycvar = name + '_pyc' self.retpycvar = name + '_pyc_r' ctype.set_titles(self) map(self.add, components) return self def __repr__(self): return '%s(%s)' % (self.__class__.__name__, ', '.join(map(repr,[self.name]+[c for (c,l) in self.components]))) def update_parent(self, parent): if isinstance(parent, PyCFunction): self.update_PyCFunction(parent) def update_PyCFunction(self, parent): ctype = self.ctype input_doc_title = '%s : %s' % (self.name, self.input_title) output_doc_title = '%s : %s' % (self.name, self.output_title) if self.input_description is not None: input_doc_descr = ' %s' % (self.input_description) else: input_doc_descr = None if self.output_description is not None: output_doc_descr = ' %s' % (self.output_description) else: output_doc_descr = None # add components to parent: parent += ctype.get_decl(self, parent) if self.input_intent=='required': parent += ReqArg(self.name) parent.signature += ReqArg(self.name) parent += ReqKWList('"' + self.name + '"') parent += ReqArgFmt(ctype.get_pyarg_fmt(self)) parent += ReqArgObj(ctype.get_pyarg_obj(self)) parent += ReqArgDoc(input_doc_title) parent += ReqArgDoc(input_doc_descr) elif self.input_intent=='optional': parent += OptArg(self.name) parent.signature += OptArg(self.name) parent += OptKWList('"' + self.name + '"') parent += OptArgFmt(ctype.get_pyarg_fmt(self)) parent += OptArgObj(ctype.get_pyarg_obj(self)) parent += OptArgDoc(input_doc_title) parent += OptArgDoc(input_doc_descr) elif self.input_intent=='extra': parent += ExtArg(self.name) parent.signature += ExtArg(self.name) parent += ExtKWList('"' + self.name + '"') parent += ExtArgFmt(ctype.get_pyarg_fmt(self)) parent += ExtArgObj(ctype.get_pyarg_obj(self)) parent += ExtArgDoc(input_doc_title) parent += ExtArgDoc(input_doc_descr) elif self.input_intent=='hide': pass else: raise NotImplementedError('input_intent=%r' % (self.input_intent)) if self.output_intent=='return': parent += RetArg(self.name) parent.signature += RetArg(self.name) parent += RetArgFmt(ctype.get_pyret_fmt(self)) parent += RetArgObj(ctype.get_pyret_obj(self)) parent += RetArgDoc(output_doc_title) parent += RetArgDoc(output_doc_descr) elif self.output_intent=='hide': pass else: raise NotImplementedError('output_intent=%r' % (self.output_intent)) class PyCReturn(PyCArgument): def initialize(self, name, ctype = object, *components, **options): return PyCArgument(name, ctype, input_intent='hide', output_intent='return', *components, **options) class PyCTypeSpec(CTypeSpec): """ >>> s = PyCTypeSpec(object) >>> print s PyCTypeSpec('object') >>> print s.generate() PyObject* >>> from __init__ import * >>> m = PyCModule('test_PyCTypeSpec') >>> f = PyCFunction('func') >>> f += PyCArgument('i', int, output_intent='return') >>> f += PyCArgument('l', long, output_intent='return') >>> f += PyCArgument('f', float, output_intent='return') >>> f += PyCArgument('c', complex, output_intent='return') >>> f += PyCArgument('s', str, output_intent='return') >>> f += PyCArgument('u', unicode, output_intent='return') >>> f += PyCArgument('t', tuple, output_intent='return') >>> f += PyCArgument('lst', list, output_intent='return') >>> f += PyCArgument('d', dict, output_intent='return') >>> f += PyCArgument('set', set, output_intent='return') >>> f += PyCArgument('o1', object, output_intent='return') >>> f += PyCArgument('o2', object, output_intent='return') >>> m += f >>> b = m.build() #doctest: +ELLIPSIS >>> b.func(23, 23l, 1.2, 1+2j, 'hello', u'hei', (2,'a'), [-2], {3:4}, set([1,2]), 2, '15') (23, 23L, 1.2, (1+2j), 'hello', u'hei', (2, 'a'), [-2], {3: 4}, set([1, 2]), 2, '15') >>> print b.func.__doc__ func(i, l, f, c, s, u, t, lst, d, set, o1, o2) -> (i, l, f, c, s, u, t, lst, d, set, o1, o2) :Parameters: i : a python int object l : a python long object f : a python float object c : a python complex object s : a python str object u : a python unicode object t : a python tuple object lst : a python list object d : a python dict object set : a python set object o1 : a python object o2 : a python object :Returns: i : a python int object l : a python long object f : a python float object c : a python complex object s : a python str object u : a python unicode object t : a python tuple object lst : a python list object d : a python dict object set : a python set object o1 : a python object o2 : a python object >>> m = PyCModule('test_PyCTypeSpec_c') >>> f = PyCFunction('func_c_int') >>> f += PyCArgument('i1', 'c_char', output_intent='return') >>> f += PyCArgument('i2', 'c_short', output_intent='return') >>> f += PyCArgument('i3', 'c_int', output_intent='return') >>> f += PyCArgument('i4', 'c_long', output_intent='return') >>> f += PyCArgument('i5', 'c_long_long', output_intent='return') >>> m += f >>> f = PyCFunction('func_c_unsigned_int') >>> f += PyCArgument('i1', 'c_unsigned_char', output_intent='return') >>> f += PyCArgument('i2', 'c_unsigned_short', output_intent='return') >>> f += PyCArgument('i3', 'c_unsigned_int', output_intent='return') >>> f += PyCArgument('i4', 'c_unsigned_long', output_intent='return') >>> f += PyCArgument('i5', 'c_unsigned_long_long', output_intent='return') >>> m += f >>> f = PyCFunction('func_c_float') >>> f += PyCArgument('f1', 'c_float', output_intent='return') >>> f += PyCArgument('f2', 'c_double', output_intent='return') >>> m += f >>> f = PyCFunction('func_c_complex') >>> f += PyCArgument('c1', 'c_Py_complex', output_intent='return') >>> m += f >>> f = PyCFunction('func_c_string') >>> f += PyCArgument('s1', 'c_const_char_ptr', output_intent='return') >>> f += PyCArgument('s2', 'c_const_char_ptr', output_intent='return') >>> f += PyCArgument('s3', 'c_Py_UNICODE', output_intent='return') >>> f += PyCArgument('s4', 'c_char1', output_intent='return') >>> m += f >>> b = m.build() >>> b.func_c_int(2,3,4,5,6) (2, 3, 4, 5, 6L) >>> b.func_c_unsigned_int(-1,-1,-1,-1,-1) (255, 65535, 4294967295, 18446744073709551615L, 18446744073709551615L) >>> b.func_c_float(1.2,1.2) (1.2000000476837158, 1.2) >>> b.func_c_complex(1+2j) (1+2j) >>> b.func_c_string('hei', None, u'tere', 'b') ('hei', None, u'tere', 'b') >>> import numpy >>> m = PyCModule('test_PyCTypeSpec_numpy') >>> f = PyCFunction('func_int') >>> f += PyCArgument('i1', numpy.int8, output_intent='return') >>> f += PyCArgument('i2', numpy.int16, output_intent='return') >>> f += PyCArgument('i3', numpy.int32, output_intent='return') >>> f += PyCArgument('i4', numpy.int64, output_intent='return') >>> m += f >>> f = PyCFunction('func_uint') >>> f += PyCArgument('i1', numpy.uint8, output_intent='return') >>> f += PyCArgument('i2', numpy.uint16, output_intent='return') >>> f += PyCArgument('i3', numpy.uint32, output_intent='return') >>> f += PyCArgument('i4', numpy.uint64, output_intent='return') >>> m += f >>> f = PyCFunction('func_float') >>> f += PyCArgument('f1', numpy.float32, output_intent='return') >>> f += PyCArgument('f2', numpy.float64, output_intent='return') >>> f += PyCArgument('f3', numpy.float128, output_intent='return') >>> m += f >>> f = PyCFunction('func_complex') >>> f += PyCArgument('c1', numpy.complex64, output_intent='return') >>> f += PyCArgument('c2', numpy.complex128, output_intent='return') >>> f += PyCArgument('c3', numpy.complex256, output_intent='return') >>> m += f >>> f = PyCFunction('func_array') >>> f += PyCArgument('a1', numpy.ndarray, output_intent='return') >>> m += f >>> b = m.build() >>> b.func_int(numpy.int8(-2), numpy.int16(-3), numpy.int32(-4), numpy.int64(-5)) (-2, -3, -4, -5) >>> b.func_uint(numpy.uint8(-1), numpy.uint16(-1), numpy.uint32(-1), numpy.uint64(-1)) (255, 65535, 4294967295, 18446744073709551615) >>> b.func_float(numpy.float32(1.2),numpy.float64(1.2),numpy.float128(1.2)) (1.20000004768, 1.2, 1.19999999999999995559) >>> b.func_complex(numpy.complex64(1+2j),numpy.complex128(1+2j),numpy.complex256(1+2j)) ((1+2j), (1+2j), (1.0+2.0j)) >>> b.func_array(numpy.array([1,2])) array([1, 2]) >>> b.func_array(numpy.array(2)) array(2) >>> b.func_array(2) Traceback (most recent call last): ... TypeError: argument 1 must be numpy.ndarray, not int >>> b.func_array(numpy.int8(2)) Traceback (most recent call last): ... TypeError: argument 1 must be numpy.ndarray, not numpy.int8 """ typeinfo_map = dict( int = ('PyInt_Type', 'PyIntObject*', 'O!', 'N', 'NULL'), long = ('PyLong_Type', 'PyLongObject*', 'O!', 'N', 'NULL'), float = ('PyFloat_Type', 'PyFloatObject*', 'O!', 'N', 'NULL'), complex = ('PyComplex_Type', 'PyComplexObject*', 'O!', 'N', 'NULL'), str = ('PyString_Type', 'PyStringObject*', 'S', 'N', 'NULL'), unicode = ('PyUnicode_Type', 'PyUnicodeObject*', 'U', 'N', 'NULL'), buffer = ('PyBuffer_Type', 'PyBufferObject*', 'O!', 'N', 'NULL'), tuple = ('PyTuple_Type', 'PyTupleObject*', 'O!', 'N', 'NULL'), list = ('PyList_Type', 'PyListObject*', 'O!', 'N', 'NULL'), dict = ('PyDict_Type', 'PyDictObject*', 'O!', 'N', 'NULL'), file = ('PyFile_Type', 'PyFileObject*', 'O!', 'N', 'NULL'), instance = ('PyInstance_Type', 'PyObject*', 'O!', 'N', 'NULL'), function = ('PyFunction_Type', 'PyFunctionObject*', 'O!', 'N', 'NULL'), method = ('PyMethod_Type', 'PyObject*', 'O!', 'N', 'NULL'), module = ('PyModule_Type', 'PyObject*', 'O!', 'N', 'NULL'), iter = ('PySeqIter_Type', 'PyObject*', 'O!', 'N', 'NULL'), property = ('PyProperty_Type', 'PyObject*', 'O!', 'N', 'NULL'), slice = ('PySlice_Type', 'PyObject*', 'O!', 'N', 'NULL'), cell = ('PyCell_Type', 'PyCellObject*', 'O!', 'N', 'NULL'), generator = ('PyGen_Type', 'PyGenObject*', 'O!', 'N', 'NULL'), set = ('PySet_Type', 'PySetObject*', 'O!', 'N', 'NULL'), frozenset = ('PyFrozenSet_Type', 'PySetObject*', 'O!', 'N', 'NULL'), cobject = (None, 'PyCObject*', 'O', 'N', 'NULL'), type = ('PyType_Type', 'PyTypeObject*', 'O!', 'N', 'NULL'), object = (None, 'PyObject*', 'O', 'N', 'NULL'), numpy_ndarray = ('PyArray_Type', 'PyArrayObject*', 'O!', 'N', 'NULL'), numpy_descr = ('PyArrayDescr_Type','PyArray_Descr', 'O!', 'N', 'NULL'), numpy_ufunc = ('PyUFunc_Type', 'PyUFuncObject*', 'O!', 'N', 'NULL'), numpy_iter = ('PyArrayIter_Type', 'PyArrayIterObject*', 'O!', 'N', 'NULL'), numpy_multiiter = ('PyArrayMultiIter_Type', 'PyArrayMultiIterObject*', 'O!', 'N', 'NULL'), numpy_int8 = ('PyInt8ArrType_Type', 'PyInt8ScalarObject*', 'O!', 'N', 'NULL'), numpy_int16 = ('PyInt16ArrType_Type', 'PyInt16ScalarObject*', 'O!', 'N', 'NULL'), numpy_int32 = ('PyInt32ArrType_Type', 'PyInt32ScalarObject*', 'O!', 'N', 'NULL'), numpy_int64 = ('PyInt64ArrType_Type', 'PyInt64ScalarObject*', 'O!', 'N', 'NULL'), numpy_int128 = ('PyInt128ArrType_Type', 'PyInt128ScalarObject*', 'O!', 'N', 'NULL'), numpy_uint8 = ('PyUInt8ArrType_Type', 'PyUInt8ScalarObject*', 'O!', 'N', 'NULL'), numpy_uint16 = ('PyUInt16ArrType_Type', 'PyUInt16ScalarObject*', 'O!', 'N', 'NULL'), numpy_uint32 = ('PyUInt32ArrType_Type', 'PyUInt32ScalarObject*', 'O!', 'N', 'NULL'), numpy_uint64 = ('PyUInt64ArrType_Type', 'PyUInt64ScalarObject*', 'O!', 'N', 'NULL'), numpy_uint128 = ('PyUInt128ArrType_Type', 'PyUInt128ScalarObject*', 'O!', 'N', 'NULL'), numpy_float16 = ('PyFloat16ArrType_Type', 'PyFloat16ScalarObject*', 'O!', 'N', 'NULL'), numpy_float32 = ('PyFloat32ArrType_Type', 'PyFloat32ScalarObject*', 'O!', 'N', 'NULL'), numpy_float64 = ('PyFloat64ArrType_Type', 'PyFloat64ScalarObject*', 'O!', 'N', 'NULL'), numpy_float80 = ('PyFloat80ArrType_Type', 'PyFloat80ScalarObject*', 'O!', 'N', 'NULL'), numpy_float96 = ('PyFloat96ArrType_Type', 'PyFloat96ScalarObject*', 'O!', 'N', 'NULL'), numpy_float128 = ('PyFloat128ArrType_Type', 'PyFloat128ScalarObject*', 'O!', 'N', 'NULL'), numpy_complex32 = ('PyComplex32ArrType_Type', 'PyComplex32ScalarObject*', 'O!', 'N', 'NULL'), numpy_complex64 = ('PyComplex64ArrType_Type', 'PyComplex64ScalarObject*', 'O!', 'N', 'NULL'), numpy_complex128 = ('PyComplex128ArrType_Type', 'PyComplex128ScalarObject*', 'O!', 'N', 'NULL'), numpy_complex160 = ('PyComplex160ArrType_Type', 'PyComplex160ScalarObject*', 'O!', 'N', 'NULL'), numpy_complex192 = ('PyComplex192ArrType_Type', 'PyComplex192ScalarObject*', 'O!', 'N', 'NULL'), numpy_complex256 = ('PyComplex256ArrType_Type', 'PyComplex256ScalarObject*', 'O!', 'N', 'NULL'), numeric_array = ('PyArray_Type', 'PyArrayObject*', 'O!', 'N', 'NULL'), c_char = (None, 'char', 'b', 'b', '0'), c_unsigned_char = (None, 'unsigned char', 'B', 'B', '0'), c_short = (None, 'short int', 'h', 'h', '0'), c_unsigned_short = (None, 'unsigned short int', 'H', 'H', '0'), c_int = (None,'int', 'i', 'i', '0'), c_unsigned_int = (None,'unsigned int', 'I', 'I', '0'), c_long = (None,'long', 'l', 'l', '0'), c_unsigned_long = (None,'unsigned long', 'k', 'k', '0'), c_long_long = (None,'PY_LONG_LONG', 'L', 'L', '0'), c_unsigned_long_long = (None,'unsigned PY_LONG_LONG', 'K', 'K', '0'), c_Py_ssize_t = (None,'Py_ssize_t', 'n', 'n', '0'), c_char1 = (None,'char', 'c', 'c', '"\\0"'), c_float = (None,'float', 'f', 'f', '0.0'), c_double = (None,'double', 'd', 'd', '0.0'), c_Py_complex = (None,'Py_complex', 'D', 'D', '{0.0, 0.0}'), c_const_char_ptr = (None,'const char *', 'z', 'z', 'NULL'), c_Py_UNICODE = (None,'Py_UNICODE*','u','u', 'NULL'), ) def initialize(self, typeobj): if isinstance(typeobj, self.__class__): return typeobj m = self.typeinfo_map key = None if isinstance(typeobj, type): if typeobj.__module__=='__builtin__': key = typeobj.__name__ if key=='array': key = 'numeric_array' elif typeobj.__module__=='numpy': key = 'numpy_' + typeobj.__name__ elif isinstance(typeobj, str): key = typeobj if key.startswith('numpy_'): k = key[6:] named_scalars = ['byte','short','int','long','longlong', 'ubyte','ushort','uint','ulong','ulonglong', 'intp','uintp', 'float_','double', 'longfloat','longdouble', 'complex_', ] if k in named_scalars: import numpy key = 'numpy_' + getattr(numpy, k).__name__ try: item = m[key] except KeyError: raise NotImplementedError('%s: need %s support' % (self.__class__.__name__, typeobj)) self.typeobj_name = key self.ctypeobj = item[0] self.line = item[1] self.arg_fmt = item[2] self.ret_fmt = item[3] self.cinit_value = item[4] self.need_numpy_support = False if key.startswith('numpy_'): self.need_numpy_support = True #self.add(Component.get('arrayobject.h'), 'CHeader') #self.add(Component.get('import_array'), 'ModuleInit') if key.startswith('numeric_'): raise NotImplementedError(self.__class__.__name__ + ': Numeric support') return self def finalize(self): if self.need_numpy_support: self.component_PyCModule.need_numpy_support = True def __repr__(self): return '%s(%s)' % (self.__class__.__name__, ', '.join([repr(self.typeobj_name)]+[repr(c) for (c,l) in self.components])) def get_pyarg_fmt(self, arg): if arg.input_intent=='hide': return None return self.arg_fmt def get_pyarg_obj(self, arg): if arg.input_intent=='hide': return None if self.arg_fmt=='O!': return '&%s, &%s' % (self.ctypeobj, arg.pycvar) return '&' + arg.pycvar def get_pyret_fmt(self, arg): if arg.output_intent=='hide': return None return self.ret_fmt def get_pyret_obj(self, arg): if arg.output_intent=='return': if self.get_pyret_fmt(arg)=='D': return '&' + arg.retpycvar return arg.retpycvar return def get_init_value(self, arg): return self.cinit_value def set_titles(self, arg): if self.typeobj_name == 'object': tn = 'a python ' + self.typeobj_name else: if self.typeobj_name.startswith('numpy_'): tn = 'a numpy.' + self.typeobj_name[6:] + ' object' elif self.typeobj_name.startswith('c_'): n = self.typeobj_name[2:] if not n.startswith('Py_'): n = ' '.join(n.split('_')) tn = 'a to C ' + n + ' convertable object' else: tn = 'a python ' + self.typeobj_name + ' object' if arg.input_intent!='hide': r = '' if arg.input_title: r = ', ' + arg.input_title arg.input_title = tn + r if arg.output_intent!='hide': r = '' if arg.output_title: r = ', ' + arg.output_title arg.output_title = tn + r def get_decl(self, arg, func): init_value = self.get_init_value(arg) if init_value: init = ' = %s' % (init_value) else: init = '' if arg.pycvar and arg.pycvar==arg.retpycvar: func += CDeclaration(self, '%s%s' % (arg.pycvar, init)) else: if self.get_pyret_obj(arg) is None: if self.get_pyret_obj(arg) is not None: func += CDeclaration(self, '%s%s' % (arg.pycvar, init)) elif self.get_pyarg_obj(arg) is not None: func += CDeclaration(self, '%s%s' % (arg.pycvar, init)) func += CDeclaration(self,'%s%s' % (arg.retpycvar, init)) else: func += CDeclaration(self, '%s%s' % (arg.retpycvar, init)) return def set_converters(self, arg): """ Notes for user: if arg is intent(optional, in, out) and not specified as function argument then function may created but it must then have *new reference* (ie use Py_INCREF unless it is a new reference already). """ # this method is called from PyCFunction.update_containers(), # note that self.parent is None put arg.parent is PyCFunction # instance. eval_a = arg.evaluate FromPyObj = arg.container_FromPyObj PyObjFrom = arg.container_PyObjFrom argfmt = self.get_pyarg_fmt(arg) retfmt = self.get_pyret_fmt(arg) if arg.output_intent=='return': if arg.input_intent in ['optional', 'extra']: if retfmt in 'SON': FromPyObj += eval_a('''\ if (!(%(pycvar)s==NULL)) { /* make %(pycvar)r a new reference */ %(retpycvar)s = %(pycvar)s; Py_INCREF((PyObject*)%(retpycvar)s); } ''') PyObjFrom += eval_a('''\ if (%(retpycvar)s==NULL) { /* %(pycvar)r was not specified */ if (%(pycvar)s==NULL) { %(retpycvar)s = Py_None; Py_INCREF((PyObject*)%(retpycvar)s); } else { %(retpycvar)s = %(pycvar)s; /* %(pycvar)r must be a new reference or expect a core dump. */ } } elif (!(%(retpycvar)s == %(pycvar)s)) { /* a new %(retpycvar)r was created, undoing %(pycvar)s new reference */ Py_DECREF((PyObject*)%(pycvar)s); } ''') elif arg.input_intent=='hide': if retfmt in 'SON': PyObjFrom += eval_a('''\ if (%(retpycvar)s==NULL) { %(retpycvar)s = Py_None; Py_INCREF((PyObject*)%(retpycvar)s); } /* else %(retpycvar)r must be a new reference or expect a core dump. */ ''') elif arg.input_intent=='required': if retfmt in 'SON': FromPyObj += eval_a('''\ /* make %(pycvar)r a new reference */ %(retpycvar)s = %(pycvar)s; Py_INCREF((PyObject*)%(retpycvar)s); ''') PyObjFrom += eval_a('''\ if (!(%(retpycvar)s==%(pycvar)s)) { /* a new %(retpycvar)r was created, undoing %(pycvar)r new reference */ /* %(retpycvar)r must be a new reference or expect a core dump. */ Py_DECREF((PyObject*)%(pycvar)s); } ''') def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test()