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', 'docextract',
'docgen', 'docgen',
'h2def', 'h2def',
'defsgen'
'mergedefs', 'mergedefs',
'mkskel', 'mkskel',
'override', 'override',

View file

@ -3,6 +3,18 @@ import string
import keyword import keyword
import struct import struct
py_ssize_t_clean = False
class ArgTypeError(Exception):
pass
class ArgTypeNotFoundError(ArgTypeError):
pass
class ArgTypeConfigurationError(ArgTypeError):
pass
class VarList: class VarList:
"""Nicely format a C variable list""" """Nicely format a C variable list"""
def __init__(self): def __init__(self):
@ -64,17 +76,17 @@ class WrapperInfo:
self.kwlist.append('"%s"' % kw) self.kwlist.append('"%s"' % kw)
class ArgType: 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 """Add code to the WrapperInfo instance to handle
parameter.""" parameter."""
raise RuntimeError, "write_param not implemented for %s" % \ raise RuntimeError("write_param not implemented for %s"
self.__class__.__name__ % self.__class__.__name__)
def write_return(self, ptype, ownsreturn, info): def write_return(self, ptype, ownsreturn, info):
"""Adds a variable named ret of the return type to """Adds a variable named ret of the return type to
info.varlist, and add any required code to info.codeafter to info.varlist, and add any required code to info.codeafter to
convert the return value to a python object.""" convert the return value to a python object."""
raise RuntimeError, "write_return not implemented for %s" % \ raise RuntimeError("write_return not implemented for %s"
self.__class__.__name__ % self.__class__.__name__)
class NoneArg(ArgType): class NoneArg(ArgType):
def write_return(self, ptype, ownsreturn, info): def write_return(self, ptype, ownsreturn, info):
@ -82,8 +94,8 @@ class NoneArg(ArgType):
' return Py_None;') ' return Py_None;')
class StringArg(ArgType): class StringArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt: if pdflt != None:
if pdflt != 'NULL': pdflt = '"' + pdflt + '"' if pdflt != 'NULL': pdflt = '"' + pdflt + '"'
info.varlist.add('char', '*' + pname + ' = ' + pdflt) info.varlist.add('char', '*' + pname + ' = ' + pdflt)
else: else:
@ -113,12 +125,15 @@ class StringArg(ArgType):
class UCharArg(ArgType): class UCharArg(ArgType):
# allows strings with embedded NULLs. # 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: if pdflt:
info.varlist.add('guchar', '*' + pname + ' = "' + pdflt + '"') info.varlist.add('guchar', '*' + pname + ' = "' + pdflt + '"')
else: else:
info.varlist.add('guchar', '*' + pname) 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) info.arglist.append(pname)
if pnull: if pnull:
info.add_parselist('z#', ['&' + pname, '&' + pname + '_len'], info.add_parselist('z#', ['&' + pname, '&' + pname + '_len'],
@ -128,7 +143,7 @@ class UCharArg(ArgType):
[pname]) [pname])
class CharArg(ArgType): class CharArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt: if pdflt:
info.varlist.add('char', pname + " = '" + pdflt + "'") info.varlist.add('char', pname + " = '" + pdflt + "'")
else: else:
@ -147,7 +162,7 @@ class GUniCharArg(ArgType):
'#endif\n' '#endif\n'
' py_ret = (Py_UNICODE)ret;\n' ' py_ret = (Py_UNICODE)ret;\n'
' return PyUnicode_FromUnicode(&py_ret, 1);\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: if pdflt:
info.varlist.add('gunichar', pname + " = '" + pdflt + "'") info.varlist.add('gunichar', pname + " = '" + pdflt + "'")
else: else:
@ -161,7 +176,7 @@ class GUniCharArg(ArgType):
class IntArg(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: if pdflt:
info.varlist.add('int', pname + ' = ' + pdflt) info.varlist.add('int', pname + ' = ' + pdflt)
else: else:
@ -191,7 +206,7 @@ class UIntArg(ArgType):
' PyErr_SetString(PyExc_TypeError, "Parameter \'%(name)s\' must be an int or a long");\n' ' PyErr_SetString(PyExc_TypeError, "Parameter \'%(name)s\' must be an int or a long");\n'
' if (PyErr_Occurred())\n' ' if (PyErr_Occurred())\n'
' return NULL;\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: if not pdflt:
pdflt = '0'; pdflt = '0';
@ -211,7 +226,7 @@ class SizeArg(ArgType):
else: else:
llp64 = False llp64 = False
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt: if pdflt:
info.varlist.add(ptype, pname + ' = ' + pdflt) info.varlist.add(ptype, pname + ' = ' + pdflt)
else: else:
@ -235,7 +250,7 @@ class SSizeArg(ArgType):
else: else:
llp64 = False llp64 = False
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt: if pdflt:
info.varlist.add(ptype, pname + ' = ' + pdflt) info.varlist.add(ptype, pname + ' = ' + pdflt)
else: else:
@ -253,7 +268,7 @@ class SSizeArg(ArgType):
info.codeafter.append(' return PyLong_FromLong(ret);\n') info.codeafter.append(' return PyLong_FromLong(ret);\n')
class LongArg(ArgType): class LongArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt: if pdflt:
info.varlist.add(ptype, pname + ' = ' + pdflt) info.varlist.add(ptype, pname + ' = ' + pdflt)
else: else:
@ -270,7 +285,7 @@ class BoolArg(IntArg):
info.codeafter.append(' return PyBool_FromLong(ret);\n') info.codeafter.append(' return PyBool_FromLong(ret);\n')
class TimeTArg(ArgType): class TimeTArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt: if pdflt:
info.varlist.add('time_t', pname + ' = ' + pdflt) info.varlist.add('time_t', pname + ' = ' + pdflt)
else: else:
@ -282,7 +297,7 @@ class TimeTArg(ArgType):
info.codeafter.append(' return PyInt_FromLong(ret);') info.codeafter.append(' return PyInt_FromLong(ret);')
class ULongArg(ArgType): class ULongArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt: if pdflt:
info.varlist.add('unsigned long', pname + ' = ' + pdflt) info.varlist.add('unsigned long', pname + ' = ' + pdflt)
else: else:
@ -294,8 +309,8 @@ class ULongArg(ArgType):
info.codeafter.append(' return PyLong_FromUnsignedLong(ret);\n') info.codeafter.append(' return PyLong_FromUnsignedLong(ret);\n')
class UInt32Arg(ULongArg): class UInt32Arg(ULongArg):
def 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, keeprefcount, info) ULongArg.write_param(self, ptype, pname, pdflt, pnull, info)
## if sizeof(unsigned long) > sizeof(unsigned int), we need to ## if sizeof(unsigned long) > sizeof(unsigned int), we need to
## check the value is within guint32 range ## check the value is within guint32 range
if struct.calcsize('L') > struct.calcsize('I'): if struct.calcsize('L') > struct.calcsize('I'):
@ -308,7 +323,7 @@ class UInt32Arg(ULongArg):
' }\n') % vars()) ' }\n') % vars())
class Int64Arg(ArgType): class Int64Arg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt: if pdflt:
info.varlist.add('gint64', pname + ' = ' + pdflt) info.varlist.add('gint64', pname + ' = ' + pdflt)
else: else:
@ -320,20 +335,26 @@ class Int64Arg(ArgType):
info.codeafter.append(' return PyLong_FromLongLong(ret);') info.codeafter.append(' return PyLong_FromLongLong(ret);')
class UInt64Arg(ArgType): 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: if pdflt:
info.varlist.add('guint64', pname + ' = ' + pdflt) info.varlist.add('guint64', pname + ' = ' + pdflt)
info.codebefore.append(self.dflt % {'name':pname})
else: else:
info.varlist.add('guint64', pname) info.varlist.add('guint64', pname)
info.codebefore.append(self.before % {'name':pname})
info.varlist.add('PyObject', "*py_" + pname + ' = NULL')
info.arglist.append(pname) 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): def write_return(self, ptype, ownsreturn, info):
info.varlist.add('guint64', 'ret') info.varlist.add('guint64', 'ret')
info.codeafter.append(' return PyLong_FromUnsignedLongLong(ret);') info.codeafter.append(' return PyLong_FromUnsignedLongLong(ret);')
class DoubleArg(ArgType): class DoubleArg(ArgType):
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt: if pdflt:
info.varlist.add('double', pname + ' = ' + pdflt) info.varlist.add('double', pname + ' = ' + pdflt)
else: else:
@ -361,7 +382,7 @@ class FileArg(ArgType):
' }\n') ' }\n')
dflt = (' if (py_%(name)s)\n' dflt = (' if (py_%(name)s)\n'
' %(name)s = PyFile_AsFile(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 pnull:
if pdflt: if pdflt:
info.varlist.add('FILE', '*' + pname + ' = ' + pdflt) info.varlist.add('FILE', '*' + pname + ' = ' + pdflt)
@ -391,12 +412,12 @@ class FileArg(ArgType):
' return Py_None;') ' return Py_None;')
class EnumArg(ArgType): 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') ' return NULL;\n')
def __init__(self, enumname, typecode): def __init__(self, enumname, typecode):
self.enumname = enumname self.enumname = enumname
self.typecode = typecode self.typecode = typecode
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt: if pdflt:
info.varlist.add(self.enumname, pname + ' = ' + pdflt) info.varlist.add(self.enumname, pname + ' = ' + pdflt)
else: else:
@ -411,12 +432,12 @@ class EnumArg(ArgType):
info.codeafter.append(' return pyg_enum_from_gtype(%s, ret);' % self.typecode) info.codeafter.append(' return pyg_enum_from_gtype(%s, ret);' % self.typecode)
class FlagsArg(ArgType): 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') ' return NULL;\n')
def __init__(self, flagname, typecode): def __init__(self, flagname, typecode):
self.flagname = flagname self.flagname = flagname
self.typecode = typecode self.typecode = typecode
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pdflt: if pdflt:
info.varlist.add(self.flagname, pname + ' = ' + pdflt) info.varlist.add(self.flagname, pname + ' = ' + pdflt)
default = "py_%s && " % (pname,) default = "py_%s && " % (pname,)
@ -456,7 +477,7 @@ class ObjectArg(ArgType):
self.objname = objname self.objname = objname
self.cast = string.replace(typecode, '_TYPE_', '_', 1) self.cast = string.replace(typecode, '_TYPE_', '_', 1)
self.parent = parent 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 pnull:
if pdflt: if pdflt:
info.varlist.add(self.objname, '*' + pname + ' = ' + pdflt) info.varlist.add(self.objname, '*' + pname + ' = ' + pdflt)
@ -506,99 +527,14 @@ class ObjectArg(ArgType):
info.varlist.add(typename, '*ret') info.varlist.add(typename, '*ret')
if ownsreturn: if ownsreturn:
info.varlist.add('PyObject', '*py_ret') 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' info.codeafter.append(' py_ret = pygobject_new((GObject *)ret);\n'
' if (ret != NULL)\n' ' if (ret != NULL)\n'
' g_object_unref((GObject *)ret);\n' ' g_object_unref(ret);\n'
' return py_ret;') ' return py_ret;')
else: else:
info.codeafter.append(' /* pygobject_new handles NULL checking */\n' + info.codeafter.append(' /* pygobject_new handles NULL checking */\n' +
' return pygobject_new((GObject *)ret);') ' 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): class BoxedArg(ArgType):
# haven't done support for default args. Is it needed? # haven't done support for default args. Is it needed?
check = (' if (pyg_boxed_check(py_%(name)s, %(typecode)s))\n' check = (' if (pyg_boxed_check(py_%(name)s, %(typecode)s))\n'
@ -616,7 +552,7 @@ class BoxedArg(ArgType):
def __init__(self, ptype, typecode): def __init__(self, ptype, typecode):
self.typename = ptype self.typename = ptype
self.typecode = typecode self.typecode = typecode
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pnull: if pnull:
info.varlist.add(self.typename, '*' + pname + ' = NULL') info.varlist.add(self.typename, '*' + pname + ' = NULL')
info.varlist.add('PyObject', '*py_' + pname + ' = Py_None') 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);' ' return pyg_boxed_new(%(typecode)s, %(ret)s, %(copy)s, TRUE);'
def write_return(self, ptype, ownsreturn, info): def write_return(self, ptype, ownsreturn, info):
if ptype[-1] == '*': if ptype[-1] == '*':
info.varlist.add(self.typename, '*ret') decl_type = self.typename
ret = 'ret' ret = 'ret'
if ptype[:6] == 'const-':
decl_type = 'const ' + self.typename
ret = '(%s*) ret' % (self.typename,)
info.varlist.add(decl_type, '*ret')
else: else:
info.varlist.add(self.typename, 'ret') info.varlist.add(self.typename, 'ret')
ret = '&ret' ret = '&ret'
@ -667,7 +607,7 @@ class CustomBoxedArg(ArgType):
self.getter = getter self.getter = getter
self.checker = 'Py' + ptype + '_Check' self.checker = 'Py' + ptype + '_Check'
self.new = new self.new = new
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pnull: if pnull:
info.varlist.add(ptype[:-1], '*' + pname + ' = NULL') info.varlist.add(ptype[:-1], '*' + pname + ' = NULL')
info.varlist.add('PyObject', '*py_' + pname + ' = Py_None') info.varlist.add('PyObject', '*py_' + pname + ' = Py_None')
@ -705,7 +645,7 @@ class PointerArg(ArgType):
def __init__(self, ptype, typecode): def __init__(self, ptype, typecode):
self.typename = ptype self.typename = ptype
self.typecode = typecode self.typecode = typecode
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pnull: if pnull:
info.varlist.add(self.typename, '*' + pname + ' = NULL') info.varlist.add(self.typename, '*' + pname + ' = NULL')
info.varlist.add('PyObject', '*py_' + pname + ' = Py_None') 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' atom = (' %(name)s = pygdk_atom_from_pyobject(py_%(name)s);\n'
' if (PyErr_Occurred())\n' ' if (PyErr_Occurred())\n'
' return NULL;\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: if pdflt:
info.varlist.add('GdkAtom', pname + ' = ' + pdflt) info.varlist.add('GdkAtom', pname + ' = ' + pdflt)
info.varlist.add('PyObject', '*py_' + pname + ' = NULL') info.varlist.add('PyObject', '*py_' + pname + ' = NULL')
@ -762,7 +702,7 @@ class AtomArg(IntArg):
class GTypeArg(ArgType): class GTypeArg(ArgType):
gtype = (' if ((%(name)s = pyg_type_from_object(py_%(name)s)) == 0)\n' gtype = (' if ((%(name)s = pyg_type_from_object(py_%(name)s)) == 0)\n'
' return NULL;\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('GType', pname)
info.varlist.add('PyObject', '*py_' + pname + ' = NULL') info.varlist.add('PyObject', '*py_' + pname + ' = NULL')
info.codebefore.append(self.gtype % {'name': pname}) info.codebefore.append(self.gtype % {'name': pname})
@ -776,7 +716,7 @@ class GTypeArg(ArgType):
class GErrorArg(ArgType): class GErrorArg(ArgType):
handleerror = (' if (pyg_error_check(&%(name)s))\n' handleerror = (' if (pyg_error_check(&%(name)s))\n'
' return NULL;\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.varlist.add('GError', '*' + pname + ' = NULL')
info.arglist.append('&' + pname) info.arglist.append('&' + pname)
info.codeafter.append(self.handleerror % { 'name': pname }) info.codeafter.append(self.handleerror % { 'name': pname })
@ -799,7 +739,7 @@ class GtkTreePathArg(ArgType):
' gtk_tree_path_free(%(name)s);\n') ' gtk_tree_path_free(%(name)s);\n')
def __init__(self): def __init__(self):
pass pass
def write_param(self, ptype, pname, pdflt, pnull, keeprefcount, info): def write_param(self, ptype, pname, pdflt, pnull, info):
if pnull: if pnull:
info.varlist.add('GtkTreePath', '*' + pname + ' = NULL') info.varlist.add('GtkTreePath', '*' + pname + ' = NULL')
info.varlist.add('PyObject', '*py_' + pname + ' = Py_None') info.varlist.add('PyObject', '*py_' + pname + ' = Py_None')
@ -840,7 +780,7 @@ class GdkRectanglePtrArg(ArgType):
' %(name)s = &%(name)s_rect;\n' ' %(name)s = &%(name)s_rect;\n'
' else\n' ' else\n'
' return NULL;\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: if pnull:
info.varlist.add('GdkRectangle', pname + '_rect = { 0, 0, 0, 0 }') info.varlist.add('GdkRectangle', pname + '_rect = { 0, 0, 0, 0 }')
info.varlist.add('GdkRectangle', '*' + pname) 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);') info.codeafter.append(' return pyg_boxed_new(GDK_TYPE_RECTANGLE, &ret, TRUE, TRUE);')
class PyObjectArg(ArgType): 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.varlist.add('PyObject', '*' + pname)
info.add_parselist('O', ['&' + pname], [pname]) info.add_parselist('O', ['&' + pname], [pname])
info.arglist.append(pname) info.arglist.append(pname)
@ -878,13 +818,29 @@ class PyObjectArg(ArgType):
' Py_INCREF(ret);\n' ' Py_INCREF(ret);\n'
' return ret;') ' 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: class ArgMatcher:
def __init__(self): def __init__(self):
self.argtypes = {} self.argtypes = {}
self.reverse_argtypes = {} self.reverse_argtypes = {}
self.reverse_rettypes = {} 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 self.argtypes[ptype] = handler
def register_reverse(self, ptype, handler): def register_reverse(self, ptype, handler):
self.reverse_argtypes[ptype] = handler self.reverse_argtypes[ptype] = handler
@ -893,12 +849,14 @@ class ArgMatcher:
def register_enum(self, ptype, typecode): def register_enum(self, ptype, typecode):
if typecode is None: if typecode is None:
typecode = "G_TYPE_NONE" self.register(ptype, IntArg())
self.register(ptype, EnumArg(ptype, typecode)) else:
self.register(ptype, EnumArg(ptype, typecode))
def register_flag(self, ptype, typecode): def register_flag(self, ptype, typecode):
if typecode is None: if typecode is None:
typecode = "G_TYPE_NONE" self.register(ptype, IntArg())
self.register(ptype, FlagsArg(ptype, typecode)) else:
self.register(ptype, FlagsArg(ptype, typecode))
def register_object(self, ptype, parent, typecode): def register_object(self, ptype, parent, typecode):
oa = ObjectArg(ptype, parent, typecode) oa = ObjectArg(ptype, parent, typecode)
self.register(ptype, oa) # in case I forget the * in the .defs self.register(ptype, oa) # in case I forget the * in the .defs
@ -908,10 +866,6 @@ class ArgMatcher:
# hack to handle GdkBitmap synonym. # hack to handle GdkBitmap synonym.
self.register('GdkBitmap', oa) self.register('GdkBitmap', oa)
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): def register_boxed(self, ptype, typecode):
if self.argtypes.has_key(ptype): return if self.argtypes.has_key(ptype): return
arg = BoxedArg(ptype, typecode) arg = BoxedArg(ptype, typecode)
@ -934,7 +888,7 @@ class ArgMatcher:
except KeyError: except KeyError:
if ptype[:8] == 'GdkEvent' and ptype[-1] == '*': if ptype[:8] == 'GdkEvent' and ptype[-1] == '*':
return self.argtypes['GdkEvent*'] return self.argtypes['GdkEvent*']
raise raise ArgTypeNotFoundError("No ArgType for %s" % (ptype,))
def _get_reverse_common(self, ptype, registry): def _get_reverse_common(self, ptype, registry):
props = dict(c_type=ptype) props = dict(c_type=ptype)
try: try:
@ -946,7 +900,7 @@ class ArgMatcher:
if ptype.startswith('GdkEvent') and ptype.endswith('*'): if ptype.startswith('GdkEvent') and ptype.endswith('*'):
handler = self.argtypes['GdkEvent*'] handler = self.argtypes['GdkEvent*']
else: else:
raise raise ArgTypeNotFoundError("No ArgType for %s" % (ptype,))
if isinstance(handler, ObjectArg): if isinstance(handler, ObjectArg):
return registry['GObject*'], props return registry['GObject*'], props
elif isinstance(handler, EnumArg): elif isinstance(handler, EnumArg):
@ -962,11 +916,20 @@ class ArgMatcher:
props['typename'] = handler.typename props['typename'] = handler.typename
return registry['GBoxed'], props return registry['GBoxed'], props
else: else:
raise raise ArgTypeNotFoundError("No ArgType for %s" % (ptype,))
def get_reverse(self, ptype): def get_reverse(self, ptype):
return self._get_reverse_common(ptype, self.reverse_argtypes) return self._get_reverse_common(ptype, self.reverse_argtypes)
def get_reverse_ret(self, ptype): 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): def object_is_a(self, otype, parent):
if otype == None: return 0 if otype == None: return 0
@ -993,6 +956,7 @@ matcher.register('static_string', arg)
arg = UCharArg() arg = UCharArg()
matcher.register('unsigned-char*', arg) matcher.register('unsigned-char*', arg)
matcher.register('const-guchar*', arg) matcher.register('const-guchar*', arg)
matcher.register('const-guint8*', arg)
matcher.register('guchar*', arg) matcher.register('guchar*', arg)
arg = CharArg() arg = CharArg()
@ -1017,6 +981,7 @@ matcher.register('guint16', arg)
matcher.register('gint16', arg) matcher.register('gint16', arg)
matcher.register('gint32', arg) matcher.register('gint32', arg)
matcher.register('GTime', arg) matcher.register('GTime', arg)
matcher.register('GSeekType', arg) # Hack, but we have no python wrapper
arg = LongArg() arg = LongArg()
matcher.register('long', arg) matcher.register('long', arg)
@ -1039,6 +1004,7 @@ matcher.register('gulong', arg)
arg = Int64Arg() arg = Int64Arg()
matcher.register('gint64', arg) matcher.register('gint64', arg)
matcher.register('long-long', arg) matcher.register('long-long', arg)
matcher.register('goffset', arg)
arg = UInt64Arg() arg = UInt64Arg()
matcher.register('guint64', arg) matcher.register('guint64', arg)
@ -1070,6 +1036,8 @@ matcher.register('PyObject*', PyObjectArg())
matcher.register('GdkNativeWindow', ULongArg()) matcher.register('GdkNativeWindow', ULongArg())
matcher.register_object('GObject', None, 'G_TYPE_OBJECT') matcher.register_object('GObject', None, 'G_TYPE_OBJECT')
matcher.register_miniobject('GstMiniObject', None, 'GST_TYPE_MINI_OBJECT')
del arg 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 from __future__ import generators
import sys, os 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 # New Parameter class, wich emulates a tuple for compatibility reasons
class Parameter(object): 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.ptype = ptype
self.pname = pname self.pname = pname
self.pdflt = pdflt self.pdflt = pdflt
self.pnull = pnull self.pnull = pnull
self.pdir = pdir self.pdir = pdir
self.keeprefcount = keeprefcount
def __len__(self): return 4 def __len__(self): return 4
def __getitem__(self, i): def __getitem__(self, i):
return (self.ptype, self.pname, self.pdflt, self.pnull)[i] return (self.ptype, self.pname, self.pdflt, self.pnull)[i]
@ -32,6 +31,17 @@ class Parameter(object):
if old.pnull is not None: if old.pnull is not None:
self.pnull = old.pnull 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 # Parameter for property based constructors
class Property(object): class Property(object):
def __init__(self, pname, optional, argname): def __init__(self, pname, optional, argname):
@ -39,6 +49,10 @@ class Property(object):
self.optional = optional self.optional = optional
self.argname = argname self.argname = argname
def __len__(self): return 4
def __getitem__(self, i):
return ('', self.pname, self.optional, self.argname)[i]
def merge(self, old): def merge(self, old):
if old.optional is not None: if old.optional is not None:
self.optional = old.optional self.optional = old.optional
@ -46,18 +60,26 @@ class Property(object):
self.argname = old.argname self.argname = old.argname
class Definition: class Definition(object):
docstring = "NULL" docstring = "NULL"
def py_name(self):
return '%s.%s' % (self.module, self.name)
py_name = property(py_name)
def __init__(self, *args): def __init__(self, *args):
"""Create a new defs object of this type. The arguments are the """Create a new defs object of this type. The arguments are the
components of the definition""" components of the definition"""
raise RuntimeError, "this is an abstract class" raise RuntimeError("this is an abstract class")
def merge(self, old): def merge(self, old):
"""Merge in customisations from older version of definition""" """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): def write_defs(self, fp=sys.stdout):
"""write out this definition in defs file format""" """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): def guess_return_value_ownership(self):
"return 1 if caller owns return value" "return 1 if caller owns return value"
@ -119,58 +141,7 @@ class ObjectDef(Definition):
for (ftype, fname) in self.fields: for (ftype, fname) in self.fields:
fp.write(' \'("' + ftype + '" "' + fname + '")\n') fp.write(' \'("' + ftype + '" "' + fname + '")\n')
fp.write(' )\n') fp.write(' )\n')
fp.write(')\n\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')
class InterfaceDef(Definition): class InterfaceDef(Definition):
def __init__(self, name, *args): def __init__(self, name, *args):
@ -349,7 +320,12 @@ class MethodDefBase(Definition):
elif arg[0] == 'gtype-id': elif arg[0] == 'gtype-id':
self.typecode = arg[1] self.typecode = arg[1]
elif arg[0] == 'return-type': 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': elif arg[0] == 'caller-owns-return':
self.caller_owns_return = arg[1] in ('t', '#t') self.caller_owns_return = arg[1] in ('t', '#t')
elif arg[0] == 'unblock-threads': elif arg[0] == 'unblock-threads':
@ -360,8 +336,7 @@ class MethodDefBase(Definition):
pname = parg[1] pname = parg[1]
pdflt = None pdflt = None
pnull = 0 pnull = 0
pdir = None pdir = None
keeprefcount = False
for farg in parg[2:]: for farg in parg[2:]:
assert isinstance(farg, tuple) assert isinstance(farg, tuple)
if farg[0] == 'default': if farg[0] == 'default':
@ -370,10 +345,7 @@ class MethodDefBase(Definition):
pnull = 1 pnull = 1
elif farg[0] == 'direction': elif farg[0] == 'direction':
pdir = farg[1] pdir = farg[1]
elif farg[0] == 'keep-refcount': self.params.append(Parameter(ptype, pname, pdflt, pnull, pdir))
keeprefcount = True
self.params.append(Parameter(ptype, pname, pdflt, pnull, pdir,
keeprefcount=keeprefcount))
elif arg[0] == 'varargs': elif arg[0] == 'varargs':
self.varargs = arg[1] in ('t', '#t') self.varargs = arg[1] in ('t', '#t')
elif arg[0] == 'deprecated': elif arg[0] == 'deprecated':
@ -435,7 +407,7 @@ class MethodDef(MethodDefBase):
for item in ('c_name', 'of_object'): for item in ('c_name', 'of_object'):
if self.__dict__[item] == None: if self.__dict__[item] == None:
self.write_defs(sys.stderr) 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): def write_defs(self, fp=sys.stdout):
fp.write('(define-method ' + self.name + '\n') fp.write('(define-method ' + self.name + '\n')
@ -483,17 +455,13 @@ class FunctionDef(Definition):
pname = parg[1] pname = parg[1]
pdflt = None pdflt = None
pnull = 0 pnull = 0
keeprefcount = False
for farg in parg[2:]: for farg in parg[2:]:
if farg[0] == 'default': if farg[0] == 'default':
pdflt = farg[1] pdflt = farg[1]
elif farg[0] == 'null-ok': elif farg[0] == 'null-ok':
pnull = 1 pnull = 1
elif farg[0] == 'keep-refcount': self.params.append(Parameter(ptype, pname, pdflt, pnull))
keeprefcount = True elif arg[0] == 'properties':
self.params.append(Parameter(ptype, pname, pdflt, pnull,
keeprefcount = keeprefcount))
elif arg[0] == 'properties':
if self.is_constructor_of is None: if self.is_constructor_of is None:
print >> sys.stderr, "Warning: (properties ...) "\ print >> sys.stderr, "Warning: (properties ...) "\
"is only valid for constructors" "is only valid for constructors"
@ -523,7 +491,7 @@ class FunctionDef(Definition):
for item in ('c_name',): for item in ('c_name',):
if self.__dict__[item] == None: if self.__dict__[item] == None:
self.write_defs(sys.stderr) 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'] _method_write_defs = MethodDef.__dict__['write_defs']
@ -545,8 +513,8 @@ class FunctionDef(Definition):
else: else:
param.merge(old_param) param.merge(old_param)
return param return param
raise RuntimeError, "could not find %s in old_parameters %r" % ( raise RuntimeError("could not find %s in old_parameters %r" % (
param.pname, [p.pname for p in old.params]) param.pname, [p.pname for p in old.params]))
try: try:
self.params = map(merge_param, self.params) self.params = map(merge_param, self.params)
except RuntimeError: except RuntimeError:

View file

@ -2,26 +2,40 @@
import os, sys import os, sys
import scmexpr import scmexpr
from definitions import BoxedDef, EnumDef, FlagsDef, FunctionDef, \ from definitions import BoxedDef, EnumDef, FlagsDef, FunctionDef, \
InterfaceDef, MethodDef, ObjectDef, MiniObjectDef, PointerDef, \ InterfaceDef, MethodDef, ObjectDef, PointerDef, VirtualDef
VirtualDef
include_path = ['.']
class IncludeParser(scmexpr.Parser): class IncludeParser(scmexpr.Parser):
"""A simple parser that follows include statements automatically""" """A simple parser that follows include statements automatically"""
def include(self, filename): def include(self, input_filename):
if not os.path.isabs(filename): global include_path
filename = os.path.join(os.path.dirname(self.filename), filename) if os.path.isabs(input_filename):
filename = input_filename
# set self.filename to the include name, to handle recursive includes # set self.filename to the include name, to handle recursive includes
oldfile = self.filename oldfile = self.filename
self.filename = filename self.filename = filename
self.startParsing() self.startParsing()
self.filename = oldfile 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): class DefsParser(IncludeParser):
def __init__(self, arg, defines={}): def __init__(self, arg, defines={}):
IncludeParser.__init__(self, arg) IncludeParser.__init__(self, arg)
self.objects = [] self.objects = []
self.miniobjects = []
self.interfaces = [] self.interfaces = []
self.enums = [] # enums and flags self.enums = [] # enums and flags
self.boxes = [] # boxed types self.boxes = [] # boxed types
@ -33,13 +47,8 @@ class DefsParser(IncludeParser):
self.defines = defines # -Dfoo=bar options, as dictionary self.defines = defines # -Dfoo=bar options, as dictionary
def define_object(self, *args): def define_object(self, *args):
odef = apply(ObjectDef, args) odef = apply(ObjectDef, args)
self.objects.append(odef) 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)
self.c_name[odef.c_name] = odef self.c_name[odef.c_name] = odef
def define_interface(self, *args): def define_interface(self, *args):
idef = apply(InterfaceDef, args) idef = apply(InterfaceDef, args)
@ -89,10 +98,7 @@ class DefsParser(IncludeParser):
f.write_defs() f.write_defs()
def write_defs(self, fp=sys.stdout): def write_defs(self, fp=sys.stdout):
for obj in self.objects: for obj in self.objects:
obj.write_defs(fp)
# TODO: Add miniobject
for obj in self.miniobjects:
obj.write_defs(fp) obj.write_defs(fp)
for enum in self.enums: for enum in self.enums:
enum.write_defs(fp) enum.write_defs(fp)
@ -108,7 +114,7 @@ class DefsParser(IncludeParser):
if obj.c_name == c_name: if obj.c_name == c_name:
return obj return obj
else: else:
raise ValueError, 'object not found' raise ValueError('object %r not found' % c_name)
def find_constructor(self, obj, overrides): def find_constructor(self, obj, overrides):
for func in self.functions: for func in self.functions:
@ -135,7 +141,11 @@ class DefsParser(IncludeParser):
def ifdef(self, *args): def ifdef(self, *args):
if args[0] in self.defines: if args[0] in self.defines:
for arg in args[1:]: for arg in args[1:]:
#print >> sys.stderr, "-----> Handling conditional definition (%s): %s" % (args[0], arg)
self.handle(arg) self.handle(arg)
else:
pass
#print >> sys.stderr, "-----> Conditional %s is not true" % (args[0],)
def ifndef(self, *args): def ifndef(self, *args):
if args[0] not in self.defines: 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 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'] __all__ = ['extract']
class FunctionDoc: class GtkDoc:
def __init__(self): def __init__(self):
self.name = None self.name = None
self.block_type = '' # The block type ('function', 'signal', 'property')
self.params = [] self.params = []
self.annotations = []
self.description = '' self.description = ''
self.ret = '' self.ret = ('', []) # (return, annotations)
def set_name(self, name): def set_name(self, name):
self.name = 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 == '...': if name == '...':
name = 'Varargs' name = 'Varargs'
self.params.append((name, description)) self.params.append((name, description, annotations))
def append_to_last_param(self, extra): 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): def append_to_named_param(self, name, extra):
for i in range(len(self.params)): for i in range(len(self.params)):
if self.params[i][0] == name: 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 return
# fall through to adding extra parameter ... # fall through to adding extra parameter ...
self.add_param(name, extra) 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 self.description = self.description + extra
def append_return(self, extra): def get_description(self):
self.ret = self.ret + extra 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): comment_start_pattern = re.compile(r'^\s*/\*\*\s')
for param, description in self.params: comment_end_pattern = re.compile(r'^\s*\*+/')
if param == name: comment_line_lead_pattern = re.compile(r'^\s*\*\s*')
return description comment_empty_line_pattern = re.compile(r'^\s*\**\s*$')
else: function_name_pattern = re.compile(r'^([a-z]\w*)\s*:?(\s*\(.*\)\s*){0,2}\s*$')
return '' 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') # These patterns determine the identifier of the current comment block. They
comment_end_pat = re.compile(r'^\s*\*+/') # are grouped in a list for easy determination of block identifiers (in
comment_line_lead = re.compile(r'^\s*\*\s*') # skip_to_identifier). The function_name_pattern should be tested for last
funcname_pat = re.compile(r'^(\w+)\s*:?') # because it always matches signal and property identifiers.
return_pat = re.compile(r'^(returns:|return\s+value:|returns\s*)(.*\n?)$', identifier_patterns = [ signal_name_pattern, property_name_pattern, function_name_pattern ]
re.IGNORECASE)
param_pat = re.compile(r'^@(\S+)\s*:(.*\n?)$') # 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): def parse_file(fp, doc_dict):
line = fp.readline() line = fp.readline()
in_comment_block = 0
while line: while line:
if not in_comment_block: cur_doc = GtkDoc()
if comment_start_pat.match(line): line = skip_to_comment_block(fp, line)
in_comment_block = 1 line = skip_to_identifier(fp, line, cur_doc)
cur_doc = FunctionDoc() # See if the identifier is found (stored in the current GtkDoc by
in_description = 0 # skip_to_identifier). If so, continue reading the rest of the comment
in_return = 0 # block.
line = fp.readline() if cur_doc.name:
continue 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 ... # Given a list of annotations as string of the form
if comment_end_pat.match(line): # '(annotation1) (annotation2) ...' return a list of annotations of the form
if not cur_doc.name: # [ (name1, value1), (name2, value2) ... ]. Not all annotations have values so
sys.stderr.write("no function name found in doc comment\n") # the values in the list of tuples could be empty ('').
else: def get_annotation_list(annotations):
doc_dict[cur_doc.name] = cur_doc annotation_list = []
in_comment_block = 0 while annotations:
line = fp.readline() match = annotation_lead_pattern.match(annotations)
continue 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 ... # Given a currently read line, test that line and continue reading until the
line = comment_line_lead.sub('', line) # 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 line: line = '\n'
if not cur_doc.name: # Given the line that ended the description (the first line of one of the final
match = funcname_pat.match(line) # 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: if match:
cur_doc.set_name(match.group(1)) if pattern == return_pattern or \
elif in_return: pattern == no_colon_return_pattern:
match = return_pat.match(line) # Dealing with a 'Returns:' so first extract the
if match: # annotations from the description and save them.
# assume the last return statement was really part of the description = match.group(2)
# description annotations = []
return_start = match.group(1) annotation_match = \
cur_doc.ret = match.group(2) annotations_pattern.match(description)
cur_doc.description = cur_doc.description + return_start + \ if annotation_match:
cur_doc.ret annotations = \
else: get_annotation_list(annotation_match.group(1))
cur_doc.append_return(line) # Remove the annotations from the description
elif in_description: description = annotations_pattern.sub('', description)
if line[:12] == 'Description:':
line = line[12:] # Now add the return.
match = return_pat.match(line) cur_doc.add_return(description, annotations)
if match: # In case more lines need to be appended.
in_return = 1 append_func = cur_doc.append_to_return
return_start = match.group(1) elif pattern == rename_to_pattern:
cur_doc.append_return(match.group(2)) # Dealing with a 'Rename to:' section (GObjectIntrospection
else: # annotation) so no further lines will be appended but this
cur_doc.append_description(line) # single one (and only to the annotations).
elif line == '\n': append_func = None
# end of parameters cur_doc.add_annotation((match.group(1),
in_description = 1 match.group(2)))
else:
match = param_pat.match(line)
if match:
param = match.group(1)
desc = match.group(2)
if param == 'returns':
cur_doc.ret = desc
else: else:
cur_doc.add_param(param, desc) # For all others ('Since:' and 'Deprecated:') just append
else: # the line to the description for now.
# must be continuation # But if --no-since is specified, don't append it.
try: if no_since and pattern == since_pattern:
if param == 'returns': pass
cur_doc.append_return(line)
else: else:
cur_doc.append_to_last_param(line) cur_doc.append_to_description(line)
except:
sys.stderr.write('something weird while reading param\n') # 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() 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): def parse_dir(dir, doc_dict):
for file in os.listdir(dir): for file in os.listdir(dir):
if file in ('.', '..'): continue if file in ('.', '..'): continue
@ -129,6 +404,7 @@ def parse_dir(dir, doc_dict):
if os.path.isdir(path): if os.path.isdir(path):
parse_dir(path, doc_dict) parse_dir(path, doc_dict)
if len(file) > 2 and file[-2:] == '.c': if len(file) > 2 and file[-2:] == '.c':
sys.stderr.write("Processing " + path + '\n')
parse_file(open(path, 'r'), doc_dict) parse_file(open(path, 'r'), doc_dict)
def extract(dirs, doc_dict=None): def extract(dirs, doc_dict=None):
@ -137,13 +413,13 @@ def extract(dirs, doc_dict=None):
parse_dir(dir, doc_dict) parse_dir(dir, doc_dict)
return 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): def parse_tmpl(fp, doc_dict):
cur_doc = None cur_doc = None
line = fp.readline() line = fp.readline()
while line: while line:
match = tmpl_section_pat.match(line) match = tmpl_section_pattern.match(line)
if match: if match:
cur_doc = None # new input shouldn't affect the old doc dict cur_doc = None # new input shouldn't affect the old doc dict
sect_type = match.group(1) sect_type = match.group(1)
@ -152,7 +428,7 @@ def parse_tmpl(fp, doc_dict):
if sect_type == 'FUNCTION': if sect_type == 'FUNCTION':
cur_doc = doc_dict.get(sect_name) cur_doc = doc_dict.get(sect_name)
if not cur_doc: if not cur_doc:
cur_doc = FunctionDoc() cur_doc = GtkDoc()
cur_doc.set_name(sect_name) cur_doc.set_name(sect_name)
doc_dict[sect_name] = cur_doc doc_dict[sect_name] = cur_doc
elif line == '<!-- # Unused Parameters # -->\n': elif line == '<!-- # Unused Parameters # -->\n':
@ -160,15 +436,15 @@ def parse_tmpl(fp, doc_dict):
elif cur_doc: elif cur_doc:
if line[:10] == '@Returns: ': if line[:10] == '@Returns: ':
if string.strip(line[10:]): if string.strip(line[10:]):
cur_doc.append_return(line[10:]) cur_doc.append_to_return(line[10:])
elif line[0] == '@': elif line[0] == '@':
pos = string.find(line, ':') pos = string.find(line, ':')
if pos >= 0: if pos >= 0:
cur_doc.append_to_named_param(line[1:pos], line[pos+1:]) cur_doc.append_to_named_param(line[1:pos], line[pos+1:])
else: else:
cur_doc.append_description(line) cur_doc.append_to_description(line)
else: else:
cur_doc.append_description(line) cur_doc.append_to_description(line)
line = fp.readline() 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 #!/usr/bin/env python
# -*- Mode: Python; py-indent-offset: 4 -*- # -*- 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 # GPL'ed
# Toby D. Reeves <toby@max.rl.plh.af.mil> # Toby D. Reeves <toby@max.rl.plh.af.mil>
# #
# Modified by James Henstridge <james@daa.com.au> to output stuff in # 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: # 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 # 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 getopt
import os import os
@ -33,16 +88,16 @@ def to_upper_str(name):
name = _upperstr_pat3.sub(r'\1_\2', name, count=1) name = _upperstr_pat3.sub(r'\1_\2', name, count=1)
return string.upper(name) return string.upper(name)
def typecode(typename): def typecode(typename, namespace=None):
"""create a typecode (eg. GTK_TYPE_WIDGET) from a typename""" """create a typecode (eg. GTK_TYPE_WIDGET) from a typename"""
typename2 = to_upper_str(typename) if namespace:
typename2 = string.replace(typename2, '_', "", 1) return string.replace(string.upper(namespace) + "_" + to_upper_str(typename[len(namespace):]), '_', '_TYPE_', 1)
typename2 = typename2[:3] + '_TYPE' + typename2[3:]
return typename2 return string.replace(to_upper_str(typename), '_', '_TYPE_', 1)
# ------------------ Find object definitions ----------------- # ------------------ Find object definitions -----------------
# Strips the comments from buffer
def strip_comments(buf): def strip_comments(buf):
parts = [] parts = []
lastpos = 0 lastpos = 0
@ -60,6 +115,12 @@ def strip_comments(buf):
break break
return string.join(parts, '') 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]*" 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]+)') 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. # filter out comments from buffer.
buf = strip_comments(buf) buf = strip_comments(buf)
# filter out dll api
buf = strip_dll_api(buf)
maybeobjdefs = [] # contains all possible objects from file maybeobjdefs = [] # contains all possible objects from file
# first find all structures that look like they may represent a GtkObject # 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) "(" + obj_name_pat + ")\s+", re.MULTILINE)
pos = 0 pos = 0
while pos < len(buf): while pos < len(buf):
@ -92,11 +156,11 @@ def find_obj_defs(buf, objdefs=[]):
while pos < len(buf): while pos < len(buf):
m = pat.search(buf, pos) m = pat.search(buf, pos)
if not m: break if not m: break
maybeobjdefs.append((m.group(2), m.group(2))) maybeobjdefs.append((m.group(2), m.group(1)))
pos = m.end() pos = m.end()
# now find all structures that look like they might represent a class: # 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) "(" + obj_name_pat + ")Class\s+", re.MULTILINE)
pos = 0 pos = 0
while pos < len(buf): while pos < len(buf):
@ -125,7 +189,7 @@ def find_obj_defs(buf, objdefs=[]):
# now find all structures that look like they might represent # now find all structures that look like they might represent
# a class inherited from GTypeInterface: # 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) "GTypeInterface\s+", re.MULTILINE)
pos = 0 pos = 0
while pos < len(buf): while pos < len(buf):
@ -141,7 +205,7 @@ def find_obj_defs(buf, objdefs=[]):
# now find all structures that look like they might represent # now find all structures that look like they might represent
# an Iface inherited from GTypeInterface: # 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) "GTypeInterface\s+", re.MULTILINE)
pos = 0 pos = 0
while pos < len(buf): while pos < len(buf):
@ -177,6 +241,13 @@ def find_enum_defs(buf, enums=[]):
# bulk comments # bulk comments
buf = strip_comments(buf) 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) buf = re.sub('\n', ' ', buf)
enum_pat = re.compile(r'enum\s*{([^}]*)}\s*([A-Z][A-Za-z]*)(\s|;)') enum_pat = re.compile(r'enum\s*{([^}]*)}\s*([A-Z][A-Za-z]*)(\s|;)')
@ -209,6 +280,9 @@ def clean_func(buf):
# bulk comments # bulk comments
buf = strip_comments(buf) buf = strip_comments(buf)
# dll api
buf = strip_dll_api(buf)
# compact continued lines # compact continued lines
pat = re.compile(r"""\\\n""", re.MULTILINE) pat = re.compile(r"""\\\n""", re.MULTILINE)
buf = pat.sub('', buf) buf = pat.sub('', buf)
@ -247,9 +321,14 @@ def clean_func(buf):
buf = pat.sub(r'[] \1', buf) buf = pat.sub(r'[] \1', buf)
# make return types that are const work. # 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, 'G_CONST_RETURN ', 'const-')
buf = string.replace(buf, 'const ', '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 return buf
proto_pat=re.compile(r""" proto_pat=re.compile(r"""
@ -266,13 +345,14 @@ pointer_pat = re.compile('.*\*$')
func_new_pat = re.compile('(\w+)_new$') func_new_pat = re.compile('(\w+)_new$')
class DefsWriter: class DefsWriter:
def __init__(self, fp=None, prefix=None, verbose=False, def __init__(self, fp=None, prefix=None, ns=None, verbose=False,
defsfilter=None): defsfilter=None):
if not fp: if not fp:
fp = sys.stdout fp = sys.stdout
self.fp = fp self.fp = fp
self.prefix = prefix self.prefix = prefix
self.namespace = ns
self.verbose = verbose self.verbose = verbose
self._enums = {} self._enums = {}
@ -284,9 +364,9 @@ class DefsWriter:
for func in filter.functions + filter.methods.values(): for func in filter.functions + filter.methods.values():
self._functions[func.c_name] = func self._functions[func.c_name] = func
for obj in filter.objects + filter.boxes + filter.interfaces: 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: for obj in filter.enums:
self._enums[obj.c_name] = func self._enums[obj.c_name] = obj
def write_def(self, deffile): def write_def(self, deffile):
buf = open(deffile).read() buf = open(deffile).read()
@ -309,10 +389,14 @@ class DefsWriter:
continue continue
name = cname name = cname
module = None module = None
m = split_prefix_pat.match(cname) if self.namespace:
if m: module = self.namespace
module = m.group(1) name = cname[len(self.namespace):]
name = m.group(2) else:
m = split_prefix_pat.match(cname)
if m:
module = m.group(1)
name = m.group(2)
if isflags: if isflags:
fp.write('(define-flags ' + name + '\n') fp.write('(define-flags ' + name + '\n')
else: else:
@ -320,12 +404,13 @@ class DefsWriter:
if module: if module:
fp.write(' (in-module "' + module + '")\n') fp.write(' (in-module "' + module + '")\n')
fp.write(' (c-name "' + cname + '")\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] prefix = entries[0]
for ent in entries: for ent in entries:
# shorten prefix til we get a match ... # shorten prefix til we get a match ...
# and handle GDK_FONT_FONT, GDK_FONT_FONTSET case # 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 = prefix[:-1]
prefix_len = len(prefix) prefix_len = len(prefix)
fp.write(' (values\n') fp.write(' (values\n')
@ -347,19 +432,23 @@ class DefsWriter:
if filter: if filter:
if klass in filter: if klass in filter:
continue continue
m = split_prefix_pat.match(klass) if self.namespace:
cmodule = None cname = klass[len(self.namespace):]
cname = klass cmodule = self.namespace
if m: else:
cmodule = m.group(1) m = split_prefix_pat.match(klass)
cname = m.group(2) cname = klass
cmodule = None
if m:
cmodule = m.group(1)
cname = m.group(2)
fp.write('(define-object ' + cname + '\n') fp.write('(define-object ' + cname + '\n')
if cmodule: if cmodule:
fp.write(' (in-module "' + cmodule + '")\n') fp.write(' (in-module "' + cmodule + '")\n')
if parent: if parent:
fp.write(' (parent "' + parent + '")\n') fp.write(' (parent "' + parent + '")\n')
fp.write(' (c-name "' + klass + '")\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 # should do something about accessible fields
fp.write(')\n\n') fp.write(')\n\n')
@ -475,11 +564,12 @@ def main(args):
onlyobjdefs = False onlyobjdefs = False
separate = False separate = False
modulename = None modulename = None
namespace = None
defsfilter = None defsfilter = None
opts, args = getopt.getopt(args[1:], 'vs:m:f:', opts, args = getopt.getopt(args[1:], 'vs:m:n:f:',
['onlyenums', 'onlyobjdefs', ['onlyenums', 'onlyobjdefs',
'modulename=', 'separate=', 'modulename=', 'namespace=',
'defsfilter=']) 'separate=', 'defsfilter='])
for o, v in opts: for o, v in opts:
if o == '-v': if o == '-v':
verbose = True verbose = True
@ -491,6 +581,8 @@ def main(args):
separate = v separate = v
if o in ('-m', '--modulename'): if o in ('-m', '--modulename'):
modulename = v modulename = v
if o in ('-n', '--namespace'):
namespace = v
if o in ('-f', '--defsfilter'): if o in ('-f', '--defsfilter'):
defsfilter = v defsfilter = v
@ -511,8 +603,8 @@ def main(args):
methods = file(separate + '.defs', 'w') methods = file(separate + '.defs', 'w')
types = file(separate + '-types.defs', 'w') types = file(separate + '-types.defs', 'w')
dw = DefsWriter(methods, prefix=modulename, verbose=verbose, dw = DefsWriter(methods, prefix=modulename, ns=namespace,
defsfilter=defsfilter) verbose=verbose, defsfilter=defsfilter)
dw.write_obj_defs(objdefs, types) dw.write_obj_defs(objdefs, types)
dw.write_enum_defs(enums, types) dw.write_enum_defs(enums, types)
print "Wrote %s-types.defs" % separate print "Wrote %s-types.defs" % separate
@ -521,8 +613,8 @@ def main(args):
dw.write_def(filename) dw.write_def(filename)
print "Wrote %s.defs" % separate print "Wrote %s.defs" % separate
else: else:
dw = DefsWriter(prefix=modulename, verbose=verbose, dw = DefsWriter(prefix=modulename, ns=namespace,
defsfilter=defsfilter) verbose=verbose, defsfilter=defsfilter)
if onlyenums: if onlyenums:
dw.write_enum_defs(enums) dw.write_enum_defs(enums)

View file

@ -19,10 +19,13 @@ def class2cname(klass, method):
c_name += c c_name += c
return c_name[1:] + '_' + method 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: class Overrides:
def __init__(self, filename=None, path=[]): def __init__(self, filename=None):
self.modulename = None self.modulename = None
self.ignores = {} self.ignores = {}
self.glob_ignores = [] self.glob_ignores = []
@ -43,27 +46,16 @@ class Overrides:
self.imports = [] self.imports = []
self.defines = {} self.defines = {}
self.functions = {} self.functions = {}
self.newstyle_constructors = {} self.newstyle_constructors = {}
self.path = [os.path.abspath(x) for x in path] self.dynamicnamespace = False
if filename: if filename:
self.handle_file(filename) self.handle_file(filename)
def handle_file(self, filename): def handle_file(self, filename):
oldpath = os.getcwd() oldpath = os.getcwd()
fp = None fp = open(filename, 'r')
for path in self.path: dirname = os.path.dirname(os.path.abspath(filename))
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
if dirname != oldpath: if dirname != oldpath:
os.chdir(dirname) os.chdir(dirname)
@ -177,7 +169,8 @@ class Overrides:
for line in string.split(buffer, '\n'): for line in string.split(buffer, '\n'):
match = import_pat.match(line) match = import_pat.match(line)
if match: 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': elif command == 'define':
"define funcname [kwargs|noargs|onearg] [classmethod|staticmethod]" "define funcname [kwargs|noargs|onearg] [classmethod|staticmethod]"
"define Class.method [kwargs|noargs|onearg] [classmethod|staticmethod]" "define Class.method [kwargs|noargs|onearg] [classmethod|staticmethod]"
@ -210,6 +203,10 @@ class Overrides:
"new-constructor GType" "new-constructor GType"
gtype, = words[1:] gtype, = words[1:]
self.newstyle_constructors[gtype] = True self.newstyle_constructors[gtype] = True
elif command == 'options':
for option in words[1:]:
if option == 'dynamicnamespace':
self.dynamicnamespace = True
def is_ignored(self, name): def is_ignored(self, name):
if self.ignores.has_key(name): if self.ignores.has_key(name):

View file

@ -88,10 +88,12 @@ class ReverseWrapper(object):
self.declarations = MemoryCodeSink() self.declarations = MemoryCodeSink()
self.post_return_code = MemoryCodeSink() self.post_return_code = MemoryCodeSink()
self.body = MemoryCodeSink() self.body = MemoryCodeSink()
self.check_exception_code = MemoryCodeSink()
self.cleanup_actions = [] self.cleanup_actions = []
self.pyargv_items = [] self.pyargv_items = []
self.pyargv_optional_items = [] self.pyargv_optional_items = []
self.pyret_parse_items = [] # list of (format_spec, parameter) 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): def set_call_target(self, called_pyobj, method_name=None):
assert called_pyobj is not None assert called_pyobj is not None
@ -122,6 +124,14 @@ class ReverseWrapper(object):
else: else:
self.pyret_parse_items.append((format_specifier, parameter)) 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, def write_code(self, code,
cleanup=None, cleanup=None,
failure_expression=None, failure_expression=None,
@ -152,7 +162,7 @@ class ReverseWrapper(object):
parses the python method return value. parses the python method return value.
''' '''
if code_sink is None: if code_sink is None:
code_sink = self.body code_sink = self.code_sinks_stack[0]
if code is not None: if code is not None:
code_sink.writeln(code) code_sink.writeln(code)
if failure_expression is not None: if failure_expression is not None:
@ -170,7 +180,13 @@ class ReverseWrapper(object):
code_sink.writeln(failure_cleanup) code_sink.writeln(failure_cleanup)
for cleanup_action in self.cleanup_actions: for cleanup_action in self.cleanup_actions:
code_sink.writeln(cleanup_action) 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.unindent()
code_sink.writeln("}") code_sink.writeln("}")
if cleanup is not None: if cleanup is not None:
@ -265,8 +281,10 @@ class ReverseWrapper(object):
if self.method_name is None: if self.method_name is None:
self.write_code("py_retval = PyObject_Call(%s, %s);" self.write_code("py_retval = PyObject_Call(%s, %s);"
% (self.called_pyobj, py_args), % (self.called_pyobj, py_args),
cleanup="Py_DECREF(py_retval);", cleanup="Py_XDECREF(py_retval);")
failure_expression="!py_retval") self.check_exception_code.flush_to(self.body)
self.write_code(None, failure_expression="!py_retval")
else: else:
self.add_declaration("PyObject *py_method;") self.add_declaration("PyObject *py_method;")
self.write_code("py_method = PyObject_GetAttrString(%s, \"%s\");" self.write_code("py_method = PyObject_GetAttrString(%s, \"%s\");"
@ -275,8 +293,9 @@ class ReverseWrapper(object):
failure_expression="!py_method") failure_expression="!py_method")
self.write_code("py_retval = PyObject_CallObject(py_method, %s);" self.write_code("py_retval = PyObject_CallObject(py_method, %s);"
% (py_args,), % (py_args,),
cleanup="Py_DECREF(py_retval);", cleanup="Py_XDECREF(py_retval);")
failure_expression="!py_retval") self.check_exception_code.flush_to(self.body)
self.write_code(None, failure_expression="!py_retval")
## -- Handle the return value -- ## -- Handle the return value --
@ -291,16 +310,25 @@ class ReverseWrapper(object):
sink.indent() sink.indent()
if len(self.pyret_parse_items) == 1: if self.pyret_parse_items == [("", "")]:
## if retval is one item only, pack it in a tuple so we ## special case when there are no return parameters
## can use PyArg_ParseTuple as usual.. self.write_code(
self.write_code('py_retval = Py_BuildValue("(N)", py_retval);') code=None,
if len(self.pyret_parse_items) > 0: failure_expression='py_retval != Py_None',
## Parse return values using PyArg_ParseTuple failure_exception=('PyErr_SetString(PyExc_TypeError, '
self.write_code(code=None, failure_expression=( '"virtual method should return None");'))
'!PyArg_ParseTuple(py_retval, "%s", %s)' % ( else:
"".join([format for format, param in self.pyret_parse_items]), if len(self.pyret_parse_items) == 1:
", ".join([param for format, param in self.pyret_parse_items])))) ## 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: if DEBUG_MODE:
self.declarations.writeln("/* end declarations */") self.declarations.writeln("/* end declarations */")
@ -331,6 +359,8 @@ class TypeHandler(object):
class ReturnType(TypeHandler): class ReturnType(TypeHandler):
supports_optional = False
def get_c_type(self): def get_c_type(self):
raise NotImplementedError raise NotImplementedError
@ -378,10 +408,22 @@ class StringParam(Parameter):
% (self.name, self.name, self.name)), % (self.name, self.name, self.name)),
cleanup=("Py_XDECREF(py_%s);" % self.name)) cleanup=("Py_XDECREF(py_%s);" % self.name))
self.wrapper.add_pyargv_item("py_%s" % self.name, optional=True) 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.add_declaration("PyObject *py_%s;" % self.name)
self.wrapper.write_code(code=("py_%s = PyString_FromString(%s);" % self.wrapper.write_code(code=("if (%s)\n"
(self.name, self.name)), " 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), cleanup=("Py_DECREF(py_%s);" % self.name),
failure_expression=("!py_%s" % self.name)) failure_expression=("!py_%s" % self.name))
self.wrapper.add_pyargv_item("py_%s" % self.name) self.wrapper.add_pyargv_item("py_%s" % self.name)
@ -394,10 +436,12 @@ del ctype
class StringReturn(ReturnType): class StringReturn(ReturnType):
def get_c_type(self): def get_c_type(self):
return "char *" return self.props.get('c_type', 'char *').replace('const-', 'const ')
#return "char *"
def write_decl(self): 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): def write_error_return(self):
self.wrapper.write_code("return NULL;") 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.add_pyret_parse_item("s", "&retval", prepend=True)
self.wrapper.write_code("retval = g_strdup(retval);", code_sink=self.wrapper.post_return_code) 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) argtypes.matcher.register_reverse_ret(ctype, StringReturn)
del ctype del ctype
@ -423,10 +467,7 @@ class VoidReturn(ReturnType):
self.wrapper.write_code("return;") self.wrapper.write_code("return;")
def write_conversion(self): def write_conversion(self):
self.wrapper.write_code( self.wrapper.add_pyret_parse_item("", "", prepend=True)
code=None,
failure_expression="py_retval != Py_None",
failure_cleanup='PyErr_SetString(PyExc_TypeError, "retval should be None");')
argtypes.matcher.register_reverse_ret('void', VoidReturn) argtypes.matcher.register_reverse_ret('void', VoidReturn)
argtypes.matcher.register_reverse_ret('none', VoidReturn) argtypes.matcher.register_reverse_ret('none', VoidReturn)
@ -452,23 +493,39 @@ argtypes.matcher.register_reverse('GObject*', GObjectParam)
class GObjectReturn(ReturnType): class GObjectReturn(ReturnType):
supports_optional = True
def get_c_type(self): def get_c_type(self):
return self.props.get('c_type', 'GObject *') return self.props.get('c_type', 'GObject *')
def write_decl(self): 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): def write_error_return(self):
self.wrapper.write_code("return NULL;") self.wrapper.write_code("return NULL;")
def write_conversion(self): def write_conversion(self):
self.wrapper.write_code( if not self.props.get('optional'):
code=None, self.wrapper.write_code(
failure_expression="!PyObject_TypeCheck(py_retval, &PyGObject_Type)", code=None,
failure_exception='PyErr_SetString(PyExc_TypeError, "retval should be a GObject");') failure_expression="!PyObject_TypeCheck(py_retval, &PyGObject_Type)",
self.wrapper.write_code("retval = (%s) pygobject_get(py_retval);" failure_exception='PyErr_SetString(PyExc_TypeError, "retval should be a GObject");')
% self.get_c_type()) self.wrapper.write_code("retval = (%s) pygobject_get(py_retval);"
self.wrapper.write_code("g_object_ref((GObject *) 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) argtypes.matcher.register_reverse_ret('GObject*', GObjectReturn)
@ -506,9 +563,12 @@ del argtype
class IntPtrParam(Parameter): class IntPtrParam(Parameter):
def __init__(self, wrapper, name, **props): def __init__(self, wrapper, name, **props):
if "direction" not in 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"): 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) Parameter.__init__(self, wrapper, name, **props)
def get_c_type(self): def get_c_type(self):
return self.props.get('c_type', 'int*') return self.props.get('c_type', 'int*')
@ -529,8 +589,9 @@ class GEnumReturn(IntReturn):
def write_conversion(self): def write_conversion(self):
self.wrapper.write_code( self.wrapper.write_code(
code=None, code=None,
failure_expression=("pyg_enum_get_value(%s, py_retval, (gint *)&retval)" % failure_expression=(
self.props['typecode'])) "pyg_enum_get_value(%s, py_retval, (gint *)&retval)"
% (self.props['typecode'],)))
argtypes.matcher.register_reverse_ret("GEnum", GEnumReturn) argtypes.matcher.register_reverse_ret("GEnum", GEnumReturn)
@ -538,9 +599,9 @@ class GEnumParam(IntParam):
def convert_c2py(self): def convert_c2py(self):
self.wrapper.add_declaration("PyObject *py_%s;" % self.name) self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
self.wrapper.write_code(code=("py_%s = pyg_enum_from_gtype(%s, %s);" % self.wrapper.write_code(code=("py_%s = pyg_enum_from_gtype(%s, %s);" %
(self.name, self.props['typecode'], self.name)), (self.name, self.props['typecode'], self.name)),
cleanup=("Py_DECREF(py_%s);" % self.name), cleanup=("Py_DECREF(py_%s);" % self.name),
failure_expression=("!py_%s" % self.name)) failure_expression=("!py_%s" % self.name))
self.wrapper.add_pyargv_item("py_%s" % self.name) self.wrapper.add_pyargv_item("py_%s" % self.name)
argtypes.matcher.register_reverse("GEnum", GEnumParam) argtypes.matcher.register_reverse("GEnum", GEnumParam)
@ -549,16 +610,18 @@ class GFlagsReturn(IntReturn):
def write_conversion(self): def write_conversion(self):
self.wrapper.write_code( self.wrapper.write_code(
code=None, code=None,
failure_expression=("pyg_flags_get_value(%s, py_retval, (gint *)&retval)" % failure_expression=(
self.props['typecode'])) "pyg_flags_get_value(%s, py_retval, (gint *)&retval)" %
self.props['typecode']))
argtypes.matcher.register_reverse_ret("GFlags", GFlagsReturn) argtypes.matcher.register_reverse_ret("GFlags", GFlagsReturn)
class GFlagsParam(IntParam): class GFlagsParam(IntParam):
def convert_c2py(self): def convert_c2py(self):
self.wrapper.add_declaration("PyObject *py_%s;" % self.name) self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
self.wrapper.write_code(code=("py_%s = pyg_flags_from_gtype(%s, %s);" % self.wrapper.write_code(code=(
(self.name, self.props['typecode'], self.name)), "py_%s = pyg_flags_from_gtype(%s, %s);" %
(self.name, self.props['typecode'], self.name)),
cleanup=("Py_DECREF(py_%s);" % self.name), cleanup=("Py_DECREF(py_%s);" % self.name),
failure_expression=("!py_%s" % self.name)) failure_expression=("!py_%s" % self.name))
self.wrapper.add_pyargv_item("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): class GtkTreePathParam(IntParam):
def convert_c2py(self): def convert_c2py(self):
self.wrapper.add_declaration("PyObject *py_%s;" % self.name) self.wrapper.add_declaration("PyObject *py_%s;" % self.name)
self.wrapper.write_code(code=("py_%s = pygtk_tree_path_to_pyobject(%s);" % self.wrapper.write_code(code=(
(self.name, self.name)), "py_%s = pygtk_tree_path_to_pyobject(%s);" %
(self.name, self.name)),
cleanup=("Py_DECREF(py_%s);" % self.name), cleanup=("Py_DECREF(py_%s);" % self.name),
failure_expression=("!py_%s" % self.name)) failure_expression=("!py_%s" % self.name))
self.wrapper.add_pyargv_item("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) 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): class BooleanReturn(ReturnType):
def get_c_type(self): def get_c_type(self):
return "gboolean" return "gboolean"
@ -588,8 +669,9 @@ class BooleanReturn(ReturnType):
self.wrapper.write_code("return FALSE;") self.wrapper.write_code("return FALSE;")
def write_conversion(self): def write_conversion(self):
self.wrapper.add_pyret_parse_item("O", "&py_main_retval", prepend=True) self.wrapper.add_pyret_parse_item("O", "&py_main_retval", prepend=True)
self.wrapper.write_code("retval = PyObject_IsTrue(py_main_retval)? TRUE : FALSE;", self.wrapper.write_code(
code_sink=self.wrapper.post_return_code) "retval = PyObject_IsTrue(py_main_retval)? TRUE : FALSE;",
code_sink=self.wrapper.post_return_code)
argtypes.matcher.register_reverse_ret("gboolean", BooleanReturn) argtypes.matcher.register_reverse_ret("gboolean", BooleanReturn)
class BooleanParam(Parameter): class BooleanParam(Parameter):
@ -617,9 +699,12 @@ class DoubleParam(Parameter):
class DoublePtrParam(Parameter): class DoublePtrParam(Parameter):
def __init__(self, wrapper, name, **props): def __init__(self, wrapper, name, **props):
if "direction" not in 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 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) Parameter.__init__(self, wrapper, name, **props)
def get_c_type(self): def get_c_type(self):
return self.props.get('c_type', 'double*') return self.props.get('c_type', 'double*')
@ -637,11 +722,7 @@ class DoubleReturn(ReturnType):
def write_error_return(self): def write_error_return(self):
self.wrapper.write_code("return -G_MAXFLOAT;") self.wrapper.write_code("return -G_MAXFLOAT;")
def write_conversion(self): def write_conversion(self):
self.wrapper.write_code( self.wrapper.add_pyret_parse_item("d", "&retval", prepend=True)
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);")
for argtype in ('float', 'double', 'gfloat', 'gdouble'): for argtype in ('float', 'double', 'gfloat', 'gdouble'):
argtypes.matcher.register_reverse(argtype, DoubleParam) argtypes.matcher.register_reverse(argtype, DoubleParam)
@ -670,6 +751,7 @@ class GBoxedParam(Parameter):
argtypes.matcher.register_reverse("GBoxed", GBoxedParam) argtypes.matcher.register_reverse("GBoxed", GBoxedParam)
class GBoxedReturn(ReturnType): class GBoxedReturn(ReturnType):
def get_c_type(self): def get_c_type(self):
return self.props.get('c_type') return self.props.get('c_type')
@ -678,17 +760,64 @@ class GBoxedReturn(ReturnType):
def write_error_return(self): def write_error_return(self):
self.wrapper.write_code("return retval;") self.wrapper.write_code("return retval;")
def write_conversion(self): def write_conversion(self):
self.wrapper.write_code( self.wrapper.write_code(code = None,
failure_expression=("!pyg_boxed_check(py_retval, %s)" % failure_expression=("!pyg_boxed_check(py_retval, %s)" %
(self.props['typecode'],)), (self.props['typecode'],)),
failure_cleanup=('PyErr_SetString(PyExc_TypeError, "retval should be a %s");' failure_exception=(
% (self.props['typename'],))) 'PyErr_SetString(PyExc_TypeError, "retval should be a %s");'
% (self.props['typename'],)))
self.wrapper.write_code('retval = pyg_boxed_get(py_retval, %s);' % self.wrapper.write_code('retval = pyg_boxed_get(py_retval, %s);' %
self.props['typename']) self.props['typename'])
argtypes.matcher.register_reverse_ret("GBoxed", GBoxedReturn) 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): class GdkRectanglePtrParam(Parameter):
def get_c_type(self): def get_c_type(self):
return self.props.get('c_type').replace('const-', 'const ') 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) 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): class PyGObjectMethodParam(Parameter):
def __init__(self, wrapper, name, method_name, **props): def __init__(self, wrapper, name, method_name, **props):
Parameter.__init__(self, wrapper, name, **props) Parameter.__init__(self, wrapper, name, **props)
@ -720,6 +860,7 @@ class PyGObjectMethodParam(Parameter):
failure_expression=("!py_%s" % self.name)) failure_expression=("!py_%s" % self.name))
self.wrapper.set_call_target("py_%s" % self.name, self.method_name) self.wrapper.set_call_target("py_%s" % self.name, self.method_name)
class CallbackInUserDataParam(Parameter): class CallbackInUserDataParam(Parameter):
def __init__(self, wrapper, name, free_it, **props): def __init__(self, wrapper, name, free_it, **props):
Parameter.__init__(self, wrapper, name, **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 from __future__ import generators
import string import string
import types
from cStringIO import StringIO from cStringIO import StringIO
class error(Exception): class error(Exception):