b96e12e1ddf48c131785d8bb795c5c9d1c362d91
[platform/upstream/pygobject2.git] / 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.check_exception_code = MemoryCodeSink()
92         self.cleanup_actions = []
93         self.pyargv_items = []
94         self.pyargv_optional_items = []
95         self.pyret_parse_items = [] # list of (format_spec, parameter)
96         self.code_sinks_stack = [self.body]
97
98     def set_call_target(self, called_pyobj, method_name=None):
99         assert called_pyobj is not None
100         assert self.called_pyobj is None
101         self.called_pyobj = called_pyobj
102         self.method_name = method_name
103
104     def set_return_type(self, return_type):
105         assert isinstance(return_type, ReturnType)
106         self.return_type = return_type
107
108     def add_parameter(self, param):
109         assert isinstance(param, Parameter)
110         self.parameters.append(param)
111
112     def add_declaration(self, decl_code):
113         self.declarations.writeln(decl_code)
114
115     def add_pyargv_item(self, variable, optional=False):
116         if optional:
117             self.pyargv_optional_items.append(variable)
118         else:
119             self.pyargv_items.append(variable)
120
121     def add_pyret_parse_item(self, format_specifier, parameter, prepend=False):
122         if prepend:
123             self.pyret_parse_items.insert(0, (format_specifier, parameter))
124         else:
125             self.pyret_parse_items.append((format_specifier, parameter))
126
127
128     def push_code_sink(self, code_sink):
129         self.code_sinks_stack.insert(0, code_sink)
130
131     def pop_code_sink(self):
132         return self.code_sinks_stack.pop(0)
133
134
135     def write_code(self, code,
136                    cleanup=None,
137                    failure_expression=None,
138                    failure_cleanup=None,
139                    failure_exception=None,
140                    code_sink=None):
141         '''Add a chunk of code with cleanup and error handling
142
143         This method is to be used by TypeHandlers when generating code
144
145         Keywork arguments:
146         code -- code to add
147         cleanup -- code to cleanup any dynamic resources created by @code
148                    (except in case of failure) (default None)
149         failure_expression -- C boolean expression to indicate
150                               if anything failed (default None)
151         failure_cleanup -- code to cleanup any dynamic resources
152                            created by @code in case of failure (default None)
153         failure_exception -- code to raise an exception in case of
154                              failure (which will be immediately
155                              printed and cleared), (default None)
156         code_sink -- "code sink" to use; by default,
157                       ReverseWrapper.body is used, which writes the
158                       main body of the wrapper, before calling the
159                       python method.  Alternatively,
160                       ReverseWrapper.after_pyret_parse can be used, to
161                       write code after the PyArg_ParseTuple that
162                       parses the python method return value.
163         '''
164         if code_sink is None:
165             code_sink = self.code_sinks_stack[0]
166         if code is not None:
167             code_sink.writeln(code)
168         if failure_expression is not None:
169             code_sink.writeln("if (%s) {" % (failure_expression,))
170             code_sink.indent()
171             if failure_exception is None:
172                 code_sink.writeln("if (PyErr_Occurred())")
173                 code_sink.indent()
174                 code_sink.writeln("PyErr_Print();")
175                 code_sink.unindent()
176             else:
177                 code_sink.writeln(failure_exception)
178                 code_sink.writeln("PyErr_Print();")
179             if failure_cleanup is not None:
180                 code_sink.writeln(failure_cleanup)
181             for cleanup_action in self.cleanup_actions:
182                 code_sink.writeln(cleanup_action)
183
184             self.push_code_sink(code_sink)
185             try:
186                 self.return_type.write_error_return()
187             finally:
188                 self.pop_code_sink()
189
190             code_sink.unindent()
191             code_sink.writeln("}")
192         if cleanup is not None:
193             self.cleanup_actions.insert(0, cleanup)
194
195     def generate(self, sink):
196         '''Generate the code into a CodeSink object'''
197         assert isinstance(sink, CodeSink)
198
199         if DEBUG_MODE:
200             self.declarations.writeln("/* begin declarations */")
201             self.body.writeln("/* begin main body */")
202             self.post_return_code.writeln("/* begin post-return code */")
203
204         self.add_declaration("PyGILState_STATE __py_state;")
205         self.write_code(code="__py_state = pyg_gil_state_ensure();",
206                         cleanup="pyg_gil_state_release(__py_state);")
207
208         for param in self.parameters:
209             param.convert_c2py()
210
211         assert self.called_pyobj is not None,\
212                "Parameters failed to provide a target function or method."
213
214         if self.is_static:
215             sink.writeln('static %s' % self.return_type.get_c_type())
216         else:
217             sink.writeln(self.return_type.get_c_type())
218         c_proto_params = map(Parameter.format_for_c_proto, self.parameters)
219         sink.writeln("%s(%s)\n{" % (self.cname, ", ".join(c_proto_params)))
220
221         self.return_type.write_decl()
222         self.add_declaration("PyObject *py_retval;")
223
224         ## Handle number of arguments
225         if self.pyargv_items:
226             self.add_declaration("PyObject *py_args;")
227             py_args = "py_args"
228             if self.pyargv_optional_items:
229                 self.add_declaration("int argc = %i;" % len(self.pyargv_items))
230                 argc = "argc"
231                 for arg in self.pyargv_optional_items:
232                     self.body.writeln("if (%s)" % arg)
233                     self.body.indent()
234                     self.body.writeln("++argc;")
235                     self.body.unindent()
236             else:
237                 argc = str(len(self.pyargv_items))
238         else:
239             if self.pyargv_optional_items:
240                 self.add_declaration("PyObject *py_args;")
241                 py_args = "py_args"
242                 self.add_declaration("int argc = 0;")
243                 argc = "argc"
244                 for arg in self.pyargv_optional_items:
245                     self.body.writeln("if (%s)" % arg)
246                     self.body.indent()
247                     self.body.writeln("++argc;")
248                     self.body.unindent()
249             else:
250                 py_args = "NULL"
251                 argc = None
252
253         self.body.writeln()
254
255         if py_args != "NULL":
256             self.write_code("py_args = PyTuple_New(%s);" % argc,
257                             cleanup="Py_DECREF(py_args);")
258             pos = 0
259             for arg in self.pyargv_items:
260                 try: # try to remove the Py_DECREF cleanup action, if we can
261                     self.cleanup_actions.remove("Py_DECREF(%s);" % arg)
262                 except ValueError: # otherwise we have to Py_INCREF..
263                     self.body.writeln("Py_INCREF(%s);" % arg)
264                 self.body.writeln("PyTuple_SET_ITEM(%s, %i, %s);" % (py_args, pos, arg))
265                 pos += 1
266             for arg in self.pyargv_optional_items:
267                 self.body.writeln("if (%s) {" % arg)
268                 self.body.indent()
269                 try: # try to remove the Py_DECREF cleanup action, if we can
270                     self.cleanup_actions.remove("Py_XDECREF(%s);" % arg)
271                 except ValueError: # otherwise we have to Py_INCREF..
272                     self.body.writeln("Py_INCREF(%s);" % arg)
273                 self.body.writeln("PyTuple_SET_ITEM(%s, %i, %s);" % (py_args, pos, arg))
274                 self.body.unindent()
275                 self.body.writeln("}")
276                 pos += 1
277
278         self.body.writeln()
279
280         ## Call the python method
281         if self.method_name is None:
282             self.write_code("py_retval = PyObject_Call(%s, %s);"
283                             % (self.called_pyobj, py_args),
284                             cleanup="Py_XDECREF(py_retval);")
285             self.check_exception_code.flush_to(self.body)
286             self.write_code(None, failure_expression="!py_retval")
287
288         else:
289             self.add_declaration("PyObject *py_method;")
290             self.write_code("py_method = PyObject_GetAttrString(%s, \"%s\");"
291                             % (self.called_pyobj, self.method_name),
292                             cleanup="Py_DECREF(py_method);",
293                             failure_expression="!py_method")
294             self.write_code("py_retval = PyObject_CallObject(py_method, %s);"
295                             % (py_args,),
296                             cleanup="Py_XDECREF(py_retval);")
297             self.check_exception_code.flush_to(self.body)
298             self.write_code(None, failure_expression="!py_retval")
299
300         ## -- Handle the return value --
301
302         ## we need to check if the return_type object is prepared to cooperate with multiple return values
303         len_before = len(self.pyret_parse_items)
304         self.return_type.write_conversion()
305         len_after = len(self.pyret_parse_items)
306         assert (self.return_type.get_c_type() == 'void'
307                 or not (len_before == len_after and len_after > 0)),\
308                ("Bug in reverse wrappers: return type handler %s"
309                 " is not prepared to cooperate multiple return values") % (type(self.return_type),)
310
311         sink.indent()
312
313         if self.pyret_parse_items == [("", "")]:
314             ## special case when there are no return parameters
315             self.write_code(
316                 code=None,
317                 failure_expression='py_retval != Py_None',
318                 failure_exception=('PyErr_SetString(PyExc_TypeError, '
319                                    '"virtual method should return None");'))
320         else:    
321             if len(self.pyret_parse_items) == 1:
322                 ## if retval is one item only, pack it in a tuple so we
323                 ## can use PyArg_ParseTuple as usual..
324                 self.write_code('py_retval = Py_BuildValue("(N)", py_retval);')
325             if len(self.pyret_parse_items) > 0:
326                 ## Parse return values using PyArg_ParseTuple
327                 params = ["py_retval",
328                           '"%s"' % "".join([format for format, param in self.pyret_parse_items])]
329                 params.extend([param for format, param in self.pyret_parse_items if param])
330                 self.write_code(code=None, failure_expression=(
331                     '!PyArg_ParseTuple(%s)' % (', '.join(params),)))
332
333         if DEBUG_MODE:
334             self.declarations.writeln("/* end declarations */")
335         self.declarations.flush_to(sink)
336         sink.writeln()
337         if DEBUG_MODE:
338             self.body.writeln("/* end main body */")
339         self.body.flush_to(sink)
340         sink.writeln()
341         if DEBUG_MODE:
342             self.post_return_code.writeln("/* end post-return code */")
343         self.post_return_code.flush_to(sink)
344         sink.writeln()
345
346         for cleanup_action in self.cleanup_actions:
347             sink.writeln(cleanup_action)
348         if self.return_type.get_c_type() != 'void':
349             sink.writeln()
350             sink.writeln("return retval;")
351         sink.unindent()
352         sink.writeln("}")
353
354 class TypeHandler(object):
355     def __init__(self, wrapper, **props):
356         assert isinstance(wrapper, ReverseWrapper)
357         self.wrapper = wrapper
358         self.props = props
359
360 class ReturnType(TypeHandler):
361
362     supports_optional = False
363
364     def get_c_type(self):
365         raise NotImplementedError
366
367     def write_decl(self):
368         raise NotImplementedError
369
370     def write_error_return(self):
371         '''Write "return <value>" code in case of error'''
372         raise NotImplementedError
373
374     def write_conversion(self):
375         '''Writes code to convert Python return value in 'py_retval'
376         into C 'retval'.  Returns a string with C boolean expression
377         that determines if anything went wrong. '''
378         raise NotImplementedError
379
380 class Parameter(TypeHandler):
381
382     def __init__(self, wrapper, name, **props):
383         TypeHandler.__init__(self, wrapper, **props)
384         self.name = name
385
386     def get_c_type(self):
387         raise NotImplementedError
388
389     def convert_c2py(self):
390         '''Write some code before calling the Python method.'''
391         pass
392
393     def format_for_c_proto(self):
394         return join_ctype_name(self.get_c_type(), self.name)
395
396
397 ###---
398 class StringParam(Parameter):
399
400     def get_c_type(self):
401         return self.props.get('c_type', 'char *').replace('const-', 'const ')
402
403     def convert_c2py(self):
404         if self.props.get('optional', False):
405             self.wrapper.add_declaration("PyObject *py_%s = NULL;" % self.name)
406             self.wrapper.write_code(code=("if (%s)\n"
407                                           "    py_%s = PyString_FromString(%s);\n"
408                                           % (self.name, self.name, self.name)),
409                                     cleanup=("Py_XDECREF(py_%s);" % self.name))
410             self.wrapper.add_pyargv_item("py_%s" % self.name, optional=True)
411         elif self.props.get('nullok', False):
412             self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
413             self.wrapper.write_code(code=("if (%s)\n"
414                                           "    py_%s = PyString_FromString(%s);\n"
415                                           "else {\n"
416                                           "    Py_INCREF(Py_None);\n"
417                                           "    py_%s = Py_None;\n"
418                                           "}\n"
419                                           % (self.name, self.name, self.name, self.name)),
420                                     cleanup=("Py_DECREF(py_%s);" % self.name))
421             self.wrapper.add_pyargv_item("py_%s" % self.name)
422         else:
423             self.wrapper.add_declaration("PyObject *py_%s = NULL;" % self.name)
424             self.wrapper.write_code(code=("if (%s)\n"
425                                           "    py_%s = PyString_FromString(%s);\n" %
426                                           (self.name, self.name, self.name)),
427                                     cleanup=("Py_DECREF(py_%s);" % self.name),
428                                     failure_expression=("!py_%s" % self.name))
429             self.wrapper.add_pyargv_item("py_%s" % self.name)
430
431 for ctype in ('char*', 'gchar*', 'const-char*', 'char-const*', 'const-gchar*',
432               'gchar-const*', 'string', 'static_string'):
433     argtypes.matcher.register_reverse(ctype, StringParam)
434 del ctype
435
436 class StringReturn(ReturnType):
437
438     def get_c_type(self):
439         return self.props.get('c_type', 'char *').replace('const-', 'const ')
440     #return "char *"
441
442     def write_decl(self):
443         self.wrapper.add_declaration("%s retval;" % self.get_c_type())
444         #self.wrapper.add_declaration("char *retval;")
445
446     def write_error_return(self):
447         self.wrapper.write_code("return NULL;")
448
449     def write_conversion(self):
450         self.wrapper.add_pyret_parse_item("s", "&retval", prepend=True)
451         self.wrapper.write_code("retval = g_strdup(retval);", code_sink=self.wrapper.post_return_code)
452
453 for ctype in ('char*', 'gchar*', 'const-gchar*'):
454     argtypes.matcher.register_reverse_ret(ctype, StringReturn)
455 del ctype
456
457
458 class VoidReturn(ReturnType):
459
460     def get_c_type(self):
461         return "void"
462
463     def write_decl(self):
464         pass
465
466     def write_error_return(self):
467         self.wrapper.write_code("return;")
468
469     def write_conversion(self):
470         self.wrapper.add_pyret_parse_item("", "", prepend=True)
471
472 argtypes.matcher.register_reverse_ret('void', VoidReturn)
473 argtypes.matcher.register_reverse_ret('none', VoidReturn)
474
475 class GObjectParam(Parameter):
476
477     def get_c_type(self):
478         return self.props.get('c_type', 'GObject *')
479
480     def convert_c2py(self):
481         self.wrapper.add_declaration("PyObject *py_%s = NULL;" % self.name)
482         self.wrapper.write_code(code=("if (%s)\n"
483                                       "    py_%s = pygobject_new((GObject *) %s);\n"
484                                       "else {\n"
485                                       "    Py_INCREF(Py_None);\n"
486                                       "    py_%s = Py_None;\n"
487                                       "}"
488                                       % (self.name, self.name, self.name, self.name)),
489                                 cleanup=("Py_DECREF(py_%s);" % self.name))
490         self.wrapper.add_pyargv_item("py_%s" % self.name)
491
492 argtypes.matcher.register_reverse('GObject*', GObjectParam)
493
494 class GObjectReturn(ReturnType):
495
496     supports_optional = True
497
498     def get_c_type(self):
499         return self.props.get('c_type', 'GObject *')
500
501     def write_decl(self):
502         if not self.props.get('optional'):
503             self.wrapper.add_declaration("%s retval;" % self.get_c_type())
504         else:
505             self.wrapper.add_declaration("%s retval = NULL;" % self.get_c_type())
506
507     def write_error_return(self):
508         self.wrapper.write_code("return NULL;")
509
510     def write_conversion(self):
511         if not self.props.get('optional'):
512             self.wrapper.write_code(
513                 code=None,
514                 failure_expression="!PyObject_TypeCheck(py_retval, &PyGObject_Type)",
515                 failure_exception='PyErr_SetString(PyExc_TypeError, "retval should be a GObject");')
516             self.wrapper.write_code("retval = (%s) pygobject_get(py_retval);"
517                                     % self.get_c_type())
518             self.wrapper.write_code("g_object_ref((GObject *) retval);")
519         else:
520             self.wrapper.write_code(
521                 code=None,
522                 failure_expression="py_retval != Py_None && !PyObject_TypeCheck(py_retval, &PyGObject_Type)",
523                 failure_exception='PyErr_SetString(PyExc_TypeError, "retval should be None or a GObject");')
524             self.wrapper.write_code("if (py_retval != Py_None) {\n"
525                                     "    retval = (%s) pygobject_get(py_retval);\n"
526                                     "    g_object_ref((GObject *) retval);\n"
527                                     "}\n"
528                                     % self.get_c_type())
529
530 argtypes.matcher.register_reverse_ret('GObject*', GObjectReturn)
531
532
533
534 class IntParam(Parameter):
535
536     def get_c_type(self):
537         return self.props.get('c_type', 'int')
538
539     def convert_c2py(self):
540         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
541         self.wrapper.write_code(code=("py_%s = PyInt_FromLong(%s);" %
542                                       (self.name, self.name)),
543                                 cleanup=("Py_DECREF(py_%s);" % self.name))
544         self.wrapper.add_pyargv_item("py_%s" % self.name)
545
546 class IntReturn(ReturnType):
547     def get_c_type(self):
548         return self.props.get('c_type', 'int')
549     def write_decl(self):
550         self.wrapper.add_declaration("%s retval;" % self.get_c_type())
551     def write_error_return(self):
552         self.wrapper.write_code("return -G_MAXINT;")
553     def write_conversion(self):
554         self.wrapper.add_pyret_parse_item("i", "&retval", prepend=True)
555
556 for argtype in ('int', 'gint', 'guint', 'short', 'gshort', 'gushort', 'long',
557                 'glong', 'gsize', 'gssize', 'guint8', 'gint8', 'guint16',
558                 'gint16', 'gint32', 'GTime'):
559     argtypes.matcher.register_reverse(argtype, IntParam)
560     argtypes.matcher.register_reverse_ret(argtype, IntReturn)
561 del argtype
562
563 class IntPtrParam(Parameter):
564     def __init__(self, wrapper, name, **props):
565         if "direction" not in props:
566             raise argtypes.ArgTypeConfigurationError(
567                 "cannot use int* parameter without direction")
568         if props["direction"] not in ("out", "inout"):
569             raise argtypes.ArgTypeConfigurationError(
570                 "cannot use int* parameter with direction '%s'"
571                 % (props["direction"],))
572         Parameter.__init__(self, wrapper, name, **props)
573     def get_c_type(self):
574         return self.props.get('c_type', 'int*')
575     def convert_c2py(self):
576         if self.props["direction"] == "inout":
577             self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
578             self.wrapper.write_code(code=("py_%s = PyInt_FromLong(*%s);" %
579                                           (self.name, self.name)),
580                                     cleanup=("Py_DECREF(py_%s);" % self.name))
581             self.wrapper.add_pyargv_item("py_%s" % self.name)
582         self.wrapper.add_pyret_parse_item("i", self.name)
583 for argtype in ('int*', 'gint*'):
584     argtypes.matcher.register_reverse(argtype, IntPtrParam)
585 del argtype
586
587
588 class GEnumReturn(IntReturn):
589     def write_conversion(self):
590         self.wrapper.write_code(
591             code=None,
592             failure_expression=(
593             "pyg_enum_get_value(%s, py_retval, (gint *)&retval)"
594             % (self.props['typecode'],)))
595
596 argtypes.matcher.register_reverse_ret("GEnum", GEnumReturn)
597
598 class GEnumParam(IntParam):
599     def convert_c2py(self):
600         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
601         self.wrapper.write_code(code=("py_%s = pyg_enum_from_gtype(%s, %s);" %
602                                 (self.name, self.props['typecode'], self.name)),
603                         cleanup=("Py_DECREF(py_%s);" % self.name),
604                         failure_expression=("!py_%s" % self.name))
605         self.wrapper.add_pyargv_item("py_%s" % self.name)
606
607 argtypes.matcher.register_reverse("GEnum", GEnumParam)
608
609 class GFlagsReturn(IntReturn):
610     def write_conversion(self):
611         self.wrapper.write_code(
612             code=None,
613             failure_expression=(
614             "pyg_flags_get_value(%s, py_retval, (gint *)&retval)" %
615             self.props['typecode']))
616
617 argtypes.matcher.register_reverse_ret("GFlags", GFlagsReturn)
618
619 class GFlagsParam(IntParam):
620     def convert_c2py(self):
621         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
622         self.wrapper.write_code(code=(
623             "py_%s = pyg_flags_from_gtype(%s, %s);" %
624             (self.name, self.props['typecode'], self.name)),
625                                 cleanup=("Py_DECREF(py_%s);" % self.name),
626                                 failure_expression=("!py_%s" % self.name))
627         self.wrapper.add_pyargv_item("py_%s" % self.name)
628
629 argtypes.matcher.register_reverse("GFlags", GFlagsParam)
630
631
632 class GtkTreePathParam(IntParam):
633     def convert_c2py(self):
634         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
635         self.wrapper.write_code(code=(
636             "py_%s = pygtk_tree_path_to_pyobject(%s);" %
637             (self.name, self.name)),
638                                 cleanup=("Py_DECREF(py_%s);" % self.name),
639                                 failure_expression=("!py_%s" % self.name))
640         self.wrapper.add_pyargv_item("py_%s" % self.name)
641
642 argtypes.matcher.register_reverse("GtkTreePath*", GtkTreePathParam)
643
644
645 class GtkTreePathReturn(ReturnType):
646     def get_c_type(self):
647         return self.props.get('c_type', 'GtkTreePath *')
648     def write_decl(self):
649         self.wrapper.add_declaration("GtkTreePath * retval;")
650     def write_error_return(self):
651         self.wrapper.write_code("return NULL;")
652     def write_conversion(self):
653         self.wrapper.write_code(
654             "retval = pygtk_tree_path_from_pyobject(py_retval);\n",
655             failure_expression=('!retval'),
656             failure_exception=(
657     'PyErr_SetString(PyExc_TypeError, "retval should be a GtkTreePath");'))
658
659 argtypes.matcher.register_reverse_ret("GtkTreePath*", GtkTreePathReturn)
660
661
662 class BooleanReturn(ReturnType):
663     def get_c_type(self):
664         return "gboolean"
665     def write_decl(self):
666         self.wrapper.add_declaration("gboolean retval;")
667         self.wrapper.add_declaration("PyObject *py_main_retval;")
668     def write_error_return(self):
669         self.wrapper.write_code("return FALSE;")
670     def write_conversion(self):
671         self.wrapper.add_pyret_parse_item("O", "&py_main_retval", prepend=True)
672         self.wrapper.write_code(
673             "retval = PyObject_IsTrue(py_main_retval)? TRUE : FALSE;",
674             code_sink=self.wrapper.post_return_code)
675 argtypes.matcher.register_reverse_ret("gboolean", BooleanReturn)
676
677 class BooleanParam(Parameter):
678     def get_c_type(self):
679         return "gboolean"
680     def convert_c2py(self):
681         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
682         self.wrapper.write_code("py_%s = %s? Py_True : Py_False;"
683                                 % (self.name, self.name))
684         self.wrapper.add_pyargv_item("py_%s" % self.name)
685
686 argtypes.matcher.register_reverse("gboolean", BooleanParam)
687
688
689 class DoubleParam(Parameter):
690     def get_c_type(self):
691         return self.props.get('c_type', 'gdouble')
692     def convert_c2py(self):
693         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
694         self.wrapper.write_code(code=("py_%s = PyFloat_FromDouble(%s);" %
695                                       (self.name, self.name)),
696                                 cleanup=("Py_DECREF(py_%s);" % self.name))
697         self.wrapper.add_pyargv_item("py_%s" % self.name)
698
699 class DoublePtrParam(Parameter):
700     def __init__(self, wrapper, name, **props):
701         if "direction" not in props:
702             raise argtypes.ArgTypeConfigurationError(
703                 "cannot use double* parameter without direction")
704         if props["direction"] not in ("out", ): # inout not yet implemented
705             raise argtypes.ArgTypeConfigurationError(
706                 "cannot use double* parameter with direction '%s'"
707                 % (props["direction"],))
708         Parameter.__init__(self, wrapper, name, **props)
709     def get_c_type(self):
710         return self.props.get('c_type', 'double*')
711     def convert_c2py(self):
712         self.wrapper.add_pyret_parse_item("d", self.name)
713 for argtype in ('double*', 'gdouble*'):
714     argtypes.matcher.register_reverse(argtype, DoublePtrParam)
715 del argtype
716
717 class DoubleReturn(ReturnType):
718     def get_c_type(self):
719         return self.props.get('c_type', 'gdouble')
720     def write_decl(self):
721         self.wrapper.add_declaration("%s retval;" % self.get_c_type())
722     def write_error_return(self):
723         self.wrapper.write_code("return -G_MAXFLOAT;")
724     def write_conversion(self):
725         self.wrapper.add_pyret_parse_item("d", "&retval", prepend=True)
726
727 for argtype in ('float', 'double', 'gfloat', 'gdouble'):
728     argtypes.matcher.register_reverse(argtype, DoubleParam)
729     argtypes.matcher.register_reverse_ret(argtype, DoubleReturn)
730
731
732 class GBoxedParam(Parameter):
733     def get_c_type(self):
734         return self.props.get('c_type').replace('const-', 'const ')
735     def convert_c2py(self):
736         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
737         ctype = self.get_c_type()
738         if ctype.startswith('const '):
739             ctype_no_const = ctype[len('const '):]
740             self.wrapper.write_code(
741                 code=('py_%s = pyg_boxed_new(%s, (%s) %s, TRUE, TRUE);' %
742                       (self.name, self.props['typecode'],
743                        ctype_no_const, self.name)),
744                 cleanup=("Py_DECREF(py_%s);" % self.name))
745         else:
746             self.wrapper.write_code(
747                 code=('py_%s = pyg_boxed_new(%s, %s, FALSE, FALSE);' %
748                       (self.name, self.props['typecode'], self.name)),
749                 cleanup=("Py_DECREF(py_%s);" % self.name))
750         self.wrapper.add_pyargv_item("py_%s" % self.name)
751
752 argtypes.matcher.register_reverse("GBoxed", GBoxedParam)
753
754
755 class GBoxedReturn(ReturnType):
756     def get_c_type(self):
757         return self.props.get('c_type')
758     def write_decl(self):
759         self.wrapper.add_declaration("%s retval;" % self.get_c_type())
760     def write_error_return(self):
761         self.wrapper.write_code("return retval;")
762     def write_conversion(self):
763         self.wrapper.write_code(code = None,
764             failure_expression=("!pyg_boxed_check(py_retval, %s)" %
765                                 (self.props['typecode'],)),
766             failure_exception=(
767             'PyErr_SetString(PyExc_TypeError, "retval should be a %s");'
768             % (self.props['typename'],)))
769         self.wrapper.write_code('retval = pyg_boxed_get(py_retval, %s);' %
770                                 self.props['typename'])
771
772 argtypes.matcher.register_reverse_ret("GBoxed", GBoxedReturn)
773
774
775 class GdkRegionPtrReturn(GBoxedReturn):
776     def write_error_return(self):
777         self.wrapper.write_code("return gdk_region_new();")
778     def write_conversion(self):
779         self.props['typecode'] = 'PYGDK_TYPE_REGION'
780         self.props['typename'] = 'GdkRegion'
781         super(GdkRegionPtrReturn, self).write_conversion()
782
783 argtypes.matcher.register_reverse_ret("GdkRegion*", GdkRegionPtrReturn)
784
785
786 class PangoFontDescriptionReturn(GBoxedReturn):
787     def write_error_return(self):
788         self.wrapper.write_code("return pango_font_description_new();")
789     def write_conversion(self):
790         self.props['typecode'] = 'PANGO_TYPE_FONT_DESCRIPTION'
791         self.props['typename'] = 'PangoFontDescription'
792         super(PangoFontDescriptionReturn, self).write_conversion()
793
794 argtypes.matcher.register_reverse_ret("PangoFontDescription*",
795                                       PangoFontDescriptionReturn)
796
797
798 class PangoFontMetricsReturn(GBoxedReturn):
799     def write_error_return(self):
800         self.wrapper.write_code("return pango_font_metrics_new();")
801     def write_conversion(self):
802         self.props['typecode'] = 'PANGO_TYPE_FONT_METRICS'
803         self.props['typename'] = 'PangoFontMetrics'
804         super(PangoFontMetricsReturn, self).write_conversion()
805
806 argtypes.matcher.register_reverse_ret("PangoFontMetrics*",
807                                       PangoFontMetricsReturn)
808
809
810 class PangoLanguageReturn(GBoxedReturn):
811     def write_error_return(self):
812         self.wrapper.write_code("return pango_language_from_string(\"\");")
813     def write_conversion(self):
814         self.props['typecode'] = 'PANGO_TYPE_LANGUAGE'
815         self.props['typename'] = 'PangoLanguage'
816         super(PangoLanguageReturn, self).write_conversion()
817
818 argtypes.matcher.register_reverse_ret("PangoLanguage*", PangoLanguageReturn)
819
820
821 class GdkRectanglePtrParam(Parameter):
822     def get_c_type(self):
823         return self.props.get('c_type').replace('const-', 'const ')
824     def convert_c2py(self):
825         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
826         self.wrapper.write_code(
827             code=('py_%s = pyg_boxed_new(GDK_TYPE_RECTANGLE, %s, TRUE, TRUE);' %
828                   (self.name, self.name)),
829             cleanup=("Py_DECREF(py_%s);" % self.name))
830         self.wrapper.add_pyargv_item("py_%s" % self.name)
831
832 argtypes.matcher.register_reverse("GdkRectangle*", GdkRectanglePtrParam)
833 argtypes.matcher.register_reverse('GtkAllocation*', GdkRectanglePtrParam)
834
835
836 class GErrorParam(Parameter):
837     def get_c_type(self):
838         return self.props.get('c_type').replace('**', ' **')
839     def convert_c2py(self):
840         self.wrapper.write_code(code=None,
841             failure_expression=("pyg_gerror_exception_check(%s)" % self.name),
842                                 code_sink=self.wrapper.check_exception_code)
843
844 argtypes.matcher.register_reverse('GError**', GErrorParam)
845
846
847 class PyGObjectMethodParam(Parameter):
848     def __init__(self, wrapper, name, method_name, **props):
849         Parameter.__init__(self, wrapper, name, **props)
850         self.method_name = method_name
851
852     def get_c_type(self):
853         return self.props.get('c_type', 'GObject *')
854
855     def convert_c2py(self):
856         self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
857         self.wrapper.write_code(code=("py_%s = pygobject_new((GObject *) %s);" %
858                                       (self.name, self.name)),
859                                 cleanup=("Py_DECREF(py_%s);" % self.name),
860                                 failure_expression=("!py_%s" % self.name))
861         self.wrapper.set_call_target("py_%s" % self.name, self.method_name)
862
863
864 class CallbackInUserDataParam(Parameter):
865     def __init__(self, wrapper, name, free_it, **props):
866         Parameter.__init__(self, wrapper, name, **props)
867         self.free_it = free_it
868
869     def get_c_type(self):
870         return "gpointer"
871
872     def convert_c2py(self):
873         self.wrapper.add_declaration("PyObject **_user_data;")
874         cleanup = self.free_it and ("g_free(%s);" % self.name) or None
875         self.wrapper.write_code(code=("_real_user_data = (PyObject **) %s;"
876                                       % self.name),
877                                 cleanup=cleanup)
878
879         self.wrapper.add_declaration("PyObject *py_func;")
880         cleanup = self.free_it and "Py_DECREF(py_func);" or None
881         self.wrapper.write_code(code="py_func = _user_data[0];",
882                                 cleanup=cleanup)
883         self.wrapper.set_call_target("py_func")
884
885         self.wrapper.add_declaration("PyObject *py_user_data;")
886         cleanup = self.free_it and "Py_XDECREF(py_user_data);" or None
887         self.wrapper.write_code(code="py_user_data = _user_data[1];",
888                                 cleanup=cleanup)
889         self.wrapper.add_pyargv_item("py_user_data", optional=True)
890
891 def _test():
892     import sys
893
894     if 1:
895         wrapper = ReverseWrapper("this_is_the_c_function_name", is_static=True)
896         wrapper.set_return_type(StringReturn(wrapper))
897         wrapper.add_parameter(PyGObjectMethodParam(wrapper, "self", method_name="do_xxx"))
898         wrapper.add_parameter(StringParam(wrapper, "param2", optional=True))
899         wrapper.add_parameter(GObjectParam(wrapper, "param3"))
900         #wrapper.add_parameter(InoutIntParam(wrapper, "param4"))
901         wrapper.generate(FileCodeSink(sys.stderr))
902
903     if 0:
904         wrapper = ReverseWrapper("this_a_callback_wrapper")
905         wrapper.set_return_type(VoidReturn(wrapper))
906         wrapper.add_parameter(StringParam(wrapper, "param1", optional=False))
907         wrapper.add_parameter(GObjectParam(wrapper, "param2"))
908         wrapper.add_parameter(CallbackInUserDataParam(wrapper, "data", free_it=True))
909         wrapper.generate(FileCodeSink(sys.stderr))
910
911 if __name__ == '__main__':
912     _test()