summaryrefslogtreecommitdiff
path: root/weave/inline_tools.py
diff options
context:
space:
mode:
authorTravis Oliphant <oliphant@enthought.com>2005-09-26 20:20:16 +0000
committerTravis Oliphant <oliphant@enthought.com>2005-09-26 20:20:16 +0000
commit45d01a4be1c4221132ba46d687e6af3a8df3329b (patch)
treece3be5290e918def7c7187e747c5460193b0ca85 /weave/inline_tools.py
parentccd1c3db37672627aa4fe0fdb5437f5dddc0fe86 (diff)
downloadnumpy-45d01a4be1c4221132ba46d687e6af3a8df3329b.tar.gz
Moved weave
Diffstat (limited to 'weave/inline_tools.py')
-rw-r--r--weave/inline_tools.py452
1 files changed, 0 insertions, 452 deletions
diff --git a/weave/inline_tools.py b/weave/inline_tools.py
deleted file mode 100644
index 1778d0963..000000000
--- a/weave/inline_tools.py
+++ /dev/null
@@ -1,452 +0,0 @@
-# should re-write compiled functions to take a local and global dict
-# as input.
-import sys,os
-import ext_tools
-import string
-import catalog
-import common_info
-
-# not an easy way for the user_path_list to come in here.
-# the PYTHONCOMPILED environment variable offers the most hope.
-
-function_catalog = catalog.catalog()
-
-class inline_ext_function(ext_tools.ext_function):
- # Some specialization is needed for inline extension functions
- def function_declaration_code(self):
- code = 'static PyObject* %s(PyObject*self, PyObject* args)\n{\n'
- return code % self.name
-
- def template_declaration_code(self):
- code = 'template<class T>\n' \
- 'static PyObject* %s(PyObject*self, PyObject* args)\n{\n'
- return code % self.name
-
- def parse_tuple_code(self):
- """ Create code block for PyArg_ParseTuple. Variable declarations
- for all PyObjects are done also.
-
- This code got a lot uglier when I added local_dict...
- """
- declare_return = 'py::object return_val;\n' \
- 'int exception_occured = 0;\n' \
- 'PyObject *py__locals = NULL;\n' \
- 'PyObject *py__globals = NULL;\n'
-
- py_objects = ', '.join(self.arg_specs.py_pointers())
- if py_objects:
- declare_py_objects = 'PyObject ' + py_objects +';\n'
- else:
- declare_py_objects = ''
-
- py_vars = ' = '.join(self.arg_specs.py_variables())
- if py_vars:
- init_values = py_vars + ' = NULL;\n\n'
- else:
- init_values = ''
-
- parse_tuple = 'if(!PyArg_ParseTuple(args,"OO:compiled_func",'\
- '&py__locals,'\
- '&py__globals))\n'\
- ' return NULL;\n'
-
- return declare_return + declare_py_objects + \
- init_values + parse_tuple
-
- def arg_declaration_code(self):
- arg_strings = []
- for arg in self.arg_specs:
- arg_strings.append(arg.declaration_code(inline=1))
- code = string.join(arg_strings,"")
- return code
-
- def arg_cleanup_code(self):
- arg_strings = []
- for arg in self.arg_specs:
- arg_strings.append(arg.cleanup_code())
- code = string.join(arg_strings,"")
- return code
-
- def arg_local_dict_code(self):
- arg_strings = []
- for arg in self.arg_specs:
- arg_strings.append(arg.local_dict_code())
- code = string.join(arg_strings,"")
- return code
-
-
- def function_code(self):
- from ext_tools import indent
- decl_code = indent(self.arg_declaration_code(),4)
- cleanup_code = indent(self.arg_cleanup_code(),4)
- function_code = indent(self.code_block,4)
- #local_dict_code = indent(self.arg_local_dict_code(),4)
-
- try_code = 'try \n' \
- '{ \n' \
- ' PyObject* raw_locals = py_to_raw_dict(' \
- 'py__locals,"_locals");\n' \
- ' PyObject* raw_globals = py_to_raw_dict(' \
- 'py__globals,"_globals");\n' + \
- ' /* argument conversion code */ \n' + \
- decl_code + \
- ' /* inline code */ \n' + \
- function_code + \
- ' /*I would like to fill in changed ' \
- 'locals and globals here...*/ \n' \
- '\n} \n'
- catch_code = "catch(...) \n" \
- "{ \n" + \
- " return_val = py::object(); \n" \
- " exception_occured = 1; \n" \
- "} \n"
- return_code = " /* cleanup code */ \n" + \
- cleanup_code + \
- " if(!(PyObject*)return_val && !exception_occured)\n" \
- " {\n \n" \
- " return_val = Py_None; \n" \
- " }\n \n" \
- " return return_val.disown(); \n" \
- "} \n"
-
- all_code = self.function_declaration_code() + \
- indent(self.parse_tuple_code(),4) + \
- indent(try_code,4) + \
- indent(catch_code,4) + \
- return_code
-
- return all_code
-
- def python_function_definition_code(self):
- args = (self.name, self.name)
- function_decls = '{"%s",(PyCFunction)%s , METH_VARARGS},\n' % args
- return function_decls
-
-class inline_ext_module(ext_tools.ext_module):
- def __init__(self,name,compiler=''):
- ext_tools.ext_module.__init__(self,name,compiler)
- self._build_information.append(common_info.inline_info())
-
-function_cache = {}
-def inline(code,arg_names=[],local_dict = None, global_dict = None,
- force = 0,
- compiler='',
- verbose = 0,
- support_code = None,
- headers = [],
- customize=None,
- type_converters = None,
- auto_downcast=1,
- **kw):
- """ Inline C/C++ code within Python scripts.
-
- inline() compiles and executes C/C++ code on the fly. Variables
- in the local and global Python scope are also available in the
- C/C++ code. Values are passed to the C/C++ code by assignment
- much like variables passed are passed into a standard Python
- function. Values are returned from the C/C++ code through a
- special argument called return_val. Also, the contents of
- mutable objects can be changed within the C/C++ code and the
- changes remain after the C code exits and returns to Python.
-
- inline has quite a few options as listed below. Also, the keyword
- arguments for distutils extension modules are accepted to
- specify extra information needed for compiling.
-
- code -- string. A string of valid C++ code. It should not specify a
- return statement. Instead it should assign results that
- need to be returned to Python in the return_val.
- arg_names -- optional. list of strings. A list of Python variable names
- that should be transferred from Python into the C/C++
- code. It defaults to an empty string.
- local_dict -- optional. dictionary. If specified, it is a dictionary
- of values that should be used as the local scope for the
- C/C++ code. If local_dict is not specified the local
- dictionary of the calling function is used.
- global_dict -- optional. dictionary. If specified, it is a dictionary
- of values that should be used as the global scope for
- the C/C++ code. If global_dict is not specified the
- global dictionary of the calling function is used.
- force -- optional. 0 or 1. default 0. If 1, the C++ code is
- compiled every time inline is called. This is really
- only useful for debugging, and probably only useful if
- your editing support_code a lot.
- compiler -- optional. string. The name of compiler to use when
- compiling. On windows, it understands 'msvc' and 'gcc'
- as well as all the compiler names understood by
- distutils. On Unix, it'll only understand the values
- understood by distutils. ( I should add 'gcc' though
- to this).
-
- On windows, the compiler defaults to the Microsoft C++
- compiler. If this isn't available, it looks for mingw32
- (the gcc compiler).
-
- On Unix, it'll probably use the same compiler that was
- used when compiling Python. Cygwin's behavior should be
- similar.
- verbose -- optional. 0,1, or 2. defualt 0. Speficies how much
- much information is printed during the compile phase
- of inlining code. 0 is silent (except on windows with
- msvc where it still prints some garbage). 1 informs
- you when compiling starts, finishes, and how long it
- took. 2 prints out the command lines for the compilation
- process and can be useful if your having problems
- getting code to work. Its handy for finding the name
- of the .cpp file if you need to examine it. verbose has
- no affect if the compilation isn't necessary.
- support_code -- optional. string. A string of valid C++ code declaring
- extra code that might be needed by your compiled
- function. This could be declarations of functions,
- classes, or structures.
- headers -- optional. list of strings. A list of strings specifying
- header files to use when compiling the code. The list
- might look like ["<vector>","'my_header'"]. Note that
- the header strings need to be in a form than can be
- pasted at the end of a #include statement in the
- C++ code.
- customize -- optional. base_info.custom_info object. An alternative
- way to specify support_code, headers, etc. needed by
- the function see the compiler.base_info module for more
- details. (not sure this'll be used much).
- type_converters -- optional. list of type converters. These
- guys are what convert Python data types to C/C++ data
- types. If you'd like to use a different set of type
- conversions than the default, specify them here. Look
- in the type conversions section of the main
- documentation for examples.
- auto_downcast -- optional. 0 or 1. default 1. This only affects
- functions that have Numeric arrays as input variables.
- Setting this to 1 will cause all floating point values
- to be cast as float instead of double if all the
- Numeric arrays are of type float. If even one of the
- arrays has type double or double complex, all
- variables maintain there standard types.
-
- Distutils keywords. These are cut and pasted from Greg Ward's
- distutils.extension.Extension class for convenience:
-
- sources : [string]
- list of source filenames, relative to the distribution root
- (where the setup script lives), in Unix form (slash-separated)
- for portability. Source files may be C, C++, SWIG (.i),
- platform-specific resource files, or whatever else is recognized
- by the "build_ext" command as source for a Python extension.
- Note: The module_path file is always appended to the front of this
- list
- include_dirs : [string]
- list of directories to search for C/C++ header files (in Unix
- form for portability)
- define_macros : [(name : string, value : string|None)]
- list of macros to define; each macro is defined using a 2-tuple,
- where 'value' is either the string to define it to or None to
- define it without a particular value (equivalent of "#define
- FOO" in source or -DFOO on Unix C compiler command line)
- undef_macros : [string]
- list of macros to undefine explicitly
- library_dirs : [string]
- list of directories to search for C/C++ libraries at link time
- libraries : [string]
- list of library names (not filenames or paths) to link against
- runtime_library_dirs : [string]
- list of directories to search for C/C++ libraries at run time
- (for shared extensions, this is when the extension is loaded)
- extra_objects : [string]
- list of extra files to link with (eg. object files not implied
- by 'sources', static library that must be explicitly specified,
- binary resource files, etc.)
- extra_compile_args : [string]
- any extra platform- and compiler-specific information to use
- when compiling the source files in 'sources'. For platforms and
- compilers where "command line" makes sense, this is typically a
- list of command-line arguments, but for other platforms it could
- be anything.
- extra_link_args : [string]
- any extra platform- and compiler-specific information to use
- when linking object files together to create the extension (or
- to create a new static Python interpreter). Similar
- interpretation as for 'extra_compile_args'.
- export_symbols : [string]
- list of symbols to be exported from a shared extension. Not
- used on all platforms, and not generally necessary for Python
- extensions, which typically export exactly one symbol: "init" +
- extension_name.
- """
- # this grabs the local variables from the *previous* call
- # frame -- that is the locals from the function that called
- # inline.
- global function_catalog
-
- call_frame = sys._getframe().f_back
- if local_dict is None:
- local_dict = call_frame.f_locals
- if global_dict is None:
- global_dict = call_frame.f_globals
- if force:
- module_dir = global_dict.get('__file__',None)
- func = compile_function(code,arg_names,local_dict,
- global_dict,module_dir,
- compiler=compiler,
- verbose=verbose,
- support_code = support_code,
- headers = headers,
- customize=customize,
- type_converters = type_converters,
- auto_downcast = auto_downcast,
- **kw)
-
- function_catalog.add_function(code,func,module_dir)
- results = attempt_function_call(code,local_dict,global_dict)
- else:
- # 1. try local cache
- try:
- results = apply(function_cache[code],(local_dict,global_dict))
- return results
- except TypeError, msg:
- msg = str(msg).strip()
- if msg[:16] == "Conversion Error":
- pass
- else:
- raise TypeError, msg
- except NameError, msg:
- msg = str(msg).strip()
- if msg[:16] == "Conversion Error":
- pass
- else:
- raise NameError, msg
- except KeyError:
- pass
- # 2. try function catalog
- try:
- results = attempt_function_call(code,local_dict,global_dict)
- # 3. build the function
- except ValueError:
- # compile the library
- module_dir = global_dict.get('__file__',None)
- func = compile_function(code,arg_names,local_dict,
- global_dict,module_dir,
- compiler=compiler,
- verbose=verbose,
- support_code = support_code,
- headers = headers,
- customize=customize,
- type_converters = type_converters,
- auto_downcast = auto_downcast,
- **kw)
-
- function_catalog.add_function(code,func,module_dir)
- results = attempt_function_call(code,local_dict,global_dict)
- return results
-
-def attempt_function_call(code,local_dict,global_dict):
- # we try 3 levels here -- a local cache first, then the
- # catalog cache, and then persistent catalog.
- #
- global function_cache
- # 2. try catalog cache.
- function_list = function_catalog.get_functions_fast(code)
- for func in function_list:
- try:
- results = apply(func,(local_dict,global_dict))
- function_catalog.fast_cache(code,func)
- function_cache[code] = func
- return results
- except TypeError, msg: # should specify argument types here.
- # This should really have its own error type, instead of
- # checking the beginning of the message, but I don't know
- # how to define that yet.
- msg = str(msg)
- if msg[:16] == "Conversion Error":
- pass
- else:
- raise TypeError, msg
- except NameError, msg:
- msg = str(msg).strip()
- if msg[:16] == "Conversion Error":
- pass
- else:
- raise NameError, msg
- # 3. try persistent catalog
- module_dir = global_dict.get('__file__',None)
- function_list = function_catalog.get_functions(code,module_dir)
- for func in function_list:
- try:
- results = apply(func,(local_dict,global_dict))
- function_catalog.fast_cache(code,func)
- function_cache[code] = func
- return results
- except: # should specify argument types here.
- pass
- # if we get here, the function wasn't found
- raise ValueError, 'function with correct signature not found'
-
-def inline_function_code(code,arg_names,local_dict=None,
- global_dict=None,auto_downcast = 1,
- type_converters=None,compiler=''):
- call_frame = sys._getframe().f_back
- if local_dict is None:
- local_dict = call_frame.f_locals
- if global_dict is None:
- global_dict = call_frame.f_globals
- ext_func = inline_ext_function('compiled_func',code,arg_names,
- local_dict,global_dict,auto_downcast,
- type_converters = type_converters)
- import build_tools
- compiler = build_tools.choose_compiler(compiler)
- ext_func.set_compiler(compiler)
- return ext_func.function_code()
-
-def compile_function(code,arg_names,local_dict,global_dict,
- module_dir,
- compiler='',
- verbose = 0,
- support_code = None,
- headers = [],
- customize = None,
- type_converters = None,
- auto_downcast=1,
- **kw):
- # figure out where to store and what to name the extension module
- # that will contain the function.
- #storage_dir = catalog.intermediate_dir()
- module_path = function_catalog.unique_module_name(code,module_dir)
- storage_dir, module_name = os.path.split(module_path)
- mod = inline_ext_module(module_name,compiler)
-
- # create the function. This relies on the auto_downcast and
- # type factories setting
- ext_func = inline_ext_function('compiled_func',code,arg_names,
- local_dict,global_dict,auto_downcast,
- type_converters = type_converters)
- mod.add_function(ext_func)
-
- # if customize (a custom_info object), then set the module customization.
- if customize:
- mod.customize = customize
-
- # add the extra "support code" needed by the function to the module.
- if support_code:
- mod.customize.add_support_code(support_code)
-
- # add the extra headers needed by the function to the module.
- for header in headers:
- mod.customize.add_header(header)
-
- # it's nice to let the users know when anything gets compiled, as the
- # slowdown is very noticeable.
- print '<weave: compiling>'
-
- # compile code in correct location, with the given compiler and verbosity
- # setting. All input keywords are passed through to distutils
- mod.compile(location=storage_dir,compiler=compiler,
- verbose=verbose, **kw)
-
- # import the module and return the function. Make sure
- # the directory where it lives is in the python path.
- try:
- sys.path.insert(0,storage_dir)
- exec 'import ' + module_name
- func = eval(module_name+'.compiled_func')
- finally:
- del sys.path[0]
- return func