[Numpy-svn] r3940 - in trunk/numpy/f2py/lib: . extgen

numpy-svn@scip... numpy-svn@scip...
Fri Aug 3 14:45:21 CDT 2007


Author: pearu
Date: 2007-08-03 14:44:53 -0500 (Fri, 03 Aug 2007)
New Revision: 3940

Added:
   trunk/numpy/f2py/lib/extgen/
   trunk/numpy/f2py/lib/extgen/__init__.py
   trunk/numpy/f2py/lib/extgen/base.py
   trunk/numpy/f2py/lib/extgen/c_code.py
   trunk/numpy/f2py/lib/extgen/c_type.py
   trunk/numpy/f2py/lib/extgen/doc.txt
   trunk/numpy/f2py/lib/extgen/extension_module.py
   trunk/numpy/f2py/lib/extgen/predefined_components.py
   trunk/numpy/f2py/lib/extgen/pyc_function.py
Log:
Initial commit of extgen - Python Extension module Generator package.

Added: trunk/numpy/f2py/lib/extgen/__init__.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/__init__.py	2007-08-02 19:26:40 UTC (rev 3939)
+++ trunk/numpy/f2py/lib/extgen/__init__.py	2007-08-03 19:44:53 UTC (rev 3940)
@@ -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

Added: trunk/numpy/f2py/lib/extgen/base.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/base.py	2007-08-02 19:26:40 UTC (rev 3939)
+++ trunk/numpy/f2py/lib/extgen/base.py	2007-08-03 19:44:53 UTC (rev 3940)
@@ -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()

Added: trunk/numpy/f2py/lib/extgen/c_code.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/c_code.py	2007-08-02 19:26:40 UTC (rev 3939)
+++ trunk/numpy/f2py/lib/extgen/c_code.py	2007-08-03 19:44:53 UTC (rev 3940)
@@ -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)
+
+        

Added: trunk/numpy/f2py/lib/extgen/c_type.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/c_type.py	2007-08-02 19:26:40 UTC (rev 3939)
+++ trunk/numpy/f2py/lib/extgen/c_type.py	2007-08-03 19:44:53 UTC (rev 3940)
@@ -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)
+

Added: trunk/numpy/f2py/lib/extgen/doc.txt
===================================================================
--- trunk/numpy/f2py/lib/extgen/doc.txt	2007-08-02 19:26:40 UTC (rev 3939)
+++ trunk/numpy/f2py/lib/extgen/doc.txt	2007-08-03 19:44:53 UTC (rev 3940)
@@ -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.
+

Added: trunk/numpy/f2py/lib/extgen/extension_module.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/extension_module.py	2007-08-02 19:26:40 UTC (rev 3939)
+++ trunk/numpy/f2py/lib/extgen/extension_module.py	2007-08-03 19:44:53 UTC (rev 3940)
@@ -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()

Added: trunk/numpy/f2py/lib/extgen/predefined_components.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/predefined_components.py	2007-08-02 19:26:40 UTC (rev 3939)
+++ trunk/numpy/f2py/lib/extgen/predefined_components.py	2007-08-03 19:44:53 UTC (rev 3940)
@@ -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')
+
+    )

Added: trunk/numpy/f2py/lib/extgen/pyc_function.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/pyc_function.py	2007-08-02 19:26:40 UTC (rev 3939)
+++ trunk/numpy/f2py/lib/extgen/pyc_function.py	2007-08-03 19:44:53 UTC (rev 3940)
@@ -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
+
+
+
+    



More information about the Numpy-svn mailing list