diff --git a/numpy/core/code_generators/genapi.py b/numpy/core/code_generators/genapi.py index c932dba3bdea..98aab303e474 100644 --- a/numpy/core/code_generators/genapi.py +++ b/numpy/core/code_generators/genapi.py @@ -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 @@ def __str__(self): 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 @@ def find_functions(filename, tag='API'): 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 read_order(order_file): 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 get_api_functions(tagname, order_file): 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 @@ def add_api_list(offset, APIname, api_list, 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] diff --git a/numpy/core/code_generators/generate_array_api.py b/numpy/core/code_generators/generate_array_api.py index 1a612aab174c..bd47e64c4484 100644 --- a/numpy/core/code_generators/generate_array_api.py +++ b/numpy/core/code_generators/generate_array_api.py @@ -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 @@ def generate_api(output_dir): (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 diff --git a/numpy/core/code_generators/generate_ufunc_api.py b/numpy/core/code_generators/generate_ufunc_api.py index a52ce08561ed..a55c75e6f853 100644 --- a/numpy/core/code_generators/generate_ufunc_api.py +++ b/numpy/core/code_generators/generate_ufunc_api.py @@ -1,6 +1,8 @@ import os import genapi +UFUNC_API_ORDER = 'ufunc_api_order.txt' + h_template = r""" #ifdef _UMATHMODULE @@ -64,9 +66,18 @@ }; """ -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 @@ -78,18 +89,32 @@ def generate_api(output_dir): 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 diff --git a/numpy/core/setup.py b/numpy/core/setup.py index 2cd3afdd4781..d7e83a4417b2 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -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 @@ def generate_config_h(ext, build_dir): # 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 @@ def generate_config_h(ext, build_dir): 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 @@ def generate_config_h(ext, build_dir): 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')