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