2
### Code to generate "Reverse Wrappers", i.e. C->Python wrappers
3
### (C) 2004 Gustavo Carneiro <gjc@gnome.org>
6
def join_ctype_name(ctype, name):
7
'''Joins a C type and a variable name into a single string'''
9
return " ".join((ctype, name))
11
return "".join((ctype, name))
14
class CodeSink(object):
16
self.indent_level = 0 # current indent level
17
self.indent_stack = [] # previous indent levels
19
def _format_code(self, code):
20
assert isinstance(code, str)
22
for line in code.split('\n'):
23
l.append(' '*self.indent_level + line)
28
def writeln(self, line=''):
29
raise NotImplementedError
31
def indent(self, level=4):
32
'''Add a certain ammount of indentation to all lines written
33
from now on and until unindent() is called'''
34
self.indent_stack.append(self.indent_level)
35
self.indent_level += level
38
'''Revert indentation level to the value before last indent() call'''
39
self.indent_level = self.indent_stack.pop()
42
class FileCodeSink(CodeSink):
43
def __init__(self, fp):
44
CodeSink.__init__(self)
45
assert isinstance(fp, file)
48
def writeln(self, line=''):
49
self.fp.write(self._format_code(line))
51
class MemoryCodeSink(CodeSink):
53
CodeSink.__init__(self)
56
def writeln(self, line=''):
57
self.lines.append(self._format_code(line))
59
def flush_to(self, sink):
60
assert isinstance(sink, CodeSink)
61
for line in self.lines:
62
sink.writeln(line.rstrip())
67
for line in self.lines:
68
l.append(self._format_code(line))
72
class ReverseWrapper(object):
73
'''Object that generates a C->Python wrapper'''
74
def __init__(self, cname, is_static=True):
75
assert isinstance(cname, str)
78
## function object we will call, or object whose method we will call
79
self.called_pyobj = None
80
## name of method of self.called_pyobj we will call
81
self.method_name = None
82
self.is_static = is_static
85
self.declarations = MemoryCodeSink()
86
self.body = MemoryCodeSink()
87
self.cleanup_actions = []
88
self.pyargv_items = []
89
self.pyargv_optional_items = []
91
def set_call_target(self, called_pyobj, method_name=None):
92
assert called_pyobj is not None
93
assert self.called_pyobj is None
94
self.called_pyobj = called_pyobj
95
self.method_name = method_name
97
def set_return_type(self, return_type):
98
assert isinstance(return_type, ReturnType)
99
self.return_type = return_type
101
def add_parameter(self, param):
102
assert isinstance(param, Parameter)
103
self.parameters.append(param)
105
def add_declaration(self, decl_code):
106
self.declarations.writeln(decl_code)
108
def add_pyargv_item(self, variable, optional=False):
110
self.pyargv_optional_items.append(variable)
112
self.pyargv_items.append(variable)
114
def write_code(self, code,
116
failure_expression=None,
117
failure_cleanup=None):
118
'''Add a chunk of code with cleanup and error handling
120
This method is to be used by TypeHandlers when generating code
124
cleanup -- code to cleanup any dynamic resources created by @code
125
(except in case of failure) (default None)
126
failure_expression -- C boolean expression to indicate
127
if anything failed (default None)
128
failure_cleanup -- code to cleanup any dynamic resources
129
created by @code in case of failure (default None)
132
self.body.writeln(code)
133
if failure_expression is not None:
134
self.body.writeln("if (%s) {" % failure_expression)
136
self.body.writeln("if (PyErr_Occurred())")
138
self.body.writeln("PyErr_Print();")
140
if failure_cleanup is not None:
141
self.body.writeln(failure_cleanup)
142
for cleanup_action in self.cleanup_actions:
143
self.body.writeln(cleanup_action)
144
self.return_type.write_error_return()
146
self.body.writeln("}")
147
if cleanup is not None:
148
self.cleanup_actions.insert(0, cleanup)
150
def generate(self, sink):
151
'''Generate the code into a CodeSink object'''
152
assert isinstance(sink, CodeSink)
154
self.add_declaration("PyGILState_STATE __py_state;")
155
self.write_code(code="__py_state = pyg_gil_state_ensure();",
156
cleanup="pyg_gil_state_release(__py_state);")
158
for param in self.parameters:
161
assert self.called_pyobj is not None,\
162
"Parameters failed to provide a target function or method."
165
sink.writeln('static %s' % self.return_type.get_c_type())
167
sink.writeln(self.return_type.get_c_type())
168
c_proto_params = map(Parameter.format_for_c_proto, self.parameters)
169
sink.writeln("%s(%s)\n{" % (self.cname, ", ".join(c_proto_params)))
171
self.return_type.write_decl()
172
self.add_declaration("PyObject *py_retval;")
174
## Handle number of arguments
175
if self.pyargv_items:
176
self.add_declaration("PyObject *py_args;")
178
if self.pyargv_optional_items:
179
self.add_declaration("int argc = %i;" % len(self.pyargv_items))
181
for arg in self.pyargv_optional_items:
182
self.body.writeln("if (%s)" % arg)
184
self.body.writeln("++argc;")
187
argc = str(len(self.pyargv_items))
189
if self.pyargv_optional_items:
190
self.add_declaration("PyObject *py_args;")
192
self.add_declaration("int argc = 0;")
194
for arg in self.pyargv_optional_items:
195
self.body.writeln("if (%s)" % arg)
197
self.body.writeln("++argc;")
205
if py_args != "NULL":
206
self.write_code("py_args = PyTuple_New(%s);" % argc,
207
cleanup="Py_DECREF(py_args);")
209
for arg in self.pyargv_items:
210
try: # try to remove the Py_DECREF cleanup action, if we can
211
self.cleanup_actions.remove("Py_DECREF(%s);" % arg)
212
except ValueError: # otherwise we have to Py_INCREF..
213
self.body.writeln("Py_INCREF(%s);" % arg)
214
self.body.writeln("PyTuple_SET_ITEM(%s, %i, %s);" % (py_args, pos, arg))
216
for arg in self.pyargv_optional_items:
217
self.body.writeln("if (%s) {" % arg)
219
try: # try to remove the Py_DECREF cleanup action, if we can
220
self.cleanup_actions.remove("Py_XDECREF(%s);" % arg)
221
except ValueError: # otherwise we have to Py_INCREF..
222
self.body.writeln("Py_INCREF(%s);" % arg)
223
self.body.writeln("PyTuple_SET_ITEM(%s, %i, %s);" % (py_args, pos, arg))
225
self.body.writeln("}")
231
if self.method_name is None:
232
self.write_code("py_retval = PyObject_Call(%s, %s);"
233
% (self.called_pyobj, py_args),
234
cleanup="Py_DECREF(py_retval);",
235
failure_expression="!py_retval")
237
self.add_declaration("PyObject *py_method;")
238
self.write_code("py_method = PyObject_GetAttrString(%s, \"%s\");"
239
% (self.called_pyobj, self.method_name),
240
cleanup="Py_DECREF(py_method);",
241
failure_expression="!py_method")
242
self.write_code("py_retval = PyObject_CallObject(py_method, %s);"
244
cleanup="Py_DECREF(py_retval);",
245
failure_expression="!py_retval")
247
self.return_type.write_conversion()
250
self.declarations.flush_to(sink)
252
self.body.flush_to(sink)
254
for cleanup_action in self.cleanup_actions:
255
sink.writeln(cleanup_action)
256
if self.return_type.get_c_type() != 'void':
258
sink.writeln("return retval;")
262
class TypeHandler(object):
263
def __init__(self, wrapper, **props):
264
assert isinstance(wrapper, ReverseWrapper)
265
self.wrapper = wrapper
268
class ReturnType(TypeHandler):
270
def get_c_type(self):
271
raise NotImplementedError
273
def write_decl(self):
274
raise NotImplementedError
276
def write_error_return(self):
277
'''Write "return <value>" code in case of error'''
278
raise NotImplementedError
280
def write_conversion(self):
281
'''Writes code to convert Python return value in 'py_retval'
282
into C 'retval'. Returns a string with C boolean expression
283
that determines if anything went wrong. '''
284
raise NotImplementedError
286
class Parameter(TypeHandler):
288
def __init__(self, wrapper, name, **props):
289
TypeHandler.__init__(self, wrapper, **props)
292
def get_c_type(self):
293
raise NotImplementedError
295
def convert_c2py(self):
296
'''Write some code before calling the Python method.'''
299
def format_for_c_proto(self):
300
return join_ctype_name(self.get_c_type(), self.name)
304
class StringParam(Parameter):
306
def get_c_type(self):
307
return self.props.get('c_type', 'char *').replace('const-', 'const ')
309
def convert_c2py(self):
310
if self.props.get('optional', False):
311
self.wrapper.add_declaration("PyObject *py_%s = NULL;" % self.name)
312
self.wrapper.write_code(code=("if (%s)\n"
313
" py_%s = PyString_FromString(%s);\n"
314
% (self.name, self.name, self.name)),
315
cleanup=("Py_XDECREF(py_%s);" % self.name))
316
self.wrapper.add_pyargv_item("py_%s" % self.name, optional=True)
318
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
319
self.wrapper.write_code(code=("py_%s = PyString_FromString(%s);" %
320
(self.name, self.name)),
321
cleanup=("Py_DECREF(py_%s);" % self.name),
322
failure_expression=("!py_%s" % self.name))
323
self.wrapper.add_pyargv_item("py_%s" % self.name)
325
for ctype in ('char*', 'gchar*', 'const-char*', 'char-const*', 'const-gchar*',
326
'gchar-const*', 'string', 'static_string'):
327
argtypes.matcher.register_reverse(ctype, StringParam)
330
class StringReturn(ReturnType):
332
def get_c_type(self):
335
def write_decl(self):
336
self.wrapper.add_declaration("char *retval;")
338
def write_error_return(self):
339
self.wrapper.write_code("return NULL;")
341
def write_conversion(self):
342
self.wrapper.write_code(
344
failure_expression="!PyString_Check(py_retval)",
345
failure_cleanup='PyErr_SetString(PyExc_TypeError, "retval should be a string");')
346
self.wrapper.write_code("retval = g_strdup(PyString_AsString(py_retval));")
348
for ctype in ('char*', 'gchar*'):
349
argtypes.matcher.register_reverse(ctype, StringReturn)
353
class VoidReturn(ReturnType):
355
def get_c_type(self):
358
def write_decl(self):
361
def write_error_return(self):
362
self.wrapper.write_code("return;")
364
def write_conversion(self):
365
self.wrapper.write_code(
367
failure_expression="py_retval != Py_None",
368
failure_cleanup='PyErr_SetString(PyExc_TypeError, "retval should be None");')
370
argtypes.matcher.register_reverse_ret('void', VoidReturn)
371
argtypes.matcher.register_reverse_ret('none', VoidReturn)
373
class GObjectParam(Parameter):
375
def get_c_type(self):
376
return self.props.get('c_type', 'GObject *')
378
def convert_c2py(self):
379
self.wrapper.add_declaration("PyObject *py_%s = NULL;" % self.name)
380
self.wrapper.write_code(code=("if (%s)\n"
381
" py_%s = pygobject_new((GObject *) %s);\n"
383
" Py_INCREF(Py_None);\n"
384
" py_%s = Py_None;\n"
386
% (self.name, self.name, self.name, self.name)),
387
cleanup=("Py_DECREF(py_%s);" % self.name))
388
self.wrapper.add_pyargv_item("py_%s" % self.name)
390
argtypes.matcher.register_reverse('GObject*', GObjectParam)
392
class GObjectReturn(ReturnType):
394
def get_c_type(self):
395
return self.props.get('c_type', 'GObject *')
397
def write_decl(self):
398
self.wrapper.add_declaration("%s retval;" % self.get_c_type())
400
def write_error_return(self):
401
self.wrapper.write_code("return NULL;")
403
def write_conversion(self):
404
self.wrapper.write_code("retval = (%s) pygobject_get(py_retval);"
406
self.wrapper.write_code("g_object_ref((GObject *) retval);")
408
argtypes.matcher.register_reverse_ret('GObject*', GObjectReturn)
412
class IntParam(Parameter):
414
def get_c_type(self):
415
return self.props.get('c_type', 'int')
417
def convert_c2py(self):
418
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
419
self.wrapper.write_code(code=("py_%s = PyInt_FromLong(%s);" %
420
(self.name, self.name)),
421
cleanup=("Py_DECREF(py_%s);" % self.name))
422
self.wrapper.add_pyargv_item("py_%s" % self.name)
424
class IntReturn(ReturnType):
425
def get_c_type(self):
426
return self.props.get('c_type', 'int')
427
def write_decl(self):
428
self.wrapper.add_declaration("%s retval;" % self.get_c_type())
429
def write_error_return(self):
430
self.wrapper.write_code("return -G_MAXINT;")
431
def write_conversion(self):
432
self.wrapper.write_code(
434
failure_expression="!PyInt_Check(py_retval)",
435
failure_cleanup='PyErr_SetString(PyExc_TypeError, "retval should be an int");')
436
self.wrapper.write_code("retval = PyInt_AsLong(py_retval);")
438
for argtype in ('int', 'gint', 'guint', 'short', 'gshort', 'gushort', 'long',
439
'glong', 'gsize', 'gssize', 'guint8', 'gint8', 'guint16',
440
'gint16', 'gint32', 'GTime'):
441
argtypes.matcher.register_reverse(argtype, IntParam)
442
argtypes.matcher.register_reverse_ret(argtype, IntReturn)
445
class GEnumReturn(IntReturn):
446
def write_conversion(self):
447
self.wrapper.write_code(
449
failure_expression=("pyg_enum_get_value(%s, py_retval, (gint *)&retval)" %
450
self.props['typecode']))
452
argtypes.matcher.register_reverse_ret("GEnum", GEnumReturn)
454
class GEnumParam(IntParam):
455
def convert_c2py(self):
456
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
457
self.wrapper.write_code(code=("py_%s = pyg_enum_from_gtype(%s, %s);" %
458
(self.name, self.props['typecode'], self.name)),
459
cleanup=("Py_DECREF(py_%s);" % self.name),
460
failure_expression=("!py_%s" % self.name))
461
self.wrapper.add_pyargv_item("py_%s" % self.name)
463
argtypes.matcher.register_reverse("GEnum", GEnumParam)
465
class GFlagsReturn(IntReturn):
466
def write_conversion(self):
467
self.wrapper.write_code(
469
failure_expression=("pyg_flags_get_value(%s, py_retval, (gint *)&retval)" %
470
self.props['typecode']))
472
argtypes.matcher.register_reverse_ret("GFlags", GFlagsReturn)
474
class GFlagsParam(IntParam):
475
def convert_c2py(self):
476
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
477
self.wrapper.write_code(code=("py_%s = pyg_flags_from_gtype(%s, %s);" %
478
(self.name, self.props['typecode'], self.name)),
479
cleanup=("Py_DECREF(py_%s);" % self.name),
480
failure_expression=("!py_%s" % self.name))
481
self.wrapper.add_pyargv_item("py_%s" % self.name)
483
argtypes.matcher.register_reverse("GFlags", GFlagsParam)
486
class GtkTreePathParam(IntParam):
487
def convert_c2py(self):
488
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
489
self.wrapper.write_code(code=("py_%s = pygtk_tree_path_to_pyobject(%s);" %
490
(self.name, self.name)),
491
cleanup=("Py_DECREF(py_%s);" % self.name),
492
failure_expression=("!py_%s" % self.name))
493
self.wrapper.add_pyargv_item("py_%s" % self.name)
495
argtypes.matcher.register_reverse("GtkTreePath*", GtkTreePathParam)
498
class BooleanReturn(ReturnType):
499
def get_c_type(self):
501
def write_decl(self):
502
self.wrapper.add_declaration("gboolean retval;")
503
def write_error_return(self):
504
self.wrapper.write_code("return FALSE;")
505
def write_conversion(self):
506
self.wrapper.write_code("retval = PyObject_IsTrue(py_retval)? TRUE : FALSE;")
507
argtypes.matcher.register_reverse_ret("gboolean", BooleanReturn)
509
class BooleanParam(Parameter):
510
def get_c_type(self):
512
def convert_c2py(self):
513
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
514
self.wrapper.write_code("py_%s = %s? Py_True : Py_False;"
515
% (self.name, self.name))
516
self.wrapper.add_pyargv_item("py_%s" % self.name)
518
argtypes.matcher.register_reverse("gboolean", BooleanParam)
521
class DoubleParam(Parameter):
522
def get_c_type(self):
523
return self.props.get('c_type', 'gdouble')
524
def convert_c2py(self):
525
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
526
self.wrapper.write_code(code=("py_%s = PyFloat_FromDouble(%s);" %
527
(self.name, self.name)),
528
cleanup=("Py_DECREF(py_%s);" % self.name))
529
self.wrapper.add_pyargv_item("py_%s" % self.name)
531
class DoubleReturn(ReturnType):
532
def get_c_type(self):
533
return self.props.get('c_type', 'gdouble')
534
def write_decl(self):
535
self.wrapper.add_declaration("%s retval;" % self.get_c_type())
536
def write_error_return(self):
537
self.wrapper.write_code("return -G_MAXFLOAT;")
538
def write_conversion(self):
539
self.wrapper.write_code(
541
failure_expression="!PyFloat_AsDouble(py_retval)",
542
failure_cleanup='PyErr_SetString(PyExc_TypeError, "retval should be a float");')
543
self.wrapper.write_code("retval = PyFloat_AsDouble(py_retval);")
545
for argtype in ('float', 'double', 'gfloat', 'gdouble'):
546
argtypes.matcher.register_reverse(argtype, DoubleParam)
547
argtypes.matcher.register_reverse_ret(argtype, DoubleReturn)
550
class GBoxedParam(Parameter):
551
def get_c_type(self):
552
return self.props.get('c_type').replace('const-', 'const ')
553
def convert_c2py(self):
554
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
555
ctype = self.get_c_type()
556
if ctype.startswith('const '):
557
ctype_no_const = ctype[len('const '):]
558
self.wrapper.write_code(
559
code=('py_%s = pyg_boxed_new(%s, (%s) %s, TRUE, TRUE);' %
560
(self.name, self.props['typecode'],
561
ctype_no_const, self.name)),
562
cleanup=("Py_DECREF(py_%s);" % self.name))
564
self.wrapper.write_code(
565
code=('py_%s = pyg_boxed_new(%s, %s, FALSE, FALSE);' %
566
(self.name, self.props['typecode'], self.name)),
567
cleanup=("Py_DECREF(py_%s);" % self.name))
568
self.wrapper.add_pyargv_item("py_%s" % self.name)
570
argtypes.matcher.register_reverse("GBoxed", GBoxedParam)
572
class GBoxedReturn(ReturnType):
573
def get_c_type(self):
574
return self.props.get('c_type')
575
def write_decl(self):
576
self.wrapper.add_declaration("%s retval;" % self.get_c_type())
577
def write_error_return(self):
578
self.wrapper.write_code("return retval;")
579
def write_conversion(self):
580
self.wrapper.write_code(
581
failure_expression=("!pyg_boxed_check(py_retval, %s)" %
582
(self.props['typecode'],)),
583
failure_cleanup=('PyErr_SetString(PyExc_TypeError, "retval should be a %s");'
584
% (self.props['typename'],)))
585
self.wrapper.write_code('retval = pyg_boxed_get(py_retval, %s);' %
586
self.props['typename'])
588
argtypes.matcher.register_reverse_ret("GBoxed", GBoxedReturn)
591
class GdkRectanglePtrParam(Parameter):
592
def get_c_type(self):
593
return self.props.get('c_type').replace('const-', 'const ')
594
def convert_c2py(self):
595
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
596
self.wrapper.write_code(
597
code=('py_%(name)s = Py_BuildValue("(ffff)", %(name)s->x, %(name)s->y,\n'
598
' %(name)s->width, %(name)s->height);'
599
% dict(name=self.name)),
600
cleanup=("Py_DECREF(py_%s);" % self.name))
601
self.wrapper.add_pyargv_item("py_%s" % self.name)
603
argtypes.matcher.register_reverse("GdkRectangle*", GdkRectanglePtrParam)
606
class PyGObjectMethodParam(Parameter):
607
def __init__(self, wrapper, name, method_name, **props):
608
Parameter.__init__(self, wrapper, name, **props)
609
self.method_name = method_name
611
def get_c_type(self):
612
return self.props.get('c_type', 'GObject *')
614
def convert_c2py(self):
615
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
616
self.wrapper.write_code(code=("py_%s = pygobject_new((GObject *) %s);" %
617
(self.name, self.name)),
618
cleanup=("Py_DECREF(py_%s);" % self.name),
619
failure_expression=("!py_%s" % self.name))
620
self.wrapper.set_call_target("py_%s" % self.name, self.method_name)
622
class CallbackInUserDataParam(Parameter):
623
def __init__(self, wrapper, name, free_it, **props):
624
Parameter.__init__(self, wrapper, name, **props)
625
self.free_it = free_it
627
def get_c_type(self):
630
def convert_c2py(self):
631
self.wrapper.add_declaration("PyObject **_user_data;")
632
cleanup = self.free_it and ("g_free(%s);" % self.name) or None
633
self.wrapper.write_code(code=("_real_user_data = (PyObject **) %s;"
637
self.wrapper.add_declaration("PyObject *py_func;")
638
cleanup = self.free_it and "Py_DECREF(py_func);" or None
639
self.wrapper.write_code(code="py_func = _user_data[0];",
641
self.wrapper.set_call_target("py_func")
643
self.wrapper.add_declaration("PyObject *py_user_data;")
644
cleanup = self.free_it and "Py_XDECREF(py_user_data);" or None
645
self.wrapper.write_code(code="py_user_data = _user_data[1];",
647
self.wrapper.add_pyargv_item("py_user_data", optional=True)
652
wrapper = ReverseWrapper("this_is_the_c_function_name", is_static=True)
653
wrapper.set_return_type(StringReturn(wrapper))
654
wrapper.add_parameter(PyGObjectMethodParam(wrapper, "self", method_name="do_xxx"))
655
wrapper.add_parameter(StringParam(wrapper, "param2", optional=True))
656
wrapper.add_parameter(GObjectParam(wrapper, "param3"))
657
wrapper.generate(FileCodeSink(sys.stderr))
659
wrapper = ReverseWrapper("this_a_callback_wrapper")
660
wrapper.set_return_type(VoidReturn(wrapper))
661
wrapper.add_parameter(StringParam(wrapper, "param1", optional=False))
662
wrapper.add_parameter(GObjectParam(wrapper, "param2"))
663
wrapper.add_parameter(CallbackInUserDataParam(wrapper, "data", free_it=True))
664
wrapper.generate(FileCodeSink(sys.stderr))
666
if __name__ == '__main__':