""" 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. This module is a template for a 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 """ from __future__ import absolute_import, print_function import sys from .c_spec import common_base_converter from . 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 a 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: 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) elif rv == 3: self.support_code.append(swigptr2.swigptr2_code_v3) else: raise AssertionError("Unsupported version of the SWIG runtime: %s" % 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 isinstance(type_this, str): try: data = value.this.split('_') if data[2] == 'p': swig_typ = 'str' except AttributeError: pass elif type_str == "": 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 from . 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())