[Numpy-svn] r2990 - in trunk/numpy/core: . code_generators

numpy-svn at scipy.org numpy-svn at scipy.org
Thu Aug 10 16:16:22 CDT 2006


Author: cookedm
Date: 2006-08-10 16:16:20 -0500 (Thu, 10 Aug 2006)
New Revision: 2990

Modified:
   trunk/numpy/core/code_generators/genapi.py
   trunk/numpy/core/code_generators/generate_array_api.py
   trunk/numpy/core/code_generators/generate_ufunc_api.py
   trunk/numpy/core/setup.py
Log:
The code generators for the API now create a .txt file (in ReST format)
with the API functions and the documentation comment from the source.
Currently, this is put in the header file directory
(`src.<platform>/numpy/core/include` in the `build` directory).
Also fixed up dependency checking: API changes should force a rebuild
of all the C extensions using the API.


Modified: trunk/numpy/core/code_generators/genapi.py
===================================================================
--- trunk/numpy/core/code_generators/genapi.py	2006-08-10 21:07:31 UTC (rev 2989)
+++ trunk/numpy/core/code_generators/genapi.py	2006-08-10 21:16:20 UTC (rev 2990)
@@ -1,5 +1,6 @@
 import sys, os, re
 import md5
+import textwrap
 
 API_FILES = ['arraymethods.c',
              'arrayobject.c',
@@ -11,6 +12,9 @@
 THIS_DIR = os.path.dirname(__file__)
 API_FILES = [os.path.join(THIS_DIR, '..', 'src', a) for a in API_FILES]
 
+def file_in_this_dir(filename):
+    return os.path.join(THIS_DIR, filename)
+
 def remove_whitespace(s):
     return ''.join(s.split())
 
@@ -44,6 +48,21 @@
             doccomment = ''
         return '%s%s %s(%s)' % (doccomment, self.return_type, self.name, argstr)
 
+    def to_ReST(self):
+        lines = ['::', '', '  ' + self.return_type]
+        argstr = ',\000'.join([self._format_arg(a) for a in self.args])
+        name = '  %s' % (self.name,)
+        s = textwrap.wrap('(%s)' % (argstr,), width=72,
+                          initial_indent=name,
+                          subsequent_indent=' ' * (len(name)+1),
+                          break_long_words=False)
+        for l in s:
+            lines.append(l.replace('\000', ' ').rstrip())
+        lines.append('')
+        if self.doc:
+            lines.append(textwrap.dedent(self.doc))
+        return '\n'.join(lines)
+
     def api_hash(self):
         m = md5.new()
         m.update(remove_whitespace(self.return_type))
@@ -133,32 +152,36 @@
                 else:
                     line = line.lstrip(' *')
                     doclist.append(line)
-            elif state == STATE_RETTYPE: #first line of declaration with return type
+            elif state == STATE_RETTYPE:
+                # first line of declaration with return type
                 m = re.match(r'static\s+(.*)$', line)
                 if m:
                     line = m.group(1)
                 return_type = line
                 state = STATE_NAME
-            elif state == STATE_NAME: # second line, with function name
+            elif state == STATE_NAME:
+                # second line, with function name
                 m = re.match(r'(\w+)\s*\(', line)
                 if m:
                     function_name = m.group(1)
                 else:
-                    raise ParseError(filename, lineno+1, 'could not find function name')
+                    raise ParseError(filename, lineno+1,
+                                     'could not find function name')
                 function_args.append(line[m.end():])
                 state = STATE_ARGS
             elif state == STATE_ARGS:
-                if line.startswith('{'): # finished
+                if line.startswith('{'):
+                    # finished
                     fargs_str = ' '.join(function_args).rstrip(' )')
                     fargs = split_arguments(fargs_str)
                     f = Function(function_name, return_type, fargs,
-                                 ' '.join(doclist))
+                                 '\n'.join(doclist))
                     functions.append(f)
                     return_type = None
                     function_name = None
                     function_args = []
                     doclist = []
-                    state = 0
+                    state = SCANNING
                 else:
                     function_args.append(line)
         except:
@@ -181,7 +204,7 @@
 
 def get_api_functions(tagname, order_file):
     if not os.path.exists(order_file):
-        order_file = os.path.join(THIS_DIR, order_file)
+        order_file = file_in_this_dir(order_file)
     order = read_order(order_file)
     functions = []
     for f in API_FILES:
@@ -195,7 +218,7 @@
 
 def add_api_list(offset, APIname, api_list,
                  module_list, extension_list, init_list):
-    """Add the API function declerations to the appropiate lists for use in
+    """Add the API function declarations to the appropiate lists for use in
     the headers.
     """
     for k, func in enumerate(api_list):
@@ -210,6 +233,15 @@
         astr = "        (void *) %s," % func.name
         init_list.append(astr)
 
+def should_rebuild(targets, source_files):
+    from distutils.dep_util import newer_group
+    for t in targets:
+        if not os.path.exists(t):
+            return True
+    sources = API_FILES + list(source_files) + [__file__]
+    if newer_group(sources, targets[0], missing='newer'):
+        return True
+    return False
 
 def main():
     tagname = sys.argv[1]

Modified: trunk/numpy/core/code_generators/generate_array_api.py
===================================================================
--- trunk/numpy/core/code_generators/generate_array_api.py	2006-08-10 21:07:31 UTC (rev 2989)
+++ trunk/numpy/core/code_generators/generate_array_api.py	2006-08-10 21:16:20 UTC (rev 2990)
@@ -1,6 +1,9 @@
 import os
 import genapi
 
+OBJECT_API_ORDER = 'array_api_order.txt'
+MULTIARRAY_API_ORDER = 'multiarray_api_order.txt'
+
 types = ['Generic','Number','Integer','SignedInteger','UnsignedInteger',
          'Inexact',
          'Floating', 'ComplexFloating', 'Flexible', 'Character',
@@ -113,11 +116,23 @@
 };
 """
 
-def generate_api(output_dir):
+def generate_api(output_dir, force=False):
+    header_file = os.path.join(output_dir, '__multiarray_api.h')
+    c_file = os.path.join(output_dir,'__multiarray_api.c')
+    doc_file = os.path.join(output_dir, 'multiarray_api.txt')
+
+    targets = (header_file, c_file, doc_file)
+    if (not force
+            and not genapi.should_rebuild(targets,
+                                          [OBJECT_API_ORDER,
+                                           MULTIARRAY_API_ORDER,
+                                           __file__])):
+        return targets
+
     objectapi_list = genapi.get_api_functions('OBJECT_API',
-                                              'array_api_order.txt')
+                                              OBJECT_API_ORDER)
     multiapi_list = genapi.get_api_functions('MULTIARRAY_API',
-                                             'multiarray_api_order.txt')
+                                             MULTIARRAY_API_ORDER)
     # API fixes for __arrayobject_api.h
 
     fixed = 10
@@ -141,23 +156,48 @@
                (types[k], num)
         extension_list.append(astr)
 
-    #setup object API
+    # set up object API
     genapi.add_api_list(numtypes, 'PyArray_API', objectapi_list,
                         module_list, extension_list, init_list)
 
-    # setup multiarray module API
+    # set up multiarray module API
     genapi.add_api_list(numobject, 'PyArray_API', multiapi_list,
                         module_list, extension_list, init_list)
 
 
     # Write to header
-    fid = open(os.path.join(output_dir, '__multiarray_api.h'),'w')
+    fid = open(header_file, 'w')
     s = h_template % ('\n'.join(module_list), '\n'.join(extension_list))
     fid.write(s)
     fid.close()
 
     # Write to c-code
-    fid = open(os.path.join(output_dir,'__multiarray_api.c'),'w')
+    fid = open(c_file, 'w')
     s = c_template % '\n'.join(init_list)
     fid.write(s)
     fid.close()
+
+    # write to documentation
+    fid = open(doc_file, 'w')
+    fid.write('''
+===========
+Numpy C-API
+===========
+
+Object API
+==========
+''')
+    for func in objectapi_list:
+        fid.write(func.to_ReST())
+        fid.write('\n\n')
+    fid.write('''
+
+Multiarray API
+==============
+''')
+    for func in multiapi_list:
+        fid.write(func.to_ReST())
+        fid.write('\n\n')
+    fid.close()
+
+    return targets

Modified: trunk/numpy/core/code_generators/generate_ufunc_api.py
===================================================================
--- trunk/numpy/core/code_generators/generate_ufunc_api.py	2006-08-10 21:07:31 UTC (rev 2989)
+++ trunk/numpy/core/code_generators/generate_ufunc_api.py	2006-08-10 21:16:20 UTC (rev 2990)
@@ -1,6 +1,8 @@
 import os
 import genapi
 
+UFUNC_API_ORDER = 'ufunc_api_order.txt'
+
 h_template = r"""
 #ifdef _UMATHMODULE
 
@@ -64,10 +66,19 @@
 };
 """
 
-def generate_api(output_dir):
-    ufunc_api_list = genapi.get_api_functions('UFUNC_API',
-                                              'ufunc_api_order.txt')
+def generate_api(output_dir, force=False):
+    header_file = os.path.join(output_dir, '__ufunc_api.h')
+    c_file = os.path.join(output_dir, '__ufunc_api.c')
+    doc_file = os.path.join(output_dir, 'ufunc_api.txt')
 
+    targets = (header_file, c_file, doc_file)
+    if (not force
+            and not genapi.should_rebuild(targets,
+                                          [UFUNC_API_ORDER, __file__])):
+        return targets
+
+    ufunc_api_list = genapi.get_api_functions('UFUNC_API', UFUNC_API_ORDER)
+
     # API fixes for __arrayobject_api.h
 
     fixed = 1
@@ -78,18 +89,32 @@
     extension_list = []
     init_list = []
 
-    #setup object API
+    # set up object API
     genapi.add_api_list(fixed, 'PyUFunc_API', ufunc_api_list,
                         module_list, extension_list, init_list)
 
     # Write to header
-    fid = open(os.path.join(output_dir, '__ufunc_api.h'),'w')
+    fid = open(header_file, 'w')
     s = h_template % ('\n'.join(module_list), '\n'.join(extension_list))
     fid.write(s)
     fid.close()
 
     # Write to c-code
-    fid = open(os.path.join(output_dir, '__ufunc_api.c'),'w')
+    fid = open(c_file, 'w')
     s = c_template % '\n'.join(init_list)
     fid.write(s)
     fid.close()
+
+    # Write to documentation
+    fid = open(doc_file, 'w')
+    fid.write('''
+=================
+Numpy Ufunc C-API
+=================
+''')
+    for func in ufunc_api_list:
+        fid.write(func.to_ReST())
+        fid.write('\n\n')
+    fid.close()
+
+    return targets

Modified: trunk/numpy/core/setup.py
===================================================================
--- trunk/numpy/core/setup.py	2006-08-10 21:07:31 UTC (rev 2989)
+++ trunk/numpy/core/setup.py	2006-08-10 21:16:20 UTC (rev 2990)
@@ -6,6 +6,19 @@
 from glob import glob
 from distutils.dep_util import newer,newer_group
 
+FUNCTIONS_TO_CHECK = [
+    ('expl', 'HAVE_LONGDOUBLE_FUNCS'),
+    ('expf', 'HAVE_FLOAT_FUNCS'),
+    ('log1p', 'HAVE_LOG1P'),
+    ('expm1', 'HAVE_EXPM1'),
+    ('asinh', 'HAVE_INVERSE_HYPERBOLIC'),
+    ('atanhf', 'HAVE_INVERSE_HYPERBOLIC_FLOAT'),
+    ('atanhl', 'HAVE_INVERSE_HYPERBOLIC_LONGDOUBLE'),
+    ('isnan', 'HAVE_ISNAN'),
+    ('isinf', 'HAVE_ISINF'),
+    ('rint', 'HAVE_RINT'),
+    ]
+
 def configuration(parent_package='',top_path=None):
     from numpy.distutils.misc_util import Configuration,dot_join
     from numpy.distutils.system_info import get_info, default_lib_dirs
@@ -50,14 +63,14 @@
                 #  are actually multiple CPUS? -- but
                 #  threaded code can be nice even on a single
                 #  CPU so that long-calculating code doesn't
-                #  block. 
+                #  block.
                 try:
                     nosmp = os.environ['NPY_NOSMP']
                     nosmp = 1
                 except KeyError:
                     nosmp = 0
             if nosmp: moredefs = [('NPY_ALLOW_THREADS', '0')]
-            else: moredefs = []                
+            else: moredefs = []
             #
             mathlibs = []
             tc = testcode_mathlib()
@@ -70,36 +83,24 @@
                     mathlibs = libs
                     break
             else:
-                raise "math library missing; rerun setup.py after setting the MATHLIB env variable"
+                raise EnvironmentError("math library missing; rerun "
+                                       "setup.py after setting the "
+                                       "MATHLIB env variable")
             ext.libraries.extend(mathlibs)
             moredefs.append(('MATHLIB',','.join(mathlibs)))
 
-            libs = mathlibs
-            kws_args = {'libraries':libs,'decl':0,'headers':['math.h']}
-            if config_cmd.check_func('expl', **kws_args):
-                moredefs.append('HAVE_LONGDOUBLE_FUNCS')
-            if config_cmd.check_func('expf', **kws_args):
-                moredefs.append('HAVE_FLOAT_FUNCS')
-            if config_cmd.check_func('log1p', **kws_args):
-                moredefs.append('HAVE_LOG1P')
-            if config_cmd.check_func('expm1', **kws_args):
-                moredefs.append('HAVE_EXPM1')
-            if config_cmd.check_func('asinh', **kws_args):
-                moredefs.append('HAVE_INVERSE_HYPERBOLIC')
-            if config_cmd.check_func('atanhf', **kws_args):
-                moredefs.append('HAVE_INVERSE_HYPERBOLIC_FLOAT')
-            if config_cmd.check_func('atanhl', **kws_args):
-                moredefs.append('HAVE_INVERSE_HYPERBOLIC_LONGDOUBLE')
-            if config_cmd.check_func('isnan', **kws_args):
-                moredefs.append('HAVE_ISNAN')
-            if config_cmd.check_func('isinf', **kws_args):
-                moredefs.append('HAVE_ISINF')
-            if config_cmd.check_func('rint', **kws_args):
-                moredefs.append('HAVE_RINT')
+            def check_func(func_name):
+                return config_cmd.check_func(func_name,
+                                             libraries=mathlibs, decl=False,
+                                             headers=['math.h'])
 
+            for func_name, defsymbol in FUNCTIONS_TO_CHECK:
+                if check_func(func_name):
+                    moredefs.append(defsymbol)
+
             if sys.version[:3] < '2.4':
                 kws_args['headers'].append('stdlib.h')
-                if config_cmd.check_func('strtod', **kws_args):
+                if check_func('strtod'):
                     moredefs.append(('PyOS_ascii_strtod', 'strtod'))
 
             if moredefs:
@@ -132,26 +133,23 @@
         config.add_data_files((header_dir,target))
         return target
 
-    def generate_api_func(header_file, module_name):
-        def generate_api(ext,build_dir):
-            target = join(build_dir, header_file)
+    def generate_api_func(module_name):
+        def generate_api(ext, build_dir):
             script = join(codegen_dir, module_name + '.py')
-            if newer(script, target):
-                sys.path.insert(0, codegen_dir)
-                try:
-                    m = __import__(module_name)
-                    print 'executing',script
-                    m.generate_api(build_dir)
-                finally:
-                    del sys.path[0]
-            config.add_data_files((header_dir,target))
-            return target
+            sys.path.insert(0, codegen_dir)
+            try:
+                m = __import__(module_name)
+                print 'executing', script
+                h_file, c_file, doc_file = m.generate_api(build_dir)
+            finally:
+                del sys.path[0]
+            config.add_data_files((header_dir, h_file),
+                                  (header_dir, doc_file))
+            return (h_file,)
         return generate_api
 
-    generate_array_api = generate_api_func('__multiarray_api.h',
-                                           'generate_array_api')
-    generate_ufunc_api = generate_api_func('__ufunc_api.h',
-                                           'generate_ufunc_api')
+    generate_array_api = generate_api_func('generate_array_api')
+    generate_ufunc_api = generate_api_func('generate_ufunc_api')
 
     def generate_umath_c(ext,build_dir):
         target = join(build_dir,'__umath_generated.c')



More information about the Numpy-svn mailing list