[Numpy-svn] r3946 - trunk/numpy/f2py/lib/extgen

numpy-svn at scipy.org numpy-svn at scipy.org
Sun Aug 5 11:23:47 EDT 2007


Author: pearu
Date: 2007-08-05 10:23:30 -0500 (Sun, 05 Aug 2007)
New Revision: 3946

Modified:
   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_argument.py
   trunk/numpy/f2py/lib/extgen/pyc_function.py
Log:
Impl CType classes and rewrote docs.

Modified: trunk/numpy/f2py/lib/extgen/__init__.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/__init__.py	2007-08-04 21:03:20 UTC (rev 3945)
+++ trunk/numpy/f2py/lib/extgen/__init__.py	2007-08-05 15:23:30 UTC (rev 3946)
@@ -11,4 +11,8 @@
 from pyc_argument import PyCArgument
 from c_code import CCode
 
+import c_type
+from c_type import *
+__all__ += c_type.__all__
+
 import predefined_components

Modified: trunk/numpy/f2py/lib/extgen/base.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/base.py	2007-08-04 21:03:20 UTC (rev 3945)
+++ trunk/numpy/f2py/lib/extgen/base.py	2007-08-05 15:23:30 UTC (rev 3946)
@@ -1,58 +1,70 @@
 """
 ExtGen --- Python Extension module Generator.
 
-Defines Base and Container classes.
+Defines Component and Container classes.
 """
 
 import re
 import sys
 import time
 
-class BaseMetaClass(type):
+class ComponentMetaClass(type):
 
     classnamespace = {}
 
     def __init__(cls,*args,**kws):
         n = cls.__name__
-        c = BaseMetaClass.classnamespace.get(n)
+        c = ComponentMetaClass.classnamespace.get(n)
         if c is None:
-            BaseMetaClass.classnamespace[n] = cls
+            ComponentMetaClass.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]
+        try: return ComponentMetaClass.classnamespace[name]
         except KeyError: pass
         raise AttributeError("'%s' object has no attribute '%s'"%
                              (cls.__name__, name))
 
-class Base(object):
+class Component(object): # XXX: rename Component to Component
 
-    __metaclass__ = BaseMetaClass
+    __metaclass__ = ComponentMetaClass
 
     container_options = dict()
     component_container_map = dict()
+    default_container_label = None
+    default_component_class_name = 'CCode'
     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.components = [] # holds pairs (<Component subclass instance>, <container name or None>)
         obj.initialize(*args, **kws)    # initialize from constructor arguments
         return obj
 
-    def initialize(self, *args, **kws):
+    def initialize(self, *components, **options):
         """
         Set additional attributes, add components to instance, etc.
         """
         # self.myattr = ..
-        # map(self.add, args)
+        # map(self.add, components)
         return
 
+    @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
+
     @staticmethod
     def warning(message):
         print >> sys.stderr, 'extgen:',message
@@ -61,59 +73,48 @@
         print >> sys.stderr, message
 
     def __repr__(self):
-        return '%s%s' % (self.__class__.__name__, `self._args`)
+        return '%s%s' % (self.__class__.__name__, `self.containers`)
 
     def __getattr__(self, attr):
-        if attr.startswith('container_'):
+        if attr.startswith('container_'): # convenience feature
             return self.get_container(attr[10:])
         raise AttributeError('%s instance has no attribute %r' % (self.__class__.__name__, attr))
 
-    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
+    def __add__(self, other): # convenience method
+        self.add(other)
+        return self
+    __iadd__ = __add__
 
-        # 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.
+    @staticmethod
+    def _get_class_names(cls):
+        if not issubclass(cls, Component):
+            return [cls]
+        r = [cls]
+        for b in cls.__bases__:
+            r += Component._get_class_names(b)
+        return r
         
-        Used in avoiding redefinitions of functions and variables.
+    def add(self, component, container_label=None):
         """
-        if self._provides is None:
-            return '%s_%s' % (self.__class__.__name__, id(self))
-        return self._provides
-
-    def get_templates(self):
+        Append component and its target container label to components list.
         """
-        Return instance templates.
-        """
-        return self.template
+        if not isinstance(component, Component) and self.default_component_class_name!=component.__class__.__name__:
+            clsname = self.default_component_class_name
+            if clsname is not None:
+                component = getattr(Component, clsname)(component)
+            else:
+                raise ValueError('%s.add requires Component instance but got %r' \
+                                 % (self.__class__.__name__, component.__class__.__name__))
+        if container_label is None:
+            container_label = self.default_container_label
+            for n in self._get_class_names(component.__class__):
+                try:
+                    container_label = self.component_container_map[n.__name__]
+                    break
+                except KeyError:
+                    pass
+        self.components.append((component, container_label))
+        return
 
     def generate(self):
         """
@@ -135,10 +136,22 @@
 
         # generate component code idioms
         for component, container_key in self.components:
+            if not isinstance(component, Component):
+                result = str(component)
+                if container_key == '<IGNORE>':
+                    pass
+                elif container_key is not None:
+                    self.get_container(container_key).add(result)
+                else:
+                    self.warning('%s: no container label specified for component %r'\
+                                 % (self.__class__.__name__,component))
+                continue
             old_parent = component.parent
             component.parent = self
             result = component.generate()
-            if container_key is not None:
+            if container_key == '<IGNORE>':
+                pass
+            elif container_key is not None:
                 if isinstance(container_key, tuple):
                     assert len(result)==len(container_key),`len(result),container_key`
                     results = result
@@ -151,8 +164,8 @@
                     container = component.get_container(k)
                     container.add(r, component.provides)
             else:
-                self.warning('no label specified for component %r, ignoring its result'\
-                             % (component.provides))
+                self.warning('%s: no container label specified for component providing %r'\
+                                 % (self.__class__.__name__,component.provides))
             component.parent = old_parent
 
         # update code idioms
@@ -184,33 +197,42 @@
         # container.add(<string>, label=None)
         return
 
-    def __iadd__(self, other):
-        """ Convenience add.
+    def get_container(self, name):
+        """ 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
         """
-        self.add(other)
-        return self
+        # local container
+        try:
+            return self.containers[name]
+        except KeyError:
+            pass
+        
+        # parent container
+        parent = self.parent
+        while parent is not None:
+            try:
+                return parent.containers[name]
+            except KeyError:
+                parent = parent.parent
+                continue
 
-    def add(self, component, container_label=None):
+        # 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__, name))
+        c = self.containers[name] = Container()
+        return c
+
+    def get_templates(self):
         """
-        Append component and its target container label to components list.
+        Return instance templates.
         """
-        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))
+        return self.template
 
-    @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, **attrs):
         """
         Evaluate template using instance attributes and code
@@ -235,8 +257,9 @@
                 template = template[:i] + str(container) + template[i+len(s):]
                 container.indent_offset = old_indent
         template = template % d
-        return re.sub(r'.*[<]KILLLINE[>].*\n','', template)
+        return re.sub(r'.*[<]KILLLINE[>].*(\n|$)','', template)
 
+
     _registered_components_map = {}
 
     @staticmethod
@@ -245,11 +268,11 @@
         Register components so that component classes can use
         predefined components via `.get(<provides>)` method.
         """
-        d = Base._registered_components_map
+        d = Component._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))
+                Component.warning('component that provides %r is already registered, ignoring.' % (provides))
             else:
                 d[provides] = component
         return
@@ -260,7 +283,7 @@
         Return predefined component with given provides property..
         """
         try:
-            return Base._registered_components_map[provides]
+            return Component._registered_components_map[provides]
         except KeyError:
             pass
         raise KeyError('no registered component provides %r' % (provides))
@@ -269,6 +292,7 @@
     def numpy_version(self):
         import numpy
         return numpy.__version__
+
     
 class Container(object):
     """
@@ -292,7 +316,7 @@
     "hey, hoo, bar"
     
     """
-    __metaclass__ = BaseMetaClass
+    __metaclass__ = ComponentMetaClass
 
     def __init__(self,
                  separator='\n', prefix='', suffix='',
@@ -303,6 +327,7 @@
                  use_indent = False,
                  indent_offset = 0,
                  use_firstline_indent = False, # implies use_indent
+                 replace_map = {}
                  ):
         self.list = []
         self.label_map = {}
@@ -318,8 +343,9 @@
         self.use_indent = use_indent or use_firstline_indent
         self.indent_offset = indent_offset
         self.use_firstline_indent = use_firstline_indent
+        self.replace_map = replace_map
         
-    def __notzero__(self):
+    def __nonzero__(self):
         return bool(self.list)
 
     def has(self, label):
@@ -353,6 +379,8 @@
             if d!=content:
                 raise ValueError("Container item %r exists with different value" % (label))
             return
+        for old, new in self.replace_map.items():
+            content = content.replace(old, new)
         self.list.append(content)
         self.label_map[label] = len(self.list)-1
         return
@@ -396,7 +424,9 @@
                        default = self.default, reverse=self.reverse,
                        user_defined_str = self.user_str,
                        use_indent = self.use_indent,
-                       indent_offset = self.indent_offset
+                       indent_offset = self.indent_offset,
+                       use_firstline_indent = self.use_firstline_indent,
+                       replace_map = self.replace_map
                        )
         options.update(extra_options)
         cpy = Container(**options)

Modified: trunk/numpy/f2py/lib/extgen/c_code.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/c_code.py	2007-08-04 21:03:20 UTC (rev 3945)
+++ trunk/numpy/f2py/lib/extgen/c_code.py	2007-08-05 15:23:30 UTC (rev 3946)
@@ -1,7 +1,7 @@
 
-from base import Base
+from base import Component
 
-class CCode(Base):
+class CCode(Component):
 
     """
     CCode(*lines, provides=..)
@@ -27,6 +27,6 @@
             assert label is None,`label`
             self.lines.extend(component.lines)
         else:
-            Base.add(self, component. label)
+            Component.add(self, component. label)
 
         

Modified: trunk/numpy/f2py/lib/extgen/c_type.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/c_type.py	2007-08-04 21:03:20 UTC (rev 3945)
+++ trunk/numpy/f2py/lib/extgen/c_type.py	2007-08-05 15:23:30 UTC (rev 3946)
@@ -1,137 +1,200 @@
 """
-Defines C type declaration templates:
+C types.
 
-  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)
 """
 
+__all__ = ['CType', 'CTypeAlias', 'CTypeFuncAlias', 'CTypePtr', 'CTypeStruct', 'CDecl']
 
-from base import Base
+from base import Component
 
-class CTypeBase(Base):
+class CTypeBase(Component):
 
-    def declare(self, name):
-        return '%s %s;' % (self.typename, name)
+    template = '%(name)s'
+    template_typedef = ''
+    default_container_label = '<IGNORE>'
+    default_component_class_name = 'CType'
 
+    @property
+    def provides(self):
+        return '%s_%s' % (self.__class__.__name__, self.name)
+
+    def initialize(self, name, *components):
+        self.name = name
+        map(self.add, components)
+
+    def update_containers(self):
+        self.container_TypeDef += self.evaluate(self.template_typedef)
+
     def __str__(self):
-        return self.typename
+        return self.name
 
-    def asPtr(self):
-        return CTypePtr(self)
+class _CatchTypeDef(Component): # for doctest
+    template = '%(TypeDef)s'
+    default_container_label = '<IGNORE>'
+    container_options = dict(TypeDef=dict(default=''))
+    def initialize(self, ctype):
+        self.add(ctype)
 
+class CType(CTypeBase):
+
+    """ CType(<name>)
+
+    Represents any predefined type in C.
+
+    >>> cint = CType('int')
+    >>> print cint
+    int
+    >>> _CatchTypeDef(cint).generate()
+    ''
+    """
+
+    def initialize(self, name):
+        self.name = name
+
+    def update_containers(self):
+        pass
+
 class CTypeAlias(CTypeBase):
 
-    def __new__(cls, typename, ctype):
-        obj = Base.__new__(cls)
-        assert isinstance(ctype, CTypeBase),`type(ctype)`
-        obj.add(typename, ctype)
-        return obj
+    """ CTypeAlias(<name>, <ctype>)
 
-    @property
-    def typename(self): return self.components[0][0]
-    @property
-    def ctype(self): return self.components[0][1]
+    >>> aint = CTypeAlias('aint', 'int')
+    >>> print aint
+    aint
+    >>> print _CatchTypeDef(aint).generate()
+    typedef int aint;
+    """
 
-    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)
+    template_typedef = 'typedef %(ctype_name)s %(name)s;'
 
+    def initialize(self, name, ctype):
+        self.name = name
+        if isinstance(ctype, str): ctype = CType(ctype)
+        self.ctype_name = ctype.name
+        self.add(ctype)
 
-class CTypeFunction(CTypeBase):
+class CTypeFuncAlias(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
+    """
+    CTypeFuncAlias(<name>, <return ctype>, *(<argument ctypes>))
 
-    @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:]]
+    >>> ifunc = CTypeFuncAlias('ifunc', 'int')
+    >>> print ifunc
+    ifunc
+    >>> print _CatchTypeDef(ifunc).generate()
+    typedef int (*ifunc)(void);
+    >>> ifunc += 'double'
+    >>> print _CatchTypeDef(ifunc).generate()
+    typedef int (*ifunc)(double);
+    """
 
-    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)
+    template_typedef = 'typedef %(RCType)s (*%(name)s)(%(ACType)s);'
+    container_options = dict(RCType = dict(default='void'),
+                             ACType = dict(default='void', separator=', '))
+    component_container_map = dict(CType = 'ACType')
+    default_component_class_name = 'CType'
 
+    def initialize(self, name, *components):
+        self.name = name
+        if components:
+            self.add(components[0], 'RCType')
+        map(self.add, components[1:])
+
+class CTypePtr(CTypeBase):
+
+    """
+    CTypePtr(<ctype>)
+
+    >>> int_ptr = CTypePtr('int')
+    >>> print int_ptr
+    int_ptr
+    >>> print _CatchTypeDef(int_ptr).generate()
+    typedef int* int_ptr;
+    >>> int_ptr_ptr = CTypePtr(int_ptr)
+    >>> print int_ptr_ptr
+    int_ptr_ptr
+    >>> print _CatchTypeDef(int_ptr_ptr).generate()
+    typedef int* int_ptr;
+    typedef int_ptr* int_ptr_ptr;
+    """
+
+    template_typedef = 'typedef %(ctype_name)s* %(name)s;'
+
+    def initialize(self, ctype):
+        if isinstance(ctype, str): ctype = CType(ctype)
+        self.name = '%s_ptr' % (ctype)
+        self.ctype_name = ctype.name
+        self.add(ctype)
+
 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
+    """
+    CTypeStruct(<name>, *(<declarations>))
 
-    @property
-    def typename(self): return self._args[0]
+    >>> s = CTypeStruct('s', CDecl('int','a'))
+    >>> print s
+    s
+    >>> print _CatchTypeDef(s).generate()
+    typedef struct {
+      int a;
+    } s;
+    >>> s += CDecl(CTypeFuncAlias('ft'), 'f')
+    >>> print _CatchTypeDef(s).generate()
+    typedef void (*ft)(void);
+    typedef struct {
+      int a;
+      ft f;
+    } s;
 
-    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):
+    container_options = dict(Decl = dict(default='<KILLLINE>', use_indent=True))
+    default_component_class_name = None #'CDecl'
+    component_container_map = dict(CDecl='Decl')
 
-    def __new__(cls, ctype):
-        obj = Base.__new__(cls)
-        assert isinstance(ctype, CTypeBase),`type(ctype)`
-        obj.add('*', ctype)
-        return obj
+    template_typedef = '''\
+typedef struct {
+  %(Decl)s
+} %(name)s;'''
 
-    @property
-    def ctype(self): return self.components[0][1]
-    
-    @property
-    def typename(self):
-        return self.ctype.typename + '*'
+    def initialize(self, name, *components):
+        self.name = name
+        map(self.add, components)
 
-    def local_generate(self, params=None):
-        return self.declare(params)
+class CDecl(Component):
 
-class CTypeDefined(CTypeBase):
+    """
+    CDecl(<ctype>, *(<names with or without initialization>))
 
-    @property
-    def typename(self): return self._args[0]
+    >>> ad = CDecl('int')
+    >>> ad.generate()
+    ''
+    >>> ad += 'a'
+    >>> print ad.generate()
+    int a;
+    >>> ad += 'b'
+    >>> print ad.generate()
+    int a, b;
+    >>> ad += 'c = 1'
+    >>> print ad.generate()
+    int a, b, c = 1;
+    """
 
-class CTypeIntrinsic(CTypeDefined):
+    template = '%(CTypeName)s %(Names)s;'
+    container_options = dict(Names=dict(default='<KILLLINE>', separator=', '),
+                             CTypeName=dict())
+    default_component_class_name = 'str'
+    component_container_map = dict(str = 'Names')
 
-    def __new__(cls, typename):
-        return Base.__new__(cls, typename)
+    def initialize(self, ctype, *names):
+        if isinstance(ctype, str): ctype = CType(ctype)
+        self.add(ctype, 'CTypeName')
+        map(self.add, names)
 
-class CPyObject(CTypeDefined):
-    def __new__(cls):
-        return Base.__new__(cls, 'PyObject')
 
-class CInt(CTypeIntrinsic):
+if 0:
 
-    def __new__(cls):
-        return Base.__new__(cls, 'int')
-
     def local_generate(self, params=None):
         container = self.get_container('CAPICode')
         code = '''\
@@ -154,3 +217,9 @@
 
         return self.declare(params)
 
+def _test():
+    import doctest
+    doctest.testmod()
+    
+if __name__ == "__main__":
+    _test()

Modified: trunk/numpy/f2py/lib/extgen/doc.txt
===================================================================
--- trunk/numpy/f2py/lib/extgen/doc.txt	2007-08-04 21:03:20 UTC (rev 3945)
+++ trunk/numpy/f2py/lib/extgen/doc.txt	2007-08-05 15:23:30 UTC (rev 3946)
@@ -22,78 +22,140 @@
   >>> f = PyCFunction('hello')
   >>> f += 'printf("Hello!\\n");'
   >>> m += f
-  >>> m.generate() # returns a string containing C source to extension module
+  >>> print m.generate() # shows a string containing C source to extension module
   >>> foo = m.build()
   >>> foo.hello()
   Hello!
   >>> 
 
 
-Extending ExtGen
-================
+Description of the ExtGen model
+===============================
 
 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()`.
+There are two important concepts in ExtGen model: components and
+containers. Components (ref. class `Component`) define code blocks or
+code idioms used in building up a code sources. Containers (ref. class
+`Container`) are named string lists that are joined together with
+specified rules resulting actual code sources. ExtGen uses two steps
+for constructing code sources:
 
-Creating an extension module is carried out by the following steps:
+- creating code components and adding them together to a parent
+  component. For example, the `ExtensionModule` instance in the
+  hello example becomes a parent component to a `PyCFunction` instance
+  after executing `m += f`.
 
-- 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.
+- generating code source by calling `.generate()` method of the
+  parent component.
 
-- generate code by calling the `.generate()` method. 
+One can iterate the above process as one wishes.
 
-- compile and build an extension module using the generated code.
-  ExtGen provides a way to do it via `.build()` method
-  of the `ExtensionModule` instance. Calling this method
-  will generate extension module, compiles it and returns the
-  corresponding extension module instance.
+The method `ExtensionModule.build()` is defined for convenience.
+It compiles the generated sources, builds an extension module,
+imports the resulting module to Python, and returns the module object.
 
-These steps will be discussed in more detail below.
+All component classes must be derived from the base class `Component`
+defined in `extgen/base.py` file. `Component` class defines the
+following methods and attributes:
 
-The `.components` attribute is a list object that contains instances
-of `Base` subclasses (components). For instance, the `PyCFunction` 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
-`PyCFunction` instance after calling the `.add()` method.
+- `.initialize(self, *args, **kws)` is used to initialize the attributes
+  and subcomponents of the `Component` instance. Derived classes
+  usually redefine it to define the signature of the component
+  constructor.
 
-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.
+- `.add(self, component, container_label=None)` is used to add
+  subcomponents to the `Component`. Derived classes can affect
+  the behavior of the `.add()` method by redefining the following
+  class attributes:
 
-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.
+  - `.default_component_class_name` is used when the `component`
+    argument is not a `Component` instance.
 
-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.
+  - `.default_container_label` is used when component
+    `container_label` is undefined. 
 
+  - `.component_containe_map` is used to find `container_label`
+    corresponding to `component` argument class.
+
+- `.generate(self)` returns a source code string. It recursively
+  processes all subcomponents, creates code containers, and
+  evaluates code templates.
+
+- `.provides(self)` property method returns an unique string
+  labeling the current component. The label is used to name
+  the result of `.generate()` method when storing it to a container.
+  The result is saved to container only if container does not
+  contain the given provides label. With this feature one avoids
+  redefining the same functions, variables, types etc that are needed
+  by different components.
+
+- `.init_containers(self)` is called before processing subcomponents.
+  Derived classes may redefine it.
+
+- `.update_containers(self)` is called after processing subcomponents.
+  Derived classes usually define it to fill up any containers.
+
+- `.get_templates(self)` is used by `.generate()` method to evaluate
+  the templates and return results. By default, `.get_templates()`
+  returns `.template` attribute. Derived classes may redefine it
+  to return a tuple of templates, then also `.generate()` will
+  return a tuple of source code strings.
+
+- `.get_container(self, name)` or `.container_<name>` can be used
+  to retrive a container with a given name. If the current component
+  does not have requested container then the method tries to find
+  the container from parent classes. If it still does not find it,
+  then a new container with the given name will be created for
+  the current component. One should acctually avoid the last
+  solution and always define the containers in `.container_options`
+  class attribute. This attribute is a mapping between container
+  names and keyword options to the `Container` constructor.
+  See `Container` options below for more detail.
+
+- `.evaluate(self, template)` will evaluate `template` using
+  the attributes (with string values) and the code from containers.
+
+- `.info(message)`, `.warning(message)` are utility methods and
+  will write messages to `sys.stderr`.
+
+- `.register(*components)` will register predefined components
+  that can be retrived via `.get(provides)` method.
+
+Deriving a new `Component` class involves the following
+tasks:
+
+- A component class must have a base class `Component`.
+
+- A component class may redefine `.initialize()`,
+  `.init_containers()`, `.update_containers()`, `.get_templates()`
+  methods, `.provides()` property method and `.container_options`,
+  `.component_container_map`, `.default_container_label`,
+  `.default_component_class_name`, `.template` attributes.
+
+- In `.initialize()` method one can process constructor options,
+  set new attributes and add predefined components.
+
+- In `.init_containers()` and `.update_containers()` methods
+  one may retrive containers from parents via `.get_container(<name>)`
+  method or `.container_<name>` attribute and fill them using
+  `.add()` method of the container.
+
+- The attribute `.template` is a string containing formatting mapping keys
+  that correspond to containers names or instance attribute names.
+
+- The attribute `.container_options` is a mapping of container
+  names and keyword argument dictionaries used as options
+  to a `Container` constructor.
+
+- The attribute `.component_container_map` is a mapping between
+  subcomponent class names and the names of containers that should
+  be used to save the code generation results.
+
+- All classes derived from `Component` are available as
+  `Component.<subclass name>`.
+
 Here follows a simplified version of `ExtensionModule.template`::
 
   #include "Python.h"
@@ -122,10 +184,13 @@
     }
     return;
   }
-  
-Here `Header`, `TypeDef`, etc are the labels of containers which will be replaced
-during evaluation of templates.
 
+Here formatting mapping keys `Header`, `TypeDef`, etc are the labels
+of containers which will be used in the templare evaluation.
+See `extgen/*.py` files for more examples how to redefine `Component`
+class methods and attributes.
+
+
 Using `Container` class
 =======================
     
@@ -142,22 +207,41 @@
   - `use_firstline_indent=False`
   - `indent_offset=0`
   - `user_defined_str=None`
+  - `replace_map={}`
 
-that can be used to change the behaviour of `Container.__str__()`
-method.  By default, `Container.__str__()` method returns
+that are used to enhance the behaviour of `Container.__str__()`
+method.  By default, `Container.__str__()` 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.
+label=None)` method. The items are saved in `.list` and `.label_map`
+attributes.
 
+`Container` instances can be combined using `+` operator and
+copied with `.copy()` method. The `.copy()` method has the
+same arguments as `Container` constructor and can be used
+to change certain container properties.
+
+The `label` argument 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.
 
+If `reverse` is `True` then the `.list` is reversed before joining
+its items. If `use_indent` is `True` then each item in `.list` will
+be prefixed with `indent_offset` spaces. If `use_firstline_indent` is
+`True` then additional indention of the number of starting spaces
+in `.line[0]` is used. The `replace_map` is used to apply
+`.replace(key, value)` method to the result of `__str__()`.
+Full control over the `__str__()` method is obtained via
+defining `user_defined_str` that should be a callable object taking
+list as input and return a string.
 
+
 Component classes
 =================
 
@@ -165,10 +249,11 @@
 
   - `ExtensionModule(<modulename>, *components, numpy=False,
     provides=.., title=.., description=..)`  ---
-    represents an extension module,
+    represents an extension module component. If `numpy` is `True` then
+    `NumPy` support will be added to an extension module.
     
   - `PyCFunction(<name>, *components, provides=.., title=.., description=..)` ---
-    represents an extension function.
+    represents an extension function component.
 
   - `PyCArgument(<name>, *components, provides=.., input_intent=..,
     output_intent=.., input_title=.., input_description=..,
@@ -177,5 +262,39 @@
     `output_intent` may have values `'required'`, `'optional'`,
     `'extra'`, `'hide'` and `'hide'`, `'return'`, respectively.
 
-  - `CCode(*lines, provides=..)` --- represents any C code block or statement.
+  - `CCode(*lines, provides=..)` --- represents any C code block or
+    statement component.
 
+  - `CType(<name>)` --- represents a predefined or intrinsic C type
+    with a given name.
+
+  - `CTypeAlias(<name>, <ctype>)` --- represents `typedef ctype name;`
+    declaration.
+
+  - `CTypeFuncAlias(<name>, <return ctype>, <argument ctypes>)` ---
+    represents `typedef rctype (*name)(actype1,..)` declaration.
+    Use `.add()` method to add more argument types.
+
+  - `CTypePtr(<ctype>)` --- represents `typedef ctype* ctype_ptr;`
+    declaration.
+
+  - `CTypeStruct(<name>, *declarations)` --- represents `typedef struct {
+    <declarations> } name;` declaration. Use `.add()` method to add
+    more declarations.
+
+  - `CDecl(<ctype>, *names)` --- represents `ctype name1, name2, ..;`
+    declaration. Use `.add()` method to add more names.
+
+
+Predefined components
+=====================
+
+ExtGen defines the following components that can be retrived
+using `Component.get(<provides>)` method:
+
+- `'Python.h'` - include `Pyhton.h` header file.
+
+- `'arrayobject.h'` - include NumPy header files.
+
+- `'import_array'` - code for importing numpy package to extension
+  module.

Modified: trunk/numpy/f2py/lib/extgen/extension_module.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/extension_module.py	2007-08-04 21:03:20 UTC (rev 3945)
+++ trunk/numpy/f2py/lib/extgen/extension_module.py	2007-08-05 15:23:30 UTC (rev 3946)
@@ -1,7 +1,7 @@
 
-from base import Base
+from base import Component
 
-class ExtensionModule(Base):
+class ExtensionModule(Component):
 
     """
     ExtensionModule(<modulename>, *components, numpy=False, provides=..)
@@ -59,15 +59,15 @@
         
         ModuleTitle = dict(default='<KILLLINE>',prefix='"\\n\\n',suffix='"',separator='\\n"\n"  ',
                            skip_prefix_when_empty=True, skip_suffix_when_empty=True,
-                           use_firstline_indent=True),
+                           use_firstline_indent=True, replace_map = {'\n':'\\n'}),
         ModuleDescr = dict(default='<KILLLINE>',prefix='"\\n\\nDescription:\\n"\n"  ',
                            suffix='"',separator='\\n"\n"  ',
                            skip_prefix_when_empty=True, skip_suffix_when_empty=True,
-                           use_firstline_indent=True),
+                           use_firstline_indent=True, replace_map={'\n':'\\n'}),
         ModuleFuncDoc = dict(default='<KILLLINE>', prefix='"\\n\\nFunctions:\\n"\n"  ',
                              separator='\\n"\n"  ', suffix='"',
                              skip_prefix_when_empty=True, skip_suffix_when_empty=True,
-                             use_firstline_indent=True),
+                             use_firstline_indent=True, replace_map={'\n':'\\n'}),
         )
     
     component_container_map = dict(PyCFunction = 'CAPICode')
@@ -131,30 +131,31 @@
 
     def initialize(self, modulename, *components, **options):
         self.modulename = modulename
-        self._provides = options.get('provides',
+        self._provides = options.pop('provides',
                                      '%s_%s' % (self.__class__.__name__, modulename))
+
+        self.title = options.pop('title', None)
+        self.description = options.pop('description', None)
+
+        if options: self.warning('%s unused options: %s\n' % (self.__class__.__name__, options))
+
         # all Python extension modules require Python.h
-        self.add(Base.get('Python.h'), 'Header')
+        self.add(Component.get('Python.h'), 'Header')
         if options.get('numpy'):
-            self.add(Base.get('arrayobject.h'), 'Header')
-            self.add(Base.get('import_array'), 'ModuleInit')
-
-        self.title = options.get('title')
-        self.description = options.get('description')
-            
+            self.add(Component.get('arrayobject.h'), 'Header')
+            self.add(Component.get('import_array'), 'ModuleInit')
         map(self.add, components)
         return
 
     def update_containers(self):
         if self.title is not None:
-            self.container_ModuleTitle += self.title.replace('\n','\\n')
+            self.container_ModuleTitle += self.title
         if self.description is not None:
-            self.container_ModuleDescr += self.description.replace('\n','\\n')
+            self.container_ModuleDescr += self.description
 
     def build(self):
         import os
         import sys
-        import subprocess
         extfile = self.generate()
         srcfile = os.path.abspath('%smodule.c' % (self.modulename))
         f = open(srcfile, 'w')
@@ -181,8 +182,6 @@
         build_dir = '.'
         from numpy.distutils.exec_command import exec_command
         status, output = 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 status:
             raise "Failed to build (status=%s)." % (`status`)
         exec 'import %s as m' % (modulename)

Modified: trunk/numpy/f2py/lib/extgen/predefined_components.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/predefined_components.py	2007-08-04 21:03:20 UTC (rev 3945)
+++ trunk/numpy/f2py/lib/extgen/predefined_components.py	2007-08-05 15:23:30 UTC (rev 3946)
@@ -1,8 +1,8 @@
 
-from base import Base
+from base import Component
 from c_code import CCode
 
-Base.register(
+Component.register(
 
     CCode('#include "Python.h"', provides='Python.h'),
 

Modified: trunk/numpy/f2py/lib/extgen/pyc_argument.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/pyc_argument.py	2007-08-04 21:03:20 UTC (rev 3945)
+++ trunk/numpy/f2py/lib/extgen/pyc_argument.py	2007-08-05 15:23:30 UTC (rev 3946)
@@ -1,7 +1,7 @@
 
-from base import Base
+from base import Component
 
-class PyCArgument(Base):
+class PyCArgument(Component):
 
     """
     PyCArgument(<name>, *components, provides=..,
@@ -19,19 +19,23 @@
 
     def initialize(self, name, *components, **options):
         self.name = name
-        self._provides = options.get('provides',
+        self._provides = options.pop('provides',
                                      '%s_%s' % (self.__class__.__name__, name))
-        self.input_intent = options.get('input_intent','required') # 'optional', 'extra', 'hide'
-        self.output_intent = options.get('output_intent','hide')   # 'return'
-        self.input_title = options.get('input_title', None)
-        self.output_title = options.get('output_title', None)
-        self.input_description = options.get('input_description', None)
-        self.output_description = options.get('output_description', None)
+        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)
+
+        if options: self.warning('%s unused options: %s\n' % (self.__class__.__name__, options))
         
         map(self.add, components)
 
     def get_ctype(self):
-        # scan components for c types
+        for component, container_label in self.components:
+            if isinstance(component, Component.CTypeBase):
+                return component
         return
 
     def init_containers(self):

Modified: trunk/numpy/f2py/lib/extgen/pyc_function.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/pyc_function.py	2007-08-04 21:03:20 UTC (rev 3945)
+++ trunk/numpy/f2py/lib/extgen/pyc_function.py	2007-08-05 15:23:30 UTC (rev 3946)
@@ -1,7 +1,7 @@
 
-from base import Base
+from base import Component
 
-class PyCFunction(Base):
+class PyCFunction(Component):
 
     """
     PyCFunction(<name>, *components, provides=..,title=.., description=..)
@@ -66,27 +66,27 @@
 
         FuncTitle = dict(default='<KILLLINE>',prefix='"\\n\\n',suffix='"',separator='\\n"\n"  ',
                          skip_prefix_when_empty=True, skip_suffix_when_empty=True,
-                         use_firstline_indent=True),
+                         use_firstline_indent=True, replace_map={'\n':'\\n'}),
         FuncDescr = dict(default='<KILLLINE>',prefix='"\\n\\nDescription:\\n"\n"  ',
                          suffix='"',separator='\\n"\n"  ',
                          skip_prefix_when_empty=True, skip_suffix_when_empty=True,
-                         use_firstline_indent=True),
+                         use_firstline_indent=True, replace_map={'\n':'\\n'}),
         ReqArgsDoc = dict(default='<KILLLINE>', prefix='"\\n\\nRequired arguments:\\n"\n"  ',
                           separator='\\n"\n"  ', suffix='"',
                           skip_prefix_when_empty=True, skip_suffix_when_empty=True,
-                          use_firstline_indent=True),
+                          use_firstline_indent=True, replace_map={'\n':'\\n'}),
         OptArgsDoc = dict(default='<KILLLINE>', prefix='"\\n\\nOptional arguments:\\n"\n"  ',
                           separator='\\n"\n"  ', suffix='"',
                           skip_prefix_when_empty=True, skip_suffix_when_empty=True,
-                          use_firstline_indent=True),
+                          use_firstline_indent=True, replace_map={'\n':'\\n'}),
         ExtArgsDoc = dict(default='<KILLLINE>', prefix='"\\n\\nExtra optional arguments:\\n"\n"  ',
                           separator='\\n"\n"  ', suffix='"',
                           skip_prefix_when_empty=True, skip_suffix_when_empty=True,
-                          use_firstline_indent=True),
+                          use_firstline_indent=True, replace_map={'\n':'\\n'}),
         RetDoc = dict(default='"Return value:\\n  None\\n"', prefix='"\\n\\nReturn values:\\n"\n"  ',
                     separator='\\n"\n"  ', suffix='"',
                       skip_prefix_when_empty=True, skip_suffix_when_empty=True,
-                      use_firstline_indent=True),
+                      use_firstline_indent=True, replace_map={'\n':'\\n'}),
         
         Decl = dict(default='<KILLLINE>', use_indent=True),
         
@@ -155,10 +155,13 @@
     def initialize(self, name, *components, **options):
         self.name = name
         self.pyc_name = 'pyc_function_'+name
-        self._provides = options.get('provides',
+        self._provides = options.pop('provides',
                                      '%s_%s' % (self.__class__.__name__, name))
-        self.title = options.get('title', None)
-        self.description = options.get('description', None)
+        self.title = options.pop('title', None)
+        self.description = options.pop('description', None)
+
+        if options: self.warning('%s unused options: %s\n' % (self.__class__.__name__, options))
+        
         map(self.add, components)
 
     def init_containers(self):
@@ -185,10 +188,10 @@
         OptExtArgs += OptArgs + ExtArgs
         ModuleFuncDoc += evaluate('%(name)s(%(ReqArgs)s%(OptExtArgs)s) -> %(RetArgs)s')
         if self.title is not None:
-            FuncTitle += self.title.replace('\n','\\n')
-            ModuleFuncDoc += '  ' + self.title.replace('\n','\\n')
+            FuncTitle += self.title
+            ModuleFuncDoc += '  ' + self.title
         if self.description is not None:
-            FuncDescr += self.description.replace('\n','\\n')
+            FuncDescr += self.description
         return
 
     




More information about the Numpy-svn mailing list