summaryrefslogtreecommitdiff
path: root/numpy/f2py/lib
diff options
context:
space:
mode:
authorPearu Peterson <pearu.peterson@gmail.com>2007-08-03 19:44:53 +0000
committerPearu Peterson <pearu.peterson@gmail.com>2007-08-03 19:44:53 +0000
commit66650bdd599110d55425467d5a99aba0a5c6026d (patch)
treed89de471bc88e7acbeace48f69017c531015bacc /numpy/f2py/lib
parent73001f4b36d4745bec81d3526800aacad5614b3d (diff)
downloadnumpy-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__.py12
-rw-r--r--numpy/f2py/lib/extgen/base.py348
-rw-r--r--numpy/f2py/lib/extgen/c_code.py32
-rw-r--r--numpy/f2py/lib/extgen/c_type.py156
-rw-r--r--numpy/f2py/lib/extgen/doc.txt169
-rw-r--r--numpy/f2py/lib/extgen/extension_module.py137
-rw-r--r--numpy/f2py/lib/extgen/predefined_components.py23
-rw-r--r--numpy/f2py/lib/extgen/pyc_function.py77
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
+
+
+
+