diff options
author | Pearu Peterson <pearu.peterson@gmail.com> | 2007-08-03 19:44:53 +0000 |
---|---|---|
committer | Pearu Peterson <pearu.peterson@gmail.com> | 2007-08-03 19:44:53 +0000 |
commit | 66650bdd599110d55425467d5a99aba0a5c6026d (patch) | |
tree | d89de471bc88e7acbeace48f69017c531015bacc /numpy/f2py/lib | |
parent | 73001f4b36d4745bec81d3526800aacad5614b3d (diff) | |
download | numpy-66650bdd599110d55425467d5a99aba0a5c6026d.tar.gz |
Initial commit of extgen - Python Extension module Generator package.
Diffstat (limited to 'numpy/f2py/lib')
-rw-r--r-- | numpy/f2py/lib/extgen/__init__.py | 12 | ||||
-rw-r--r-- | numpy/f2py/lib/extgen/base.py | 348 | ||||
-rw-r--r-- | numpy/f2py/lib/extgen/c_code.py | 32 | ||||
-rw-r--r-- | numpy/f2py/lib/extgen/c_type.py | 156 | ||||
-rw-r--r-- | numpy/f2py/lib/extgen/doc.txt | 169 | ||||
-rw-r--r-- | numpy/f2py/lib/extgen/extension_module.py | 137 | ||||
-rw-r--r-- | numpy/f2py/lib/extgen/predefined_components.py | 23 | ||||
-rw-r--r-- | numpy/f2py/lib/extgen/pyc_function.py | 77 |
8 files changed, 954 insertions, 0 deletions
diff --git a/numpy/f2py/lib/extgen/__init__.py b/numpy/f2py/lib/extgen/__init__.py new file mode 100644 index 000000000..30eb25bd9 --- /dev/null +++ b/numpy/f2py/lib/extgen/__init__.py @@ -0,0 +1,12 @@ +""" +Python Extensions Generator +""" + +__all__ = ['ExtensionModule', 'PyCFunction', 'CCode'] + +import base +from extension_module import ExtensionModule +from pyc_function import PyCFunction +from c_code import CCode + +import predefined_components diff --git a/numpy/f2py/lib/extgen/base.py b/numpy/f2py/lib/extgen/base.py new file mode 100644 index 000000000..d494156f1 --- /dev/null +++ b/numpy/f2py/lib/extgen/base.py @@ -0,0 +1,348 @@ +""" +ExtGen --- Python Extension module Generator. + +Defines Base and Container classes. +""" + +import re +import sys +import time + +class BaseMetaClass(type): + + classnamespace = {} + + def __init__(cls,*args,**kws): + n = cls.__name__ + c = BaseMetaClass.classnamespace.get(n) + if c is None: + BaseMetaClass.classnamespace[n] = cls + else: + print 'Ignoring redefinition of %s: %s defined earlier than %s' % (n, c, cls) + type.__init__(cls, *args, **kws) + + def __getattr__(cls, name): + try: return BaseMetaClass.classnamespace[name] + except KeyError: pass + raise AttributeError("'%s' object has no attribute '%s'"% + (cls.__name__, name)) + +class Base(object): + + __metaclass__ = BaseMetaClass + + container_options = dict() + component_container_map = dict() + template = '' + + def __new__(cls, *args, **kws): + obj = object.__new__(cls) + obj._args = args + obj._provides = kws.get('provides', None) + obj.parent = None + obj.containers = {} # holds containers for named string lists + obj.components = [] # holds pairs (<Base subclass instance>, <container name or None>) + obj.initialize(*args, **kws) # initialize from constructor arguments + return obj + + def initialize(self, *args, **kws): + """ + Set additional attributes, add components to instance, etc. + """ + # self.myattr = .. + # map(self.add, args) + return + + @staticmethod + def warning(message): + print >> sys.stderr, 'extgen:',message + @staticmethod + def info(message): + print >> sys.stderr, message + + def __repr__(self): + return '%s%s' % (self.__class__.__name__, `self._args`) + + def get_container(self, key): + """ Return named container. + + Rules for returning containers: + (1) return local container if exists + (2) return parent container if exists + (3) create local container and return it with warning + """ + # local container + try: + return self.containers[key] + except KeyError: + pass + + # parent container + parent = self.parent + while parent is not None: + try: + return parent.containers[key] + except KeyError: + parent = parent.parent + continue + + # create local container + self.warning('Created container for %r with name %r, define it in'\ + ' .container_options mapping to get rid of this warning' \ + % (self.__class__.__name__, key)) + c = self.containers[key] = Container() + return c + + @property + def provides(self): + """ + Return a code idiom name that the current class defines. + + Used in avoiding redefinitions of functions and variables. + """ + if self._provides is None: + return '%s_%s' % (self.__class__.__name__, id(self)) + return self._provides + + def get_templates(self): + """ + Return instance templates. + """ + return self.template + + def generate(self): + """ + Generate code idioms (saved in containers) and + return evaluated template strings. + """ + # clean up containers + self.containers = {} + for k,kwargs in self.container_options.items(): + self.containers[k] = Container(**kwargs) + + # initialize code idioms + self.init_containers() + + # generate component code idioms + for component, container_key in self.components: + old_parent = component.parent + component.parent = self + result = component.generate() + if container_key is not None: + if isinstance(container_key, tuple): + assert len(result)==len(container_key),`len(result),container_key` + results = result + keys = container_key + else: + assert isinstance(result, str) and isinstance(container_key, str), `result, container_key` + results = result, + keys = container_key, + for r,k in zip(results, keys): + container = component.get_container(k) + container.add(r, component.provides) + else: + self.warning('no label specified for component %r, ignoring its result'\ + % (component.provides)) + component.parent = old_parent + + # update code idioms + self.update_containers() + + # fill templates with code idioms + templates = self.get_templates() + if isinstance(templates, str): + result = self.evaluate(templates) + else: + assert isinstance(templates, (tuple, list)),`type(templates)` + result = tuple(map(self.evaluate, templates)) + + return result + + def init_containers(self): + """ + Update containers before processing components. + """ + # container = self.get_container(<key>) + # container.add(<string>, label=None) + return + + def update_containers(self): + """ + Update containers after processing components. + """ + # container = self.get_container(<key>) + # container.add(<string>, label=None) + return + + def __iadd__(self, other): + """ Convenience add. + """ + self.add(other) + return self + + def add(self, component, container_label=None): + """ + Append component and its target container label to components list. + """ + if isinstance(component, str): + component = Base.CCode(component) + if container_label is None: + container_label = self.component_container_map.get(component.__class__.__name__, None) + assert isinstance(component, Base), `type(component)` + self.components.append((component, container_label)) + + @property + def show(self): + # display the content of containers + self.generate() + r = [self.__class__.__name__] + for k, v in self.containers.items(): + if v.list: + r.append('--- %s ---\n%s' % (k,v)) + return '\n'.join(r) + + def evaluate(self, template): + """ + Evaluate template using instance attributes and code + idioms from containers. + """ + d = self.containers.copy() + for n in dir(self): + if n in ['show', 'build'] or n.startswith('_'): + continue + v = getattr(self, n) + if isinstance(v, str): + d[n] = v + for label, container in self.containers.items(): + if container.use_indent is None: + continue + replace_list = set(re.findall(r'[ ]*%\('+label+r'\)s', template)) + for s in replace_list: + old_indent = container.use_indent + container.use_indent = len(s) - len(s.lstrip()) + i = template.index(s) + template = template[:i] + str(container) + template[i+len(s):] + container.use_indent = old_indent + return re.sub(r'[ \t]*[<]KILLLINE[>]\n','', template % d) + + _registered_components_map = {} + + @staticmethod + def register(*components): + """ + Register components so that component classes can use + predefined components via `.get(<provides>)` method. + """ + d = Base._registered_components_map + for component in components: + provides = component.provides + if d.has_key(provides): + Base.warning('component that provides %r is already registered, ignoring.' % (provides)) + else: + d[provides] = component + return + + @staticmethod + def get(provides): + """ + Return predefined component with given provides property.. + """ + try: + return Base._registered_components_map[provides] + except KeyError: + pass + raise KeyError('no registered component provides %r' % (provides)) + + +class Container(object): + """ + Container of a list of named strings. + + >>> c = Container(separator=', ', prefix='"', suffix='"') + >>> c.add(1, 'hey') + >>> c.add(2, 'hoo') + >>> str(c) + '"hey, hoo"' + >>> c.add(1, 'hey') + >>> c.add(1, 'hey2') + Traceback (most recent call last): + ... + ValueError: Container item 1 exists with different value + + """ + __metaclass__ = BaseMetaClass + + def __init__(self, separator='\n', prefix='', suffix='', + skip_prefix_when_empty=False, + skip_suffix_when_empty=False, + default = '', reverse=False, + user_defined_str = None, + use_indent = None, + ): + self.list = [] + self.label_map = {} + + self.separator = separator + self.prefix = prefix + self.suffix = suffix + self.skip_prefix = skip_prefix_when_empty + self.skip_suffix = skip_suffix_when_empty + self.default = default + self.reverse = reverse + self.user_str = user_defined_str + self.use_indent = use_indent + + def has(self, label): + return self.label_map.has_key(label) + + def get(self, label): + return self.list[self.label_map[label]] + + def __iadd__(self, other): + self.add(other) + return self + + def add(self, content, label=None): + """ Add content to container using label. + If label is None, an unique label will be generated using time.time(). + """ + assert isinstance(content, str),`type(content)` + if label is None: + label = time.time() + if self.has(label): + d = self.get(label) + if d!=content: + raise ValueError("Container item %r exists with different value" % (label)) + return + self.list.append(content) + self.label_map[label] = len(self.list)-1 + return + + def __str__(self): + if self.user_str is not None: + return self.user_str(self) + if self.list: + l = self.list + if self.reverse: + l = l[:] + l.reverse() + r = self.separator.join(l) + r = self.prefix + r + r = r + self.suffix + else: + r = self.default + if not self.skip_prefix: + r = self.prefix + r + if not self.skip_suffix: + r = r + self.suffix + if r and self.use_indent: + indent = self.use_indent * ' ' + r = ''.join([indent + line for line in r.splitlines(True)]) + return r + +def _test(): + import doctest + doctest.testmod() + +if __name__ == "__main__": + _test() diff --git a/numpy/f2py/lib/extgen/c_code.py b/numpy/f2py/lib/extgen/c_code.py new file mode 100644 index 000000000..eed661f7b --- /dev/null +++ b/numpy/f2py/lib/extgen/c_code.py @@ -0,0 +1,32 @@ + +from base import Base + +class CCode(Base): + + """ + CCode(*lines, provides=..) + """ + + container_options = dict(CCodeLines=dict()) + + template = '%(CCodeLines)s' + + def initialize(self, *lines, **options): + self.lines = [] + map(self.add, lines) + + def update_containers(self): + CCodeLines = self.get_container('CCodeLines') + CCodeLines.add('\n'.join(self.lines)) + + def add(self, component, label=None): + if isinstance(component, str): + assert label is None,`label` + self.lines.append(component) + elif isinstance(component, CCode): + assert label is None,`label` + self.lines.extend(component.lines) + else: + Base.add(self, component. label) + + diff --git a/numpy/f2py/lib/extgen/c_type.py b/numpy/f2py/lib/extgen/c_type.py new file mode 100644 index 000000000..a8c21e48f --- /dev/null +++ b/numpy/f2py/lib/extgen/c_type.py @@ -0,0 +1,156 @@ +""" +Defines C type declaration templates: + + CTypeAlias(name, ctype) --- typedef ctype name; + CTypeFunction(name, rtype, atype1, atype2,..) --- typedef rtype (*name)(atype1, atype2,...); + CTypeStruct(name, (name1,type1), (name2,type2), ...) --- typedef struct { type1 name1; type2 name2; .. } name; + CTypePtr(ctype) --- ctype * + CInt(), CLong(), ... --- int, long, ... + CPyObject() + +The instances of CTypeBase have the following public methods and properties: + + - .asPtr() + - .declare(name) +""" + + +from base import Base + +class CTypeBase(Base): + + def declare(self, name): + return '%s %s;' % (self.typename, name) + + def __str__(self): + return self.typename + + def asPtr(self): + return CTypePtr(self) + +class CTypeAlias(CTypeBase): + + def __new__(cls, typename, ctype): + obj = Base.__new__(cls) + assert isinstance(ctype, CTypeBase),`type(ctype)` + obj.add(typename, ctype) + return obj + + @property + def typename(self): return self.components[0][0] + @property + def ctype(self): return self.components[0][1] + + def local_generate(self, params=None): + container = self.get_container('TypeDef') + container.add(self.typename, 'typedef %s %s;' % (self.ctype, self.typename)) + return self.declare(params) + + +class CTypeFunction(CTypeBase): + + def __new__(cls, typename, rctype, *arguments): + obj = Base.__new__(cls) + assert isinstance(rctype, CTypeBase),`type(rctype)` + obj.add(typename, rctype) + for i in range(len(arguments)): + a = arguments[i] + assert isinstance(a, CTypeBase),`type(a)` + obj.add('_a%i' % (i), a) + return obj + + @property + def typename(self): return self.components[0][0] + @property + def rctype(self): return self.components[0][1] + @property + def arguments(self): return [v for n,v in self.components[1:]] + + def local_generate(self, params=None): + container = self.get_container('TypeDef') + container.add(self.typename, 'typedef %s (*%s)(%s);' \ + % (self.rctype, self.typename, + ', '.join([str(ctype) for ctype in self.arguments]))) + return self.declare(params) + +class CTypeStruct(CTypeBase): + + def __new__(cls, typename, *components): + obj = Base.__new__(cls, typename) + for n,v in components: + assert isinstance(v,CTypeBase),`type(v)` + obj.add(n,v) + return obj + + @property + def typename(self): return self._args[0] + + def local_generate(self, params=None): + container = self.get_container('TypeDef') + decls = [ctype.declare(name) for name, ctype in self.components] + if decls: + d = 'typedef struct {\n %s\n} %s;' % ('\n '.join(decls),self.typename) + else: + d = 'typedef struct {} %s;' % (self.typename) + container.add(self.typename, d) + return self.declare(params) + +class CTypePtr(CTypeBase): + + def __new__(cls, ctype): + obj = Base.__new__(cls) + assert isinstance(ctype, CTypeBase),`type(ctype)` + obj.add('*', ctype) + return obj + + @property + def ctype(self): return self.components[0][1] + + @property + def typename(self): + return self.ctype.typename + '*' + + def local_generate(self, params=None): + return self.declare(params) + +class CTypeDefined(CTypeBase): + + @property + def typename(self): return self._args[0] + +class CTypeIntrinsic(CTypeDefined): + + def __new__(cls, typename): + return Base.__new__(cls, typename) + +class CPyObject(CTypeDefined): + def __new__(cls): + return Base.__new__(cls, 'PyObject') + +class CInt(CTypeIntrinsic): + + def __new__(cls): + return Base.__new__(cls, 'int') + + def local_generate(self, params=None): + container = self.get_container('CAPICode') + code = '''\ +static int pyobj_to_int(PyObject *obj, int* value) { + int status = 1; + if (PyInt_Check(obj)) { + *value = PyInt_AS_LONG(obj); + status = 0; + } + return status; +} +''' + container.add('pyobj_to_int', code) + code = '''\ +static PyObject* pyobj_from_int(int* value) { + return PyInt_FromLong(*value); +} +''' + container.add('pyobj_from_int', code) + + return self.declare(params) + diff --git a/numpy/f2py/lib/extgen/doc.txt b/numpy/f2py/lib/extgen/doc.txt new file mode 100644 index 000000000..17ffef138 --- /dev/null +++ b/numpy/f2py/lib/extgen/doc.txt @@ -0,0 +1,169 @@ +.. -*- rest -*- + +============================================ +ExtGen --- Python extension module generator +============================================ + +:Author: + Pearu Peterson <pearu.peterson@gmail.com> +:Created: August 2007 + +.. contents:: Table of Contents + +Introduction +============ + +ExtGen is a pure Python package that provides a high-level +tool for constructing and building Python extension modules. +Hello example follows:: + + >>> from numpy.f2py.lib.extgen import * + >>> f = PyCFunction('hello') + >>> f.add('printf("Hello!\\n");') + >>> m = ExtensionModule('foo', f) + >>> m.generate() # returns a string containing C source to extension module + >>> foo = m.build + >>> foo.hello() + Hello! + >>> + + +Extending ExtGen +================ + +To extend ExtGen, one needs to understand the infrastructure of +generating extension modules. + +The `extgen` package provides many classes that are derived from Base +class (defined in extgen/base.py). Each such a class represents +certain code block or a code idiom in an extension module that is +defined in `.template` attribute. Most important `Base` methods, that +are used to generate code idioms, are: `.initialize()`, `.add()`, +`.generate()`, `init_containers()`, `.update_containers()`, +`.get_templates()`. + +Creating an extension module is carried out by the following steps: + +- create and add components to `Base` subclass instances, + for example, start with creating an `ExtensionModule` instance. + Components can be added with `.add(component, label=None)` method. + Note that some components (dependencies) may be added + in `.initialize()` method that is called by the constructor + of the `Base` subclass. + +- generate code by calling the `.generate()` method. + +- compile and build an extension module using the generated code. + ExtGen provides a way to do it via accessing the `.build` attribute + of the `ExtensionModule` instance. Accessing this attribute + will generate extension module, compilers it and returns the + corresponding extension module instance. + +These steps will be discussed in more detail below. + +The `.components` attribute is a list object that contains instances +of `Base` subclasses (components). For instance, the `CAPIFunction` instance +defined in the Hello example above, is a component of +`ExtensionModule` instances after calling `.add()` method. Similarly, +the C statement `'printf("Hello!\\n");'` is a component of +`CAPIFunction` instance after calling the `.add_execution()` method. + +The `.template` attribute is a string containing an template +to a code idiom. Such an template may contain string replacements +names that are replaced with code idioms generated by the components +--- template evaluation. +If the class should have more than one template then redefine +`.get_templates()` method that should return a tuple of templates. + +The `.containers` attribute is a mapping between a replacement name +(container label) used in template strings and a `Container` instance +holding code idioms from component generation process. The mapping +`.containers` is updated by the `.init_containers()` and +`.update_containers()` methods. These methods should use +`.get_container(<container label>)` to inquire container instances +and `Container.add(<code idiom string>, label=None)` method to add +new code idioms to containers. + +The `.generate()` method will call `.init_containers()` method, the +`.generate()` methods of components, and `.update_containers()` method +to generate code idioms and save the results to the corresponding +containers. Finally, it returns the results of applying +`.evaluate(<string>)` method to templates which replaces the +replacement names with code idioms from containers as well as string +valued attributes of the given `Base` subclass instance. One can set +attributes inside `.initilize()` method. + +Here follows a simplified version of `ExtensionModule.template`:: + + #include "Python.h" + + %(Header)s + %(TypeDef)s + %(Extern)s + %(CCode)s + %(CAPICode)s + %(ObjDecl)s + + static PyObject* extgen_module; + + static PyMethodDef extgen_module_methods[] = { + %(ModuleMethod)s + {NULL,NULL,0,NULL} + }; + + PyMODINIT_FUNC init%(modulename)s(void) { + extgen_module = Py_InitModule("%(modulename)s", extgen_module_methods); + %(ModuleInit)s + return; + capi_error: + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_RuntimeError, "failed to initialize %(modulename)s module."); + } + return; + } + +Here `Header`, `TypeDef`, etc are the labels of containers which will be replaced +during evaluation of templates. + +Using `Container` class +======================= + +`Container` class has the following optional arguments: + + - `separator='\n'` + - `prefix=''` + - `suffix=''` + - `skip_prefix_when_empty=False` + - `skip_suffix_when_empty=False` + - `default=''` + - `reverse=False` + - `user_defined_str=None` + +that can be used to change the behaviour of `Container.__str__()` +method. By default, `Container.__str__()` method returns +`prefix+separator.join(<Container instance>.list)+suffix`. + +One can add items to `Container` instance using `.add(<string>, +label=None)` method. Here `label` should contain an unique value that +represents the content of `<string>`. If `label` is `None` then +`label = time.time()` will be set. + +If one tries to add items with the same label to the container then +the equality of the corresponding string values will be checked. If +they are not equal then `ValueError` is raised, otherwise adding an +item is ignored. + + +Reference manual +================ + +ExtGen package defines the following extension module component classes: + + - `ExtensionModule(<modulename>, *components, numpy=False, provides=..)` --- + represents an extension module, + + - `PyCFunction(<name>, *components, provides=..)` --- + represents an extension function. + + - `CCode(*lines, provides=..)` --- represents any C code block or statement. + diff --git a/numpy/f2py/lib/extgen/extension_module.py b/numpy/f2py/lib/extgen/extension_module.py new file mode 100644 index 000000000..b469bcf18 --- /dev/null +++ b/numpy/f2py/lib/extgen/extension_module.py @@ -0,0 +1,137 @@ + +from base import Base + +class ExtensionModule(Base): + + """ + ExtensionModule(<modulename>, *components, numpy=False, provides=..) + + Hello example: + + >>> # in general use: + >>> # from numpy.f2py.lib.extgen import * + >>> # instead of the following import statement + >>> from __init__ import * #doctest: +ELLIPSIS + Ignoring... + >>> f = PyCFunction('hello') + >>> f.add('printf("Hello!\\\\n");') + >>> f.add('printf("Bye!\\\\n");') + >>> m = ExtensionModule('foo', f) + >>> foo = m.build #doctest: +ELLIPSIS + exec_command... + >>> foo.hello() + >>> # you should now see Hello! printed to stdout stream. + + """ + + container_options = dict(\ + Header=dict(default='<KILLLINE>'), + TypeDef=dict(default='<KILLLINE>'), + Extern=dict(default='<KILLLINE>'), + CCode=dict(default='<KILLLINE>'), + CAPICode=dict(default='<KILLLINE>'), + ObjDecl=dict(default='<KILLLINE>'), + ModuleMethod=dict(suffix=',', skip_suffix_when_empty=True, + default='<KILLLINE>', use_indent=True), + ModuleInit=dict(default='<KILLLINE>', use_indent=True), + ) + + component_container_map = dict(PyCFunction = 'CAPICode') + + template = '''\ +/* -*- c -*- */ +/* This Python C/API extension module "%(modulename)s" is generated + using extgen tool. extgen is part of numpy.f2py.lib package + developed by Pearu Peterson <pearu.peterson@gmail.com>. +*/ + +#ifdef __cplusplus +extern \"C\" { +#endif + +%(Header)s +%(TypeDef)s +%(Extern)s +%(CCode)s +%(CAPICode)s +%(ObjDecl)s + +static PyObject* extgen_module; + +static PyMethodDef extgen_module_methods[] = { + %(ModuleMethod)s + {NULL,NULL,0,NULL} +}; + +PyMODINIT_FUNC init%(modulename)s(void) { + extgen_module = Py_InitModule("%(modulename)s", extgen_module_methods); + %(ModuleInit)s + return; +capi_error: + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_RuntimeError, "failed to initialize %(modulename)s module."); + } + return; +} + +#ifdef __cplusplus +} +#endif +''' + + def initialize(self, modulename, *components, **options): + self.modulename = modulename + self._provides = options.get('provides', + '%s_%s' % (self.__class__.__name__, modulename)) + # all Python extension modules require Python.h + self.add(Base.get('Python.h'), 'Header') + if options.get('numpy'): + self.add(Base.get('arrayobject.h'), 'Header') + self.add(Base.get('import_array'), 'ModuleInit') + map(self.add, components) + return + + @property + def build(self): + import os + import sys + import subprocess + extfile = self.generate() + srcfile = os.path.abspath('%smodule.c' % (self.modulename)) + f = open(srcfile, 'w') + f.write(extfile) + f.close() + modulename = self.modulename + setup_py = """ +def configuration(parent_package='', top_path = ''): + from numpy.distutils.misc_util import Configuration + config = Configuration('',parent_package,top_path) + config.add_extension('%(modulename)s', + sources = ['%(srcfile)s']) + return config +if __name__ == '__main__': + from numpy.distutils.core import setup + setup(configuration=configuration) +""" % (locals()) + setupfile = os.path.abspath('setup_extgen.py') + f = open(setupfile, 'w') + f.write(setup_py) + f.close() + setup_args = ['build_ext','--build-lib','.'] + setup_cmd = ' '.join([sys.executable,setupfile]+setup_args) + build_dir = '.' + from numpy.distutils.exec_command import exec_command + sts = exec_command(setup_cmd) + #p = subprocess.Popen(setup_cmd, cwd=build_dir, shell=True, stdout=subprocess.PIPE,stderr=subprocess.PIPE) + #sts = os.waitpid(p.pid, 0) + if sts[0]: + raise "Failed to build (status=%s)." % (`sts`) + exec 'import %s as m' % (modulename) + return m + +def _test(): + import doctest + doctest.testmod() + +if __name__ == "__main__": + _test() diff --git a/numpy/f2py/lib/extgen/predefined_components.py b/numpy/f2py/lib/extgen/predefined_components.py new file mode 100644 index 000000000..e177ac4b5 --- /dev/null +++ b/numpy/f2py/lib/extgen/predefined_components.py @@ -0,0 +1,23 @@ + +from base import Base +from c_code import CCode + +Base.register( + + CCode('#include "Python.h"', provides='Python.h'), + + CCode('''\ +#define PY_ARRAY_UNIQUE_SYMBOL PyArray_API +#include "numpy/arrayobject.h" +#include "numpy/arrayscalars.h" +''', provides='arrayobject.h'), + + CCode('''\ +import_array(); +if (PyErr_Occurred()) { + PyErr_SetString(PyExc_ImportError, "failed to load array module."); + goto capi_error; +} +''', provides='import_array') + + ) diff --git a/numpy/f2py/lib/extgen/pyc_function.py b/numpy/f2py/lib/extgen/pyc_function.py new file mode 100644 index 000000000..7641dab38 --- /dev/null +++ b/numpy/f2py/lib/extgen/pyc_function.py @@ -0,0 +1,77 @@ + +from base import Base + +class PyCFunction(Base): + + """ + PyCFunction(<name>, *components, provides=..) + + """ + + container_options = dict(FuncDoc=dict(separator='"\n"', prefix='"', suffix='"'), + Args = dict(), + Decl = dict(default='<KILLLINE>', use_indent=True), + KWList = dict(separator=', ', suffix=', ', skip_suffix_when_empty=True), + PyArgFormat = dict(separator=''), + PyArgObj = dict(separator=', ', prefix=', ', skip_prefix_when_empty=True), + FromPyObj = dict(default='<KILLLINE>', use_indent=True), + Exec = dict(default='<KILLLINE>', use_indent=True), + PyObjFrom = dict(default='<KILLLINE>', use_indent=True), + RetFormat = dict(separator=''), + RetObj = dict(separator=', ', prefix=', ', skip_prefix_when_empty=True), + CleanPyObjFrom = dict(default='<KILLLINE>', reverse=True, use_indent=True), + CleanExec = dict(default='<KILLLINE>', reverse=True, use_indent=True), + CleanFromPyObj = dict(default='<KILLLINE>', reverse=True, use_indent=True), + ) + + component_container_map = dict(CCode = 'Exec', + PyCArgument = 'Args') + + template = ''' +static char %(pyc_name)s_doc[] = %(FuncDoc)s; + +static PyObject* +%(pyc_name)s +(PyObject *pyc_self, PyObject *pyc_args, PyObject *pyc_keywds) { + PyObject * volatile pyc_buildvalue = NULL; + volatile int capi_success = 1; + %(Decl)s + static char *capi_kwlist[] = {%(KWList)sNULL}; + if (PyArg_ParseTupleAndKeywords(pyc_args, pyc_keywds,"%(PyArgFormat)s", capi_kwlist%(PyArgObj)s)) { + %(FromPyObj)s + %(Exec)s + capi_success = !PyErr_Occurred(); + if (capi_success) { + %(PyObjFrom)s + pyc_buildvalue = Py_BuildValue("%(RetFormat)s"%(RetObj)s); + %(CleanPyObjFrom)s + } + %(CleanExec)s + %(CleanFromPyObj)s + } + return pyc_buildvalue; +} +''' + + def initialize(self, name, *components, **options): + self.name = name + self.pyc_name = 'pyc_function_'+name + self._provides = options.get('provides', + '%s_%s' % (self.__class__.__name__, name)) + map(self.add, components) + + def init_containers(self): + # set header to FuncDoc, for example. + FuncDoc = self.get_container('FuncDoc') + FuncDoc.add(self.name) + return + + def update_containers(self, params=None): + ModuleMethod = self.get_container('ModuleMethod') + t = '{"%(name)s", (PyCFunction)%(pyc_name)s,\n METH_VARARGS | METH_KEYWORDS, %(pyc_name)s_doc}' + ModuleMethod.add(self.evaluate(t), self.name) + return + + + + |