import types
from base_spec import base_converter
import base_info
#----------------------------------------------------------------------------
# C++ code template for converting code from python objects to C++ objects
#
# This is silly code. There is absolutely no reason why these simple
# conversion functions should be classes. However, some versions of
# Mandrake Linux ship with broken C++ compilers (or libraries) that do not
# handle exceptions correctly when they are thrown from functions. However,
# exceptions thrown from class methods always work, so we make everything
# a class method to solve this error.
#----------------------------------------------------------------------------
#----------------------------------------------------------------------------
# speed note
# the convert_to_int macro below takes about 25 ns per conversion on my
# 850 MHz PIII. A slightly more sophisticated macro version can trim this
# to 20 ns, but this savings is dang near useless because the other
# overhead swamps it...
#----------------------------------------------------------------------------
py_to_c_template = \
"""
class %(type_name)s_handler
{
public:
%(return_type)s convert_to_%(type_name)s(PyObject* py_obj, const char* name)
{
// Incref occurs even if conversion fails so that
// the decref in cleanup_code has a matching incref.
%(inc_ref_count)s
if (!py_obj || !%(check_func)s(py_obj))
handle_conversion_error(py_obj,"%(type_name)s", name);
return %(to_c_return)s;
}
%(return_type)s py_to_%(type_name)s(PyObject* py_obj, const char* name)
{
// !! Pretty sure INCREF should only be called on success since
// !! py_to_xxx is used by the user -- not the code generator.
if (!py_obj || !%(check_func)s(py_obj))
handle_bad_type(py_obj,"%(type_name)s", name);
%(inc_ref_count)s
return %(to_c_return)s;
}
};
%(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)
"""
#----------------------------------------------------------------------------
# C++ code template for converting code from C++ objects to Python objects
#
#----------------------------------------------------------------------------
simple_c_to_py_template = \
"""
PyObject* %(type_name)s_to_py(PyObject* obj)
{
return (PyObject*) obj;
}
"""
class common_base_converter(base_converter):
def __init__(self):
self.init_info()
self._build_information = [self.generate_build_info()]
def init_info(self):
self.matching_types = []
self.headers = []
self.include_dirs = []
self.libraries = []
self.library_dirs = []
self.sources = []
self.support_code = []
self.module_init_code = []
self.warnings = []
self.define_macros = []
self.extra_compile_args = []
self.extra_link_args = []
self.use_ref_count = 1
self.name = "no_name"
self.c_type = 'PyObject*'
self.return_type = 'PyObject*'
self.to_c_return = 'py_obj'
def info_object(self):
return base_info.custom_info()
def generate_build_info(self):
info = self.info_object()
for header in self.headers:
info.add_header(header)
for d in self.include_dirs:
info.add_include_dir(d)
for lib in self.libraries:
info.add_library(lib)
for d in self.library_dirs:
info.add_library_dir(d)
for source in self.sources:
info.add_source(source)
for code in self.support_code:
info.add_support_code(code)
info.add_support_code(self.py_to_c_code())
info.add_support_code(self.c_to_py_code())
for init_code in self.module_init_code:
info.add_module_init_code(init_code)
for macro in self.define_macros:
info.add_define_macro(macro)
for warning in self.warnings:
info.add_warning(warning)
for arg in self.extra_compile_args:
info.add_extra_compile_arg(arg)
for arg in self.extra_link_args:
info.add_extra_link_arg(arg)
return info
def type_match(self,value):
return type(value) in self.matching_types
def get_var_type(self,value):
return type(value)
def type_spec(self,name,value):
# factory
new_spec = self.__class__()
new_spec.name = name
new_spec.var_type = self.get_var_type(value)
return new_spec
def template_vars(self,inline=0):
d = {}
d['type_name'] = self.type_name
d['check_func'] = self.check_func
d['c_type'] = self.c_type
d['return_type'] = self.return_type
d['to_c_return'] = self.to_c_return
d['name'] = self.name
d['py_var'] = self.py_variable()
d['var_lookup'] = self.retrieve_py_variable(inline)
code = 'convert_to_%(type_name)s(%(py_var)s,"%(name)s")' % d
d['var_convert'] = code
if self.use_ref_count:
d['inc_ref_count'] = "Py_XINCREF(py_obj);"
else:
d['inc_ref_count'] = ""
return d
def py_to_c_code(self):
return py_to_c_template % self.template_vars()
def c_to_py_code(self):
return simple_c_to_py_template % self.template_vars()
def declaration_code(self,templatize = 0,inline=0):
code = '%(py_var)s = %(var_lookup)s;\n' \
'%(c_type)s %(name)s = %(var_convert)s;\n' % \
self.template_vars(inline=inline)
return code
def cleanup_code(self):
if self.use_ref_count:
code = 'Py_XDECREF(%(py_var)s);\n' % self.template_vars()
#code += 'printf("cleaning up %(py_var)s\\n");\n' % self.template_vars()
else:
code = ""
return code
def __repr__(self):
msg = "(file:: name: %s)" % self.name
return msg
def __cmp__(self,other):
#only works for equal
result = -1
try:
result = cmp(self.name,other.name) or \
cmp(self.__class__, other.__class__)
except AttributeError:
pass
return result
#----------------------------------------------------------------------------
# Module Converter
#----------------------------------------------------------------------------
class module_converter(common_base_converter):
def init_info(self):
common_base_converter.init_info(self)
self.type_name = 'module'
self.check_func = 'PyModule_Check'
# probably should test for callable classes here also.
self.matching_types = [types.ModuleType]
#----------------------------------------------------------------------------
# String Converter
#----------------------------------------------------------------------------
class string_converter(common_base_converter):
def init_info(self):
common_base_converter.init_info(self)
self.type_name = 'string'
self.check_func = 'PyString_Check'
self.c_type = 'std::string'
self.return_type = 'std::string'
self.to_c_return = "std::string(PyString_AsString(py_obj))"
self.matching_types = [types.StringType]
self.headers.append('<string>')
def c_to_py_code(self):
# !! Need to dedent returned code.
code = """
PyObject* string_to_py(std::string s)
{
return PyString_FromString(s.c_str());
}
"""
return code
#----------------------------------------------------------------------------
# Unicode Converter
#----------------------------------------------------------------------------
class unicode_converter(common_base_converter):
def init_info(self):
common_base_converter.init_info(self)
self.type_name = 'unicode'
self.check_func = 'PyUnicode_Check'
# This isn't supported by gcc 2.95.3 -- MSVC works fine with it.
#self.c_type = 'std::wstring'
#self.to_c_return = "std::wstring(PyUnicode_AS_UNICODE(py_obj))"
self.c_type = 'Py_UNICODE*'
self.return_type = self.c_type
self.to_c_return = "PyUnicode_AS_UNICODE(py_obj)"
self.matching_types = [types.UnicodeType]
#self.headers.append('<string>')
def declaration_code(self,templatize = 0,inline=0):
# since wstring doesn't seem to work everywhere, we need to provide
# the length variable Nxxx for the unicode string xxx.
code = '%(py_var)s = %(var_lookup)s;\n' \
'%(c_type)s %(name)s = %(var_convert)s;\n' \
'int N%(name)s = PyUnicode_GET_SIZE(%(py_var)s);\n' \
% self.template_vars(inline=inline)
return code
#----------------------------------------------------------------------------
# File Converter
#----------------------------------------------------------------------------
class file_converter(common_base_converter):
def init_info(self):
common_base_converter.init_info(self)
self.type_name = 'file'
self.check_func = 'PyFile_Check'
self.c_type = 'FILE*'
self.return_type = self.c_type
self.to_c_return = "PyFile_AsFile(py_obj)"
self.headers = ['<stdio.h>']
self.matching_types = [types.FileType]
def c_to_py_code(self):
# !! Need to dedent returned code.
code = """
PyObject* file_to_py(FILE* file, const char* name,
const char* mode)
{
return (PyObject*) PyFile_FromFile(file,
const_cast<char*>(name),
const_cast<char*>(mode), fclose);
}
"""
return code
#----------------------------------------------------------------------------
#
# Scalar Number Conversions
#
#----------------------------------------------------------------------------
# the following typemaps are for 32 bit platforms. A way to do this
# general case? maybe ask numeric types how long they are and base
# the decisions on that.
#----------------------------------------------------------------------------
# Standard Python numeric --> C type maps
#----------------------------------------------------------------------------
num_to_c_types = {}
num_to_c_types[type(1)] = 'long'
num_to_c_types[type(1.)] = 'double'
num_to_c_types[type(1.+1.j)] = 'std::complex<double> '
# !! hmmm. The following is likely unsafe...
num_to_c_types[type(1L)] = 'npy_longlong'
#----------------------------------------------------------------------------
# Numeric array Python numeric --> C type maps
#----------------------------------------------------------------------------
num_to_c_types['T'] = 'T' # for templates
num_to_c_types['G'] = 'std::complex<longdouble> '
num_to_c_types['F'] = 'std::complex<float> '
num_to_c_types['D'] = 'std::complex<double> '
num_to_c_types['g'] = 'npy_longdouble'
num_to_c_types['f'] = 'float'
num_to_c_types['d'] = 'double'
num_to_c_types['b'] = 'char'
num_to_c_types['B'] = 'npy_uchar'
num_to_c_types['B'] = 'npy_ubyte' # numpy
num_to_c_types['h'] = 'short'
num_to_c_types['H'] = 'npy_ushort'
num_to_c_types['i'] = 'int'
num_to_c_types['I'] = 'npy_uint'
num_to_c_types['?'] = 'bool'
num_to_c_types['l'] = 'long'
num_to_c_types['L'] = 'npy_ulong'
num_to_c_types['q'] = 'npy_longlong'
num_to_c_types['Q'] = 'npy_ulonglong'
class scalar_converter(common_base_converter):
def init_info(self):
common_base_converter.init_info(self)
self.warnings = ['disable: 4275', 'disable: 4101']
self.headers = ['<complex>','<math.h>']
self.use_ref_count = 0
# This has to be int for SCXX to work.
class int_converter(scalar_converter):
def init_info(self):
scalar_converter.init_info(self)
self.type_name = 'int'
self.check_func = 'PyInt_Check'
self.c_type = 'int'
self.return_type = 'int'
self.to_c_return = "(int) PyInt_AsLong(py_obj)"
self.matching_types = [types.IntType]
class long_converter(scalar_converter):
def init_info(self):
scalar_converter.init_info(self)
# !! long to int conversion isn't safe!
self.type_name = 'long'
self.check_func = 'PyLong_Check'
self.c_type = 'longlong'
self.return_type = 'longlong'
self.to_c_return = "(longlong) PyLong_AsLongLong(py_obj)"
self.matching_types = [types.LongType]
class float_converter(scalar_converter):
def init_info(self):
scalar_converter.init_info(self)
# Not sure this is really that safe...
self.type_name = 'float'
self.check_func = 'PyFloat_Check'
self.c_type = 'double'
self.return_type = 'double'
self.to_c_return = "PyFloat_AsDouble(py_obj)"
self.matching_types = [types.FloatType]
class complex_converter(scalar_converter):
def init_info(self):
scalar_converter.init_info(self)
self.type_name = 'complex'
self.check_func = 'PyComplex_Check'
self.c_type = 'std::complex<double>'
self.return_type = 'std::complex<double>'
self.to_c_return = "std::complex<double>(PyComplex_RealAsDouble(py_obj),"\
"PyComplex_ImagAsDouble(py_obj))"
self.matching_types = [types.ComplexType]
#----------------------------------------------------------------------------
#
# List, Tuple, and Dict converters.
#
# Based on SCXX by Gordon McMillan
#----------------------------------------------------------------------------
import os, c_spec # yes, I import myself to find out my __file__ location.
local_dir,junk = os.path.split(os.path.abspath(c_spec.__file__))
scxx_dir = os.path.join(local_dir,'scxx')
class scxx_converter(common_base_converter):
def init_info(self):
common_base_converter.init_info(self)
self.headers = ['"scxx/object.h"','"scxx/list.h"','"scxx/tuple.h"',
'"scxx/dict.h"','<iostream>']
self.include_dirs = [local_dir,scxx_dir]
self.sources = [os.path.join(scxx_dir,'weave_imp.cpp'),]
class list_converter(scxx_converter):
def init_info(self):
scxx_converter.init_info(self)
self.type_name = 'list'
self.check_func = 'PyList_Check'
self.c_type = 'py::list'
self.return_type = 'py::list'
self.to_c_return = 'py::list(py_obj)'
self.matching_types = [types.ListType]
# ref counting handled by py::list
self.use_ref_count = 0
class tuple_converter(scxx_converter):
def init_info(self):
scxx_converter.init_info(self)
self.type_name = 'tuple'
self.check_func = 'PyTuple_Check'
self.c_type = 'py::tuple'
self.return_type = 'py::tuple'
self.to_c_return = 'py::tuple(py_obj)'
self.matching_types = [types.TupleType]
# ref counting handled by py::tuple
self.use_ref_count = 0
class dict_converter(scxx_converter):
def init_info(self):
scxx_converter.init_info(self)
self.type_name = 'dict'
self.check_func = 'PyDict_Check'
self.c_type = 'py::dict'
self.return_type = 'py::dict'
self.to_c_return = 'py::dict(py_obj)'
self.matching_types = [types.DictType]
# ref counting handled by py::dict
self.use_ref_count = 0
#----------------------------------------------------------------------------
# Instance Converter
#----------------------------------------------------------------------------
class instance_converter(scxx_converter):
def init_info(self):
scxx_converter.init_info(self)
self.type_name = 'instance'
self.check_func = 'PyInstance_Check'
self.c_type = 'py::object'
self.return_type = 'py::object'
self.to_c_return = 'py::object(py_obj)'
self.matching_types = [types.InstanceType]
# ref counting handled by py::object
self.use_ref_count = 0
#----------------------------------------------------------------------------
# Catchall Converter
#
# catch all now handles callable objects
#----------------------------------------------------------------------------
class catchall_converter(scxx_converter):
def init_info(self):
scxx_converter.init_info(self)
self.type_name = 'catchall'
self.check_func = ''
self.c_type = 'py::object'
self.return_type = 'py::object'
self.to_c_return = 'py::object(py_obj)'
# ref counting handled by py::object
self.use_ref_count = 0
def type_match(self,value):
return 1
if __name__ == "__main__":
x = list_converter().type_spec("x",1)
print x.py_to_c_code()
print
print x.c_to_py_code()
print
print x.declaration_code(inline=1)
print
print x.cleanup_code()
|