diff options
Diffstat (limited to 'scipy/weave/swig2_spec.py')
-rw-r--r-- | scipy/weave/swig2_spec.py | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/scipy/weave/swig2_spec.py b/scipy/weave/swig2_spec.py new file mode 100644 index 000000000..f58be2758 --- /dev/null +++ b/scipy/weave/swig2_spec.py @@ -0,0 +1,370 @@ +""" +This module allows one to use SWIG2 (SWIG version >= 1.3) wrapped +objects from Weave. SWIG-1.3 wraps objects differently from SWIG-1.1. + +The code here is based on wx_spec.py. However, this module is more +like a template for any SWIG2 wrapped converter. To wrap any special +code that uses SWIG the user simply needs to override the defaults in +the swig2_converter class. These special circumstances arise when one +has wrapped code that uses C++ namespaces. However, for most +straightforward SWIG wrappers this converter should work fine out of +the box. + +Newer versions of SWIG (>=1.3.22) represent the wrapped object using a +PyCObject and also a PySwigObject (>=1.3.24). This code supports all +of these options transparently. + +Since SWIG-1.3.x is under intense development there are several issues +to consider when using the swig2_converter. + + 1. For SWIG versions <= 1.3.19, the runtime code was built either + into the module or into a separate library called libswigpy (or + something like that). In the latter case, the users Python + modules were linked to this library and shared type information + (this was common for large projects with several modules that + needed to share type information). If you are using multiple + inheritance and want to be certain that type coercions from a + derived class to a base class are done correctly, you will need to + link to the libswigpy library. You will then need to add these to + the keyword arguments passed along to `weave.inline`: + + a. Add a define_macros=[('SWIG_NOINCLUDE', None)] + + b. Add the swigpy library to the libraries like so: + libraries=['swigpy'] + + c. If the libswigpy is in a non-standard location add the path + to the library_dirs argument as + `library_dirs=['/usr/local/lib']` or whatever. + + OTOH if you do not need to link to libswigpy (this is likely if + you are not using multiple inheritance), then you do not need the + above. However you are likely to get an annoying message of the + form:: + + WARNING: swig_type_info is NULL. + + for each SWIG object you are inlining (during each call). To + avoid this add a define_macros=[('NO_SWIG_WARN', None)]. + + 2. Since keeping track of a separate runtime is a pain, for SWIG + versions >= 1.3.23 the type information was stored inside a + special module. Thus in these versions there is no need to link + to this special SWIG runtime library. This module handles these + cases automatically and nothing special need be done. + + Using modules wrapped with different SWIG versions simultaneously. + Lets say you have library 'A' that is wrapped using SWIG version + 1.3.20. Then lets say you have a library 'B' wrapped using + version 1.3.24. Now if you want to use both in weave.inline, we + have a serious problem. The trouble is that both 'A' and 'B' may + use different and incompatible runtime layouts. It is impossible + to get the type conversions right in these cases. Thus it is + strongly advised that you use one version of SWIG to wrap all of + the code that you intend to inline using weave. Note that you can + certainly use SWIG-1.3.23 for everything and do not have to use + the latest and greatest SWIG to use weave.inline. Just make sure + that when inlining SWIG wrapped objects that all such objects use + the same runtime layout. By default, if you are using different + versions and do need to inline these objects, the latest layout + will be assumed. This might leave you with holes in your feet, + but you have been warned. You can force the converter to use a + specific runtime version if you want (see the + `swig2_converter.__init__` method and its documentation). + + +Prabhu Ramachandran <prabhu_r@users.sf.net> +""" + +import sys +import common_info +from c_spec import common_base_converter +import converters +import swigptr2 + + +#---------------------------------------------------------------------- +# Commonly used functions for the type query. This is done mainly to +# avoid code duplication. +#---------------------------------------------------------------------- +swig2_common_code = \ +''' +swig_type_info * +Weave_SWIG_TypeQuery(const char *name) { + swig_type_info *ty = SWIG_TypeQuery(name); +#ifndef NO_SWIG_WARN + if (ty == NULL) { + printf("WARNING: swig_type_info is NULL.\\n"); + } +#endif + return ty; +} +''' +#---------------------------------------------------------------------- +# This code obtains the C++ pointer given a a SWIG2 wrapped C++ object +# in Python. +#---------------------------------------------------------------------- + +swig2_py_to_c_template = \ +""" +class %(type_name)s_handler +{ +public: + %(c_type)s convert_to_%(type_name)s(PyObject* py_obj, const char* name) + { + %(c_type)s c_ptr; + swig_type_info *ty = Weave_SWIG_TypeQuery("%(c_type)s"); + // work on this error reporting... + if (SWIG_ConvertPtr(py_obj, (void **) &c_ptr, ty, + SWIG_POINTER_EXCEPTION | 0) == -1) { + handle_conversion_error(py_obj,"%(type_name)s", name); + } + %(inc_ref_count)s + return c_ptr; + } + + %(c_type)s py_to_%(type_name)s(PyObject* py_obj,const char* name) + { + %(c_type)s c_ptr; + swig_type_info *ty = Weave_SWIG_TypeQuery("%(c_type)s"); + // work on this error reporting... + if (SWIG_ConvertPtr(py_obj, (void **) &c_ptr, ty, + SWIG_POINTER_EXCEPTION | 0) == -1) { + handle_bad_type(py_obj,"%(type_name)s", name); + } + %(inc_ref_count)s + return c_ptr; + } +}; + +%(type_name)s_handler x__%(type_name)s_handler = %(type_name)s_handler(); +#define convert_to_%(type_name)s(py_obj,name) \\ + x__%(type_name)s_handler.convert_to_%(type_name)s(py_obj,name) +#define py_to_%(type_name)s(py_obj,name) \\ + x__%(type_name)s_handler.py_to_%(type_name)s(py_obj,name) + +""" + +#---------------------------------------------------------------------- +# This code generates a new SWIG pointer object given a C++ pointer. +# +# Important note: The thisown flag of the returned object is set to 0 +# by default. +#---------------------------------------------------------------------- + +swig2_c_to_py_template = """ +PyObject* %(type_name)s_to_py(void *obj) +{ + swig_type_info *ty = Weave_SWIG_TypeQuery("%(c_type)s"); + return SWIG_NewPointerObj(obj, ty, 0); +} +""" + +class swig2_converter(common_base_converter): + """ A converter for SWIG >= 1.3 wrapped objects.""" + def __init__(self, class_name="undefined", pycobj=0, runtime_version=None): + """Initializes the instance. + + Parameters + ---------- + + - class_name : `string` + + Name of class, this is set dynamically at build time by the + `type_spec` method. + + - pycobj : `int` + + If `pycobj` is 0 then code is generated to deal with string + representations of the SWIG wrapped pointer. If it is 1, + then code is generated to deal with a PyCObject. If it is 2 + then code is generated to deal with with PySwigObject. + + - runtime_version : `int` + + Specifies the SWIG_RUNTIME_VERSION to use. Defaults to + `None`. In this case the runtime is automatically + determined. This option is useful if you want to force the + runtime_version to be a specific one and override the + auto-detected one. + + """ + self.class_name = class_name + self.pycobj = pycobj # This is on if a PyCObject has been used. + self.runtime_version = runtime_version + common_base_converter.__init__(self) + + def _get_swig_runtime_version(self): + """This method tries to deduce the SWIG runtime version. If + the SWIG runtime layout changes, the `SWIG_TypeQuery` function + will not work properly. + """ + versions = [] + for key in sys.modules.keys(): + idx = key.find('swig_runtime_data') + if idx > -1: + ver = int(key[idx+17:]) + if ver not in versions: + versions.append(ver) + nver = len(versions) + if nver == 0: + return 0 + elif nver == 1: + return versions[0] + else: + print "WARNING: Multiple SWIG versions detected. No version was" + print "explicitly specified. Using the highest possible version." + return max(versions) + + def init_info(self, runtime=0): + """Keyword arguments: + + runtime -- If false (default), the user does not need to + link to the swig runtime (libswipy). Newer versions of SWIG + (>=1.3.23) do not need to build a SWIG runtime library at + all. In these versions of SWIG the swig_type_info is stored + in a common module. swig_type_info stores the type + information and the type converters to cast pointers + correctly. + + With earlier versions of SWIG (<1.3.22) one has to either + link the weave module with a SWIG runtime library + (libswigpy) in order to get the swig_type_info. Thus, if + `runtime` is True, the user must link to the swipy runtime + library and in this case type checking will be performed. + With these versions of SWIG, if runtime is `False`, no type + checking is done. + + """ + common_base_converter.init_info(self) + # These are generated on the fly instead of defined at + # the class level. + self.type_name = self.class_name + self.c_type = self.class_name + "*" + self.return_type = self.class_name + "*" + self.to_c_return = None # not used + self.check_func = None # not used + + if self.pycobj == 1: + self.define_macros.append(("SWIG_COBJECT_TYPES", None)) + self.define_macros.append(("SWIG_COBJECT_PYTHON", None)) + elif self.pycobj == 2: + self.define_macros.append(("SWIG_COBJECT_TYPES", None)) + + + if self.runtime_version is None: + self.runtime_version = self._get_swig_runtime_version() + + rv = self.runtime_version + if rv == 0: + # The runtime option is only useful for older versions of + # SWIG. + if runtime: + self.define_macros.append(("SWIG_NOINCLUDE", None)) + self.support_code.append(swigptr2.swigptr2_code_v0) + elif rv == 1: + self.support_code.append(swigptr2.swigptr2_code_v1) + elif rv == 2: + self.support_code.append(swigptr2.swigptr2_code_v2) + else: + raise AssertionError, "Unsupported version of the SWIG runtime:", rv + + self.support_code.append(swig2_common_code) + + def _get_swig_type(self, value): + """Given the object in the form of `value`, this method + returns information on the SWIG internal object repesentation + type. Different versions of SWIG use different object + representations. This method provides information on the type + of internal representation. + + Currently returns one of ['', 'str', 'pycobj', 'pyswig']. + """ + swig_typ = '' + if hasattr(value, 'this'): + type_this = type(value.this) + type_str = str(type_this) + if type_this == type('str'): + try: + data = value.this.split('_') + if data[2] == 'p': + swig_typ = 'str' + except AttributeError: + pass + elif type_str == "<type 'PyCObject'>": + swig_typ = 'pycobj' + elif type_str.find('PySwig') > -1: + swig_typ = 'pyswig' + + return swig_typ + + def type_match(self,value): + """ This is a generic type matcher for SWIG-1.3 objects. For + specific instances, override this method. The method also + handles cases where SWIG uses a PyCObject for the `this` + attribute and not a string. + + """ + if self._get_swig_type(value): + return 1 + else: + return 0 + + def generate_build_info(self): + if self.class_name != "undefined": + res = common_base_converter.generate_build_info(self) + else: + # if there isn't a class_name, we don't want the + # support_code to be included + import base_info + res = base_info.base_info() + return res + + def py_to_c_code(self): + return swig2_py_to_c_template % self.template_vars() + + def c_to_py_code(self): + return swig2_c_to_py_template % self.template_vars() + + def type_spec(self,name,value): + """ This returns a generic type converter for SWIG-1.3 + objects. For specific instances, override this function if + necessary.""" + # factory + swig_ob_type = self._get_swig_type(value) + pycobj = 0 + if swig_ob_type == 'str': + class_name = value.this.split('_')[-1] + elif swig_ob_type == 'pycobj': + pycobj = 1 + elif swig_ob_type == 'pyswig': + pycobj = 2 + else: + raise AssertionError, "Does not look like a SWIG object: %s"%value + + if pycobj: + class_name = value.__class__.__name__ + if class_name[-3:] == 'Ptr': + class_name = class_name[:-3] + + new_spec = self.__class__(class_name, pycobj, self.runtime_version) + new_spec.name = name + return new_spec + + def __cmp__(self,other): + #only works for equal + res = -1 + try: + res = cmp(self.name,other.name) or \ + cmp(self.__class__, other.__class__) or \ + cmp(self.class_name, other.class_name) or \ + cmp(self.type_name,other.type_name) + except: + pass + return res + +#---------------------------------------------------------------------- +# Uncomment the next line if you want this to be a default converter +# that is magically invoked by inline. +#---------------------------------------------------------------------- +#converters.default.insert(0, swig2_converter()) |