Add codegen to the tracked files
[platform/upstream/gst-editing-services.git] / bindings / python / codegen / reversewrapper.py
1 ### -*- python -*-
2 ### Code to generate "Reverse Wrappers", i.e. C->Python wrappers
3 ### (C) 2004 Gustavo Carneiro <gjc@gnome.org>
4 import argtypes
5 import os
6
7 DEBUG_MODE = ('PYGTK_CODEGEN_DEBUG' in os.environ)
8
9 def join_ctype_name(ctype, name):
10     '''Joins a C type and a variable name into a single string'''
11     if ctype[-1] != '*':
12         return " ".join((ctype, name))
13     else:
14         return "".join((ctype, name))
15
16
17 class CodeSink(object):
18     def __init__(self):
19         self.indent_level = 0 # current indent level
20         self.indent_stack = [] # previous indent levels
21
22     def _format_code(self, code):
23         assert isinstance(code, str)
24         l = []
25         for line in code.split('\n'):
26             l.append(' '*self.indent_level + line)
27         if l[-1]:
28             l.append('')
29         return '\n'.join(l)
30
31     def writeln(self, line=''):
32         raise NotImplementedError
33
34     def indent(self, level=4):
35         '''Add a certain ammount of indentation to all lines written
36         from now on and until unindent() is called'''
37         self.indent_stack.append(self.indent_level)
38         self.indent_level += level
39
40     def unindent(self):
41         '''Revert indentation level to the value before last indent() call'''
42         self.indent_level = self.indent_stack.pop()
43
44
45 class FileCodeSink(CodeSink):
46     def __init__(self, fp):
47         CodeSink.__init__(self)
48         assert isinstance(fp, file)
49         self.fp = fp
50
51     def writeln(self, line=''):
52         self.fp.write(self._format_code(line))
53
54 class MemoryCodeSink(CodeSink):
55     def __init__(self):
56         CodeSink.__init__(self)
57         self.lines = []
58
59     def writeln(self, line=''):
60         self.lines.append(self._format_code(line))
61
62     def flush_to(self, sink):
63         assert isinstance(sink, CodeSink)
64         for line in self.lines:
65             sink.writeln(line.rstrip())
66         self.lines = []
67
68     def flush(self):
69         l = []
70         for line in self.lines:
71             l.append(self._format_code(line))
72         self.lines = []
73         return "".join(l)
74
75 class ReverseWrapper(object):
76     '''Object that generates a C->Python wrapper'''
77     def __init__(self, cname, is_static=True):
78         assert isinstance(cname, str)
79
80         self.cname = cname
81         ## function object we will call, or object whose method we will call
82         self.called_pyobj = None
83         ## name of method of self.called_pyobj we will call
84         self.method_name = None
85         self.is_static = is_static
86
87         self.parameters = []
88         self.declarations = MemoryCodeSink()
89         self.post_return_code = MemoryCodeSink()
90         self.body = MemoryCodeSink()
91         self.cleanup_actions = []
92         self.pyargv_items = []
93         self.pyargv_optional_items = []
94         self.pyret_parse_items = [] # list of (format_spec, parameter)
95
96     def set_call_target(self, called_pyobj, method_name=None):
97         assert called_pyobj is not None
98         assert self.called_pyobj is None
99         self.called_pyobj = called_pyobj
100         self.method_name = method_name
101
102     def set_return_type(self, return_type):
103         assert isinstance(return_type, ReturnType)
104         self.return_type = return_type
105
106     def add_parameter(self, param):
107         assert isinstance(param, Parameter)
108         self.parameters.append(param)
109
110     def add_declaration(self, decl_code):
111         self.declarations.writeln(decl_code)
112
113     def add_pyargv_item(self, variable, optional=False):
114         if optional:
115             self.pyargv_optional_items.append(variable)
116         else:
117             self.pyargv_items.append(variable)
118
119     def add_pyret_parse_item(self, format_specifier, parameter, prepend=False):
120         if prepend:
121             self.pyret_parse_items.insert(0, (format_specifier, parameter))
122         else:
123             self.pyret_parse_items.append((format_specifier, parameter))
124
125     def write_code(self, code,
126                    cleanup=None,
127                    failure_expression=None,
128                    failure_cleanup=None,
129                    failure_exception=None,
130                    code_sink=None):
131         '''Add a chunk of code with cleanup and error handling
132
133         This method is to be used by TypeHandlers when generating code
134
135         Keywork arguments:
136         code -- code to add
137         cleanup -- code to cleanup any dynamic resources created by @code
138                    (except in case of failure) (default None)
139         failure_expression -- C boolean expression to indicate
140                               if anything failed (default None)
141         failure_cleanup -- code to cleanup any dynamic resources
142                            created by @code in case of failure (default None)
143         failure_exception -- code to raise an exception in case of
144                              failure (which will be immediately
145                              printed and cleared), (default None)
146         code_sink -- "code sink" to use; by default,
147                       ReverseWrapper.body is used, which writes the
148                       main body of the wrapper, before calling the
149                       python method.  Alternatively,
150                       ReverseWrapper.after_pyret_parse can be used, to
151                       write code after the PyArg_ParseTuple that
152                       parses the python method return value.
153         '''
154         if code_sink is None:
155             code_sink = self.body
156         if code is not None:
157             code_sink.writeln(code)
158         if failure_expression is not None:
159             code_sink.writeln("if (%s) {" % (failure_expression,))
160             code_sink.indent()
161             if failure_exception is None:
162                 code_sink.writeln("if (PyErr_Occurred())")
163                 code_sink.indent()
164                 code_sink.writeln("PyErr_Print();")
165                 code_sink.unindent()
166             else:
167                 code_sink.writeln(failure_exception)
168                 code_sink.writeln("PyErr_Print();")
169             if failure_cleanup is not None:
170                 code_sink.writeln(failure_cleanup)
171             for cleanup_action in self.cleanup_actions:
172                 code_sink.writeln(cleanup_action)
173             self.return_type.write_error_return()
174             code_sink.unindent()
175             code_sink.writeln("}")
176         if cleanup is not None:
177             self.cleanup_actions.insert(0, cleanup)
178
179     def generate(self, sink):
180         '''Generate the code into a CodeSink object'''
181         assert isinstance(sink, CodeSink)
182
183         if DEBUG_MODE:
184             self.declarations.writeln("/* begin declarations */")
185             self.body.writeln("/* begin main body */")
186             self.post_return_code.writeln("/* begin post-return code */")
187
188         self.add_declaration("PyGILState_STATE __py_state;")
189         self.write_code(code="__py_state = pyg_gil_state_ensure();",
190                         cleanup="pyg_gil_state_release(__py_state);")
191
192         for param in self.parameters:
193             param.convert_c2py()
194
195         assert self.called_pyobj is not None,\
196                "Parameters failed to provide a target function or method."
197
198         if self.is_static:
199             sink.writeln('static %s' % self.return_type.get_c_type())
200         else:
201             sink.writeln(self.return_type.get_c_type())
202         c_proto_params = map(Parameter.format_for_c_proto, self.parameters)
203         sink.writeln("%s(%s)\n{" % (self.cname, ", ".join(c_proto_params)))
204
205         self.return_type.write_decl()
206         self.add_declaration("PyObject *py_retval;")
207
208         ## Handle number of arguments
209         if self.pyargv_items:
210             self.add_declaration("PyObject *py_args;")
211             py_args = "py_args"
212             if self.pyargv_optional_items:
213                 self.add_declaration("int argc = %i;" % len(self.pyargv_items))
214                 argc = "argc"
215                 for arg in self.pyargv_optional_items:
216                     self.body.writeln("if (%s)" % arg)
217                     self.body.indent()
218                     self.body.writeln("++argc;")
219                     self.body.unindent()
220             else:
221                 argc = str(len(self.pyargv_items))
222         else:
223             if self.pyargv_optional_items:
224                 self.add_declaration("PyObject *py_args;")
225                 py_args = "py_args"
226                 self.add_declaration("int argc = 0;")
227                 argc = "argc"
228                 for arg in self.pyargv_optional_items:
229                     self.body.writeln("if (%s)" % arg)
230                     self.body.indent()
231                     self.body.writeln("++argc;")
232                     self.body.unindent()
233             else:
234                 py_args = "NULL"
235                 argc = None
236
237         self.body.writeln()
238
239         if py_args != "NULL":
240             self.write_code("py_args = PyTuple_New(%s);" % argc,
241                             cleanup="Py_DECREF(py_args);")
242             pos = 0
243             for arg in self.pyargv_items:
244                 try: # try to remove the Py_DECREF cleanup action, if we can
245                     self.cleanup_actions.remove("Py_DECREF(%s);" % arg)
246                 except ValueError: # otherwise we have to Py_INCREF..
247                     self.body.writeln("Py_INCREF(%s);" % arg)
248                 self.body.writeln("PyTuple_SET_ITEM(%s, %i, %s);" % (py_args, pos, arg))
249                 pos += 1
250             for arg in self.pyargv_optional_items:
251                 self.body.writeln("if (%s) {" % arg)
252                 self.body.indent()
253                 try: # try to remove the Py_DECREF cleanup action, if we can
254                     self.cleanup_actions.remove("Py_XDECREF(%s);" % arg)
255                 except ValueError: # otherwise we have to Py_INCREF..
256                     self.body.writeln("Py_INCREF(%s);" % arg)
257                 self.body.writeln("PyTuple_SET_ITEM(%s, %i, %s);" % (py_args, pos, arg))
258                 self.body.unindent()
259                 self.body.writeln("}")
260                 pos += 1
261
262         self.body.writeln()
263
264         ## Call the python method
265         if self.method_name is None:
266             self.write_code("py_retval = PyObject_Call(%s, %s);"
267                             % (self.called_pyobj, py_args),
268                             cleanup="Py_DECREF(py_retval);",
269                             failure_expression="!py_retval")
270         else:
271             self.add_declaration("PyObject *py_method;")
272             self.write_code("py_method = PyObject_GetAttrString(%s, \"%s\");"
273                             % (self.called_pyobj, self.method_name),
274                             cleanup="Py_DECREF(py_method);",
275                             failure_expression="!py_method")
276             self.write_code("py_retval = PyObject_CallObject(py_method, %s);"
277                             % (py_args,),
278                             cleanup="Py_DECREF(py_retval);",
279                             failure_expression="!py_retval")
280
281         ## -- Handle the return value --
282
283         ## we need to check if the return_type object is prepared to cooperate with multiple return values
284         len_before = len(self.pyret_parse_items)
285         self.return_type.write_conversion()
286         len_after = len(self.pyret_parse_items)
287         assert (self.return_type.get_c_type() == 'void'
288                 or not (len_before == len_after and len_after > 0)),\
289                ("Bug in reverse wrappers: return type handler %s"
290                 " is not prepared to cooperate multiple return values") % (type(self.return_type),)
291
292         sink.indent()
293
294         if len(self.pyret_parse_items) == 1:
295             ## if retval is one item only, pack it in a tuple so we
296             ## can use PyArg_ParseTuple as usual..
297             self.write_code('py_retval = Py_BuildValue("(N)", py_retval);')
298         if len(self.pyret_parse_items) > 0:
299             ## Parse return values using PyArg_ParseTuple
300             self.write_code(code=None, failure_expression=(
301                 '!PyArg_ParseTuple(py_retval, "%s", %s)' % (
302                 "".join([format for format, param in self.pyret_parse_items]),
303                 ", ".join([param for format, param in self.pyret_parse_items]))))
304
305         if DEBUG_MODE:
306             self.declarations.writeln("/* end declarations */")
307         self.declarations.flush_to(sink)
308         sink.writeln()
309         if DEBUG_MODE:
310             self.body.writeln("/* end main body */")
311         self.body.flush_to(sink)
312         sink.writeln()
313         if DEBUG_MODE:
314             self.post_return_code.writeln("/* end post-return code */")
315         self.post_return_code.flush_to(sink)
316         sink.writeln()
317
318         for cleanup_action in self.cleanup_actions:
319             sink.writeln(cleanup_action)
320         if self.return_type.get_c_type() != 'void':
321             sink.writeln()
322             sink.writeln("return retval;")
323         sink.unindent()
324         sink.writeln("}")
325
326 class TypeHandler(object):
327     def __init__(self, wrapper, **props):
328         assert isinstance(wrapper, ReverseWrapper)
329         self.wrapper = wrapper
330         self.props = props
331
332 class ReturnType(TypeHandler):
333
334     def get_c_type(self):
335         raise NotImplementedError
336
337     def write_decl(self):
338         raise NotImplementedError
339
340     def write_error_return(self):
341         '''Write "return <value>" code in case of error'''
342         raise NotImplementedError
343
344     def write_conversion(self):
345         '''Writes code to convert Python return value in 'py_retval'
346         into C 'retval'.  Returns a string with C boolean expression
347         that determines if anything went wrong. '''
348         raise NotImplementedError
349
350 class Parameter(TypeHandler):
351
352     def __init__(self, wrapper, name, **props):
353         TypeHandler.__init__(self, wrapper, **props)
354         self.name = name
355
356     def get_c_type(self):
357         raise NotImplementedError
358
359     def convert_c2py(self):
360         '''Write some code before calling the Python method.'''
361         pass
362
363     def format_for_c_proto(self):
364         return join_ctype_name(self.get_c_type(), self.name)
365
366
367 ###---
368 class StringParam(Parameter):
369
370     def get_c_type(self):
371         return self.props.get('c_type', 'char *').replace('const-', 'const ')
372
373     def convert_c2py(self):
374         if self.props.get('optional', False):
375             self.wrapper.add_declaration("PyObject *py_%s = NULL;" % self.name)
376             self.wrapper.write_code(code=("if (%s)\n"
377                                           "    py_%s = PyString_FromString(%s);\n"
378                                           % (self.name, self.name, self.name)),
379                                     cleanup=("Py_XDECREF(py_%s);" % self.name))
380             self.wrapper.add_pyargv_item("py_%s" % self.name, optional=True)
381         else:
382             self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
383             self.wrapper.write_code(code=("py_%s = PyString_FromString(%s);" %
384                                           (self.name, self.name)),
385                                     cleanup=("Py_DECREF(py_%s);" % self.name),
386                                     failure_expression=("!py_%s" % self.name))
387             self.wrapper.add_pyargv_item("py_%s" % self.name)
388
389 for ctype in ('char*', 'gchar*', 'const-char*', 'char-const*', 'const-gchar*',
390               'gchar-const*', 'string', 'static_string'):
391     argtypes.matcher.register_reverse(ctype, StringParam)
392 del ctype
393
394 class StringReturn(ReturnType):
395
396     def get_c_type(self):
397         return "char *"
398
399     def write_decl(self):
400         self.wrapper.add_declaration("char *retval;")
401
402     def write_error_return(self):
403         self.wrapper.write_code("return NULL;")
404
405     def write_conversion(self):
406         self.wrapper.add_pyret_parse_item("s", "&retval", prepend=True)
407         self.wrapper.write_code("retval = g_strdup(retval);", code_sink=self.wrapper.post_return_code)
408
409 for ctype in ('char*', 'gchar*'):
410     argtypes.matcher.register_reverse_ret(ctype, StringReturn)
411 del ctype
412
413
414 class VoidReturn(ReturnType):
415
416     def get_c_type(self):
417         return "void"
418
419     def write_decl(self):
420         pass
421
422     def write_error_return(self):
423         self.wrapper.write_code("return;")
424
425     def write_conversion(self):
426         self.wrapper.write_code(
427             code=None,
428             failure_expression="py_retval != Py_None",
429             failure_cleanup='PyErr_SetString(PyExc_TypeError, "retval should be None");')
430
431 argtypes.matcher.register_reverse_ret('void', VoidReturn)
432 argtypes.matcher.register_reverse_ret('none', VoidReturn)
433
434 class GObjectParam(Parameter):
435
436     def get_c_type(self):
437         return self.props.get('c_type', 'GObject *')
438
439     def convert_c2py(self):
440         self.wrapper.add_declaration("PyObject *py_%s = NULL;" % self.name)
441         self.wrapper.write_code(code=("if (%s)\n"
442                                       "    py_%s = pygobject_new((GObject *) %s);\n"
443                                       "else {\n"
444                                       "    Py_INCREF(Py_None);\n"
445                                       "    py_%s = Py_None;\n"
446                                       "}"
447                                       % (self.name, self.name, self.name, self.name)),
448                                 cleanup=("Py_DECREF(py_%s);" % self.name))
449         self.wrapper.add_pyargv_item("py_%s" % self.name)
450
451 argtypes.matcher.register_reverse('GObject*', GObjectParam)
452
453 class GObjectReturn(ReturnType):
454
455     def get_c_type(self):
456         return self.props.get('c_type', 'GObject *')
457
458     def write_decl(self):
459         self.wrapper.add_declaration("%s retval;" % self.get_c_type())
460
461     def write_error_return(self):
462         self.wrapper.write_code("return NULL;")
463
464     def write_conversion(self):
465         self.wrapper.write_code(
466             code=None,
467             failure_expression="!PyObject_TypeCheck(py_retval, &PyGObject_Type)",
468             failure_exception='PyErr_SetString(PyExc_TypeError, "retval should be a GObject");')
469         self.wrapper.write_code("retval = (%s) pygobject_get(py_retval);"
470                                 % self.get_c_type())
471         self.wrapper.write_code("g_object_ref((GObject *) retval);")
472
473 argtypes.matcher.register_reverse_ret('GObject*', GObjectReturn)
474
475
476
477 class IntParam(Parameter):
478
479     def get_c_type(self):
480         return self.props.get('c_type', 'int')
481
482     def convert_c2py(self):
483         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
484         self.wrapper.write_code(code=("py_%s = PyInt_FromLong(%s);" %
485                                       (self.name, self.name)),
486                                 cleanup=("Py_DECREF(py_%s);" % self.name))
487         self.wrapper.add_pyargv_item("py_%s" % self.name)
488
489 class IntReturn(ReturnType):
490     def get_c_type(self):
491         return self.props.get('c_type', 'int')
492     def write_decl(self):
493         self.wrapper.add_declaration("%s retval;" % self.get_c_type())
494     def write_error_return(self):
495         self.wrapper.write_code("return -G_MAXINT;")
496     def write_conversion(self):
497         self.wrapper.add_pyret_parse_item("i", "&retval", prepend=True)
498
499 for argtype in ('int', 'gint', 'guint', 'short', 'gshort', 'gushort', 'long',
500                 'glong', 'gsize', 'gssize', 'guint8', 'gint8', 'guint16',
501                 'gint16', 'gint32', 'GTime'):
502     argtypes.matcher.register_reverse(argtype, IntParam)
503     argtypes.matcher.register_reverse_ret(argtype, IntReturn)
504 del argtype
505
506 class IntPtrParam(Parameter):
507     def __init__(self, wrapper, name, **props):
508         if "direction" not in props:
509             raise ValueError("cannot use int* parameter without direction")
510         if props["direction"] not in ("out", "inout"):
511             raise ValueError("cannot use int* parameter with direction '%s'" % (props["direction"],))
512         Parameter.__init__(self, wrapper, name, **props)
513     def get_c_type(self):
514         return self.props.get('c_type', 'int*')
515     def convert_c2py(self):
516         if self.props["direction"] == "inout":
517             self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
518             self.wrapper.write_code(code=("py_%s = PyInt_FromLong(*%s);" %
519                                           (self.name, self.name)),
520                                     cleanup=("Py_DECREF(py_%s);" % self.name))
521             self.wrapper.add_pyargv_item("py_%s" % self.name)
522         self.wrapper.add_pyret_parse_item("i", self.name)
523 for argtype in ('int*', 'gint*'):
524     argtypes.matcher.register_reverse(argtype, IntPtrParam)
525 del argtype
526
527
528 class GEnumReturn(IntReturn):
529     def write_conversion(self):
530         self.wrapper.write_code(
531             code=None,
532             failure_expression=("pyg_enum_get_value(%s, py_retval, (gint *)&retval)" %
533                                 self.props['typecode']))
534
535 argtypes.matcher.register_reverse_ret("GEnum", GEnumReturn)
536
537 class GEnumParam(IntParam):
538     def convert_c2py(self):
539         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
540         self.wrapper.write_code(code=("py_%s = pyg_enum_from_gtype(%s, %s);" %
541                                       (self.name, self.props['typecode'], self.name)),
542                                 cleanup=("Py_DECREF(py_%s);" % self.name),
543                                 failure_expression=("!py_%s" % self.name))
544         self.wrapper.add_pyargv_item("py_%s" % self.name)
545
546 argtypes.matcher.register_reverse("GEnum", GEnumParam)
547
548 class GFlagsReturn(IntReturn):
549     def write_conversion(self):
550         self.wrapper.write_code(
551             code=None,
552             failure_expression=("pyg_flags_get_value(%s, py_retval, (gint *)&retval)" %
553                                 self.props['typecode']))
554
555 argtypes.matcher.register_reverse_ret("GFlags", GFlagsReturn)
556
557 class GFlagsParam(IntParam):
558     def convert_c2py(self):
559         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
560         self.wrapper.write_code(code=("py_%s = pyg_flags_from_gtype(%s, %s);" %
561                                       (self.name, self.props['typecode'], self.name)),
562                                 cleanup=("Py_DECREF(py_%s);" % self.name),
563                                 failure_expression=("!py_%s" % self.name))
564         self.wrapper.add_pyargv_item("py_%s" % self.name)
565
566 argtypes.matcher.register_reverse("GFlags", GFlagsParam)
567
568
569 class GtkTreePathParam(IntParam):
570     def convert_c2py(self):
571         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
572         self.wrapper.write_code(code=("py_%s = pygtk_tree_path_to_pyobject(%s);" %
573                                       (self.name, self.name)),
574                                 cleanup=("Py_DECREF(py_%s);" % self.name),
575                                 failure_expression=("!py_%s" % self.name))
576         self.wrapper.add_pyargv_item("py_%s" % self.name)
577
578 argtypes.matcher.register_reverse("GtkTreePath*", GtkTreePathParam)
579
580
581 class BooleanReturn(ReturnType):
582     def get_c_type(self):
583         return "gboolean"
584     def write_decl(self):
585         self.wrapper.add_declaration("gboolean retval;")
586         self.wrapper.add_declaration("PyObject *py_main_retval;")
587     def write_error_return(self):
588         self.wrapper.write_code("return FALSE;")
589     def write_conversion(self):
590         self.wrapper.add_pyret_parse_item("O", "&py_main_retval", prepend=True)
591         self.wrapper.write_code("retval = PyObject_IsTrue(py_main_retval)? TRUE : FALSE;",
592                                 code_sink=self.wrapper.post_return_code)
593 argtypes.matcher.register_reverse_ret("gboolean", BooleanReturn)
594
595 class BooleanParam(Parameter):
596     def get_c_type(self):
597         return "gboolean"
598     def convert_c2py(self):
599         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
600         self.wrapper.write_code("py_%s = %s? Py_True : Py_False;"
601                                 % (self.name, self.name))
602         self.wrapper.add_pyargv_item("py_%s" % self.name)
603
604 argtypes.matcher.register_reverse("gboolean", BooleanParam)
605
606
607 class DoubleParam(Parameter):
608     def get_c_type(self):
609         return self.props.get('c_type', 'gdouble')
610     def convert_c2py(self):
611         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
612         self.wrapper.write_code(code=("py_%s = PyFloat_FromDouble(%s);" %
613                                       (self.name, self.name)),
614                                 cleanup=("Py_DECREF(py_%s);" % self.name))
615         self.wrapper.add_pyargv_item("py_%s" % self.name)
616
617 class DoublePtrParam(Parameter):
618     def __init__(self, wrapper, name, **props):
619         if "direction" not in props:
620             raise ValueError("cannot use double* parameter without direction")
621         if props["direction"] not in ("out", ): # inout not yet implemented
622             raise ValueError("cannot use double* parameter with direction '%s'" % (props["direction"],))
623         Parameter.__init__(self, wrapper, name, **props)
624     def get_c_type(self):
625         return self.props.get('c_type', 'double*')
626     def convert_c2py(self):
627         self.wrapper.add_pyret_parse_item("d", self.name)
628 for argtype in ('double*', 'gdouble*'):
629     argtypes.matcher.register_reverse(argtype, DoublePtrParam)
630 del argtype
631
632 class DoubleReturn(ReturnType):
633     def get_c_type(self):
634         return self.props.get('c_type', 'gdouble')
635     def write_decl(self):
636         self.wrapper.add_declaration("%s retval;" % self.get_c_type())
637     def write_error_return(self):
638         self.wrapper.write_code("return -G_MAXFLOAT;")
639     def write_conversion(self):
640         self.wrapper.write_code(
641             code=None,
642             failure_expression="!PyFloat_AsDouble(py_retval)",
643             failure_cleanup='PyErr_SetString(PyExc_TypeError, "retval should be a float");')
644         self.wrapper.write_code("retval = PyFloat_AsDouble(py_retval);")
645
646 for argtype in ('float', 'double', 'gfloat', 'gdouble'):
647     argtypes.matcher.register_reverse(argtype, DoubleParam)
648     argtypes.matcher.register_reverse_ret(argtype, DoubleReturn)
649
650
651 class GBoxedParam(Parameter):
652     def get_c_type(self):
653         return self.props.get('c_type').replace('const-', 'const ')
654     def convert_c2py(self):
655         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
656         ctype = self.get_c_type()
657         if ctype.startswith('const '):
658             ctype_no_const = ctype[len('const '):]
659             self.wrapper.write_code(
660                 code=('py_%s = pyg_boxed_new(%s, (%s) %s, TRUE, TRUE);' %
661                       (self.name, self.props['typecode'],
662                        ctype_no_const, self.name)),
663                 cleanup=("Py_DECREF(py_%s);" % self.name))
664         else:
665             self.wrapper.write_code(
666                 code=('py_%s = pyg_boxed_new(%s, %s, FALSE, FALSE);' %
667                       (self.name, self.props['typecode'], self.name)),
668                 cleanup=("Py_DECREF(py_%s);" % self.name))
669         self.wrapper.add_pyargv_item("py_%s" % self.name)
670
671 argtypes.matcher.register_reverse("GBoxed", GBoxedParam)
672
673 class GBoxedReturn(ReturnType):
674     def get_c_type(self):
675         return self.props.get('c_type')
676     def write_decl(self):
677         self.wrapper.add_declaration("%s retval;" % self.get_c_type())
678     def write_error_return(self):
679         self.wrapper.write_code("return retval;")
680     def write_conversion(self):
681         self.wrapper.write_code(
682             failure_expression=("!pyg_boxed_check(py_retval, %s)" %
683                                 (self.props['typecode'],)),
684             failure_cleanup=('PyErr_SetString(PyExc_TypeError, "retval should be a %s");'
685                              % (self.props['typename'],)))
686         self.wrapper.write_code('retval = pyg_boxed_get(py_retval, %s);' %
687                                 self.props['typename'])
688
689 argtypes.matcher.register_reverse_ret("GBoxed", GBoxedReturn)
690
691
692 class GdkRectanglePtrParam(Parameter):
693     def get_c_type(self):
694         return self.props.get('c_type').replace('const-', 'const ')
695     def convert_c2py(self):
696         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
697         self.wrapper.write_code(
698             code=('py_%s = pyg_boxed_new(GDK_TYPE_RECTANGLE, %s, TRUE, TRUE);' %
699                   (self.name, self.name)),
700             cleanup=("Py_DECREF(py_%s);" % self.name))
701         self.wrapper.add_pyargv_item("py_%s" % self.name)
702
703 argtypes.matcher.register_reverse("GdkRectangle*", GdkRectanglePtrParam)
704 argtypes.matcher.register_reverse('GtkAllocation*', GdkRectanglePtrParam)
705
706
707 class PyGObjectMethodParam(Parameter):
708     def __init__(self, wrapper, name, method_name, **props):
709         Parameter.__init__(self, wrapper, name, **props)
710         self.method_name = method_name
711
712     def get_c_type(self):
713         return self.props.get('c_type', 'GObject *')
714
715     def convert_c2py(self):
716         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
717         self.wrapper.write_code(code=("py_%s = pygobject_new((GObject *) %s);" %
718                                       (self.name, self.name)),
719                                 cleanup=("Py_DECREF(py_%s);" % self.name),
720                                 failure_expression=("!py_%s" % self.name))
721         self.wrapper.set_call_target("py_%s" % self.name, self.method_name)
722
723 class CallbackInUserDataParam(Parameter):
724     def __init__(self, wrapper, name, free_it, **props):
725         Parameter.__init__(self, wrapper, name, **props)
726         self.free_it = free_it
727
728     def get_c_type(self):
729         return "gpointer"
730
731     def convert_c2py(self):
732         self.wrapper.add_declaration("PyObject **_user_data;")
733         cleanup = self.free_it and ("g_free(%s);" % self.name) or None
734         self.wrapper.write_code(code=("_real_user_data = (PyObject **) %s;"
735                                       % self.name),
736                                 cleanup=cleanup)
737
738         self.wrapper.add_declaration("PyObject *py_func;")
739         cleanup = self.free_it and "Py_DECREF(py_func);" or None
740         self.wrapper.write_code(code="py_func = _user_data[0];",
741                                 cleanup=cleanup)
742         self.wrapper.set_call_target("py_func")
743
744         self.wrapper.add_declaration("PyObject *py_user_data;")
745         cleanup = self.free_it and "Py_XDECREF(py_user_data);" or None
746         self.wrapper.write_code(code="py_user_data = _user_data[1];",
747                                 cleanup=cleanup)
748         self.wrapper.add_pyargv_item("py_user_data", optional=True)
749
750 def _test():
751     import sys
752
753     if 1:
754         wrapper = ReverseWrapper("this_is_the_c_function_name", is_static=True)
755         wrapper.set_return_type(StringReturn(wrapper))
756         wrapper.add_parameter(PyGObjectMethodParam(wrapper, "self", method_name="do_xxx"))
757         wrapper.add_parameter(StringParam(wrapper, "param2", optional=True))
758         wrapper.add_parameter(GObjectParam(wrapper, "param3"))
759         #wrapper.add_parameter(InoutIntParam(wrapper, "param4"))
760         wrapper.generate(FileCodeSink(sys.stderr))
761
762     if 0:
763         wrapper = ReverseWrapper("this_a_callback_wrapper")
764         wrapper.set_return_type(VoidReturn(wrapper))
765         wrapper.add_parameter(StringParam(wrapper, "param1", optional=False))
766         wrapper.add_parameter(GObjectParam(wrapper, "param2"))
767         wrapper.add_parameter(CallbackInUserDataParam(wrapper, "data", free_it=True))
768         wrapper.generate(FileCodeSink(sys.stderr))
769
770 if __name__ == '__main__':
771     _test()