1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
|
"""
C wrapper wrapper
"""
from typehandlers.base import TypeConfigurationError, CodeGenerationError, NotSupportedError
from typehandlers.base import ForwardWrapperBase
from typehandlers.codesink import NullCodeSink
import utils
import settings
import traceback
import sys
try:
set
except NameError:
from sets import Set as set # Python 2.3 fallback
def isiterable(obj):
"""Returns True if an object appears to be iterable"""
return hasattr(obj, '__iter__') or isinstance(obj, basestring)
def vector_counter(vec):
"""
>>> list(vector_counter([[1,2], ['a', 'b'], ['x', 'y']]))
[[1, 'a', 'x'], [1, 'a', 'y'], [1, 'b', 'x'], [1, 'b', 'y'], [2, 'a', 'x'], [2, 'a', 'y'], [2, 'b', 'x'], [2, 'b', 'y']]
"""
iters = [iter(l) for l in vec]
values = [it.next() for it in iters[:-1]] + [vec[-1][0]]
while 1:
for idx in xrange(len(iters)-1, -1, -1):
try:
values[idx] = iters[idx].next()
except StopIteration:
iters[idx] = iter(vec[idx])
values[idx] = iters[idx].next()
else:
break
else:
raise StopIteration
yield list(values)
class OverloadedWrapper(object):
"""
An object that aggregates a set of wrapper objects; it generates
a single python wrapper wrapper that supports overloading,
i.e. tries to parse parameters according to each individual
Function parameter list, and uses the first wrapper that doesn't
generate parameter parsing error.
"""
RETURN_TYPE = NotImplemented
ERROR_RETURN = NotImplemented
def __init__(self, wrapper_name):
"""
wrapper_name -- C/C++ name of the wrapper
"""
self.wrappers = []
self.all_wrappers = None
self.wrapper_name = wrapper_name
self.wrapper_actual_name = None
self.wrapper_return = None
self.wrapper_args = None
self.pystruct = 'PyObject'
#self.static_decl = True ## FIXME: unused?
# self.enable_implicit_conversions = True
def add(self, wrapper):
"""
Add a wrapper to the overloaded wrapper
wrapper -- a Wrapper object
"""
assert isinstance(wrapper, ForwardWrapperBase)
self.wrappers.append(wrapper)
return wrapper
def _normalize_py_method_flags(self):
"""
Checks that if all overloaded wrappers have similar method
flags, forcing similar flags if needed (via method.force_parse
= ForwardWrapperBase.PARSE_TUPLE_AND_KEYWORDS)
"""
if len(self.wrappers) == 1:
return
for wrapper in self.wrappers:
wrapper.force_parse = ForwardWrapperBase.PARSE_TUPLE_AND_KEYWORDS
# loop that keeps removing wrappers until all remaining wrappers have the same flags
modified = True
while modified:
existing_flags = None
modified = False
for wrapper in self.wrappers:
try:
wrapper_flags = utils.call_with_error_handling(
wrapper.get_py_method_def_flags, args=(), kwargs={}, wrapper=wrapper)
except utils.SkipWrapper, ex:
modified = True
self.wrappers.remove(wrapper)
dummy1, dummy2, tb = sys.exc_info()
settings.error_handler.handle_error(wrapper, ex, tb)
break
wrapper_flags = set(wrapper_flags)
if existing_flags is None:
existing_flags = wrapper_flags
else:
if wrapper_flags != existing_flags:
modified = True
self.wrappers.remove(wrapper)
tb = traceback.extract_stack()
settings.error_handler.handle_error(wrapper, ex, tb)
break
def _compute_all_wrappers(self):
"""
Computes all the wrappers that should be generated; this
includes not only the regular overloaded wrappers but also
additional wrappers created in runtime to fulfil implicit
conversion requirements. The resulting list is stored as
self.all_wrappers
"""
self.all_wrappers = list(self.wrappers)
def generate(self, code_sink):
"""
Generate all the wrappers plus the 'aggregator' wrapper to a code sink.
"""
self._normalize_py_method_flags()
self._compute_all_wrappers()
if len(self.all_wrappers) == 0:
raise utils.SkipWrapper
elif len(self.all_wrappers) == 1 \
and not getattr(self.all_wrappers[0], 'NEEDS_OVERLOADING_INTERFACE', False):
## special case when there's only one wrapper; keep
## simple things simple
#self.all_wrappers[0].generate(code_sink)
prototype_line = utils.call_with_error_handling(self.all_wrappers[0].generate,
(code_sink,), {}, self.all_wrappers[0])
self.wrapper_actual_name = self.all_wrappers[0].wrapper_actual_name
assert self.wrapper_actual_name is not None
self.wrapper_return = self.all_wrappers[0].wrapper_return
self.wrapper_args = self.all_wrappers[0].wrapper_args
else:
## multiple overloaded wrappers case..
flags = self.all_wrappers[0].get_py_method_def_flags()
## Generate the individual "low level" wrappers that handle a single prototype
self.wrapper_actual_name = self.all_wrappers[0].wrapper_base_name
delegate_wrappers = []
for number, wrapper in enumerate(self.all_wrappers):
## enforce uniform method flags
wrapper.force_parse = wrapper.PARSE_TUPLE_AND_KEYWORDS
## an extra parameter 'return_exception' is used to
## return parse error exceptions to the 'main wrapper'
error_return = """{
PyObject *exc_type, *traceback;
PyErr_Fetch(&exc_type, return_exception, &traceback);
Py_XDECREF(exc_type);
Py_XDECREF(traceback);
}
%s""" % (self.ERROR_RETURN,)
wrapper_name = "%s__%i" % (self.wrapper_actual_name, number)
wrapper.set_parse_error_return(error_return)
code_sink.writeln()
# wrapper.generate(code_sink, wrapper_name,
# extra_wrapper_params=["PyObject **return_exception"])
try:
utils.call_with_error_handling(
wrapper.generate, args=(code_sink, wrapper_name),
kwargs=dict(extra_wrapper_params=["PyObject **return_exception"]),
wrapper=wrapper)
except utils.SkipWrapper:
continue
delegate_wrappers.append(wrapper.wrapper_actual_name)
## if all wrappers did not generate, then the overload
## aggregator wrapper should not be generated either..
if not delegate_wrappers:
raise utils.SkipWrapper
## Generate the 'main wrapper' that calls the other ones
code_sink.writeln()
self.wrapper_return = self.RETURN_TYPE
self.wrapper_args = ['%s *self' % self.pystruct]
if 'METH_VARARGS' in flags:
self.wrapper_args.append('PyObject *args')
if 'METH_KEYWORDS' in flags:
self.wrapper_args.append('PyObject *kwargs')
prototype_line = "%s %s(%s)" % (self.wrapper_return, self.wrapper_actual_name, ', '.join(self.wrapper_args))
code_sink.writeln(prototype_line)
code_sink.writeln('{')
code_sink.indent()
code_sink.writeln(self.RETURN_TYPE + ' retval;')
code_sink.writeln('PyObject *error_list;')
code_sink.writeln('PyObject *exceptions[%i] = {0,};' % len(delegate_wrappers))
for number, delegate_wrapper in enumerate(delegate_wrappers):
## call the delegate wrapper
args = ['self']
if 'METH_VARARGS' in flags:
args.append('args')
if 'METH_KEYWORDS' in flags:
args.append('kwargs')
args.append('&exceptions[%i]' % number)
code_sink.writeln("retval = %s(%s);" % (delegate_wrapper, ', '.join(args)))
## if no parse exception, call was successful:
## free previous exceptions and return the result
code_sink.writeln("if (!exceptions[%i]) {" % number)
code_sink.indent()
for i in xrange(number):
code_sink.writeln("Py_DECREF(exceptions[%i]);" % i)
code_sink.writeln("return retval;")
code_sink.unindent()
code_sink.writeln("}")
## If the following generated code is reached it means
## that all of our delegate wrappers had parsing errors:
## raise an appropriate exception, free the previous
## exceptions, and return NULL
code_sink.writeln('error_list = PyList_New(%i);' % len(delegate_wrappers))
for i in xrange(len(delegate_wrappers)):
code_sink.writeln(
'PyList_SET_ITEM(error_list, %i, PyObject_Str(exceptions[%i]));'
% (i, i))
code_sink.writeln("Py_DECREF(exceptions[%i]);" % i)
code_sink.writeln('PyErr_SetObject(PyExc_TypeError, error_list);')
code_sink.writeln("Py_DECREF(error_list);")
code_sink.writeln(self.ERROR_RETURN)
code_sink.unindent()
code_sink.writeln('}')
return prototype_line
def get_py_method_def(self, name):
"""
Returns an array element to use in a PyMethodDef table.
Should only be called after code generation.
name -- python wrapper/method name
"""
if len(self.all_wrappers) == 1 \
and not getattr(self.all_wrappers[0], 'NEEDS_OVERLOADING_INTERFACE', False):
return self.all_wrappers[0].get_py_method_def(name)
else:
self._normalize_py_method_flags()
flags = self.all_wrappers[0].get_py_method_def_flags()
## detect inconsistencies in flags; they must all be the same
if __debug__:
for func in self.all_wrappers:
try:
assert set(func.get_py_method_def_flags()) == set(flags),\
("Expected PyMethodDef flags %r, got %r"
% (flags, func.get_py_method_def_flags()))
except (TypeConfigurationError,
CodeGenerationError,
NotSupportedError):
pass
docstring = None # FIXME
assert isinstance(self.wrapper_return, basestring)
assert isinstance(self.wrapper_actual_name, basestring)
assert isinstance(self.wrapper_args, list)
return "{(char *) \"%s\", (PyCFunction) %s, %s, %s }," % \
(name, self.wrapper_actual_name, '|'.join(flags),
(docstring is None and "NULL" or ('"'+docstring+'"')))
def generate_declaration(self, code_sink):
self.reset_code_generation_state()
self._compute_all_wrappers()
self.generate(NullCodeSink())
assert isinstance(self.wrapper_return, basestring)
assert isinstance(self.wrapper_actual_name, basestring)
assert isinstance(self.wrapper_args, list)
code_sink.writeln("%s %s(%s);" % (self.wrapper_return, self.wrapper_actual_name, ', '.join(self.wrapper_args)))
self.reset_code_generation_state()
def generate_class_declaration(self, code_sink):
self.reset_code_generation_state()
self._compute_all_wrappers()
self.generate(NullCodeSink())
assert isinstance(self.wrapper_return, basestring)
assert isinstance(self.wrapper_actual_name, basestring)
assert isinstance(self.wrapper_args, list)
name = self.wrapper_actual_name.split('::')[-1]
code_sink.writeln("static %s %s(%s);" % (self.wrapper_return, name, ', '.join(self.wrapper_args)))
if len(self.all_wrappers) > 1:
for wrapper in self.all_wrappers:
name = wrapper.wrapper_actual_name.split('::')[-1]
code_sink.writeln("static %s %s(%s);" % (wrapper.wrapper_return, name, ', '.join(wrapper.wrapper_args)))
self.reset_code_generation_state()
def reset_code_generation_state(self):
self._compute_all_wrappers()
for wrapper in self.all_wrappers:
wrapper.reset_code_generation_state()
def get_section(self):
section = None
if self.all_wrappers is None:
self._compute_all_wrappers()
for wrapper in self.all_wrappers:
if section is None:
section = wrapper.section
return section
section = property(get_section)
from cppclass import CppClassParameter, CppClassRefParameter
|