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

numpy-svn@scip... numpy-svn@scip...
Sat Aug 4 14:59:08 CDT 2007


Author: pearu
Date: 2007-08-04 14:58:57 -0500 (Sat, 04 Aug 2007)
New Revision: 3943

Added:
   trunk/numpy/f2py/lib/extgen/pyc_argument.py
Modified:
   trunk/numpy/f2py/lib/extgen/__init__.py
   trunk/numpy/f2py/lib/extgen/base.py
   trunk/numpy/f2py/lib/extgen/doc.txt
   trunk/numpy/f2py/lib/extgen/extension_module.py
   trunk/numpy/f2py/lib/extgen/pyc_function.py
Log:
Impl basic argument support and documentation generation.

Modified: trunk/numpy/f2py/lib/extgen/__init__.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/__init__.py	2007-08-03 22:19:13 UTC (rev 3942)
+++ trunk/numpy/f2py/lib/extgen/__init__.py	2007-08-04 19:58:57 UTC (rev 3943)
@@ -2,11 +2,13 @@
 Python Extensions Generator
 """
 
-__all__ = ['ExtensionModule', 'PyCFunction', 'CCode']
+__all__ = ['ExtensionModule', 'PyCFunction', 'PyCArgument',
+           'CCode']
 
 import base
 from extension_module import ExtensionModule
 from pyc_function import PyCFunction
+from pyc_argument import PyCArgument
 from c_code import CCode
 
 import predefined_components

Modified: trunk/numpy/f2py/lib/extgen/base.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/base.py	2007-08-03 22:19:13 UTC (rev 3942)
+++ trunk/numpy/f2py/lib/extgen/base.py	2007-08-04 19:58:57 UTC (rev 3943)
@@ -63,6 +63,11 @@
     def __repr__(self):
         return '%s%s' % (self.__class__.__name__, `self._args`)
 
+    def __getattr__(self, attr):
+        if attr.startswith('container_'):
+            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.
         
@@ -117,6 +122,11 @@
         """
         # clean up containers
         self.containers = {}
+        for n in dir(self):
+            if n.startswith('container_') and isinstance(getattr(self, n), Container):
+                delattr(self, n)
+
+        # create containers
         for k,kwargs in self.container_options.items():
             self.containers[k] = Container(**kwargs)
 
@@ -201,7 +211,7 @@
                 r.append('--- %s ---\n%s' % (k,v))
         return '\n'.join(r)
 
-    def evaluate(self, template):
+    def evaluate(self, template, **attrs):
         """
         Evaluate template using instance attributes and code
         idioms from containers.
@@ -213,17 +223,19 @@
             v = getattr(self, n)
             if isinstance(v, str):
                 d[n] = v
+        d.update(attrs)
         for label, container in self.containers.items():
-            if container.use_indent is None:
+            if not container.use_indent:
                 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())
+                old_indent = container.indent_offset
+                container.indent_offset = old_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)
+                container.indent_offset = old_indent
+        template = template % d
+        return re.sub(r'.*[<]KILLLINE[>].*\n','', template)
 
     _registered_components_map = {}
 
@@ -253,31 +265,44 @@
             pass
         raise KeyError('no registered component provides %r' % (provides))
 
+    @property
+    def numpy_version(self):
+        import numpy
+        return numpy.__version__
     
 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')
+    >>> c.add('hey',1)
+    >>> c.add('hoo',2)
+    >>> print c
+    "hey, hoo"
+    >>> c.add('hey',1)
+    >>> c.add('hey2',1)
     Traceback (most recent call last):
     ...
     ValueError: Container item 1 exists with different value
+
+    >>> c2 = Container()
+    >>> c2.add('bar')
+    >>> c += c2
+    >>> print c
+    "hey, hoo, bar"
     
     """
     __metaclass__ = BaseMetaClass
 
-    def __init__(self, separator='\n', prefix='', suffix='',
+    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,
+                 use_indent = False,
+                 indent_offset = 0,
+                 use_firstline_indent = False, # implies use_indent
                  ):
         self.list = []
         self.label_map = {}
@@ -290,7 +315,12 @@
         self.default = default
         self.reverse = reverse
         self.user_str = user_defined_str
-        self.use_indent = use_indent
+        self.use_indent = use_indent or use_firstline_indent
+        self.indent_offset = indent_offset
+        self.use_firstline_indent = use_firstline_indent
+        
+    def __notzero__(self):
+        return bool(self.list)
 
     def has(self, label):
         return self.label_map.has_key(label)
@@ -298,14 +328,23 @@
     def get(self, label):
         return self.list[self.label_map[label]]
 
-    def __iadd__(self, other):
-        self.add(other)
+    def __add__(self, other):
+        if isinstance(other, Container):
+            lst = [(i,l) for (l,i) in other.label_map.items()]
+            lst.sort()
+            for i,l in lst:
+                self.add(other.list[i], l)
+        else:
+            self.add(other)        
         return self
+    __iadd__ = __add__
 
     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().
         """
+        if content is None:
+            return
         assert isinstance(content, str),`type(content)`
         if label is None:
             label = time.time()
@@ -326,6 +365,15 @@
             if self.reverse:
                 l = l[:]
                 l.reverse()
+            if self.use_firstline_indent:
+                new_l = []
+                for l1 in l:
+                    lines = l1.split('\\n')
+                    i = len(lines[0]) - len(lines[0].lstrip())
+                    indent = i * ' '
+                    new_l.append(lines[0])
+                    new_l.extend([indent + l2 for l2 in lines[1:]])
+                l = new_l
             r = self.separator.join(l)
             r = self.prefix + r
             r = r + self.suffix
@@ -336,10 +384,31 @@
             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)])
+            lines = r.splitlines(True)
+            indent = self.indent_offset * ' '
+            r = ''.join([indent + line for line in lines])
         return r
 
+    def copy(self, mapping=None, **extra_options):
+        options = dict(separator=self.separator, prefix=self.prefix, suffix=self.suffix,
+                       skip_prefix_when_empty=self.skip_prefix,
+                       skip_suffix_when_empty=self.skip_suffix,
+                       default = self.default, reverse=self.reverse,
+                       user_defined_str = self.user_str,
+                       use_indent = self.use_indent,
+                       indent_offset = self.indent_offset
+                       )
+        options.update(extra_options)
+        cpy = Container(**options)
+        if mapping is None:
+            cpy += self
+        else:
+            lst = [(i,l) for (l,i) in self.label_map.items()]
+            lst.sort()
+            for i,l in lst:
+                cpy.add(mapping(other.list[i]), l)            
+        return cpy
+
 def _test():
     import doctest
     doctest.testmod()

Modified: trunk/numpy/f2py/lib/extgen/doc.txt
===================================================================
--- trunk/numpy/f2py/lib/extgen/doc.txt	2007-08-03 22:19:13 UTC (rev 3942)
+++ trunk/numpy/f2py/lib/extgen/doc.txt	2007-08-04 19:58:57 UTC (rev 3943)
@@ -18,11 +18,12 @@
 Hello example follows::
 
   >>> from numpy.f2py.lib.extgen import *
+  >>> m = ExtensionModule('foo')
   >>> f = PyCFunction('hello')
-  >>> f.add('printf("Hello!\\n");')
-  >>> m = ExtensionModule('foo', f)
+  >>> f += 'printf("Hello!\\n");'
+  >>> m += f
   >>> m.generate() # returns a string containing C source to extension module
-  >>> foo = m.build
+  >>> foo = m.build()
   >>> foo.hello()
   Hello!
   >>> 
@@ -137,6 +138,9 @@
   - `skip_suffix_when_empty=False`
   - `default=''`
   - `reverse=False`
+  - `use_indent=False`
+  - `use_firstline_indent=False`
+  - `indent_offset=0`
   - `user_defined_str=None`
 
 that can be used to change the behaviour of `Container.__str__()`
@@ -154,16 +158,24 @@
 item is ignored.
 
 
-Reference manual
-================
+Component classes
+=================
 
 ExtGen package defines the following extension module component classes:
 
-  - `ExtensionModule(<modulename>, *components, numpy=False, provides=..)`  ---
+  - `ExtensionModule(<modulename>, *components, numpy=False,
+    provides=.., title=.., description=..)`  ---
     represents an extension module,
     
-  - `PyCFunction(<name>, *components, provides=..)` ---
+  - `PyCFunction(<name>, *components, provides=.., title=.., description=..)` ---
     represents an extension function.
 
+  - `PyCArgument(<name>, *components, provides=.., input_intent=..,
+    output_intent=.., input_title=.., input_description=..,
+    output_title=, output_description=..)` --- represents an argument component for
+    `PyCFunction`. Keyword arguments `input_intent` and
+    `output_intent` may have values `'required'`, `'optional'`,
+    `'extra'`, `'hide'` and `'hide'`, `'return'`, respectively.
+
   - `CCode(*lines, provides=..)` --- represents any C code block or statement.
 

Modified: trunk/numpy/f2py/lib/extgen/extension_module.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/extension_module.py	2007-08-03 22:19:13 UTC (rev 3942)
+++ trunk/numpy/f2py/lib/extgen/extension_module.py	2007-08-04 19:58:57 UTC (rev 3943)
@@ -13,28 +13,62 @@
     >>> # 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
+    >>> f = PyCFunction('hello', title='Say Hello\\nand Bye.')
+    >>> f += 'printf("Hello!\\\\n");'
+    >>> f += 'printf("Bye!\\\\n");'
+    >>> m = ExtensionModule('foo', f, title='Hello module', description='First line.\\nSecond line.')
+    >>> foo = m.build() #doctest: +ELLIPSIS
     exec_command...
     >>> foo.hello()
     >>> # you should now see Hello! printed to stdout stream.
 
+    We now add a new function to module and rebuild. But we need to change the
+    module name as one cannot reload extension modules in Python.
+
+    >>> m.modulename = 'foo2'
+    >>> m += PyCFunction('hi', 'printf("Hi\\\\n");', title='Say just Hi.')
+    >>> foo = m.build() #doctest: +ELLIPSIS
+    exec_command...
+    >>> print foo.__doc__ #doctest: +ELLIPSIS
+    This module "foo2" is generated with ExtGen from NumPy version ...
+    <BLANKLINE>
+    Hello module
+    <BLANKLINE>
+    Description:
+      First line.
+      Second line.
+    <BLANKLINE>
+    Functions:
+      hello() -> None
+        Say Hello
+        and Bye.
+      hi() -> None
+        Say just Hi.
     """
 
     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),
-                             )
+        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,separator=',\n',
+                        default='<KILLLINE>', use_indent=True),
+        ModuleInit=dict(default='<KILLLINE>', use_indent=True),
+        
+        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),
+        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),
+        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),
+        )
     
     component_container_map = dict(PyCFunction = 'CAPICode')
 
@@ -63,9 +97,25 @@
   {NULL,NULL,0,NULL}
 };
 
+static char %(modulename)s_doc[] =
+"This module \\"%(modulename)s\\" is generated with ExtGen from NumPy version %(numpy_version)s."
+%(ModuleTitle)s
+%(ModuleDescr)s
+%(ModuleFuncDoc)s
+;
+
 PyMODINIT_FUNC init%(modulename)s(void) {
+  PyObject* extgen_module_dict = NULL;
+  PyObject* extgen_str_obj = NULL;
+
   extgen_module = Py_InitModule("%(modulename)s", extgen_module_methods);
+  if ((extgen_module_dict = PyModule_GetDict(extgen_module))==NULL) goto capi_error;
+  if ((extgen_str_obj = PyString_FromString(%(modulename)s_doc))==NULL) goto capi_error;
+  PyDict_SetItemString(extgen_module_dict, "__doc__", extgen_str_obj);
+  Py_DECREF(extgen_str_obj);
+
   %(ModuleInit)s
+
   return;
 capi_error:
   if (!PyErr_Occurred()) {
@@ -88,10 +138,19 @@
         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')
+            
         map(self.add, components)
         return
 
-    @property
+    def update_containers(self):
+        if self.title is not None:
+            self.container_ModuleTitle += self.title.replace('\n','\\n')
+        if self.description is not None:
+            self.container_ModuleDescr += self.description.replace('\n','\\n')
+
     def build(self):
         import os
         import sys
@@ -121,11 +180,11 @@
         setup_cmd = ' '.join([sys.executable,setupfile]+setup_args)
         build_dir = '.'
         from numpy.distutils.exec_command import exec_command
-        sts = exec_command(setup_cmd)
+        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 sts[0]:
-            raise "Failed to build (status=%s)." % (`sts`)
+        if status:
+            raise "Failed to build (status=%s)." % (`status`)
         exec 'import %s as m' % (modulename)
         return m
 

Added: trunk/numpy/f2py/lib/extgen/pyc_argument.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/pyc_argument.py	2007-08-03 22:19:13 UTC (rev 3942)
+++ trunk/numpy/f2py/lib/extgen/pyc_argument.py	2007-08-04 19:58:57 UTC (rev 3943)
@@ -0,0 +1,135 @@
+
+from base import Base
+
+class PyCArgument(Base):
+
+    """
+    PyCArgument(<name>, *components, provides=..,
+                input_intent = 'required' | 'optional' | 'extra' | 'hide',
+                output_intent = 'hide' | 'return',
+                input_title = None,
+                output_title = None,
+                input_description = None,
+                output_description = None
+               )
+
+    """
+
+    template = '%(name)s'
+
+    def initialize(self, name, *components, **options):
+        self.name = name
+        self._provides = options.get('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)
+        
+        map(self.add, components)
+
+    def get_ctype(self):
+        # scan components for c types
+        return
+
+    def init_containers(self):
+        ctype = self.get_ctype()
+        if ctype is None:
+            self.cname = self.name
+        else:
+            self.cname = self.provides
+
+    def update_containers(self):
+        evaluate = self.evaluate
+        ctype = self.get_ctype()
+
+        # get containers
+        ReqArgs = self.container_ReqArgs
+        OptArgs = self.container_OptArgs
+        ExtArgs = self.container_ExtArgs
+        RetArgs = self.container_RetArgs
+
+        ReqArgsDoc = self.container_ReqArgsDoc
+        OptArgsDoc = self.container_OptArgsDoc
+        ExtArgsDoc = self.container_ExtArgsDoc
+
+        Decl = self.container_Decl
+
+        ReqKWList = self.container_ReqKWList
+        OptKWList = self.container_OptKWList
+        ExtKWList = self.container_ExtKWList
+
+        ReqPyArgFmt = self.container_ReqPyArgFmt
+        OptPyArgFmt = self.container_OptPyArgFmt
+        ExtPyArgFmt = self.container_ExtPyArgFmt
+
+        ReqPyArgObj = self.container_ReqPyArgObj
+        OptPyArgObj = self.container_OptPyArgObj
+        ExtPyArgObj = self.container_ExtPyArgObj
+
+        RetDoc = self.container_RetDoc
+        RetFmt = self.container_RetFmt
+        RetObj = self.container_RetObj
+
+        # update PyCFunction containers
+        Decl.add('PyObject* %s = NULL;' % (self.cname))
+        if ctype is not None:
+            Decl.add(ctype.declare(self.name))
+        
+        if ctype is None:
+            input_doc_title = '%s - %s' % (self.name, self.input_title)
+            output_doc_title = '%s - %s' % (self.name, self.output_title)
+            if self.input_description is not None:
+                input_doc_descr = '  %s' % (self.input_description.replace('\n','\\n'))
+            else:
+                input_doc_descr = None
+            if self.output_description is not None:
+                output_doc_descr = '  %s' % (self.output_description.replace('\n','\\n'))
+            else:
+                output_doc_descr = None
+            iopt = (self.name, '"%s"' % (self.name), 'O', '&%s' % (self.cname), input_doc_title, input_doc_descr)
+            ropt = (self.name, 'N', self.cname, output_doc_title, output_doc_descr)
+        else:
+            raise NotImplementedError('ctype=%r' % (ctype))
+            
+        if self.input_intent=='required':
+            ReqArgs.add(iopt[0])
+            ReqKWList.add(iopt[1])
+            ReqPyArgFmt.add(iopt[2])
+            ReqPyArgObj.add(iopt[3])
+            ReqArgsDoc.add(iopt[4])
+            ReqArgsDoc.add(iopt[5])
+        elif self.input_intent=='optional':
+            OptArgs.add(iopt[0])
+            OptKWList.add(iopt[1])
+            OptPyArgFmt.add(iopt[2])
+            OptPyArgObj.add(iopt[3])
+            OptArgsDoc.add(iopt[4])
+            OptArgsDoc.add(iopt[5])
+        elif self.input_intent=='extra':
+            ExtArgs.add(iopt[0])
+            ExtKWList.add(iopt[1])
+            ExtPyArgFmt.add(iopt[2])
+            ExtPyArgObj.add(iopt[3])
+            ExtArgsDoc.add(iopt[4])
+            ExtArgsDoc.add(iopt[5])
+        elif self.input_intent=='hide':
+            ropt = (self.name, 'O', self.cname, output_doc_title, output_doc_descr)
+        else:
+            raise NotImplementedError('input_intent=%r' % (self.input_intent))
+            
+        if self.output_intent=='return':
+            RetArgs.add(ropt[0])
+            RetFmt.add(ropt[1])
+            RetObj.add(ropt[2])
+            RetDoc.add(ropt[3])
+            RetDoc.add(ropt[4])
+        elif self.output_intent=='hide':
+            pass
+        else:
+            raise NotImplementedError('output_intent=%r' % (self.output_intent))
+
+        return
+        

Modified: trunk/numpy/f2py/lib/extgen/pyc_function.py
===================================================================
--- trunk/numpy/f2py/lib/extgen/pyc_function.py	2007-08-03 22:19:13 UTC (rev 3942)
+++ trunk/numpy/f2py/lib/extgen/pyc_function.py	2007-08-04 19:58:57 UTC (rev 3943)
@@ -4,31 +4,129 @@
 class PyCFunction(Base):
 
     """
-    PyCFunction(<name>, *components, provides=..)
-    
+    PyCFunction(<name>, *components, provides=..,title=.., description=..)
+
+    >>> from __init__ import * #doctest: +ELLIPSIS
+    Ignoring...
+    >>> f = PyCFunction('hello', title='A function.', description='\\nFirst line.\\n2nd line.')
+    >>> a1_in_doc = '''First line.\\nSecond line.'''
+    >>> a1_out_doc = '''Single line.'''
+    >>> f += PyCArgument('a1',output_intent='return', input_title='a Python object',
+    ...    input_description=a1_in_doc, output_description=a1_out_doc)
+    >>> f += PyCArgument('c1',input_intent='extra')
+    >>> f += PyCArgument('b1',input_intent='optional')
+    >>> f += PyCArgument('d2',input_intent='hide', output_intent='return')
+    >>> f += PyCArgument('a2',input_intent='required')
+    >>> f += PyCArgument('c2',input_intent='extra')
+    >>> f += PyCArgument('b2',input_intent='optional')
+    >>> m = ExtensionModule('foo', f)
+    >>> foo = m.build() #doctest: +ELLIPSIS
+    exec_command...
+    >>> print foo.hello.__doc__
+      hello(a1, a2 [, b1, b2, c1, c2]) -> (a1, d2)
+    <BLANKLINE>
+    A function.
+    <BLANKLINE>
+    Required arguments:
+      a1 - a Python object
+        First line.
+        Second line.
+      a2 - None
+    <BLANKLINE>
+    Optional arguments:
+      b1 - None
+      b2 - None
+    <BLANKLINE>
+    Extra optional arguments:
+      c1 - None
+      c2 - None
+    <BLANKLINE>
+    Return values:
+      a1 - None
+        Single line.
+      d2 - None
+    <BLANKLINE>
+    Description:
+    <BLANKLINE>
+      First line.
+      2nd line.
     """
 
-    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),
-                             )
+    container_options = dict(\
+        Args = dict(separator=', '),
 
+        ReqArgs = dict(separator=', '),
+        OptArgs = dict(separator=', '),
+        ExtArgs = dict(separator=', '),
+        RetArgs = dict(separator=', ', prefix='(', suffix=')', default = 'None',
+                       skip_prefix_when_empty=True, skip_suffix_when_empty=True),
+        
+        OptExtArgs = dict(separator=', ', prefix=' [, ', skip_prefix_when_empty=True,
+                          suffix=']', skip_suffix_when_empty=True),
+
+        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),
+        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),
+        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),
+        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),
+        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),
+        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),
+        
+        Decl = dict(default='<KILLLINE>', use_indent=True),
+        
+        ReqKWList = dict(separator=', ', suffix=', ', skip_suffix_when_empty=True),
+        OptKWList = dict(separator=', ', suffix=', ', skip_suffix_when_empty=True),
+        ExtKWList = dict(separator=', ', suffix=', ', skip_suffix_when_empty=True),
+        
+        ReqPyArgFmt = dict(separator=''),
+        OptPyArgFmt = dict(separator=''),
+        ExtPyArgFmt = dict(separator=''),
+        
+        ReqPyArgObj = dict(separator=', ', prefix=', ', skip_prefix_when_empty=True),
+        OptPyArgObj = dict(separator=', ', prefix=', ', skip_prefix_when_empty=True),
+        ExtPyArgObj = 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),
+        
+        RetFmt = 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 char %(pyc_name)s_doc[] =
+"  %(name)s(%(ReqArgs)s%(OptExtArgs)s) -> %(RetArgs)s"
+%(FuncTitle)s
+%(ReqArgsDoc)s
+%(OptArgsDoc)s
+%(ExtArgsDoc)s
+%(RetDoc)s
+%(FuncDescr)s
+;
 
 static PyObject*
 %(pyc_name)s
@@ -36,14 +134,15 @@
   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)) {
+  static char *capi_kwlist[] = {%(ReqKWList)s%(OptKWList)s%(ExtKWList)sNULL};
+  if (PyArg_ParseTupleAndKeywords(pyc_args, pyc_keywds,"%(ReqPyArgFmt)s%(OptPyArgFmt)s%(ExtPyArgFmt)s",
+                                  capi_kwlist%(ReqPyArgObj)s%(OptPyArgObj)s%(ExtPyArgObj)s)) {
     %(FromPyObj)s
     %(Exec)s
     capi_success = !PyErr_Occurred();
     if (capi_success) {
       %(PyObjFrom)s
-      pyc_buildvalue = Py_BuildValue("%(RetFormat)s"%(RetObj)s);
+      pyc_buildvalue = Py_BuildValue("%(RetFmt)s"%(RetObj)s);
       %(CleanPyObjFrom)s
     }
     %(CleanExec)s
@@ -58,20 +157,44 @@
         self.pyc_name = 'pyc_function_'+name
         self._provides = options.get('provides',
                                      '%s_%s' % (self.__class__.__name__, name))
+        self.title = options.get('title', None)
+        self.description = options.get('description', None)
         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')
+    def update_containers(self):
+        evaluate = self.evaluate
+
+        # get containers
+        FuncTitle = self.container_FuncTitle
+        FuncDescr = self.container_FuncDescr
+        ReqArgs = self.container_ReqArgs
+        OptArgs = self.container_OptArgs
+        ExtArgs = self.container_ExtArgs
+        OptExtArgs = self.container_OptExtArgs
+        ModuleMethod = self.container_ModuleMethod
+        ModuleFuncDoc = self.container_ModuleFuncDoc
+
+        # update ExtensionModule containers:
         t = '{"%(name)s", (PyCFunction)%(pyc_name)s,\n METH_VARARGS | METH_KEYWORDS, %(pyc_name)s_doc}'
-        ModuleMethod.add(self.evaluate(t), self.name)
+        ModuleMethod.add(evaluate(t), self.name)
+
+        # update local containers:
+        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')
+        if self.description is not None:
+            FuncDescr += self.description.replace('\n','\\n')
         return
 
-
-
     
+def _test():
+    import doctest
+    doctest.testmod()
+    
+if __name__ == "__main__":
+    _test()



More information about the Numpy-svn mailing list