pyges: Sync codegen with upstream

This commit is contained in:
Thibault Saunier 2011-06-07 15:20:46 -04:00
parent 20db3b7e78
commit 90f23b9a47
12 changed files with 1835 additions and 1217 deletions

View file

@ -8,6 +8,7 @@ __all__ = [
'docextract',
'docgen',
'h2def',
'defsgen'
'mergedefs',
'mkskel',
'override',

View file

@ -3,6 +3,18 @@ import string
import keyword
import struct
py_ssize_t_clean = False
class ArgTypeError(Exception):
pass
class ArgTypeNotFoundError(ArgTypeError):
pass
class ArgTypeConfigurationError(ArgTypeError):
pass
class VarList:
"""Nicely format a C variable list"""
def __init__(self):
@ -64,17 +76,17 @@ class WrapperInfo:
self.kwlist.append('"%s"' % kw)
class ArgType:
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
"""Add code to the WrapperInfo instance to handle
parameter."""
raise RuntimeError, "write_param not implemented for %s" % \
self.__class__.__name__
raise RuntimeError("write_param not implemented for %s"
% self.__class__.__name__)
def write_return(self, ptype, ownsreturn, info):
"""Adds a variable named ret of the return type to
info.varlist, and add any required code to info.codeafter to
convert the return value to a python object."""
raise RuntimeError, "write_return not implemented for %s" % \
self.__class__.__name__
raise RuntimeError("write_return not implemented for %s"
% self.__class__.__name__)
class NoneArg(ArgType):
def write_return(self, ptype, ownsreturn, info):
@ -82,8 +94,8 @@ class NoneArg(ArgType):
' return Py_None;')
class StringArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
if pdflt:
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt != None:
if pdflt != 'NULL': pdflt = '"' + pdflt + '"'
info.varlist.add('char', '*' + pname + ' = ' + pdflt)
else:
@ -113,12 +125,15 @@ class StringArg(ArgType):
class UCharArg(ArgType):
# allows strings with embedded NULLs.
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add('guchar', '*' + pname + ' = "' + pdflt + '"')
else:
info.varlist.add('guchar', '*' + pname)
info.varlist.add('int', pname + '_len')
if py_ssize_t_clean:
info.varlist.add('Py_ssize_t', pname + '_len')
else:
info.varlist.add('int', pname + '_len')
info.arglist.append(pname)
if pnull:
info.add_parselist('z#', ['&' + pname, '&' + pname + '_len'],
@ -128,7 +143,7 @@ class UCharArg(ArgType):
[pname])
class CharArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add('char', pname + " = '" + pdflt + "'")
else:
@ -147,7 +162,7 @@ class GUniCharArg(ArgType):
'#endif\n'
' py_ret = (Py_UNICODE)ret;\n'
' return PyUnicode_FromUnicode(&py_ret, 1);\n')
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add('gunichar', pname + " = '" + pdflt + "'")
else:
@ -161,7 +176,7 @@ class GUniCharArg(ArgType):
class IntArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add('int', pname + ' = ' + pdflt)
else:
@ -191,7 +206,7 @@ class UIntArg(ArgType):
' PyErr_SetString(PyExc_TypeError, "Parameter \'%(name)s\' must be an int or a long");\n'
' if (PyErr_Occurred())\n'
' return NULL;\n')
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if not pdflt:
pdflt = '0';
@ -211,7 +226,7 @@ class SizeArg(ArgType):
else:
llp64 = False
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add(ptype, pname + ' = ' + pdflt)
else:
@ -235,7 +250,7 @@ class SSizeArg(ArgType):
else:
llp64 = False
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add(ptype, pname + ' = ' + pdflt)
else:
@ -253,7 +268,7 @@ class SSizeArg(ArgType):
info.codeafter.append(' return PyLong_FromLong(ret);\n')
class LongArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add(ptype, pname + ' = ' + pdflt)
else:
@ -270,7 +285,7 @@ class BoolArg(IntArg):
info.codeafter.append(' return PyBool_FromLong(ret);\n')
class TimeTArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add('time_t', pname + ' = ' + pdflt)
else:
@ -282,7 +297,7 @@ class TimeTArg(ArgType):
info.codeafter.append(' return PyInt_FromLong(ret);')
class ULongArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add('unsigned long', pname + ' = ' + pdflt)
else:
@ -294,8 +309,8 @@ class ULongArg(ArgType):
info.codeafter.append(' return PyLong_FromUnsignedLong(ret);\n')
class UInt32Arg(ULongArg):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
ULongArg.write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info)
def write_param(self, ptype, pname, pdflt, pnull, info):
ULongArg.write_param(self, ptype, pname, pdflt, pnull, info)
## if sizeof(unsigned long) > sizeof(unsigned int), we need to
## check the value is within guint32 range
if struct.calcsize('L') > struct.calcsize('I'):
@ -308,7 +323,7 @@ class UInt32Arg(ULongArg):
' }\n') % vars())
class Int64Arg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add('gint64', pname + ' = ' + pdflt)
else:
@ -320,20 +335,26 @@ class Int64Arg(ArgType):
info.codeafter.append(' return PyLong_FromLongLong(ret);')
class UInt64Arg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
dflt = ' if (py_%(name)s)\n' \
' %(name)s = PyLong_AsUnsignedLongLong(py_%(name)s);\n'
before = ' %(name)s = PyLong_AsUnsignedLongLong(py_%(name)s);\n'
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add('guint64', pname + ' = ' + pdflt)
info.codebefore.append(self.dflt % {'name':pname})
else:
info.varlist.add('guint64', pname)
info.codebefore.append(self.before % {'name':pname})
info.varlist.add('PyObject', "*py_" + pname + ' = NULL')
info.arglist.append(pname)
info.add_parselist('K', ['&' + pname], [pname])
info.add_parselist('O!', ['&PyLong_Type', '&py_' + pname], [pname])
def write_return(self, ptype, ownsreturn, info):
info.varlist.add('guint64', 'ret')
info.codeafter.append(' return PyLong_FromUnsignedLongLong(ret);')
class DoubleArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add('double', pname + ' = ' + pdflt)
else:
@ -361,7 +382,7 @@ class FileArg(ArgType):
' }\n')
dflt = (' if (py_%(name)s)\n'
' %(name)s = PyFile_AsFile(py_%(name)s);\n')
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pnull:
if pdflt:
info.varlist.add('FILE', '*' + pname + ' = ' + pdflt)
@ -391,12 +412,12 @@ class FileArg(ArgType):
' return Py_None;')
class EnumArg(ArgType):
enum = (' if (pyg_enum_get_value(%(typecode)s, py_%(name)s, (gint *)&%(name)s))\n'
enum = (' if (pyg_enum_get_value(%(typecode)s, py_%(name)s, (gpointer)&%(name)s))\n'
' return NULL;\n')
def __init__(self, enumname, typecode):
self.enumname = enumname
self.typecode = typecode
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add(self.enumname, pname + ' = ' + pdflt)
else:
@ -411,12 +432,12 @@ class EnumArg(ArgType):
info.codeafter.append(' return pyg_enum_from_gtype(%s, ret);' % self.typecode)
class FlagsArg(ArgType):
flag = (' if (%(default)spyg_flags_get_value(%(typecode)s, py_%(name)s, (gint *)&%(name)s))\n'
flag = (' if (%(default)spyg_flags_get_value(%(typecode)s, py_%(name)s, (gpointer)&%(name)s))\n'
' return NULL;\n')
def __init__(self, flagname, typecode):
self.flagname = flagname
self.typecode = typecode
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add(self.flagname, pname + ' = ' + pdflt)
default = "py_%s && " % (pname,)
@ -456,7 +477,7 @@ class ObjectArg(ArgType):
self.objname = objname
self.cast = string.replace(typecode, '_TYPE_', '_', 1)
self.parent = parent
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pnull:
if pdflt:
info.varlist.add(self.objname, '*' + pname + ' = ' + pdflt)
@ -506,99 +527,14 @@ class ObjectArg(ArgType):
info.varlist.add(typename, '*ret')
if ownsreturn:
info.varlist.add('PyObject', '*py_ret')
# < GLib 2.8: using our custom _new and _unref functions
# makes sure we update the proper GstObject refcount
info.codeafter.append(' py_ret = pygobject_new((GObject *)ret);\n'
' if (ret != NULL)\n'
' g_object_unref((GObject *)ret);\n'
' g_object_unref(ret);\n'
' return py_ret;')
else:
info.codeafter.append(' /* pygobject_new handles NULL checking */\n' +
' return pygobject_new((GObject *)ret);')
class MiniObjectArg(ArgType):
# should change these checks to more typesafe versions that check
# a little further down in the class heirachy.
nulldflt = (' if ((PyObject *)py_%(name)s == Py_None)\n'
' %(name)s = NULL;\n'
' else if (py_%(name)s) && pygstminiobject_check(py_%(name)s, &Py%(type)s_Type))\n'
' %(name)s = %(cast)s(py_%(name)s->obj);\n'
' else if (py_%(name)s) {\n'
' PyErr_SetString(PyExc_TypeError, "%(name)s should be a %(type)s or None");\n'
' return NULL;\n'
' }\n')
null = (' if (py_%(name)s && pygstminiobject_check(py_%(name)s, &Py%(type)s_Type))\n'
' %(name)s = %(cast)s(py_%(name)s->obj);\n'
' else if ((PyObject *)py_%(name)s != Py_None) {\n'
' PyErr_SetString(PyExc_TypeError, "%(name)s should be a %(type)s or None");\n'
' return NULL;\n'
' }\n')
dflt = ' if (py_%(name)s)\n' \
' %(name)s = %(cast)s(py_%(name)s->obj);\n'
def __init__(self, objname, parent, typecode):
self.objname = objname
self.cast = string.replace(typecode, '_TYPE_', '_', 1)
self.parent = parent
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
if pnull:
if pdflt:
info.varlist.add(self.objname, '*' + pname + ' = ' + pdflt)
info.varlist.add('PyGstMiniObject', '*py_' + pname + ' = NULL')
info.codebefore.append(self.nulldflt % {'name':pname,
'cast':self.cast,
'type':self.objname})
else:
info.varlist.add(self.objname, '*' + pname + ' = NULL')
info.varlist.add('PyGstMiniObject', '*py_' + pname)
info.codebefore.append(self.null % {'name':pname,
'cast':self.cast,
'type':self.objname})
if ptype.endswith('*'):
typename = ptype[:-1]
try:
const, typename = typename.split('const-')
except ValueError:
const = ''
if typename != ptype:
info.arglist.append('(%s *) %s' % (ptype[:-1], pname))
else:
info.arglist.append(pname)
info.add_parselist('O', ['&py_' + pname], [pname])
else:
if pdflt:
info.varlist.add(self.objname, '*' + pname + ' = ' + pdflt)
info.varlist.add('PyGstMiniObject', '*py_' + pname + ' = NULL')
info.codebefore.append(self.dflt % {'name':pname,
'cast':self.cast})
info.arglist.append(pname)
info.add_parselist('O', ['&Py%s_Type' % self.objname,
'&py_' + pname], [pname])
else:
info.varlist.add('PyGstMiniObject', '*' + pname)
info.arglist.append('%s(%s->obj)' % (self.cast, pname))
info.add_parselist('O!', ['&Py%s_Type' % self.objname,
'&' + pname], [pname])
if keeprefcount:
info.codebefore.append(' gst_mini_object_ref(GST_MINI_OBJECT(%s->obj));\n' % pname)
def write_return(self, ptype, ownsreturn, info):
if ptype.endswith('*'):
typename = ptype[:-1]
try:
const, typename = typename.split('const-')
except ValueError:
const = ''
info.varlist.add(typename, '*ret')
if ownsreturn:
info.varlist.add('PyObject', '*py_ret')
info.codeafter.append(' py_ret = pygstminiobject_new((GstMiniObject *)ret);\n'
' if (ret != NULL)\n'
' gst_mini_object_unref((GstMiniObject *)ret);\n'
' return py_ret;')
else:
info.codeafter.append(' /* pygobject_new handles NULL checking */\n' +
' return pygstminiobject_new((GstMiniObject *)ret);')
class BoxedArg(ArgType):
# haven't done support for default args. Is it needed?
check = (' if (pyg_boxed_check(py_%(name)s, %(typecode)s))\n'
@ -616,7 +552,7 @@ class BoxedArg(ArgType):
def __init__(self, ptype, typecode):
self.typename = ptype
self.typecode = typecode
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pnull:
info.varlist.add(self.typename, '*' + pname + ' = NULL')
info.varlist.add('PyObject', '*py_' + pname + ' = Py_None')
@ -643,8 +579,12 @@ class BoxedArg(ArgType):
' return pyg_boxed_new(%(typecode)s, %(ret)s, %(copy)s, TRUE);'
def write_return(self, ptype, ownsreturn, info):
if ptype[-1] == '*':
info.varlist.add(self.typename, '*ret')
decl_type = self.typename
ret = 'ret'
if ptype[:6] == 'const-':
decl_type = 'const ' + self.typename
ret = '(%s*) ret' % (self.typename,)
info.varlist.add(decl_type, '*ret')
else:
info.varlist.add(self.typename, 'ret')
ret = '&ret'
@ -667,7 +607,7 @@ class CustomBoxedArg(ArgType):
self.getter = getter
self.checker = 'Py' + ptype + '_Check'
self.new = new
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pnull:
info.varlist.add(ptype[:-1], '*' + pname + ' = NULL')
info.varlist.add('PyObject', '*py_' + pname + ' = Py_None')
@ -705,7 +645,7 @@ class PointerArg(ArgType):
def __init__(self, ptype, typecode):
self.typename = ptype
self.typecode = typecode
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pnull:
info.varlist.add(self.typename, '*' + pname + ' = NULL')
info.varlist.add('PyObject', '*py_' + pname + ' = Py_None')
@ -739,7 +679,7 @@ class AtomArg(IntArg):
atom = (' %(name)s = pygdk_atom_from_pyobject(py_%(name)s);\n'
' if (PyErr_Occurred())\n'
' return NULL;\n')
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt:
info.varlist.add('GdkAtom', pname + ' = ' + pdflt)
info.varlist.add('PyObject', '*py_' + pname + ' = NULL')
@ -762,7 +702,7 @@ class AtomArg(IntArg):
class GTypeArg(ArgType):
gtype = (' if ((%(name)s = pyg_type_from_object(py_%(name)s)) == 0)\n'
' return NULL;\n')
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
info.varlist.add('GType', pname)
info.varlist.add('PyObject', '*py_' + pname + ' = NULL')
info.codebefore.append(self.gtype % {'name': pname})
@ -776,7 +716,7 @@ class GTypeArg(ArgType):
class GErrorArg(ArgType):
handleerror = (' if (pyg_error_check(&%(name)s))\n'
' return NULL;\n')
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
info.varlist.add('GError', '*' + pname + ' = NULL')
info.arglist.append('&' + pname)
info.codeafter.append(self.handleerror % { 'name': pname })
@ -799,7 +739,7 @@ class GtkTreePathArg(ArgType):
' gtk_tree_path_free(%(name)s);\n')
def __init__(self):
pass
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pnull:
info.varlist.add('GtkTreePath', '*' + pname + ' = NULL')
info.varlist.add('PyObject', '*py_' + pname + ' = Py_None')
@ -840,7 +780,7 @@ class GdkRectanglePtrArg(ArgType):
' %(name)s = &%(name)s_rect;\n'
' else\n'
' return NULL;\n')
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
if pnull:
info.varlist.add('GdkRectangle', pname + '_rect = { 0, 0, 0, 0 }')
info.varlist.add('GdkRectangle', '*' + pname)
@ -861,7 +801,7 @@ class GdkRectangleArg(ArgType):
info.codeafter.append(' return pyg_boxed_new(GDK_TYPE_RECTANGLE, &ret, TRUE, TRUE);')
class PyObjectArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info):
def write_param(self, ptype, pname, pdflt, pnull, info):
info.varlist.add('PyObject', '*' + pname)
info.add_parselist('O', ['&' + pname], [pname])
info.arglist.append(pname)
@ -878,13 +818,29 @@ class PyObjectArg(ArgType):
' Py_INCREF(ret);\n'
' return ret;')
class CairoArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, info):
info.varlist.add('PycairoContext', '*' + pname)
info.add_parselist('O!', ['&PycairoContext_Type', '&' + pname], [pname])
info.arglist.append('%s->ctx' % pname)
def write_return(self, ptype, ownsreturn, info):
info.varlist.add("cairo_t", "*ret")
if ownsreturn:
info.codeafter.append(' return PycairoContext_FromContext(ret, NULL, NULL);')
else:
info.codeafter.append(' cairo_reference(ret);\n'
' return PycairoContext_FromContext(ret, NULL, NULL);')
class ArgMatcher:
def __init__(self):
self.argtypes = {}
self.reverse_argtypes = {}
self.reverse_rettypes = {}
def register(self, ptype, handler):
def register(self, ptype, handler, overwrite=False):
if not overwrite and ptype in self.argtypes:
return
self.argtypes[ptype] = handler
def register_reverse(self, ptype, handler):
self.reverse_argtypes[ptype] = handler
@ -893,12 +849,14 @@ class ArgMatcher:
def register_enum(self, ptype, typecode):
if typecode is None:
typecode = "G_TYPE_NONE"
self.register(ptype, EnumArg(ptype, typecode))
self.register(ptype, IntArg())
else:
self.register(ptype, EnumArg(ptype, typecode))
def register_flag(self, ptype, typecode):
if typecode is None:
typecode = "G_TYPE_NONE"
self.register(ptype, FlagsArg(ptype, typecode))
self.register(ptype, IntArg())
else:
self.register(ptype, FlagsArg(ptype, typecode))
def register_object(self, ptype, parent, typecode):
oa = ObjectArg(ptype, parent, typecode)
self.register(ptype, oa) # in case I forget the * in the .defs
@ -908,10 +866,6 @@ class ArgMatcher:
# hack to handle GdkBitmap synonym.
self.register('GdkBitmap', oa)
self.register('GdkBitmap*', oa)
def register_miniobject(self, ptype, parent, typecode):
oa = MiniObjectArg(ptype, parent, typecode)
self.register(ptype, oa) # in case I forget the * in the .defs
self.register(ptype+'*', oa)
def register_boxed(self, ptype, typecode):
if self.argtypes.has_key(ptype): return
arg = BoxedArg(ptype, typecode)
@ -934,7 +888,7 @@ class ArgMatcher:
except KeyError:
if ptype[:8] == 'GdkEvent' and ptype[-1] == '*':
return self.argtypes['GdkEvent*']
raise
raise ArgTypeNotFoundError("No ArgType for %s" % (ptype,))
def _get_reverse_common(self, ptype, registry):
props = dict(c_type=ptype)
try:
@ -946,7 +900,7 @@ class ArgMatcher:
if ptype.startswith('GdkEvent') and ptype.endswith('*'):
handler = self.argtypes['GdkEvent*']
else:
raise
raise ArgTypeNotFoundError("No ArgType for %s" % (ptype,))
if isinstance(handler, ObjectArg):
return registry['GObject*'], props
elif isinstance(handler, EnumArg):
@ -962,11 +916,20 @@ class ArgMatcher:
props['typename'] = handler.typename
return registry['GBoxed'], props
else:
raise
raise ArgTypeNotFoundError("No ArgType for %s" % (ptype,))
def get_reverse(self, ptype):
return self._get_reverse_common(ptype, self.reverse_argtypes)
def get_reverse_ret(self, ptype):
return self._get_reverse_common(ptype, self.reverse_rettypes)
ret, props = self._get_reverse_common(ptype, self.reverse_rettypes)
if hasattr(ptype, 'optional') and ptype.optional:
if ret.supports_optional:
props['optional'] = True
else:
raise ArgTypeNotFoundError("Unsupported 'optional' for %s"
% (ptype,))
return ret, props
def object_is_a(self, otype, parent):
if otype == None: return 0
@ -993,6 +956,7 @@ matcher.register('static_string', arg)
arg = UCharArg()
matcher.register('unsigned-char*', arg)
matcher.register('const-guchar*', arg)
matcher.register('const-guint8*', arg)
matcher.register('guchar*', arg)
arg = CharArg()
@ -1017,6 +981,7 @@ matcher.register('guint16', arg)
matcher.register('gint16', arg)
matcher.register('gint32', arg)
matcher.register('GTime', arg)
matcher.register('GSeekType', arg) # Hack, but we have no python wrapper
arg = LongArg()
matcher.register('long', arg)
@ -1039,6 +1004,7 @@ matcher.register('gulong', arg)
arg = Int64Arg()
matcher.register('gint64', arg)
matcher.register('long-long', arg)
matcher.register('goffset', arg)
arg = UInt64Arg()
matcher.register('guint64', arg)
@ -1070,6 +1036,8 @@ matcher.register('PyObject*', PyObjectArg())
matcher.register('GdkNativeWindow', ULongArg())
matcher.register_object('GObject', None, 'G_TYPE_OBJECT')
matcher.register_miniobject('GstMiniObject', None, 'GST_TYPE_MINI_OBJECT')
del arg
matcher.register('cairo_t*', CairoArg())
matcher.register_boxed("GClosure", "G_TYPE_CLOSURE")

View file

@ -1,3 +1,5 @@
#! /usr/bin/env python
from __future__ import generators
import sys, os

824
bindings/python/codegen/codegen.py Normal file → Executable file

File diff suppressed because it is too large Load diff

View file

@ -14,14 +14,13 @@ def make_docstring(lines):
# New Parameter class, wich emulates a tuple for compatibility reasons
class Parameter(object):
def __init__(self, ptype, pname, pdflt, pnull, pdir=None, keeprefcount = False):
def __init__(self, ptype, pname, pdflt, pnull, pdir=None):
self.ptype = ptype
self.pname = pname
self.pdflt = pdflt
self.pnull = pnull
self.pdir = pdir
self.keeprefcount = keeprefcount
self.pdir = pdir
def __len__(self): return 4
def __getitem__(self, i):
return (self.ptype, self.pname, self.pdflt, self.pnull)[i]
@ -32,6 +31,17 @@ class Parameter(object):
if old.pnull is not None:
self.pnull = old.pnull
# We currently subclass 'str' to make impact on the rest of codegen as
# little as possible. Later we can subclass 'object' instead, but
# then we must find and adapt all places which expect return types to
# be strings.
class ReturnType(str):
def __new__(cls, *args, **kwds):
return str.__new__(cls, *args[:1])
def __init__(self, type_name, optional=False):
str.__init__(self)
self.optional = optional
# Parameter for property based constructors
class Property(object):
def __init__(self, pname, optional, argname):
@ -39,6 +49,10 @@ class Property(object):
self.optional = optional
self.argname = argname
def __len__(self): return 4
def __getitem__(self, i):
return ('', self.pname, self.optional, self.argname)[i]
def merge(self, old):
if old.optional is not None:
self.optional = old.optional
@ -46,18 +60,26 @@ class Property(object):
self.argname = old.argname
class Definition:
class Definition(object):
docstring = "NULL"
def py_name(self):
return '%s.%s' % (self.module, self.name)
py_name = property(py_name)
def __init__(self, *args):
"""Create a new defs object of this type. The arguments are the
components of the definition"""
raise RuntimeError, "this is an abstract class"
raise RuntimeError("this is an abstract class")
def merge(self, old):
"""Merge in customisations from older version of definition"""
raise RuntimeError, "this is an abstract class"
raise RuntimeError("this is an abstract class")
def write_defs(self, fp=sys.stdout):
"""write out this definition in defs file format"""
raise RuntimeError, "this is an abstract class"
raise RuntimeError("this is an abstract class")
def guess_return_value_ownership(self):
"return 1 if caller owns return value"
@ -119,58 +141,7 @@ class ObjectDef(Definition):
for (ftype, fname) in self.fields:
fp.write(' \'("' + ftype + '" "' + fname + '")\n')
fp.write(' )\n')
fp.write(')\n\n')
class MiniObjectDef(Definition):
def __init__(self, name, *args):
self.name = name
self.module = None
self.parent = None
self.c_name = None
self.typecode = None
self.fields = []
self.implements = []
for arg in args:
if type(arg) != type(()) or len(arg) < 2:
continue
if arg[0] == 'in-module':
self.module = arg[1]
elif arg[0] == 'parent':
self.parent = arg[1]
elif arg[0] == 'c-name':
self.c_name = arg[1]
elif arg[0] == 'gtype-id':
self.typecode = arg[1]
elif arg[0] == 'fields':
for parg in arg[1:]:
self.fields.append((parg[0], parg[1]))
elif arg[0] == 'implements':
self.implements.append(arg[1])
def merge(self, old):
# currently the .h parser doesn't try to work out what fields of
# an object structure should be public, so we just copy the list
# from the old version ...
self.fields = old.fields
self.implements = old.implements
def write_defs(self, fp=sys.stdout):
fp.write('(define-object ' + self.name + '\n')
if self.module:
fp.write(' (in-module "' + self.module + '")\n')
if self.parent != (None, None):
fp.write(' (parent "' + self.parent + '")\n')
for interface in self.implements:
fp.write(' (implements "' + interface + '")\n')
if self.c_name:
fp.write(' (c-name "' + self.c_name + '")\n')
if self.typecode:
fp.write(' (gtype-id "' + self.typecode + '")\n')
if self.fields:
fp.write(' (fields\n')
for (ftype, fname) in self.fields:
fp.write(' \'("' + ftype + '" "' + fname + '")\n')
fp.write(' )\n')
fp.write(')\n\n')
fp.write(')\n\n')
class InterfaceDef(Definition):
def __init__(self, name, *args):
@ -349,7 +320,12 @@ class MethodDefBase(Definition):
elif arg[0] == 'gtype-id':
self.typecode = arg[1]
elif arg[0] == 'return-type':
self.ret = arg[1]
type_name = arg[1]
optional = False
for prop in arg[2:]:
if prop[0] == 'optional':
optional = True
self.ret = ReturnType(type_name, optional)
elif arg[0] == 'caller-owns-return':
self.caller_owns_return = arg[1] in ('t', '#t')
elif arg[0] == 'unblock-threads':
@ -360,8 +336,7 @@ class MethodDefBase(Definition):
pname = parg[1]
pdflt = None
pnull = 0
pdir = None
keeprefcount = False
pdir = None
for farg in parg[2:]:
assert isinstance(farg, tuple)
if farg[0] == 'default':
@ -370,10 +345,7 @@ class MethodDefBase(Definition):
pnull = 1
elif farg[0] == 'direction':
pdir = farg[1]
elif farg[0] == 'keep-refcount':
keeprefcount = True
self.params.append(Parameter(ptype, pname, pdflt, pnull, pdir,
keeprefcount=keeprefcount))
self.params.append(Parameter(ptype, pname, pdflt, pnull, pdir))
elif arg[0] == 'varargs':
self.varargs = arg[1] in ('t', '#t')
elif arg[0] == 'deprecated':
@ -435,7 +407,7 @@ class MethodDef(MethodDefBase):
for item in ('c_name', 'of_object'):
if self.__dict__[item] == None:
self.write_defs(sys.stderr)
raise RuntimeError, "definition missing required %s" % (item,)
raise RuntimeError("definition missing required %s" % (item,))
def write_defs(self, fp=sys.stdout):
fp.write('(define-method ' + self.name + '\n')
@ -483,17 +455,13 @@ class FunctionDef(Definition):
pname = parg[1]
pdflt = None
pnull = 0
keeprefcount = False
for farg in parg[2:]:
if farg[0] == 'default':
pdflt = farg[1]
elif farg[0] == 'null-ok':
pnull = 1
elif farg[0] == 'keep-refcount':
keeprefcount = True
self.params.append(Parameter(ptype, pname, pdflt, pnull,
keeprefcount = keeprefcount))
elif arg[0] == 'properties':
self.params.append(Parameter(ptype, pname, pdflt, pnull))
elif arg[0] == 'properties':
if self.is_constructor_of is None:
print >> sys.stderr, "Warning: (properties ...) "\
"is only valid for constructors"
@ -523,7 +491,7 @@ class FunctionDef(Definition):
for item in ('c_name',):
if self.__dict__[item] == None:
self.write_defs(sys.stderr)
raise RuntimeError, "definition missing required %s" % (item,)
raise RuntimeError("definition missing required %s" % (item,))
_method_write_defs = MethodDef.__dict__['write_defs']
@ -545,8 +513,8 @@ class FunctionDef(Definition):
else:
param.merge(old_param)
return param
raise RuntimeError, "could not find %s in old_parameters %r" % (
param.pname, [p.pname for p in old.params])
raise RuntimeError("could not find %s in old_parameters %r" % (
param.pname, [p.pname for p in old.params]))
try:
self.params = map(merge_param, self.params)
except RuntimeError:

View file

@ -2,26 +2,40 @@
import os, sys
import scmexpr
from definitions import BoxedDef, EnumDef, FlagsDef, FunctionDef, \
InterfaceDef, MethodDef, ObjectDef, MiniObjectDef, PointerDef, \
VirtualDef
InterfaceDef, MethodDef, ObjectDef, PointerDef, VirtualDef
include_path = ['.']
class IncludeParser(scmexpr.Parser):
"""A simple parser that follows include statements automatically"""
def include(self, filename):
if not os.path.isabs(filename):
filename = os.path.join(os.path.dirname(self.filename), filename)
# set self.filename to the include name, to handle recursive includes
oldfile = self.filename
self.filename = filename
self.startParsing()
self.filename = oldfile
def include(self, input_filename):
global include_path
if os.path.isabs(input_filename):
filename = input_filename
# set self.filename to the include name, to handle recursive includes
oldfile = self.filename
self.filename = filename
self.startParsing()
self.filename = oldfile
else:
inc_path = [os.path.dirname(self.filename)] + include_path
for filename in [os.path.join(path_entry, input_filename)
for path_entry in inc_path]:
if not os.path.exists(filename):
continue
# set self.filename to the include name, to handle recursive includes
oldfile = self.filename
self.filename = filename
self.startParsing()
self.filename = oldfile
break
else:
raise IOError("%s not found in include path %s" % (input_filename, inc_path))
class DefsParser(IncludeParser):
def __init__(self, arg, defines={}):
IncludeParser.__init__(self, arg)
self.objects = []
self.miniobjects = []
IncludeParser.__init__(self, arg)
self.objects = []
self.interfaces = []
self.enums = [] # enums and flags
self.boxes = [] # boxed types
@ -33,13 +47,8 @@ class DefsParser(IncludeParser):
self.defines = defines # -Dfoo=bar options, as dictionary
def define_object(self, *args):
odef = apply(ObjectDef, args)
self.objects.append(odef)
self.c_name[odef.c_name] = odef
# TODO: define_mini_object
def define_miniobject(self, *args):
odef = apply(MiniObjectDef, args)
self.miniobjects.append(odef)
odef = apply(ObjectDef, args)
self.objects.append(odef)
self.c_name[odef.c_name] = odef
def define_interface(self, *args):
idef = apply(InterfaceDef, args)
@ -89,10 +98,7 @@ class DefsParser(IncludeParser):
f.write_defs()
def write_defs(self, fp=sys.stdout):
for obj in self.objects:
obj.write_defs(fp)
# TODO: Add miniobject
for obj in self.miniobjects:
for obj in self.objects:
obj.write_defs(fp)
for enum in self.enums:
enum.write_defs(fp)
@ -108,7 +114,7 @@ class DefsParser(IncludeParser):
if obj.c_name == c_name:
return obj
else:
raise ValueError, 'object not found'
raise ValueError('object %r not found' % c_name)
def find_constructor(self, obj, overrides):
for func in self.functions:
@ -135,7 +141,11 @@ class DefsParser(IncludeParser):
def ifdef(self, *args):
if args[0] in self.defines:
for arg in args[1:]:
#print >> sys.stderr, "-----> Handling conditional definition (%s): %s" % (args[0], arg)
self.handle(arg)
else:
pass
#print >> sys.stderr, "-----> Conditional %s is not true" % (args[0],)
def ifndef(self, *args):
if args[0] not in self.defines:

View file

@ -4,124 +4,399 @@ sources, so I can use them for other purposes.'''
import sys, os, string, re
# Used to tell if the "Since: ..." portion of the gtkdoc function description
# should be omitted. This is useful for some C++ modules such as gstreamermm
# that wrap C API which is still unstable and including this information would
# not be useful.
# This variable is modified from docextract_to_xml based on the --no-since
# option being specified.
no_since = False
__all__ = ['extract']
class FunctionDoc:
class GtkDoc:
def __init__(self):
self.name = None
self.block_type = '' # The block type ('function', 'signal', 'property')
self.params = []
self.annotations = []
self.description = ''
self.ret = ''
self.ret = ('', []) # (return, annotations)
def set_name(self, name):
self.name = name
def add_param(self, name, description):
def set_type(self, block_type):
self.block_type = block_type
def get_type(self):
return self.block_type
def add_param(self, name, description, annotations=[]):
if name == '...':
name = 'Varargs'
self.params.append((name, description))
self.params.append((name, description, annotations))
def append_to_last_param(self, extra):
self.params[-1] = (self.params[-1][0], self.params[-1][1] + extra)
self.params[-1] = (self.params[-1][0], self.params[-1][1] + extra,
self.params[-1][2])
def append_to_named_param(self, name, extra):
for i in range(len(self.params)):
if self.params[i][0] == name:
self.params[i] = (name, self.params[i][1] + extra)
self.params[i] = (name, self.params[i][1] + extra,
self.params[i][2])
return
# fall through to adding extra parameter ...
self.add_param(name, extra)
def append_description(self, extra):
def add_annotation(self, annotation):
self.annotations.append(annotation)
def get_annotations(self):
return self.annotations
def append_to_description(self, extra):
self.description = self.description + extra
def append_return(self, extra):
self.ret = self.ret + extra
def get_description(self):
return self.description
def add_return(self, first_line, annotations=[]):
self.ret = (first_line, annotations)
def append_to_return(self, extra):
self.ret = (self.ret[0] + extra, self.ret[1])
def get_param_description(self, name):
for param, description in self.params:
if param == name:
return description
else:
return ''
comment_start_pattern = re.compile(r'^\s*/\*\*\s')
comment_end_pattern = re.compile(r'^\s*\*+/')
comment_line_lead_pattern = re.compile(r'^\s*\*\s*')
comment_empty_line_pattern = re.compile(r'^\s*\**\s*$')
function_name_pattern = re.compile(r'^([a-z]\w*)\s*:?(\s*\(.*\)\s*){0,2}\s*$')
signal_name_pattern = re.compile(r'^([A-Z]\w+::[a-z0-9-]+)\s*:?(\s*\(.*\)\s*){0,2}\s*$')
property_name_pattern = re.compile(r'^([A-Z]\w+:[a-z0-9-]+)\s*:?(\s*\(.*\)\s*){0,2}\s*$')
return_pattern = re.compile(r'^@?(returns:|return\s+value:)(.*\n?)$', re.IGNORECASE)
deprecated_pattern = re.compile(r'^(deprecated\s*:\s*.*\n?)$', re.IGNORECASE)
rename_to_pattern = re.compile(r'^(rename\s+to)\s*:\s*(.*\n?)$', re.IGNORECASE)
param_pattern = re.compile(r'^@(\S+)\s*:(.*\n?)$')
# Used to extract the annotations in the parameter and return descriptions
# extracted using above [param|return]_pattern patterns.
annotations_pattern = re.compile(r'^(?:(\s*\(.*\)\s*)*:)')
# Used to construct the annotation lists.
annotation_lead_pattern = re.compile(r'^\s*\(\s*(.*?)\s*\)\s*')
comment_start_pat = re.compile(r'^\s*/\*\*\s')
comment_end_pat = re.compile(r'^\s*\*+/')
comment_line_lead = re.compile(r'^\s*\*\s*')
funcname_pat = re.compile(r'^(\w+)\s*:?')
return_pat = re.compile(r'^(returns:|return\s+value:|returns\s*)(.*\n?)$',
re.IGNORECASE)
param_pat = re.compile(r'^@(\S+)\s*:(.*\n?)$')
# These patterns determine the identifier of the current comment block. They
# are grouped in a list for easy determination of block identifiers (in
# skip_to_identifier). The function_name_pattern should be tested for last
# because it always matches signal and property identifiers.
identifier_patterns = [ signal_name_pattern, property_name_pattern, function_name_pattern ]
# This pattern is to match return sections that forget to have a colon (':')
# after the initial 'Return' phrase. It is not included by default in the list
# of final sections below because a lot of function descriptions begin with
# 'Returns ...' and the process_description() function would stop right at that
# first line, thinking it is a return section.
no_colon_return_pattern = re.compile(r'^@?(returns|return\s+value)\s*(.*\n?)$', re.IGNORECASE)
since_pattern = re.compile(r'^(since\s*:\s*.*\n?)$', re.IGNORECASE)
# These patterns normally will be encountered after the description. Knowing
# the order of their appearance is difficult so this list is used to test when
# one begins and the other ends when processing the rest of the sections after
# the description.
final_section_patterns = [ return_pattern, since_pattern, deprecated_pattern, rename_to_pattern ]
def parse_file(fp, doc_dict):
line = fp.readline()
in_comment_block = 0
while line:
if not in_comment_block:
if comment_start_pat.match(line):
in_comment_block = 1
cur_doc = FunctionDoc()
in_description = 0
in_return = 0
line = fp.readline()
continue
cur_doc = GtkDoc()
line = skip_to_comment_block(fp, line)
line = skip_to_identifier(fp, line, cur_doc)
# See if the identifier is found (stored in the current GtkDoc by
# skip_to_identifier). If so, continue reading the rest of the comment
# block.
if cur_doc.name:
line = process_params(fp, line, cur_doc)
line = process_description(fp, line, cur_doc)
line = process_final_sections(fp, line, cur_doc)
# Add the current doc block to the dictionary of doc blocks.
doc_dict[cur_doc.name] = cur_doc
# we are inside a comment block ...
if comment_end_pat.match(line):
if not cur_doc.name:
sys.stderr.write("no function name found in doc comment\n")
else:
doc_dict[cur_doc.name] = cur_doc
in_comment_block = 0
line = fp.readline()
continue
# Given a list of annotations as string of the form
# '(annotation1) (annotation2) ...' return a list of annotations of the form
# [ (name1, value1), (name2, value2) ... ]. Not all annotations have values so
# the values in the list of tuples could be empty ('').
def get_annotation_list(annotations):
annotation_list = []
while annotations:
match = annotation_lead_pattern.match(annotations)
if match:
annotation_contents = match.group(1)
name, split, value = annotation_contents.strip().partition(' ')
annotation_list.append((name, value))
# Remove first occurrence to continue processing.
annotations = annotation_lead_pattern.sub('', annotations)
else:
break
return annotation_list
# inside a comment block, and not the end of the block ...
line = comment_line_lead.sub('', line)
# Given a currently read line, test that line and continue reading until the
# beginning of a comment block is found or eof is reached. Return the last
# read line.
def skip_to_comment_block(fp, line):
while line:
if comment_start_pattern.match(line):
break
line = fp.readline()
return line
# Given the current line in a comment block, continue skipping lines until a
# non-blank line in the comment block is found or until the end of the block
# (or eof) is reached. Returns the line where reading stopped.
def skip_to_nonblank(fp, line):
while line:
if not comment_empty_line_pattern.match(line):
break
line = fp.readline()
# Stop processing if eof or end of comment block is reached.
if not line or comment_end_pattern.match(line):
break
return line
# Given the first line of a comment block (the '/**'), see if the next
# non-blank line is the identifier of the comment block. Stop processing if
# the end of the block or eof is reached. Store the identifier (if there is
# one) and its type ('function', 'signal' or 'property') in the given GtkDoc.
# Return the line where the identifier is found or the line that stops the
# processing (if eof or the end of the comment block is found first).
def skip_to_identifier(fp, line, cur_doc):
# Skip the initial comment block line ('/**') if not eof.
if line: line = fp.readline()
# Now skip empty lines.
line = skip_to_nonblank(fp, line)
# See if the first non-blank line is the identifier.
if line and not comment_end_pattern.match(line):
# Remove the initial ' * ' in comment block line and see if there is an
# identifier.
line = comment_line_lead_pattern.sub('', line)
for pattern in identifier_patterns:
match = pattern.match(line)
if match:
# Set the GtkDoc name.
cur_doc.set_name(match.group(1))
# Get annotations and add them to the GtkDoc.
annotations = get_annotation_list(match.group(2))
for annotation in annotations:
cur_doc.add_annotation(annotation)
# Set the GtkDoc type.
if pattern == signal_name_pattern:
cur_doc.set_type('signal')
elif pattern == property_name_pattern:
cur_doc.set_type('property')
elif pattern == function_name_pattern:
cur_doc.set_type('function')
return line
return line
# Given a currently read line (presumably the identifier line), read the next
# lines, testing to see if the lines are part of parameter descriptions. If
# so, store the parameter descriptions in the given doc block. Stop on eof and
# return the last line that stops the processing.
def process_params(fp, line, cur_doc):
# Skip the identifier line if not eof. Also skip any blank lines in the
# comment block. Return if eof or the end of the comment block are
# encountered.
if line: line = fp.readline()
line = skip_to_nonblank(fp, line)
if not line or comment_end_pattern.match(line):
return line
# Remove initial ' * ' in first non-empty comment block line.
line = comment_line_lead_pattern.sub('', line)
# Now process possible parameters as long as no eof or the end of the
# param section is not reached (which could be triggered by anything that
# doesn't match a '@param:..." line, even the end of the comment block).
match = param_pattern.match(line)
while line and match:
description = match.group(2)
# First extract the annotations from the description and save them.
annotations = []
annotation_match = annotations_pattern.match(description)
if annotation_match:
annotations = get_annotation_list(annotation_match.group(1))
# Remove the annotations from the description
description = annotations_pattern.sub('', description)
# Default to appending lines to current parameter.
append_func = cur_doc.append_to_last_param
# See if the return has been included as part of the parameter
# section and make sure that lines are added to the GtkDoc return if
# so.
if match.group(1).lower() == "returns":
cur_doc.add_return(description, annotations)
append_func = cur_doc.append_to_return
# If not, just add it as a regular parameter.
else:
cur_doc.add_param(match.group(1), description, annotations)
# Now read lines and append them until next parameter, beginning of
# description (an empty line), the end of the comment block or eof.
line = fp.readline()
while line:
# Stop processing if end of comment block or a blank comment line
# is encountered.
if comment_empty_line_pattern.match(line) or \
comment_end_pattern.match(line):
break
# Remove initial ' * ' in comment block line.
line = comment_line_lead_pattern.sub('', line)
# Break from current param processing if a new one is
# encountered.
if param_pattern.match(line): break;
# Otherwise, just append the current line and get the next line.
append_func(line)
line = fp.readline()
# Re-evaluate match for while condition
match = param_pattern.match(line)
# End by returning the current line.
return line
# Having processed parameters, read the following lines into the description of
# the current doc block until the end of the comment block, the end of file or
# a return section is encountered.
def process_description(fp, line, cur_doc):
# First skip empty lines returning on eof or end of comment block.
line = skip_to_nonblank(fp, line)
if not line or comment_end_pattern.match(line):
return line
# Remove initial ' * ' in non-empty comment block line.
line = comment_line_lead_pattern.sub('', line)
# Also remove possible 'Description:' prefix.
if line[:12] == 'Description:': line = line[12:]
# Used to tell if the previous line was blank and a return section
# uncommonly marked with 'Returns ...' instead of 'Returns: ...' has
# started (assume it is non-empty to begin with).
prev_line = 'non-empty'
# Now read lines until a new section (like a return or a since section) is
# encountered.
while line:
# See if the description section has ended (if the line begins with
# 'Returns ...' and the previous line was empty -- this loop replaces
# empty lines with a newline).
if no_colon_return_pattern.match(line) and prev_line == '\n':
return line
# Or if one of the patterns of the final sections match
for pattern in final_section_patterns:
if pattern.match(line):
return line
# If not, append lines to description in the doc comment block.
cur_doc.append_to_description(line)
prev_line = line
line = fp.readline()
# Stop processing on eof or at the end of comment block.
if not line or comment_end_pattern.match(line):
return line
# Remove initial ' * ' in line so that the text can be appended to the
# description of the comment block and make sure that if the line is
# empty it be interpreted as a newline.
line = comment_line_lead_pattern.sub('', line)
if not line: line = '\n'
if not cur_doc.name:
match = funcname_pat.match(line)
# Given the line that ended the description (the first line of one of the final
# sections) process the final sections ('Returns:', 'Since:', etc.) until the
# end of the comment block or eof. Return the line that ends the processing.
def process_final_sections(fp, line, cur_doc):
while line and not comment_end_pattern.match(line):
# Remove leading ' * ' from current non-empty comment line.
line = comment_line_lead_pattern.sub('', line)
# Temporarily append the no colon return pattern to the final section
# patterns now that the description has been processed. It will be
# removed after the for loop below executes so that future descriptions
# that begin with 'Returns ...' are not interpreted as a return
# section.
final_section_patterns.append(no_colon_return_pattern)
for pattern in final_section_patterns:
match = pattern.match(line)
if match:
cur_doc.set_name(match.group(1))
elif in_return:
match = return_pat.match(line)
if match:
# assume the last return statement was really part of the
# description
return_start = match.group(1)
cur_doc.ret = match.group(2)
cur_doc.description = cur_doc.description + return_start + \
cur_doc.ret
else:
cur_doc.append_return(line)
elif in_description:
if line[:12] == 'Description:':
line = line[12:]
match = return_pat.match(line)
if match:
in_return = 1
return_start = match.group(1)
cur_doc.append_return(match.group(2))
else:
cur_doc.append_description(line)
elif line == '\n':
# end of parameters
in_description = 1
else:
match = param_pat.match(line)
if match:
param = match.group(1)
desc = match.group(2)
if param == 'returns':
cur_doc.ret = desc
if pattern == return_pattern or \
pattern == no_colon_return_pattern:
# Dealing with a 'Returns:' so first extract the
# annotations from the description and save them.
description = match.group(2)
annotations = []
annotation_match = \
annotations_pattern.match(description)
if annotation_match:
annotations = \
get_annotation_list(annotation_match.group(1))
# Remove the annotations from the description
description = annotations_pattern.sub('', description)
# Now add the return.
cur_doc.add_return(description, annotations)
# In case more lines need to be appended.
append_func = cur_doc.append_to_return
elif pattern == rename_to_pattern:
# Dealing with a 'Rename to:' section (GObjectIntrospection
# annotation) so no further lines will be appended but this
# single one (and only to the annotations).
append_func = None
cur_doc.add_annotation((match.group(1),
match.group(2)))
else:
cur_doc.add_param(param, desc)
else:
# must be continuation
try:
if param == 'returns':
cur_doc.append_return(line)
# For all others ('Since:' and 'Deprecated:') just append
# the line to the description for now.
# But if --no-since is specified, don't append it.
if no_since and pattern == since_pattern:
pass
else:
cur_doc.append_to_last_param(line)
except:
sys.stderr.write('something weird while reading param\n')
cur_doc.append_to_description(line)
# In case more lines need to be appended.
append_func = cur_doc.append_to_description
# Stop final section pattern matching for loop since a match
# has already been found.
break
# Remove the no colon return pattern (which was temporarily added in
# the just executed loop) from the list of final section patterns.
final_section_patterns.pop()
line = fp.readline()
# Now continue appending lines to current section until a new one is
# found or an eof or the end of the comment block is encountered.
finished = False
while not finished and line and \
not comment_end_pattern.match(line):
# Remove leading ' * ' from line and make sure that if it is empty,
# it be interpreted as a newline.
line = comment_line_lead_pattern.sub('', line)
if not line: line = '\n'
for pattern in final_section_patterns:
if pattern.match(line):
finished = True
break
# Break out of loop if a new section is found (determined in above
# inner loop).
if finished: break
# Now it's safe to append line.
if append_func: append_func(line)
# Get the next line to continue processing.
line = fp.readline()
return line
def parse_dir(dir, doc_dict):
for file in os.listdir(dir):
if file in ('.', '..'): continue
@ -129,6 +404,7 @@ def parse_dir(dir, doc_dict):
if os.path.isdir(path):
parse_dir(path, doc_dict)
if len(file) > 2 and file[-2:] == '.c':
sys.stderr.write("Processing " + path + '\n')
parse_file(open(path, 'r'), doc_dict)
def extract(dirs, doc_dict=None):
@ -137,13 +413,13 @@ def extract(dirs, doc_dict=None):
parse_dir(dir, doc_dict)
return doc_dict
tmpl_section_pat = re.compile(r'^<!-- ##### (\w+) (\w+) ##### -->$')
tmpl_section_pattern = re.compile(r'^<!-- ##### (\w+) (\w+) ##### -->$')
def parse_tmpl(fp, doc_dict):
cur_doc = None
line = fp.readline()
while line:
match = tmpl_section_pat.match(line)
match = tmpl_section_pattern.match(line)
if match:
cur_doc = None # new input shouldn't affect the old doc dict
sect_type = match.group(1)
@ -152,7 +428,7 @@ def parse_tmpl(fp, doc_dict):
if sect_type == 'FUNCTION':
cur_doc = doc_dict.get(sect_name)
if not cur_doc:
cur_doc = FunctionDoc()
cur_doc = GtkDoc()
cur_doc.set_name(sect_name)
doc_dict[sect_name] = cur_doc
elif line == '<!-- # Unused Parameters # -->\n':
@ -160,15 +436,15 @@ def parse_tmpl(fp, doc_dict):
elif cur_doc:
if line[:10] == '@Returns: ':
if string.strip(line[10:]):
cur_doc.append_return(line[10:])
cur_doc.append_to_return(line[10:])
elif line[0] == '@':
pos = string.find(line, ':')
if pos >= 0:
cur_doc.append_to_named_param(line[1:pos], line[pos+1:])
else:
cur_doc.append_description(line)
cur_doc.append_to_description(line)
else:
cur_doc.append_description(line)
cur_doc.append_to_description(line)
line = fp.readline()

874
bindings/python/codegen/docgen.py Normal file → Executable file

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,69 @@
#!/usr/bin/env python
# -*- Mode: Python; py-indent-offset: 4 -*-
# Search through a header file looking for function prototypes.
# For each prototype, generate a scheme style definition.
# GPL'ed
# Toby D. Reeves <toby@max.rl.plh.af.mil>
#
# Modified by James Henstridge <james@daa.com.au> to output stuff in
# Havoc's new defs format. Info on this format can be seen at:
# http://www.gnome.org/mailing-lists/archives/gtk-devel-list/2000-January/0085.shtml
# http://mail.gnome.org/archives/gtk-devel-list/2000-January/msg00070.html
# Updated to be PEP-8 compatible and refactored to use OOP
#
# Scan the given public .h files of a GTK module (or module using
# GTK object conventions) and generates a set of scheme defs.
#
# h2def searches through a header file looking for function prototypes and
# generates a scheme style defenition for each prototype.
# Basically the operation of h2def is:
#
# - read each .h file into a buffer which is scrubbed of extraneous data
# - find all object defenitions:
# - find all structures that may represent a GtkObject
# - find all structures that might represent a class
# - find all structures that may represent a GtkObject subclass
# - find all structures that might represent a class/Iface inherited from
# GTypeInterface
# - find all enum defenitions
# - write out the defs
#
# The command line options are:
#
# -s --separate Create separate files for objects and function/method defs
# using the given name as the base name (optional). If this
# is not specified the combined object and function defs
# will be output to sys.stdout.
# -f --defsfilter Extract defs from the given file to filter the output defs
# that is don't output defs that are defined in the
# defsfile. More than one deffile may be specified.
# -m --modulename The prefix to be stripped from the front of function names
# for the given module
# -n --namespace The module or namespace name to be used, for example
# WebKit where h2def is unable to detect the module name
# automatically. it also sets the gtype-id prefix.
# --onlyenums Only produce defs for enums and flags
# --onlyobjdefs Only produce defs for objects
# -v Verbose output
#
# Examples:
#
# python h2def.py /usr/local/include/pango-1.0/pango/*.h >/tmp/pango.defs
#
# - Outputs all defs for the pango module.
#
# python h2def.py -m gdk -s /tmp/gdk-2.10 \
# -f /usr/tmp/pygtk/gtk/gdk-base.defs \
# /usr/local/include/gtk-2.0/gdk/*.h \
# /usr/local/include/gtk-2.0/gdk-pixbuf/*.h
#
# - Outputs the gdk module defs that are not contained in the defs file
# /usr/tmp/pygtk/gtk/gdk-base.defs. Two output files are created:
# /tmp/gdk-2.10-types.defs and /tmp/gdk-2.10.defs.
#
# python h2def.py -n WebKit /usr/incude/webkit-1.0/webkit/*.h \
# >/tmp/webkit.defs
#
# - Outputs all the defs for webkit module, setting the module name to WebKit
# and the gtype-id prefix to WEBKIT_ which can't be detected automatically.
#
import getopt
import os
@ -33,16 +88,16 @@ def to_upper_str(name):
name = _upperstr_pat3.sub(r'\1_\2', name, count=1)
return string.upper(name)
def typecode(typename):
def typecode(typename, namespace=None):
"""create a typecode (eg. GTK_TYPE_WIDGET) from a typename"""
typename2 = to_upper_str(typename)
typename2 = string.replace(typename2, '_', "", 1)
typename2 = typename2[:3] + '_TYPE' + typename2[3:]
return typename2
if namespace:
return string.replace(string.upper(namespace) + "_" + to_upper_str(typename[len(namespace):]), '_', '_TYPE_', 1)
return string.replace(to_upper_str(typename), '_', '_TYPE_', 1)
# ------------------ Find object definitions -----------------
# Strips the comments from buffer
def strip_comments(buf):
parts = []
lastpos = 0
@ -60,6 +115,12 @@ def strip_comments(buf):
break
return string.join(parts, '')
# Strips the dll API from buffer, for example WEBKIT_API
def strip_dll_api(buf):
pat = re.compile("[A-Z]*_API ")
buf = pat.sub("", buf)
return buf
obj_name_pat = "[A-Z][a-z]*[A-Z][A-Za-z0-9]*"
split_prefix_pat = re.compile('([A-Z]+[a-z]*)([A-Za-z0-9]+)')
@ -72,10 +133,13 @@ def find_obj_defs(buf, objdefs=[]):
# filter out comments from buffer.
buf = strip_comments(buf)
# filter out dll api
buf = strip_dll_api(buf)
maybeobjdefs = [] # contains all possible objects from file
# first find all structures that look like they may represent a GtkObject
pat = re.compile("struct _(" + obj_name_pat + ")\s*{\s*" +
pat = re.compile("struct\s+_(" + obj_name_pat + ")\s*{\s*" +
"(" + obj_name_pat + ")\s+", re.MULTILINE)
pos = 0
while pos < len(buf):
@ -92,11 +156,11 @@ def find_obj_defs(buf, objdefs=[]):
while pos < len(buf):
m = pat.search(buf, pos)
if not m: break
maybeobjdefs.append((m.group(2), m.group(2)))
maybeobjdefs.append((m.group(2), m.group(1)))
pos = m.end()
# now find all structures that look like they might represent a class:
pat = re.compile("struct _(" + obj_name_pat + ")Class\s*{\s*" +
pat = re.compile("struct\s+_(" + obj_name_pat + ")Class\s*{\s*" +
"(" + obj_name_pat + ")Class\s+", re.MULTILINE)
pos = 0
while pos < len(buf):
@ -125,7 +189,7 @@ def find_obj_defs(buf, objdefs=[]):
# now find all structures that look like they might represent
# a class inherited from GTypeInterface:
pat = re.compile("struct _(" + obj_name_pat + ")Class\s*{\s*" +
pat = re.compile("struct\s+_(" + obj_name_pat + ")Class\s*{\s*" +
"GTypeInterface\s+", re.MULTILINE)
pos = 0
while pos < len(buf):
@ -141,7 +205,7 @@ def find_obj_defs(buf, objdefs=[]):
# now find all structures that look like they might represent
# an Iface inherited from GTypeInterface:
pat = re.compile("struct _(" + obj_name_pat + ")Iface\s*{\s*" +
pat = re.compile("struct\s+_(" + obj_name_pat + ")Iface\s*{\s*" +
"GTypeInterface\s+", re.MULTILINE)
pos = 0
while pos < len(buf):
@ -177,6 +241,13 @@ def find_enum_defs(buf, enums=[]):
# bulk comments
buf = strip_comments(buf)
# strip dll api macros
buf = strip_dll_api(buf)
# strip # directives
pat = re.compile(r"""^[#].*?$""", re.MULTILINE)
buf = pat.sub('', buf)
buf = re.sub('\n', ' ', buf)
enum_pat = re.compile(r'enum\s*{([^}]*)}\s*([A-Z][A-Za-z]*)(\s|;)')
@ -209,6 +280,9 @@ def clean_func(buf):
# bulk comments
buf = strip_comments(buf)
# dll api
buf = strip_dll_api(buf)
# compact continued lines
pat = re.compile(r"""\\\n""", re.MULTILINE)
buf = pat.sub('', buf)
@ -247,9 +321,14 @@ def clean_func(buf):
buf = pat.sub(r'[] \1', buf)
# make return types that are const work.
buf = re.sub(r'\s*\*\s*G_CONST_RETURN\s*\*\s*', '** ', buf)
buf = string.replace(buf, 'G_CONST_RETURN ', 'const-')
buf = string.replace(buf, 'const ', 'const-')
#strip GSEAL macros from the middle of function declarations:
pat = re.compile(r"""GSEAL""", re.VERBOSE)
buf = pat.sub('', buf)
return buf
proto_pat=re.compile(r"""
@ -266,13 +345,14 @@ pointer_pat = re.compile('.*\*$')
func_new_pat = re.compile('(\w+)_new$')
class DefsWriter:
def __init__(self, fp=None, prefix=None, verbose=False,
def __init__(self, fp=None, prefix=None, ns=None, verbose=False,
defsfilter=None):
if not fp:
fp = sys.stdout
self.fp = fp
self.prefix = prefix
self.namespace = ns
self.verbose = verbose
self._enums = {}
@ -284,9 +364,9 @@ class DefsWriter:
for func in filter.functions + filter.methods.values():
self._functions[func.c_name] = func
for obj in filter.objects + filter.boxes + filter.interfaces:
self._objects[obj.c_name] = func
self._objects[obj.c_name] = obj
for obj in filter.enums:
self._enums[obj.c_name] = func
self._enums[obj.c_name] = obj
def write_def(self, deffile):
buf = open(deffile).read()
@ -309,10 +389,14 @@ class DefsWriter:
continue
name = cname
module = None
m = split_prefix_pat.match(cname)
if m:
module = m.group(1)
name = m.group(2)
if self.namespace:
module = self.namespace
name = cname[len(self.namespace):]
else:
m = split_prefix_pat.match(cname)
if m:
module = m.group(1)
name = m.group(2)
if isflags:
fp.write('(define-flags ' + name + '\n')
else:
@ -320,12 +404,13 @@ class DefsWriter:
if module:
fp.write(' (in-module "' + module + '")\n')
fp.write(' (c-name "' + cname + '")\n')
fp.write(' (gtype-id "' + typecode(cname) + '")\n')
fp.write(' (gtype-id "' + typecode(cname, self.namespace) + '")\n')
prefix = entries[0]
for ent in entries:
# shorten prefix til we get a match ...
# and handle GDK_FONT_FONT, GDK_FONT_FONTSET case
while ent[:len(prefix)] != prefix or len(prefix) >= len(ent):
while ((len(prefix) and prefix[-1] != '_') or ent[:len(prefix)] != prefix
or len(prefix) >= len(ent)):
prefix = prefix[:-1]
prefix_len = len(prefix)
fp.write(' (values\n')
@ -347,19 +432,23 @@ class DefsWriter:
if filter:
if klass in filter:
continue
m = split_prefix_pat.match(klass)
cmodule = None
cname = klass
if m:
cmodule = m.group(1)
cname = m.group(2)
if self.namespace:
cname = klass[len(self.namespace):]
cmodule = self.namespace
else:
m = split_prefix_pat.match(klass)
cname = klass
cmodule = None
if m:
cmodule = m.group(1)
cname = m.group(2)
fp.write('(define-object ' + cname + '\n')
if cmodule:
fp.write(' (in-module "' + cmodule + '")\n')
if parent:
fp.write(' (parent "' + parent + '")\n')
fp.write(' (c-name "' + klass + '")\n')
fp.write(' (gtype-id "' + typecode(klass) + '")\n')
fp.write(' (gtype-id "' + typecode(klass, self.namespace) + '")\n')
# should do something about accessible fields
fp.write(')\n\n')
@ -475,11 +564,12 @@ def main(args):
onlyobjdefs = False
separate = False
modulename = None
namespace = None
defsfilter = None
opts, args = getopt.getopt(args[1:], 'vs:m:f:',
opts, args = getopt.getopt(args[1:], 'vs:m:n:f:',
['onlyenums', 'onlyobjdefs',
'modulename=', 'separate=',
'defsfilter='])
'modulename=', 'namespace=',
'separate=', 'defsfilter='])
for o, v in opts:
if o == '-v':
verbose = True
@ -491,6 +581,8 @@ def main(args):
separate = v
if o in ('-m', '--modulename'):
modulename = v
if o in ('-n', '--namespace'):
namespace = v
if o in ('-f', '--defsfilter'):
defsfilter = v
@ -511,8 +603,8 @@ def main(args):
methods = file(separate + '.defs', 'w')
types = file(separate + '-types.defs', 'w')
dw = DefsWriter(methods, prefix=modulename, verbose=verbose,
defsfilter=defsfilter)
dw = DefsWriter(methods, prefix=modulename, ns=namespace,
verbose=verbose, defsfilter=defsfilter)
dw.write_obj_defs(objdefs, types)
dw.write_enum_defs(enums, types)
print "Wrote %s-types.defs" % separate
@ -521,8 +613,8 @@ def main(args):
dw.write_def(filename)
print "Wrote %s.defs" % separate
else:
dw = DefsWriter(prefix=modulename, verbose=verbose,
defsfilter=defsfilter)
dw = DefsWriter(prefix=modulename, ns=namespace,
verbose=verbose, defsfilter=defsfilter)
if onlyenums:
dw.write_enum_defs(enums)

View file

@ -19,10 +19,13 @@ def class2cname(klass, method):
c_name += c
return c_name[1:] + '_' + method
import_pat = re.compile(r'\s*import\s+(\S+)\.([^\s.]+)\s+as\s+(\S+)')
# import python_type as c_name [for arg_type]
# Last ('for') clause is optional. If present, the type will be
# imported only if given 'arg_type' is registered.
import_pat = re.compile(r'\s*import\s+(\S+)\.([^\s.]+)\s+as\s+(\S+)(\s+for\s+(\S+))?')
class Overrides:
def __init__(self, filename=None, path=[]):
def __init__(self, filename=None):
self.modulename = None
self.ignores = {}
self.glob_ignores = []
@ -43,27 +46,16 @@ class Overrides:
self.imports = []
self.defines = {}
self.functions = {}
self.newstyle_constructors = {}
self.path = [os.path.abspath(x) for x in path]
if filename:
self.newstyle_constructors = {}
self.dynamicnamespace = False
if filename:
self.handle_file(filename)
def handle_file(self, filename):
oldpath = os.getcwd()
fp = None
for path in self.path:
os.chdir(oldpath)
os.chdir(path)
try:
fp = open(filename, 'r')
break
except:
os.chdir(oldpath)
if not fp:
raise Exception, "Couldn't find file %s" % filename
dirname = path
fp = open(filename, 'r')
dirname = os.path.dirname(os.path.abspath(filename))
if dirname != oldpath:
os.chdir(dirname)
@ -177,7 +169,8 @@ class Overrides:
for line in string.split(buffer, '\n'):
match = import_pat.match(line)
if match:
self.imports.append(match.groups())
module, pyname, cname, conditional, importing_for = match.groups()
self.imports.append((module, pyname, cname, importing_for or None))
elif command == 'define':
"define funcname [kwargs|noargs|onearg] [classmethod|staticmethod]"
"define Class.method [kwargs|noargs|onearg] [classmethod|staticmethod]"
@ -210,6 +203,10 @@ class Overrides:
"new-constructor GType"
gtype, = words[1:]
self.newstyle_constructors[gtype] = True
elif command == 'options':
for option in words[1:]:
if option == 'dynamicnamespace':
self.dynamicnamespace = True
def is_ignored(self, name):
if self.ignores.has_key(name):

View file

@ -88,10 +88,12 @@ class ReverseWrapper(object):
self.declarations = MemoryCodeSink()
self.post_return_code = MemoryCodeSink()
self.body = MemoryCodeSink()
self.check_exception_code = MemoryCodeSink()
self.cleanup_actions = []
self.pyargv_items = []
self.pyargv_optional_items = []
self.pyret_parse_items = [] # list of (format_spec, parameter)
self.code_sinks_stack = [self.body]
def set_call_target(self, called_pyobj, method_name=None):
assert called_pyobj is not None
@ -122,6 +124,14 @@ class ReverseWrapper(object):
else:
self.pyret_parse_items.append((format_specifier, parameter))
def push_code_sink(self, code_sink):
self.code_sinks_stack.insert(0, code_sink)
def pop_code_sink(self):
return self.code_sinks_stack.pop(0)
def write_code(self, code,
cleanup=None,
failure_expression=None,
@ -152,7 +162,7 @@ class ReverseWrapper(object):
parses the python method return value.
'''
if code_sink is None:
code_sink = self.body
code_sink = self.code_sinks_stack[0]
if code is not None:
code_sink.writeln(code)
if failure_expression is not None:
@ -170,7 +180,13 @@ class ReverseWrapper(object):
code_sink.writeln(failure_cleanup)
for cleanup_action in self.cleanup_actions:
code_sink.writeln(cleanup_action)
self.return_type.write_error_return()
self.push_code_sink(code_sink)
try:
self.return_type.write_error_return()
finally:
self.pop_code_sink()
code_sink.unindent()
code_sink.writeln("}")
if cleanup is not None:
@ -265,8 +281,10 @@ class ReverseWrapper(object):
if self.method_name is None:
self.write_code("py_retval = PyObject_Call(%s, %s);"
% (self.called_pyobj, py_args),
cleanup="Py_DECREF(py_retval);",
failure_expression="!py_retval")
cleanup="Py_XDECREF(py_retval);")
self.check_exception_code.flush_to(self.body)
self.write_code(None, failure_expression="!py_retval")
else:
self.add_declaration("PyObject *py_method;")
self.write_code("py_method = PyObject_GetAttrString(%s, \"%s\");"
@ -275,8 +293,9 @@ class ReverseWrapper(object):
failure_expression="!py_method")
self.write_code("py_retval = PyObject_CallObject(py_method, %s);"
% (py_args,),
cleanup="Py_DECREF(py_retval);",
failure_expression="!py_retval")
cleanup="Py_XDECREF(py_retval);")
self.check_exception_code.flush_to(self.body)
self.write_code(None, failure_expression="!py_retval")
## -- Handle the return value --
@ -291,16 +310,25 @@ class ReverseWrapper(object):
sink.indent()
if len(self.pyret_parse_items) == 1:
## if retval is one item only, pack it in a tuple so we
## can use PyArg_ParseTuple as usual..
self.write_code('py_retval = Py_BuildValue("(N)", py_retval);')
if len(self.pyret_parse_items) > 0:
## Parse return values using PyArg_ParseTuple
self.write_code(code=None, failure_expression=(
'!PyArg_ParseTuple(py_retval, "%s", %s)' % (
"".join([format for format, param in self.pyret_parse_items]),
", ".join([param for format, param in self.pyret_parse_items]))))
if self.pyret_parse_items == [("", "")]:
## special case when there are no return parameters
self.write_code(
code=None,
failure_expression='py_retval != Py_None',
failure_exception=('PyErr_SetString(PyExc_TypeError, '
'"virtual method should return None");'))
else:
if len(self.pyret_parse_items) == 1:
## if retval is one item only, pack it in a tuple so we
## can use PyArg_ParseTuple as usual..
self.write_code('py_retval = Py_BuildValue("(N)", py_retval);')
if len(self.pyret_parse_items) > 0:
## Parse return values using PyArg_ParseTuple
params = ["py_retval",
'"%s"' % "".join([format for format, param in self.pyret_parse_items])]
params.extend([param for format, param in self.pyret_parse_items if param])
self.write_code(code=None, failure_expression=(
'!PyArg_ParseTuple(%s)' % (', '.join(params),)))
if DEBUG_MODE:
self.declarations.writeln("/* end declarations */")
@ -331,6 +359,8 @@ class TypeHandler(object):
class ReturnType(TypeHandler):
supports_optional = False
def get_c_type(self):
raise NotImplementedError
@ -378,10 +408,22 @@ class StringParam(Parameter):
% (self.name, self.name, self.name)),
cleanup=("Py_XDECREF(py_%s);" % self.name))
self.wrapper.add_pyargv_item("py_%s" % self.name, optional=True)
else:
elif self.props.get('nullok', False):
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
self.wrapper.write_code(code=("py_%s = PyString_FromString(%s);" %
(self.name, self.name)),
self.wrapper.write_code(code=("if (%s)\n"
" py_%s = PyString_FromString(%s);\n"
"else {\n"
" Py_INCREF(Py_None);\n"
" py_%s = Py_None;\n"
"}\n"
% (self.name, self.name, self.name, self.name)),
cleanup=("Py_DECREF(py_%s);" % self.name))
self.wrapper.add_pyargv_item("py_%s" % self.name)
else:
self.wrapper.add_declaration("PyObject *py_%s = NULL;" % self.name)
self.wrapper.write_code(code=("if (%s)\n"
" py_%s = PyString_FromString(%s);\n" %
(self.name, self.name, self.name)),
cleanup=("Py_DECREF(py_%s);" % self.name),
failure_expression=("!py_%s" % self.name))
self.wrapper.add_pyargv_item("py_%s" % self.name)
@ -394,10 +436,12 @@ del ctype
class StringReturn(ReturnType):
def get_c_type(self):
return "char *"
return self.props.get('c_type', 'char *').replace('const-', 'const ')
#return "char *"
def write_decl(self):
self.wrapper.add_declaration("char *retval;")
self.wrapper.add_declaration("%s retval;" % self.get_c_type())
#self.wrapper.add_declaration("char *retval;")
def write_error_return(self):
self.wrapper.write_code("return NULL;")
@ -406,7 +450,7 @@ class StringReturn(ReturnType):
self.wrapper.add_pyret_parse_item("s", "&retval", prepend=True)
self.wrapper.write_code("retval = g_strdup(retval);", code_sink=self.wrapper.post_return_code)
for ctype in ('char*', 'gchar*'):
for ctype in ('char*', 'gchar*', 'const-gchar*'):
argtypes.matcher.register_reverse_ret(ctype, StringReturn)
del ctype
@ -423,10 +467,7 @@ class VoidReturn(ReturnType):
self.wrapper.write_code("return;")
def write_conversion(self):
self.wrapper.write_code(
code=None,
failure_expression="py_retval != Py_None",
failure_cleanup='PyErr_SetString(PyExc_TypeError, "retval should be None");')
self.wrapper.add_pyret_parse_item("", "", prepend=True)
argtypes.matcher.register_reverse_ret('void', VoidReturn)
argtypes.matcher.register_reverse_ret('none', VoidReturn)
@ -452,23 +493,39 @@ argtypes.matcher.register_reverse('GObject*', GObjectParam)
class GObjectReturn(ReturnType):
supports_optional = True
def get_c_type(self):
return self.props.get('c_type', 'GObject *')
def write_decl(self):
self.wrapper.add_declaration("%s retval;" % self.get_c_type())
if not self.props.get('optional'):
self.wrapper.add_declaration("%s retval;" % self.get_c_type())
else:
self.wrapper.add_declaration("%s retval = NULL;" % self.get_c_type())
def write_error_return(self):
self.wrapper.write_code("return NULL;")
def write_conversion(self):
self.wrapper.write_code(
code=None,
failure_expression="!PyObject_TypeCheck(py_retval, &PyGObject_Type)",
failure_exception='PyErr_SetString(PyExc_TypeError, "retval should be a GObject");')
self.wrapper.write_code("retval = (%s) pygobject_get(py_retval);"
% self.get_c_type())
self.wrapper.write_code("g_object_ref((GObject *) retval);")
if not self.props.get('optional'):
self.wrapper.write_code(
code=None,
failure_expression="!PyObject_TypeCheck(py_retval, &PyGObject_Type)",
failure_exception='PyErr_SetString(PyExc_TypeError, "retval should be a GObject");')
self.wrapper.write_code("retval = (%s) pygobject_get(py_retval);"
% self.get_c_type())
self.wrapper.write_code("g_object_ref((GObject *) retval);")
else:
self.wrapper.write_code(
code=None,
failure_expression="py_retval != Py_None && !PyObject_TypeCheck(py_retval, &PyGObject_Type)",
failure_exception='PyErr_SetString(PyExc_TypeError, "retval should be None or a GObject");')
self.wrapper.write_code("if (py_retval != Py_None) {\n"
" retval = (%s) pygobject_get(py_retval);\n"
" g_object_ref((GObject *) retval);\n"
"}\n"
% self.get_c_type())
argtypes.matcher.register_reverse_ret('GObject*', GObjectReturn)
@ -506,9 +563,12 @@ del argtype
class IntPtrParam(Parameter):
def __init__(self, wrapper, name, **props):
if "direction" not in props:
raise ValueError("cannot use int* parameter without direction")
raise argtypes.ArgTypeConfigurationError(
"cannot use int* parameter without direction")
if props["direction"] not in ("out", "inout"):
raise ValueError("cannot use int* parameter with direction '%s'" % (props["direction"],))
raise argtypes.ArgTypeConfigurationError(
"cannot use int* parameter with direction '%s'"
% (props["direction"],))
Parameter.__init__(self, wrapper, name, **props)
def get_c_type(self):
return self.props.get('c_type', 'int*')
@ -529,8 +589,9 @@ class GEnumReturn(IntReturn):
def write_conversion(self):
self.wrapper.write_code(
code=None,
failure_expression=("pyg_enum_get_value(%s, py_retval, (gint *)&retval)" %
self.props['typecode']))
failure_expression=(
"pyg_enum_get_value(%s, py_retval, (gint *)&retval)"
% (self.props['typecode'],)))
argtypes.matcher.register_reverse_ret("GEnum", GEnumReturn)
@ -538,9 +599,9 @@ class GEnumParam(IntParam):
def convert_c2py(self):
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
self.wrapper.write_code(code=("py_%s = pyg_enum_from_gtype(%s, %s);" %
(self.name, self.props['typecode'], self.name)),
cleanup=("Py_DECREF(py_%s);" % self.name),
failure_expression=("!py_%s" % self.name))
(self.name, self.props['typecode'], self.name)),
cleanup=("Py_DECREF(py_%s);" % self.name),
failure_expression=("!py_%s" % self.name))
self.wrapper.add_pyargv_item("py_%s" % self.name)
argtypes.matcher.register_reverse("GEnum", GEnumParam)
@ -549,16 +610,18 @@ class GFlagsReturn(IntReturn):
def write_conversion(self):
self.wrapper.write_code(
code=None,
failure_expression=("pyg_flags_get_value(%s, py_retval, (gint *)&retval)" %
self.props['typecode']))
failure_expression=(
"pyg_flags_get_value(%s, py_retval, (gint *)&retval)" %
self.props['typecode']))
argtypes.matcher.register_reverse_ret("GFlags", GFlagsReturn)
class GFlagsParam(IntParam):
def convert_c2py(self):
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
self.wrapper.write_code(code=("py_%s = pyg_flags_from_gtype(%s, %s);" %
(self.name, self.props['typecode'], self.name)),
self.wrapper.write_code(code=(
"py_%s = pyg_flags_from_gtype(%s, %s);" %
(self.name, self.props['typecode'], self.name)),
cleanup=("Py_DECREF(py_%s);" % self.name),
failure_expression=("!py_%s" % self.name))
self.wrapper.add_pyargv_item("py_%s" % self.name)
@ -569,8 +632,9 @@ argtypes.matcher.register_reverse("GFlags", GFlagsParam)
class GtkTreePathParam(IntParam):
def convert_c2py(self):
self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
self.wrapper.write_code(code=("py_%s = pygtk_tree_path_to_pyobject(%s);" %
(self.name, self.name)),
self.wrapper.write_code(code=(
"py_%s = pygtk_tree_path_to_pyobject(%s);" %
(self.name, self.name)),
cleanup=("Py_DECREF(py_%s);" % self.name),
failure_expression=("!py_%s" % self.name))
self.wrapper.add_pyargv_item("py_%s" % self.name)
@ -578,6 +642,23 @@ class GtkTreePathParam(IntParam):
argtypes.matcher.register_reverse("GtkTreePath*", GtkTreePathParam)
class GtkTreePathReturn(ReturnType):
def get_c_type(self):
return self.props.get('c_type', 'GtkTreePath *')
def write_decl(self):
self.wrapper.add_declaration("GtkTreePath * retval;")
def write_error_return(self):
self.wrapper.write_code("return NULL;")
def write_conversion(self):
self.wrapper.write_code(
"retval = pygtk_tree_path_from_pyobject(py_retval);\n",
failure_expression=('!retval'),
failure_exception=(
'PyErr_SetString(PyExc_TypeError, "retval should be a GtkTreePath");'))
argtypes.matcher.register_reverse_ret("GtkTreePath*", GtkTreePathReturn)
class BooleanReturn(ReturnType):
def get_c_type(self):
return "gboolean"
@ -588,8 +669,9 @@ class BooleanReturn(ReturnType):
self.wrapper.write_code("return FALSE;")
def write_conversion(self):
self.wrapper.add_pyret_parse_item("O", "&py_main_retval", prepend=True)
self.wrapper.write_code("retval = PyObject_IsTrue(py_main_retval)? TRUE : FALSE;",
code_sink=self.wrapper.post_return_code)
self.wrapper.write_code(
"retval = PyObject_IsTrue(py_main_retval)? TRUE : FALSE;",
code_sink=self.wrapper.post_return_code)
argtypes.matcher.register_reverse_ret("gboolean", BooleanReturn)
class BooleanParam(Parameter):
@ -617,9 +699,12 @@ class DoubleParam(Parameter):
class DoublePtrParam(Parameter):
def __init__(self, wrapper, name, **props):
if "direction" not in props:
raise ValueError("cannot use double* parameter without direction")
raise argtypes.ArgTypeConfigurationError(
"cannot use double* parameter without direction")
if props["direction"] not in ("out", ): # inout not yet implemented
raise ValueError("cannot use double* parameter with direction '%s'" % (props["direction"],))
raise argtypes.ArgTypeConfigurationError(
"cannot use double* parameter with direction '%s'"
% (props["direction"],))
Parameter.__init__(self, wrapper, name, **props)
def get_c_type(self):
return self.props.get('c_type', 'double*')
@ -637,11 +722,7 @@ class DoubleReturn(ReturnType):
def write_error_return(self):
self.wrapper.write_code("return -G_MAXFLOAT;")
def write_conversion(self):
self.wrapper.write_code(
code=None,
failure_expression="!PyFloat_AsDouble(py_retval)",
failure_cleanup='PyErr_SetString(PyExc_TypeError, "retval should be a float");')
self.wrapper.write_code("retval = PyFloat_AsDouble(py_retval);")
self.wrapper.add_pyret_parse_item("d", "&retval", prepend=True)
for argtype in ('float', 'double', 'gfloat', 'gdouble'):
argtypes.matcher.register_reverse(argtype, DoubleParam)
@ -670,6 +751,7 @@ class GBoxedParam(Parameter):
argtypes.matcher.register_reverse("GBoxed", GBoxedParam)
class GBoxedReturn(ReturnType):
def get_c_type(self):
return self.props.get('c_type')
@ -678,17 +760,64 @@ class GBoxedReturn(ReturnType):
def write_error_return(self):
self.wrapper.write_code("return retval;")
def write_conversion(self):
self.wrapper.write_code(
self.wrapper.write_code(code = None,
failure_expression=("!pyg_boxed_check(py_retval, %s)" %
(self.props['typecode'],)),
failure_cleanup=('PyErr_SetString(PyExc_TypeError, "retval should be a %s");'
% (self.props['typename'],)))
failure_exception=(
'PyErr_SetString(PyExc_TypeError, "retval should be a %s");'
% (self.props['typename'],)))
self.wrapper.write_code('retval = pyg_boxed_get(py_retval, %s);' %
self.props['typename'])
argtypes.matcher.register_reverse_ret("GBoxed", GBoxedReturn)
class GdkRegionPtrReturn(GBoxedReturn):
def write_error_return(self):
self.wrapper.write_code("return gdk_region_new();")
def write_conversion(self):
self.props['typecode'] = 'PYGDK_TYPE_REGION'
self.props['typename'] = 'GdkRegion'
super(GdkRegionPtrReturn, self).write_conversion()
argtypes.matcher.register_reverse_ret("GdkRegion*", GdkRegionPtrReturn)
class PangoFontDescriptionReturn(GBoxedReturn):
def write_error_return(self):
self.wrapper.write_code("return pango_font_description_new();")
def write_conversion(self):
self.props['typecode'] = 'PANGO_TYPE_FONT_DESCRIPTION'
self.props['typename'] = 'PangoFontDescription'
super(PangoFontDescriptionReturn, self).write_conversion()
argtypes.matcher.register_reverse_ret("PangoFontDescription*",
PangoFontDescriptionReturn)
class PangoFontMetricsReturn(GBoxedReturn):
def write_error_return(self):
self.wrapper.write_code("return pango_font_metrics_new();")
def write_conversion(self):
self.props['typecode'] = 'PANGO_TYPE_FONT_METRICS'
self.props['typename'] = 'PangoFontMetrics'
super(PangoFontMetricsReturn, self).write_conversion()
argtypes.matcher.register_reverse_ret("PangoFontMetrics*",
PangoFontMetricsReturn)
class PangoLanguageReturn(GBoxedReturn):
def write_error_return(self):
self.wrapper.write_code("return pango_language_from_string(\"\");")
def write_conversion(self):
self.props['typecode'] = 'PANGO_TYPE_LANGUAGE'
self.props['typename'] = 'PangoLanguage'
super(PangoLanguageReturn, self).write_conversion()
argtypes.matcher.register_reverse_ret("PangoLanguage*", PangoLanguageReturn)
class GdkRectanglePtrParam(Parameter):
def get_c_type(self):
return self.props.get('c_type').replace('const-', 'const ')
@ -704,6 +833,17 @@ argtypes.matcher.register_reverse("GdkRectangle*", GdkRectanglePtrParam)
argtypes.matcher.register_reverse('GtkAllocation*', GdkRectanglePtrParam)
class GErrorParam(Parameter):
def get_c_type(self):
return self.props.get('c_type').replace('**', ' **')
def convert_c2py(self):
self.wrapper.write_code(code=None,
failure_expression=("pyg_gerror_exception_check(%s)" % self.name),
code_sink=self.wrapper.check_exception_code)
argtypes.matcher.register_reverse('GError**', GErrorParam)
class PyGObjectMethodParam(Parameter):
def __init__(self, wrapper, name, method_name, **props):
Parameter.__init__(self, wrapper, name, **props)
@ -720,6 +860,7 @@ class PyGObjectMethodParam(Parameter):
failure_expression=("!py_%s" % self.name))
self.wrapper.set_call_target("py_%s" % self.name, self.method_name)
class CallbackInUserDataParam(Parameter):
def __init__(self, wrapper, name, free_it, **props):
Parameter.__init__(self, wrapper, name, **props)

1
bindings/python/codegen/scmexpr.py Normal file → Executable file
View file

@ -3,7 +3,6 @@
from __future__ import generators
import string
import types
from cStringIO import StringIO
class error(Exception):