lasso/bindings/python/lang.py

1081 lines
45 KiB
Python

# Lasso - A free implementation of the Liberty Alliance specifications.
#
# Copyright (C) 2004-2007 Entr'ouvert
# http://lasso.entrouvert.org
#
# Authors: See AUTHORS file in top-level directory.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import os
from six import print_
import sys
import re
import textwrap
from utils import *
def remove_bad_optional(args):
args.reverse()
non_opt = False
new_args = []
for x in args:
if not '=' in x:
non_opt = True
elif non_opt:
print_('W: changed', x, file=sys.stderr, end=' ')
x = re.sub(' *=.*', '', x)
print_('to', x, file=sys.stderr)
new_args.append(x)
new_args.reverse()
return new_args
def defval_to_python_value(defval):
if defval is None:
return 'None'
if defval.startswith('b:'):
if defval[2:].lower() == 'true':
return 'True'
if defval[2:].lower() == 'false':
return 'False'
if defval.startswith('c:'):
try:
return str(int(defval[2:]))
except:
return defval[8:]
raise Exception('Could not convert %s to python value' % defval)
def get_python_arg_decl(arg):
if is_optional(arg):
return '%s = %s' % (arg_name(arg), defval_to_python_value(arg_default(arg)))
else:
return arg_name(arg)
class Binding:
def __init__(self, binding_data):
self.binding_data = binding_data
self.src_dir = os.path.dirname(__file__)
def free_value(self, fd, type, name = None):
if not name:
name = arg_name(type)
if not name:
raise Exception('Cannot free, missing a name')
if is_cstring(type):
print_(' lasso_release_string(%s);' % name, file=fd)
elif is_int(type, self.binding_data) or is_boolean(type):
pass
elif is_xml_node(type):
print_(' lasso_release_xml_node(%s);' % name, file=fd)
elif is_glist(type):
etype = element_type(type)
if is_cstring(etype):
print_(' lasso_release_list_of_strings(%s);' % name, file=fd)
elif is_object(etype):
print_(' lasso_release_list_of_gobjects(%s);' % name, file=fd)
else:
raise Exception('Unsupported caller owned return type %s' % ((repr(type), name),))
elif is_hashtable(type):
el_type = element_type(type)
k_type = key_type(type)
v_type = value_type(type)
if is_cstring(el_type) or (is_cstring(k_type) and is_cstring(v_type)):
print_(' if (%s) { g_hash_table_destroy(%s); }' % (name, name), file=fd)
else:
raise Exception('Unsupported free value of type GHashTable: %s' % type)
elif is_object(type):
print_(' if (return_value) g_object_unref(%s);' % name, file=fd)
else:
raise Exception('Unsupported caller owned return type %s' % ((repr(type), name),))
def generate(self):
fd = open('lasso.py', 'w')
self.generate_header(fd)
self.generate_exceptions(fd)
self.generate_constants(fd)
for clss in self.binding_data.structs:
self.generate_class(clss, fd)
self.generate_functions(fd)
self.generate_footer(fd)
fd.close()
fd = open('_lasso.c', 'w')
self.generate_wrapper(fd)
fd.close()
def generate_header(self, fd):
print_('''\
# this file has been generated automatically; do not edit
import _lasso
import sys
def cptrToPy(cptr):
if cptr is None:
return None
klass = getattr(lasso, cptr.typename)
o = klass.__new__(klass)
o._cptr = cptr
return o
if sys.version_info >= (3,):
def str2lasso(s):
return s
else: # Python 2.x
def str2lasso(s):
if isinstance(s, unicode):
return s.encode('utf-8')
return s
class frozendict(dict):
\'\'\'Immutable dict\'\'\'
# from Python Cookbook:
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/414283
def _blocked_attribute(obj):
raise AttributeError('A frozendict cannot be modified.')
_blocked_attribute = property(_blocked_attribute)
__delitem__ = __setitem__ = clear = _blocked_attribute
pop = popitem = setdefault = update = _blocked_attribute
def __new__(cls, *args):
new = dict.__new__(cls)
dict.__init__(new, *args)
return new
def __init__(self, *args):
pass
def __hash__(self):
try:
return self._cached_hash
except AttributeError:
h = self._cached_hash = hash(tuple(sorted(self.items())))
return h
def __repr__(self):
return 'frozendict(%s)' % dict.__repr__(self)
''', file=fd)
def generate_exceptions(self, fd):
done_cats = []
print_('''\
class Error(Exception):
code = None
@staticmethod
def raise_on_rc(rc):
global exceptions_dict
if rc != 0:
exception = exceptions_dict.get(rc, Error())
exception.code = rc
raise exception
def __str__(self):
if self.code:
return '<lasso.%s(%s): %s>' % (self.__class__.__name__, self.code, _lasso.strError(self.code))
else:
if sys.version_info >= (3,):
return '<lasso.%s: %s>' % (self.__class__.__name__, super().__str__())
else:
return '<lasso.%s: %s>' % (self.__class__.__name__, self.message)
def __getitem__(self, i):
# compatibility with SWIG bindings
if i == 0:
return self.code
elif i == 1:
return _lasso.strError(self.code)
else:
raise IndexError()
''', file=fd)
for exc_cat in self.binding_data.overrides.findall('exception/category'):
cat = exc_cat.attrib.get('name')
done_cats.append(cat)
parent_cat = exc_cat.attrib.get('parent', '')
print_('''\
class %sError(%sError):
pass
''' % (cat, parent_cat), file=fd)
exceptions_dict = {}
for c in self.binding_data.constants:
m = re.match(r'LASSO_(\w+)_ERROR_(.*)', c[1])
if not m:
continue
cat, detail = m.groups()
cat = cat.title().replace('_', '')
detail = (cat + '_' + detail).title().replace('_', '')
if not cat in done_cats:
done_cats.append(cat)
for exc_cat in self.binding_data.overrides.findall('exception/category'):
if exc_cat.attrib.get('name') == cat:
parent_cat = exc_cat.attrib.get('parent')
break
else:
parent_cat = ''
print_('''\
class %sError(%sError):
pass
''' % (cat, parent_cat), file=fd)
exceptions_dict[detail] = c[1][6:]
if (detail, cat) == ('UnsupportedProfile', 'Logout'):
# skip Logout/UnsupportedProfile exception as its name would
# be the same as Profile/UnsupportedProfile; it is not a
# problem skipping it as they both inherit from ProfileError
# and the exception code will correctly be set by raise_on_rc
# afterwards. (actually it is even totally unnecessary to skip
# it here as Profile/UnsupportedProfile is handled after
# Logout/UnsupportedProfile, this is just done in the case the
# ordering would change)
continue
print_('''\
class %sError(%sError):
pass
''' % (detail, cat), file=fd)
print_('exceptions_dict = {', file=fd)
for k, v in exceptions_dict.items():
print_(' _lasso.%s: %sError,' % (v, k), file=fd)
print_('}', file=fd)
print_('', file=fd)
def generate_footer(self, fd):
print_('''
def _profileGetIssuer(cls, *args, **kwargs):
return profileGetIssuer(*args, **kwargs)
Profile.getIssuer = classmethod(_profileGetIssuer)
import lasso
# backward compatibility with the SWIG binding
Profile.isIdentityDirty = property(Profile.hasDirtyIdentity)
Profile.isSessionDirty = property(Profile.hasDirtySession)
def identity_get_provider_ids(self):
return self.federations.keys()
Identity.providerIds = property(identity_get_provider_ids)
def server_get_provider_ids(self):
return self.providers.keys()
Server.providerIds = property(server_get_provider_ids)
def session_get_provider_ids(self):
return self.assertions.keys()
Session.providerIds = property(session_get_provider_ids)
def LassoNode__getstate__(self):
return { '__dump__': self.dump() }
def LassoNode__setstate__(self, d):
self._cptr = _lasso.node_new_from_dump(d.pop('__dump__'))
Node.__getstate__ = LassoNode__getstate__
Node.__setstate__ = LassoNode__setstate__
Samlp2AuthnRequest.nameIDPolicy = Samlp2AuthnRequest.nameIdPolicy
LibAuthnRequest.nameIDPolicy = LibAuthnRequest.nameIdPolicy
Saml2Subject.nameID = Saml2Subject.nameId
MiscTextNode.text_child = MiscTextNode.textChild
NodeList = list
StringList = list
StringDict = dict
''', file=fd)
def generate_constants(self, fd):
print_('### Constants (both enums and defines)', file=fd)
for c in self.binding_data.constants:
print_('%s = _lasso.%s' % (c[1][6:], c[1][6:]), file=fd)
for c in self.binding_data.overrides.findall('constant'):
name = c.attrib.get('name')
if c.attrib.get('value'):
name = name[6:] # dropping LASSO_
value = c.attrib.get('value')
if value == 'True':
print_('%s = True' % name, file=fd)
else:
print_('E: unknown value for constant: %r' % value, file=sys.stderr)
print_('', file=fd)
def generate_class(self, clss, fd):
klassname = clss.name[5:] # remove Lasso from class name
if clss.parent == 'GObject':
parentname = 'object'
else:
parentname = clss.parent[5:]
print_('''class %(klassname)s(%(parentname)s):''' % locals(), file=fd)
methods = clss.methods[:]
# constructor(s)
method_prefix = 'lasso_' + format_as_underscored(klassname) + '_'
empty = True
for m in self.binding_data.functions:
if m.name == method_prefix + 'new':
empty = False
c_args = []
py_args = []
for arg in m.args:
py_args.append(get_python_arg_decl(arg))
if not is_int(arg, self.binding_data) and is_object(arg):
c_args.append('%(name)s and %(name)s._cptr' % { 'name' : arg_name(arg) })
elif is_cstring(arg):
c_args.append('str2lasso(%s)' % arg_name(arg))
else:
c_args.append(arg_name(arg))
py_args = remove_bad_optional(py_args)
c_args = ', '.join(c_args)
py_args = ', ' + ', '.join(py_args)
print_(' def __init__(self%s):' % py_args, file=fd)
# XXX: could check self._cptr.typename to see if it got the
# right class type
print_(' self._cptr = _lasso.%s(%s)' % (
m.name[6:], c_args), file=fd)
print_(' if self._cptr is None:', file=fd)
print_(' raise Error(\'failed to create object\')', file=fd)
print_('', file=fd)
for m in self.binding_data.functions:
if m.name.startswith(method_prefix + 'new_'):
empty = False
constructor_name = format_as_camelcase(m.name[len(method_prefix):])
c_args = []
py_args = []
for arg in m.args:
aname = arg_name(arg)
py_args.append(get_python_arg_decl(arg))
if not is_int(arg, self.binding_data) and is_object(arg):
c_args.append('%s and %s._cptr' % (aname, aname))
elif is_cstring(arg):
c_args.append('str2lasso(%s)' % arg_name(arg))
else:
c_args.append(aname)
opt = False
py_args = remove_bad_optional(py_args)
for x in py_args:
if '=' in x:
opt = True
elif opt:
print_('W: non-optional follows optional,', m, file=sys.stderr)
c_args = ', '.join(c_args)
py_args = ', ' + ', '.join(py_args)
print_(' @classmethod', file=fd)
print_(' def %s(cls%s):' % (constructor_name, py_args), file=fd)
print_(' return cptrToPy(_lasso.%s(%s))' % (m.name[6:], c_args), file=fd)
print_('', file=fd)
# create properties for members
for m in clss.members:
empty = False
mname = format_as_camelcase(m[1])
options = m[2]
# getter
print_(' def get_%s(self):' % mname, file=fd)
print_(' t = _lasso.%s_%s_get(self._cptr)' % (
klassname, mname), file=fd)
if is_int(m, self.binding_data) or is_xml_node(m) or is_cstring(m) or is_boolean(m):
pass
elif is_object(m):
print_(' t = cptrToPy(t)', file=fd)
elif is_glist(m):
el_type = element_type(m)
if is_cstring(el_type):
pass
elif is_xml_node(el_type):
pass
elif is_object(el_type):
print_(' if not t: return t', file=fd)
print_(' t = tuple([cptrToPy(x) for x in t])', file=fd)
else:
raise Exception('Unsupported python getter %s.%s' % (clss, m))
elif is_hashtable(m):
el_type = element_type(m)
print_(' if not t: return t', file=fd)
if is_object(el_type):
print_(' d2 = {}', file=fd)
print_(' for k, v in t.items():', file=fd)
print_(' d2[k] = cptrToPy(v)', file=fd)
print_(' t = frozendict(d2)', file=fd)
else:
print_(' t = frozendict(t)', file=fd)
elif is_boolean(m) or is_int(m, self.binding_data) or is_xml_node(m) or is_cstring(m):
pass
else:
raise Exception('Unsupported python getter %s.%s' % (clss, m))
print_(' return t;', file=fd)
# setter
print_(' def set_%s(self, value):' % mname, file=fd)
if is_int(m, self.binding_data) or is_xml_node(m) or is_boolean(m):
pass
elif is_cstring(m):
print_(' value = str2lasso(value)', file=fd)
elif is_object(m):
print_(' if value is not None:', file=fd)
print_(' value = value and value._cptr', file=fd)
elif is_glist(m):
el_type = element_type(m)
if is_cstring(el_type) or is_xml_node(el_type):
pass
elif is_object(el_type):
print_(' if value is not None:', file=fd)
print_(' value = tuple([x._cptr for x in value])', file=fd)
else:
raise Exception('Unsupported python setter %s.%s' % (clss, m))
elif is_hashtable(m):
print_('W: unsupported setter for hashtable %s' % (m,), file=sys.stderr)
else:
print_('W: unsupported setter for %s' % (m,), file=sys.stderr)
print_(' _lasso.%s_%s_set(self._cptr, value)' % (
klassname, mname), file=fd)
print_(' %s = property(get_%s, set_%s)' % (mname, mname, mname), file=fd)
old_mname = old_format_as_camelcase(m[1])
if mname != old_mname:
print_(' %s = %s' % (old_mname, mname), file=fd)
print_('', file=fd)
# first pass on methods, getting accessors
# second pass on methods, real methods
for m in methods:
empty = False
if m.name.endswith('_new') or m.name.endswith('_new_from_dump') or \
m.name.endswith('_new_full'):
continue
if not m.name.startswith(method_prefix):
print_('W:', m.name, 'vs', method_prefix, file=sys.stderr)
continue
if m.rename:
mname = m.rename[len(method_prefix):]
function_name = m.rename[6:]
else:
mname = m.name[len(method_prefix):]
function_name = m.name[6:]
py_args = []
c_args = []
outarg = None
for arg in m.args[1:]:
if is_out(arg):
assert not outarg
outarg = arg
outvar = '_%s_out' % arg_name(arg)
else:
py_args.append(get_python_arg_decl(arg))
if is_out(arg):
c_args.append(outvar)
elif is_cstring(arg):
c_args.append('str2lasso(%s)' % arg_name(arg))
elif is_xml_node(arg) or is_boolean(arg) or is_cstring(arg) or is_int(arg, self.binding_data) or is_glist(arg) or is_hashtable(arg) or is_time_t_pointer(arg):
c_args.append(arg_name(arg))
elif is_object(arg):
c_args.append('%(name)s and %(name)s._cptr' % { 'name': arg_name(arg) })
else:
raise Exception('Does not handle argument of type: %s' % ((m, arg),))
# check py_args
py_args = remove_bad_optional(py_args)
opt = False
for x in py_args:
if '=' in x:
opt = True
elif opt:
print_('W: non-optional follow optional,', m, file=sys.stderr)
if py_args:
py_args = ', ' + ', '.join(py_args)
else:
py_args = ''
if c_args:
c_args = ', ' + ', '.join(c_args)
else:
c_args = ''
print_(' def %s(self%s):' % (
format_underscore_as_camelcase(mname), py_args), file=fd)
if m.docstring:
print_(" '''", file=fd)
print_(self.format_docstring(m, mname, 8), file=fd)
print_(" '''", file=fd)
if outarg:
print_(" %s = list((None,))" % outvar, file=fd)
return_type = m.return_type
return_type_qualifier = m.return_type_qualifier
assert is_int(make_arg(return_type),self.binding_data) or not outarg
if return_type in (None, 'void'):
print_(' _lasso.%s(self._cptr%s)' % (
function_name, c_args), file=fd)
elif is_rc(m.return_arg):
print_(' rc = _lasso.%s(self._cptr%s)' % (
function_name, c_args), file=fd)
print_(' Error.raise_on_rc(rc)', file=fd)
elif (is_int(m.return_arg, self.binding_data) or is_xml_node(m.return_arg) or
is_cstring(m.return_arg) or is_boolean(m.return_arg) or
is_hashtable(m.return_arg)):
print_(' return _lasso.%s(self._cptr%s)' % (
function_name, c_args), file=fd)
elif is_glist(m.return_arg):
el_type = element_type(m.return_arg)
if is_object(el_type):
print_(' value = _lasso.%s(self._cptr%s)' % (
function_name, c_args), file=fd)
print_(' if value is not None:', file=fd)
print_(' value = tuple([cptrToPy(x) for x in value])', file=fd)
print_(' return value', file=fd)
elif is_cstring(el_type) or is_xml_node(el_type):
print_(' return _lasso.%s(self._cptr%s)' % (
function_name, c_args), file=fd)
else:
raise Exception('Return Type GList<%s> is not supported' % el_type)
elif is_object(m.return_arg):
print_(' return cptrToPy(_lasso.%s(self._cptr%s))' % (
function_name, c_args), file=fd)
else:
raise Exception('Return type %s is unsupported' % (m.return_arg,))
if outarg:
print_(' return %s[0]' % outvar, file=fd)
print_('', file=fd)
# transform methods to properties
for m in methods:
if len(m.args) > 1:
continue
name = m.rename or m.name
suffix = name[len(method_prefix)+len('get_'):]
if clss.getMember(suffix):
print_('W: method %s and member %s clashes' % (m.name, arg_name(clss.getMember(suffix))), file=sys.stderr)
continue
if not name.startswith(method_prefix) or not name[len(method_prefix):].startswith('get_'):
continue
setter_suffix = 'set_' + suffix
setter = None
for n in methods:
if n.name.endswith(setter_suffix) and len(n.args) == 2:
setter = n
pname = format_as_camelcase(name[len(method_prefix)+len('get_'):])
fname = format_as_camelcase(name[len(method_prefix):])
if not setter:
print_(' %s = property(%s)' % (pname, fname), file=fd)
else:
f2name = format_as_camelcase(setter.name[len(method_prefix):])
print_(' %s = property(%s, %s)' % (pname, fname, f2name), file=fd)
if empty:
print_(' pass', file=fd)
print_('', file=fd)
def format_docstring(self, func, method_name, indent):
if func.args:
first_arg_name = func.args[0][1]
else:
first_arg_name = None
def format_inlines_sub(s):
type = s.group(1)[0]
var = s.group(1)[1:]
if type == '#': # struct
if var.startswith('Lasso'):
return 'L{%s}' % var[5:]
elif type == '%': # %TRUE, %FALSE
if var == 'TRUE':
return 'True'
if var == 'FALSE':
return 'False'
print_('W: unknown docstring thingie: %s' % s.group(1), file=sys.stderr)
elif type == '@':
if var == first_arg_name:
var = 'self'
return 'C{%s}' % var
return s.group(1)
regex = re.compile(r'([\#%@]\w+)', re.DOTALL)
def format_inline(s):
s = regex.sub(format_inlines_sub, s)
return s.replace('NULL', 'None')
docstring = func.docstring
s = []
if docstring.description:
for paragraph in docstring.description.split('\n\n'):
if '<itemizedlist>' in paragraph:
before, after = paragraph.split('<itemizedlist>' ,1)
if before:
s.append('\n'.join(textwrap.wrap(
format_inline(before), 70)))
# remove tags
after = after.replace('<itemizedlist>', '')
after = after.replace('</itemizedlist>', '')
for listitem in after.split('<listitem><para>'):
listitem = listitem.replace('</para></listitem>', '').strip()
s.append('\n'.join(textwrap.wrap(
format_inline(listitem), 70,
initial_indent = ' - ',
subsequent_indent = ' ')))
s.append('\n\n')
else:
s.append('\n'.join(textwrap.wrap(
format_inline(paragraph), 70)))
s.append('\n\n')
for param in docstring.parameters:
s.append('\n'.join(textwrap.wrap(
format_inline(param[1]), 70,
initial_indent = '@param %s: ' % param[0],
subsequent_indent = 4*' ')))
s.append('\n')
if docstring.return_value:
rv = docstring.return_value
exceptions_instead = ['0 on success; or a negative value otherwise.',
'0 on success; a negative value if an error occured.',
'0 on success; another value if an error occured.']
if not rv in exceptions_instead:
owner_info = ['This xmlnode must be freed by caller.',
'The string must be freed by the caller.',
'It must be freed by the caller.',
'This string must be freed by the caller.']
for o_i in owner_info:
rv = rv.replace(o_i, '')
s.append('\n'.join(textwrap.wrap(
format_inline(rv), 70,
initial_indent = '@return: ',
subsequent_indent = 4*' ')))
s.append('\n')
if s:
s[-1] = s[-1].rstrip() # remove trailing newline from last line
return '\n'.join([(indent*' ')+x for x in ''.join(s).splitlines()])
def generate_functions(self, fd):
for m in self.binding_data.functions:
if m.name.endswith('_new') or '_new_' in m.name:
continue
if m.rename:
pname = m.rename
name = m.rename
if name.startswith('lasso_'):
name = name[6:]
pname = format_as_camelcase(name)
else:
name = m.name[6:]
pname = format_as_camelcase(name)
print_('%s = _lasso.%s' % (pname, name), file=fd)
def generate_wrapper(self, fd):
print_(open(os.path.join(self.src_dir,'wrapper_top.c')).read(), file=fd)
for h in self.binding_data.headers:
print_('#include <%s>' % h, file=fd)
print_('', file=fd)
self.generate_constants_wrapper(fd)
self.wrapper_list = []
for m in self.binding_data.functions:
self.generate_function_wrapper(m, fd)
for c in self.binding_data.structs:
self.generate_member_wrapper(c, fd)
for m in c.methods:
self.generate_function_wrapper(m, fd)
self.generate_wrapper_list(fd)
print_(open(os.path.join(self.src_dir,'wrapper_bottom.c')).read(), file=fd)
def generate_constants_wrapper(self, fd):
print_('''static void
register_constants(PyObject *d)
{
PyObject *obj;
''', file=fd)
for c in self.binding_data.constants:
if c[0] == 'i':
print_(' obj = PyInt_FromLong(%s);' % c[1], file=fd)
elif c[0] == 's':
print_(' obj = PyString_FromString((char*)%s);' % c[1], file=fd)
elif c[0] == 'b':
print_('''\
#ifdef %s
obj = Py_True;
#else
obj = Py_False;
#endif''' % c[1], file=fd)
else:
print_('E: unknown constant type: %r' % c[0], file=sys.stderr)
print_(' PyDict_SetItemString(d, "%s", obj);' % c[1][6:], file=fd)
if c[0] != 'b': # refcount of Py_True/False should not be changed
print_(' Py_DECREF(obj);', file=fd)
print_('}', file=fd)
print_('', file=fd)
def generate_member_wrapper(self, c, fd):
klassname = c.name
for m in c.members:
name = arg_name(m)
mname = format_as_camelcase(arg_name(m))
# getter
print_('''static PyObject*
%s_%s_get(G_GNUC_UNUSED PyObject *self, PyObject *args)
{''' % (klassname[5:], mname), file=fd)
self.wrapper_list.append('%s_%s_get' % (klassname[5:], mname))
ftype = arg_type(m)
if is_cstring(m):
ftype = 'char*'
print_(' %s return_value;' % ftype, file=fd)
print_(' PyObject* return_pyvalue;', file=fd)
print_(' PyGObjectPtr* cvt_this;', file=fd)
print_(' %s* this;' % klassname, file=fd)
print_('', file=fd)
print_(' if (! PyArg_ParseTuple(args, "O", &cvt_this)) return NULL;', file=fd)
print_(' this = (%s*)cvt_this->obj;' % klassname, file=fd)
print_(' return_value = this->%s;' % arg_name(m), file=fd)
try:
self.return_value(fd, m)
except:
print_('W: cannot make an assignment for', c, m, file=sys.stderr)
raise
print_(' return return_pyvalue;', file=fd)
print_('}', file=fd)
print_('', file=fd)
# setter
print_('''static PyObject*
%s_%s_set(G_GNUC_UNUSED PyObject *self, PyObject *args)
{''' % (klassname[5:], mname), file=fd)
self.wrapper_list.append('%s_%s_set' % (klassname[5:], mname))
print_(' PyGObjectPtr* cvt_this;', file=fd)
print_(' %s* this;' % klassname, file=fd)
type = m[0]
# Determine type class
if is_cstring(m):
type = type.replace('const ', '')
parse_format = 'z'
parse_arg = '&value'
print_(' %s value;' % type, file=fd)
elif is_int(m, self.binding_data):
parse_format = 'l'
parse_arg = '&value'
print_(' long value;', file=fd)
elif is_glist(m) or is_hashtable(m) or is_xml_node(m) or is_boolean(m):
parse_format = 'O'
print_(' PyObject *cvt_value;', file=fd)
parse_arg = '&cvt_value'
elif is_object(m):
parse_format = 'O'
print_(' PyGObjectPtr *cvt_value;', file=fd)
parse_arg = '&cvt_value'
else:
raise Exception('Unsupported field: %s' % (m,))
# Get GObject
print_(' if (! PyArg_ParseTuple(args, "O%s", &cvt_this, %s)) return NULL;' % (
parse_format, parse_arg), file=fd)
print_(' this = (%s*)cvt_this->obj;' % klassname, file=fd)
# Change value
if is_int(m, self.binding_data):
print_(' this->%s = value;' % name, file=fd)
elif is_boolean(m):
print_(' this->%s = PyInt_AS_LONG(cvt_value) ? TRUE : FALSE;' % name, file=fd)
elif is_cstring(m):
print_(' lasso_assign_string(this->%s, value);' % name, file=fd)
elif is_xml_node(m):
print_(' if (this->%s) xmlFreeNode(this->%s);' % (name, name), file=fd)
print_(' this->%s = get_xml_node_from_pystring(cvt_value);' % name, file=fd)
elif is_glist(m):
el_type = element_type(m)
if is_cstring(el_type):
print_(' RETURN_IF_FAIL(set_list_of_strings(&this->%s, cvt_value));' % name, file=fd)
elif is_xml_node(el_type):
print_(' RETURN_IF_FAIL(set_list_of_xml_nodes(&this->%s, cvt_value));' % name, file=fd)
elif is_object(el_type):
print_(' RETURN_IF_FAIL(set_list_of_pygobject(&this->%s, cvt_value));' % name, file=fd)
else:
raise Exception('Unsupported setter for %s' % (m,))
elif is_hashtable(m):
el_type = element_type(m)
if is_object(el_type):
print_(' RETURN_IF_FAIL(set_hashtable_of_pygobject(this->%s, cvt_value));' % name, file=fd)
else:
print_(' RETURN_IF_FAIL(set_hashtable_of_strings(this->%s, cvt_value));' % name, file=fd)
elif is_object(m):
print_(' set_object_field((GObject**)&this->%s, cvt_value);' % name, file=fd)
else:
raise Exception('Unsupported member %s.%s' % (klassname, m))
print_(' return noneRef();', file=fd)
print_('}', file=fd)
print_('', file=fd)
def return_value(self, fd, arg, return_var_name = 'return_value', return_pyvar_name = 'return_pyvalue'):
if is_boolean(arg):
print_(' if (%s) {' % return_var_name, file=fd)
print_(' Py_INCREF(Py_True);', file=fd)
print_(' %s = Py_True;' % return_pyvar_name, file=fd)
print_(' } else {', file=fd)
print_(' Py_INCREF(Py_False);', file=fd)
print_(' %s = Py_False;' % return_pyvar_name, file=fd)
print_(' }', file=fd)
elif is_int(arg, self.binding_data):
print_(' %s = PyInt_FromLong(%s);' % (return_pyvar_name, return_var_name), file=fd)
elif is_cstring(arg) and is_transfer_full(arg):
print_(' if (%s) {' % return_var_name, file=fd)
print_(' %s = PyString_FromString(%s);' % (return_pyvar_name, return_var_name), file=fd)
print_(' } else {', file=fd)
print_(' %s = noneRef();' % return_pyvar_name, file=fd)
print_(' }', file=fd)
elif is_cstring(arg):
print_(' if (%s) {' % return_var_name, file=fd)
print_(' %s = PyString_FromString(%s);' % (return_pyvar_name, return_var_name), file=fd)
print_(' } else {', file=fd)
print_(' %s = noneRef();' % return_pyvar_name, file=fd)
print_(' }', file=fd)
elif is_glist(arg):
el_type = element_type(arg)
if is_object(el_type):
print_(' %s = get_list_of_pygobject(%s);' % (return_pyvar_name, return_var_name), file=fd)
elif is_cstring(el_type):
print_(' %s = get_list_of_strings(%s);' % (return_pyvar_name, return_var_name), file=fd)
elif is_xml_node(el_type):
print_(' %s = get_list_of_xml_nodes(%s);' % (return_pyvar_name, return_var_name), file=fd)
else:
raise Exception('failed to make an assignment for %s' % (arg,))
elif is_hashtable(arg):
el_type = element_type(arg)
if is_object(el_type):
print_(' %s = get_dict_from_hashtable_of_objects(%s);' % (return_pyvar_name, return_var_name), file=fd)
else:
print_(' %s = get_dict_from_hashtable_of_strings(%s);' % (return_pyvar_name, return_var_name), file=fd)
elif is_xml_node(arg):
# convert xmlNode* to strings
print_(' if (%s) {' % return_var_name, file=fd)
print_(' %s = get_pystring_from_xml_node(%s);' % (return_pyvar_name, return_var_name), file=fd)
print_(' } else {', file=fd)
print_(' %s = noneRef();' % return_pyvar_name, file=fd)
print_(' }', file=fd)
elif is_object(arg):
# return a PyGObjectPtr (wrapper around GObject)
print_('''\
if (%s) {
%s = PyGObjectPtr_New(G_OBJECT(%s));
} else {
%s = noneRef();
}
''' % (return_var_name, return_pyvar_name, return_var_name, return_pyvar_name), file=fd)
else:
raise Exception('failed to make an assignment for %s' % (arg,))
def generate_function_wrapper(self, m, fd):
if m.rename:
name = m.rename
if name.startswith('lasso_'):
name = name[6:]
else:
name = m.name[6:]
self.wrapper_list.append(name)
print_('''static PyObject*
%s(G_GNUC_UNUSED PyObject *self, PyObject *args)
{
int ok = 1;''' % name, file=fd)
parse_tuple_format = []
parse_tuple_args = []
for arg in m.args:
atype = arg_type(arg)
aname = arg_name(arg)
arg_def = None
python_cvt_def = None
defval = None
if is_optional(arg):
if not '|' in parse_tuple_format:
parse_tuple_format.append('|')
if is_cstring(arg):
atype = unconstify(atype)
if is_optional(arg):
parse_tuple_format.append('z')
else:
parse_tuple_format.append('s')
parse_tuple_args.append('&%s' % aname)
arg_def = ' %s %s = NULL;' % (arg[0], arg[1])
elif is_int(arg, self.binding_data) or is_boolean(arg):
parse_tuple_format.append('i')
parse_tuple_args.append('&%s' % aname)
if arg_default(arg):
defval = arg_default(arg)
if defval.startswith('b:'):
defval = defval[2:].upper()
else:
defval = defval[2:]
arg_def = ' %s %s = %s;' % (arg[0], arg[1], defval)
else:
arg_def = ' %s %s;' % (arg[0], arg[1])
elif is_hashtable(arg) or is_xml_node(arg) or is_list(arg) or is_time_t_pointer(arg):
parse_tuple_format.append('O')
parse_tuple_args.append('&cvt_%s' % aname)
arg_def = ' %s %s = NULL;' % (arg[0], arg[1])
python_cvt_def = ' PyObject *cvt_%s = NULL;' % aname
else:
parse_tuple_format.append('O')
parse_tuple_args.append('&cvt_%s' % aname)
arg_def = ' %s %s = NULL;' % (arg[0], arg[1])
python_cvt_def = ' PyGObjectPtr *cvt_%s = NULL;' % aname
if is_out(arg):
arg_def = ' %s %s = NULL;' % (var_type(arg), arg[1])
parse_tuple_format.pop()
parse_tuple_format.append('O')
parse_tuple_args.pop()
parse_tuple_args.append('&cvt_%s_out' % aname)
python_cvt_def = ' PyObject *cvt_%s_out = NULL;' % aname
print_(' PyObject *out_pyvalue = NULL;', file=fd)
print_(arg_def, file=fd)
if python_cvt_def:
print_(python_cvt_def, file=fd)
if m.return_type:
print_(' %s return_value;' % m.return_type, file=fd)
print_(' PyObject* return_pyvalue = NULL;', file=fd)
print_('', file=fd)
parse_tuple_args = ', '.join(parse_tuple_args)
if parse_tuple_args:
parse_tuple_args = ', ' + parse_tuple_args
print_(' if (! PyArg_ParseTuple(args, "%s"%s)) return NULL;' % (
''.join(parse_tuple_format), parse_tuple_args), file=fd)
for f, arg in zip([ x for x in parse_tuple_format if x != '|'], m.args):
if is_out(arg):
continue
if is_list(arg):
qualifier = element_type(arg)
if is_cstring(qualifier):
print_(' EXIT_IF_FAIL(set_list_of_strings(&%s, cvt_%s));' % (arg[1], arg[1]), file=fd)
elif is_xml_node(qualifier):
print_(' EXIT_IF_FAIL(set_list_of_xml_nodes(&%s, cvt_%s));' % (arg[1], arg[1]), file=fd)
elif isinstance(qualifier, str) and qualifier.startswith('Lasso'):
print_(' EXIT_IF_FAIL(set_list_of_pygobject(&%s, cvt_%s));' % (arg[1], arg[1]), file=fd)
else:
print_('E: unqualified GList argument in', name, qualifier, arg, file=sys.stderr)
elif is_xml_node(arg):
print_(' %s = get_xml_node_from_pystring(cvt_%s);' % (arg[1], arg[1]), file=fd)
elif is_time_t_pointer(arg):
print_(' %s = get_time_t(cvt_%s);' % (arg[1], arg[1]), file=fd)
elif is_hashtable(arg):
el_type = element_type(arg)
k_type = key_type(arg)
v_type = value_type(arg)
if is_cstring(el_type) or (is_cstring(k_type) and is_cstring(v_type)):
print_(' %s = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);' % arg[1], file=fd)
print_(' EXIT_IF_FAIL(set_hashtable_of_strings(%s, cvt_%s));' % (arg[1], arg[1]), file=fd)
elif f == 'O':
if is_optional(arg):
print_(' if (PyObject_TypeCheck((PyObject*)cvt_%s, &PyGObjectPtrType)) {' % arg[1], file=fd)
print_(' %s = (%s)cvt_%s->obj;' % (arg[1], arg[0], arg[1]), file=fd)
print_(' } else {', file=fd)
print_(' %s = NULL;' % arg[1], file=fd)
print_(' }', file=fd)
else:
print_(' if (PyObject_TypeCheck((PyObject*)cvt_%s, &PyGObjectPtrType)) {' % arg[1], file=fd)
print_(' %s = (%s)cvt_%s->obj;' % (arg[1], arg[0], arg[1]), file=fd)
print_(' } else {', file=fd)
print_(' PyErr_SetString(PyExc_TypeError, "value should be a PyGObject");', file=fd)
print_(' return NULL;', file=fd)
print_(' }', file=fd)
if m.return_type:
print_(' return_value = ', file=fd, end='')
if 'new' in m.name:
print_('(%s)' % m.return_type, file=fd)
else:
print_(' ', file=fd, end='')
print_('%s(%s);' % (m.name, ', '.join([ref_name(x) for x in m.args])), file=fd)
if m.return_type:
# Constructor so decrease refcount (it was incremented by PyGObjectPtr_New called
# in self.return_value
try:
self.return_value(fd, m.return_arg)
except:
print_('W: cannot assign return value of', m, file=sys.stderr)
raise
if is_transfer_full(m.return_arg, default=True):
self.free_value(fd, m.return_arg, name = 'return_value')
for f, arg in zip(parse_tuple_format, m.args):
if is_out(arg):
self.return_value(fd, arg, return_var_name = arg[1], return_pyvar_name = 'out_pyvalue')
print_(' EXIT_IF_FAIL(%s);' % arg[1], file=fd)
print_(' if (PyList_SetItem(cvt_%s_out, 0, out_pyvalue) == -1) {' % arg[1], file=fd)
print_(' ok = 0;', file=fd)
print_(' Py_XDECREF(out_pyvalue);', file=fd)
print_(' }', file=fd)
elif arg[0] == 'GList*':
qualifier = arg[2].get('element-type')
if is_cstring(qualifier):
print_(' free_list(&%s, (GFunc)g_free);' % arg[1], file=fd)
elif is_xml_node(qualifier):
print_(' free_list(&%s, (GFunc)xmlFreeNode);' % arg[1], file=fd)
elif is_object(qualifier):
print_(' free_list(&%s, (GFunc)g_object_unref);' % arg[1], file=fd)
elif is_time_t_pointer(arg):
print_(' if (%s) free(%s);' % (arg[1], arg[1]), file=fd)
elif not is_transfer_full(arg) and is_hashtable(arg):
self.free_value(fd, arg)
elif not is_transfer_full(arg) and is_xml_node(arg):
self.free_value(fd, arg)
print_('failure:', file=fd)
if not m.return_type:
print_(' if (ok) {', file=fd)
print_(' return noneRef();', file=fd)
else:
print_(' if (ok && return_pyvalue) {', file=fd)
print_(' return return_pyvalue;', file=fd)
print_(' } else {', file=fd)
if m.return_type:
print_(' Py_XDECREF(return_pyvalue);', file=fd)
print_(' return NULL;', file=fd)
print_(' }', file=fd)
print_('}', file=fd)
print_('', file=fd)
def generate_wrapper_list(self, fd):
print_('''
static PyMethodDef lasso_methods[] = {''', file=fd)
for m in self.wrapper_list:
print_(' {"%s", %s, METH_VARARGS, NULL},' % (m, m), file=fd)
print_(' {NULL, NULL, 0, NULL}', file=fd)
print_('};', file=fd)
print_('', file=fd)