summaryrefslogtreecommitdiff
path: root/gi
diff options
context:
space:
mode:
Diffstat (limited to 'gi')
-rw-r--r--gi/Makefile.am2
-rw-r--r--gi/Makefile.in13
-rw-r--r--gi/__init__.py29
-rw-r--r--gi/gimodule.c132
-rw-r--r--gi/importer.py1
-rw-r--r--gi/module.py45
-rw-r--r--gi/overrides/Gdk.py116
-rw-r--r--gi/overrides/Gio.py99
-rw-r--r--gi/overrides/Gtk.py53
-rw-r--r--gi/pygi-argument.c153
-rw-r--r--gi/pygi-argument.h2
-rw-r--r--gi/pygi-closure.c8
-rw-r--r--gi/pygi-info.c15
-rw-r--r--gi/pygi-invoke.c54
-rw-r--r--gi/pygi-private.h1
-rw-r--r--gi/pygi-repository.c23
-rw-r--r--gi/pygi-signal-closure.c245
-rw-r--r--gi/pygi-signal-closure.h46
-rw-r--r--gi/pygi.h38
-rw-r--r--gi/types.py56
20 files changed, 1071 insertions, 60 deletions
diff --git a/gi/Makefile.am b/gi/Makefile.am
index a98993b..28825ab 100644
--- a/gi/Makefile.am
+++ b/gi/Makefile.am
@@ -54,6 +54,8 @@ _gi_la_SOURCES = \
pygi-private.h \
pygi-property.c \
pygi-property.h \
+ pygi-signal-closure.c \
+ pygi-signal-closure.h \
pygobject-external.h \
gimodule.c
diff --git a/gi/Makefile.in b/gi/Makefile.in
index 173abf6..a5ccc12 100644
--- a/gi/Makefile.in
+++ b/gi/Makefile.in
@@ -81,7 +81,7 @@ am__gi_la_OBJECTS = _gi_la-pygi-repository.lo _gi_la-pygi-info.lo \
_gi_la-pygi-argument.lo _gi_la-pygi-type.lo \
_gi_la-pygi-boxed.lo _gi_la-pygi-closure.lo \
_gi_la-pygi-callbacks.lo _gi_la-pygi-property.lo \
- _gi_la-gimodule.lo
+ _gi_la-pygi-signal-closure.lo _gi_la-gimodule.lo
_gi_la_OBJECTS = $(am__gi_la_OBJECTS)
AM_V_lt = $(am__v_lt_$(V))
am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
@@ -381,6 +381,8 @@ _gi_la_SOURCES = \
pygi-private.h \
pygi-property.c \
pygi-property.h \
+ pygi-signal-closure.c \
+ pygi-signal-closure.h \
pygobject-external.h \
gimodule.c
@@ -489,6 +491,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/_gi_la-pygi-invoke.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/_gi_la-pygi-property.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/_gi_la-pygi-repository.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/_gi_la-pygi-signal-closure.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/_gi_la-pygi-struct.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/_gi_la-pygi-type.Plo@am__quote@
@@ -612,6 +615,14 @@ _gi_la-pygi-property.lo: pygi-property.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(_gi_la_CFLAGS) $(CFLAGS) -c -o _gi_la-pygi-property.lo `test -f 'pygi-property.c' || echo '$(srcdir)/'`pygi-property.c
+_gi_la-pygi-signal-closure.lo: pygi-signal-closure.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(_gi_la_CFLAGS) $(CFLAGS) -MT _gi_la-pygi-signal-closure.lo -MD -MP -MF $(DEPDIR)/_gi_la-pygi-signal-closure.Tpo -c -o _gi_la-pygi-signal-closure.lo `test -f 'pygi-signal-closure.c' || echo '$(srcdir)/'`pygi-signal-closure.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/_gi_la-pygi-signal-closure.Tpo $(DEPDIR)/_gi_la-pygi-signal-closure.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pygi-signal-closure.c' object='_gi_la-pygi-signal-closure.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(_gi_la_CFLAGS) $(CFLAGS) -c -o _gi_la-pygi-signal-closure.lo `test -f 'pygi-signal-closure.c' || echo '$(srcdir)/'`pygi-signal-closure.c
+
_gi_la-gimodule.lo: gimodule.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(_gi_la_CFLAGS) $(CFLAGS) -MT _gi_la-gimodule.lo -MD -MP -MF $(DEPDIR)/_gi_la-gimodule.Tpo -c -o _gi_la-gimodule.lo `test -f 'gimodule.c' || echo '$(srcdir)/'`gimodule.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/_gi_la-gimodule.Tpo $(DEPDIR)/_gi_la-gimodule.Plo
diff --git a/gi/__init__.py b/gi/__init__.py
index fb711c3..ed20606 100644
--- a/gi/__init__.py
+++ b/gi/__init__.py
@@ -20,8 +20,35 @@
from __future__ import absolute_import
-from ._gi import _API
+from ._gi import _API, Repository
# Force loading the GObject typelib so we have available the wrappers for
# base classes such as GInitiallyUnowned
from gi.repository import GObject
+
+_versions = {}
+
+def require_version(namespace, version):
+ repository = Repository.get_default()
+
+ if namespace in repository.get_loaded_namespaces():
+ if repository.get_version(namespace) != version:
+ raise ValueError('Namespace %s is already loaded with version %s' % \
+ (namespace, loaded_version))
+
+ if namespace in _versions and _versions[namespace] != version:
+ raise ValueError('Namespace %s already requires version %s' % \
+ (namespace, _versions[namespace]))
+
+ available_versions = repository.enumerate_versions(namespace)
+ if not available_versions:
+ raise ValueError('Namespace %s not available' % namespace)
+
+ if version not in available_versions:
+ raise ValueError('Namespace %s not available for version %s' % \
+ (namespace, version))
+
+ _versions[namespace] = version
+
+def get_required_version(namespace):
+ return _versions.get(namespace, None)
diff --git a/gi/gimodule.c b/gi/gimodule.c
index f70d0f2..2c4c335 100644
--- a/gi/gimodule.c
+++ b/gi/gimodule.c
@@ -51,6 +51,70 @@ _wrap_pyg_enum_add (PyObject *self,
}
static PyObject *
+_wrap_pyg_enum_register_new_gtype_and_add (PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ static char *kwlist[] = { "info", NULL };
+ PyGIBaseInfo *py_info;
+ GIEnumInfo *info;
+ gint n_values;
+ GEnumValue *g_enum_values;
+ GType g_type;
+ const gchar *type_name;
+
+ if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+ "O:enum_add_make_new_gtype",
+ kwlist, (PyObject *)&py_info)) {
+ return NULL;
+ }
+
+ if (!GI_IS_ENUM_INFO (py_info->info) ||
+ g_base_info_get_type ((GIBaseInfo *) py_info->info) != GI_INFO_TYPE_ENUM) {
+ PyErr_SetString (PyExc_TypeError, "info must be an EnumInfo with info type GI_INFO_TYPE_ENUM");
+ return NULL;
+ }
+
+ info = (GIEnumInfo *)py_info->info;
+ n_values = g_enum_info_get_n_values (info);
+ g_enum_values = g_new0 (GEnumValue, n_values + 1);
+
+ for (int i=0; i < n_values; i++) {
+ GIValueInfo *value_info;
+ GEnumValue *enum_value;
+ const gchar *name;
+ const gchar *c_identifier;
+
+ value_info = g_enum_info_get_value (info, i);
+ name = g_base_info_get_name ((GIBaseInfo *) value_info);
+ c_identifier = g_base_info_get_attribute ((GIBaseInfo *) value_info,
+ "c:identifier");
+
+ enum_value = &g_enum_values[i];
+ enum_value->value_nick = g_strdup (name);
+ enum_value->value = g_value_info_get_value (value_info);
+
+ if (c_identifier == NULL) {
+ enum_value->value_name = enum_value->value_nick;
+ } else {
+ enum_value->value_name = g_strdup (c_identifier);
+ }
+
+ g_base_info_unref ((GIBaseInfo *) value_info);
+ }
+
+ g_enum_values[n_values].value = 0;
+ g_enum_values[n_values].value_nick = NULL;
+ g_enum_values[n_values].value_name = NULL;
+
+ type_name = g_base_info_get_name ((GIBaseInfo *) info);
+ type_name = g_strdup (type_name);
+ g_type = g_enum_register_static (type_name, g_enum_values);
+
+ return pyg_enum_add (NULL, g_type_name (g_type), NULL, g_type);
+}
+
+static PyObject *
_wrap_pyg_flags_add (PyObject *self,
PyObject *args,
PyObject *kwargs)
@@ -74,6 +138,71 @@ _wrap_pyg_flags_add (PyObject *self,
}
static PyObject *
+_wrap_pyg_flags_register_new_gtype_and_add (PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ static char *kwlist[] = { "info", NULL };
+ PyGIBaseInfo *py_info;
+ GIEnumInfo *info;
+ gint n_values;
+ GFlagsValue *g_flags_values;
+ GType g_type;
+ const gchar *type_name;
+
+ if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+ "O:flags_add_make_new_gtype",
+ kwlist, (PyObject *)&py_info)) {
+ return NULL;
+ }
+
+ if (!GI_IS_ENUM_INFO (py_info->info) ||
+ g_base_info_get_type ((GIBaseInfo *) py_info->info) != GI_INFO_TYPE_FLAGS) {
+ PyErr_SetString (PyExc_TypeError, "info must be an EnumInfo with info type GI_INFO_TYPE_FLAGS");
+ return NULL;
+ }
+
+ info = (GIEnumInfo *)py_info->info;
+ n_values = g_enum_info_get_n_values (info);
+ g_flags_values = g_new0 (GFlagsValue, n_values + 1);
+
+ for (int i=0; i < n_values; i++) {
+ GIValueInfo *value_info;
+ GFlagsValue *flags_value;
+ const gchar *name;
+ const gchar *c_identifier;
+
+ value_info = g_enum_info_get_value (info, i);
+ name = g_base_info_get_name ((GIBaseInfo *) value_info);
+ c_identifier = g_base_info_get_attribute ((GIBaseInfo *) value_info,
+ "c:identifier");
+
+ flags_value = &g_flags_values[i];
+ flags_value->value_nick = g_strdup (name);
+ flags_value->value = g_value_info_get_value (value_info);
+
+ if (c_identifier == NULL) {
+ flags_value->value_name = flags_value->value_nick;
+ } else {
+ flags_value->value_name = g_strdup (c_identifier);
+ }
+
+ g_base_info_unref ((GIBaseInfo *) value_info);
+ }
+
+ g_flags_values[n_values].value = 0;
+ g_flags_values[n_values].value_nick = NULL;
+ g_flags_values[n_values].value_name = NULL;
+
+ type_name = g_base_info_get_name ((GIBaseInfo *) info);
+ type_name = g_strdup (type_name);
+ g_type = g_flags_register_static (type_name, g_flags_values);
+
+ return pyg_flags_add (NULL, g_type_name (g_type), NULL, g_type);
+}
+
+
+static PyObject *
_wrap_pyg_set_object_has_new_constructor (PyObject *self,
PyObject *args,
PyObject *kwargs)
@@ -353,7 +482,9 @@ _wrap_pyg_variant_type_from_string (PyObject *self, PyObject *args)
static PyMethodDef _gi_functions[] = {
{ "enum_add", (PyCFunction) _wrap_pyg_enum_add, METH_VARARGS | METH_KEYWORDS },
+ { "enum_register_new_gtype_and_add", (PyCFunction) _wrap_pyg_enum_register_new_gtype_and_add, METH_VARARGS | METH_KEYWORDS },
{ "flags_add", (PyCFunction) _wrap_pyg_flags_add, METH_VARARGS | METH_KEYWORDS },
+ { "flags_register_new_gtype_and_add", (PyCFunction) _wrap_pyg_flags_register_new_gtype_and_add, METH_VARARGS | METH_KEYWORDS },
{ "set_object_has_new_constructor", (PyCFunction) _wrap_pyg_set_object_has_new_constructor, METH_VARARGS | METH_KEYWORDS },
{ "register_interface_info", (PyCFunction) _wrap_pyg_register_interface_info, METH_VARARGS },
@@ -367,6 +498,7 @@ static struct PyGI_API CAPI = {
pygi_type_import_by_g_type_real,
pygi_get_property_value_real,
pygi_set_property_value_real,
+ pygi_signal_closure_new_real,
pygi_register_foreign_struct_real,
};
diff --git a/gi/importer.py b/gi/importer.py
index 1cb9b92..df08274 100644
--- a/gi/importer.py
+++ b/gi/importer.py
@@ -73,6 +73,7 @@ class DynamicImporter(object):
dynamic_module.__loader__ = self
sys.modules[fullname] = dynamic_module
+ dynamic_module._load()
return dynamic_module
diff --git a/gi/module.py b/gi/module.py
index 9b935ed..3e2b59e 100644
--- a/gi/module.py
+++ b/gi/module.py
@@ -25,6 +25,7 @@ from __future__ import absolute_import
import os
import gobject
+import gi
from .overrides import registry
from ._gi import \
@@ -40,12 +41,13 @@ from ._gi import \
Struct, \
Boxed, \
enum_add, \
- flags_add
+ enum_register_new_gtype_and_add, \
+ flags_add, \
+ flags_register_new_gtype_and_add
from .types import \
GObjectMeta, \
StructMeta, \
- Function, \
- Enum
+ Function
repository = Repository.get_default()
@@ -102,13 +104,18 @@ class IntrospectionModule(object):
wrapper = g_type.pytype
if wrapper is None:
- if g_type.is_a(gobject.TYPE_ENUM):
- wrapper = enum_add(g_type)
- elif g_type.is_a(gobject.TYPE_NONE):
- # An enum with a GType of None is an enum without GType
- wrapper = type(info.get_name(), (Enum,), {})
+ if info.is_flags():
+ if g_type.is_a(gobject.TYPE_FLAGS):
+ wrapper = flags_add(g_type)
+ else:
+ assert g_type == gobject.TYPE_NONE
+ wrapper = flags_register_new_gtype_and_add(info)
else:
- wrapper = flags_add(g_type)
+ if g_type.is_a(gobject.TYPE_ENUM):
+ wrapper = enum_add(g_type)
+ else:
+ assert g_type == gobject.TYPE_NONE
+ wrapper = enum_register_new_gtype_and_add(info)
wrapper.__info__ = info
wrapper.__module__ = 'gi.repository.' + info.get_namespace()
@@ -224,28 +231,19 @@ class DynamicModule(object):
def __init__(self, namespace):
self._namespace = namespace
self._introspection_module = None
- self._version = None
self._overrides_module = None
self.__path__ = None
- def require_version(self, version):
- if self._introspection_module is not None and \
- self._introspection_module._version != version:
- raise RuntimeError('Module has been already loaded ')
- self._version = version
-
- def _import(self):
+ def _load(self):
+ version = gi.get_required_version(self._namespace)
self._introspection_module = IntrospectionModule(self._namespace,
- self._version)
+ version)
overrides_modules = __import__('gi.overrides', fromlist=[self._namespace])
self._overrides_module = getattr(overrides_modules, self._namespace, None)
self.__path__ = repository.get_typelib_path(self._namespace)
def __getattr__(self, name):
- if self._introspection_module is None:
- self._import()
-
if self._overrides_module is not None:
override_exports = getattr(self._overrides_module, '__all__', ())
if name in override_exports:
@@ -263,9 +261,6 @@ class DynamicModule(object):
return getattr(self._introspection_module, name)
def __dir__ (self):
- if self._introspection_module is None:
- self._import()
-
# Python's default dir() is just dir(self.__class__) + self.__dict__.keys()
result = set(dir(self.__class__))
result.update(self.__dict__.keys())
@@ -276,8 +271,6 @@ class DynamicModule(object):
return list(result)
def __repr__(self):
- repository.require(self._namespace, self._version)
-
path = repository.get_typelib_path(self._namespace)
return "<%s.%s %r from %r>" % (self.__class__.__module__,
self.__class__.__name__,
diff --git a/gi/overrides/Gdk.py b/gi/overrides/Gdk.py
index 4ed71a6..16a0548 100644
--- a/gi/overrides/Gdk.py
+++ b/gi/overrides/Gdk.py
@@ -22,6 +22,8 @@
from ..overrides import override
from ..importer import modules
+import sys
+
Gdk = modules['Gdk']._introspection_module
__all__ = []
@@ -61,6 +63,11 @@ if Gdk._version == '2.0':
Rectangle = override(Rectangle)
__all__.append('Rectangle')
+else:
+ from gi.repository import cairo as _cairo
+ Rectangle = _cairo.RectangleInt
+
+ __all__.append('Rectangle')
if Gdk._version == '2.0':
class Drawable(Gdk.Drawable):
@@ -136,6 +143,74 @@ class Event(Gdk.Event):
Event = override(Event)
__all__.append('Event')
+# manually bind GdkEvent members to GdkEvent
+
+modname = globals()['__name__']
+module = sys.modules[modname]
+
+# right now we can't get the type_info from the
+# field info so manually list the class names
+event_member_classes = ['EventAny',
+ 'EventExpose',
+ 'EventVisibility',
+ 'EventMotion',
+ 'EventButton',
+ 'EventScroll',
+ 'EventKey',
+ 'EventCrossing',
+ 'EventFocus',
+ 'EventConfigure',
+ 'EventProperty',
+ 'EventSelection',
+ 'EventOwnerChange',
+ 'EventProximity',
+ 'EventDND',
+ 'EventWindowState',
+ 'EventSetting',
+ 'EventGrabBroken']
+
+if Gdk._version == '2.0':
+ event_member_classes.append('EventNoExpose')
+
+# whitelist all methods that have a success return we want to mask
+gsuccess_mask_funcs = ['get_state',
+ 'get_axis',
+ 'get_coords',
+ 'get_root_coords']
+
+def _gsuccess_mask(func):
+ def cull_success(*args):
+ result = func(*args)
+ success = result[0]
+ if success == False:
+ return None
+ else:
+ if len(result) == 2:
+ return result[1]
+ else:
+ return result[1:]
+ return cull_success
+
+for event_class in event_member_classes:
+ override_class = type(event_class, (getattr(Gdk, event_class),), {})
+ # add the event methods
+ for method_info in Gdk.Event.__info__.get_methods():
+ name = method_info.get_name()
+ event_method = getattr(Gdk.Event, name)
+ # python2 we need to use the __func__ attr to avoid internal
+ # instance checks
+ event_method = getattr(event_method, '__func__', event_method)
+
+ # use the _gsuccess_mask decorator if this method is whitelisted
+ if name in gsuccess_mask_funcs:
+ event_method = _gsuccess_mask(event_method)
+ setattr(override_class, name, event_method)
+
+ setattr(module, event_class, override_class)
+ __all__.append(event_class)
+
+# end GdkEvent overrides
+
class DragContext(Gdk.DragContext):
def finish(self, success, del_, time):
Gtk = modules['Gtk']._introspection_module
@@ -144,6 +219,47 @@ class DragContext(Gdk.DragContext):
DragContext = override(DragContext)
__all__.append('DragContext')
+class Cursor(Gdk.Cursor):
+ def __new__(cls, *args, **kwds):
+ arg_len = len(args)
+ kwd_len = len(kwds)
+ total_len = arg_len + kwd_len
+
+ def _new(cursor_type):
+ return cls.new(cursor_type)
+
+ def _new_for_display(display, cursor_type):
+ return cls.new_for_display(display, cursor_type)
+
+ def _new_from_pixbuf(display, pixbuf, x, y):
+ return cls.new_from_pixbuf(display, pixbuf, x, y)
+
+ def _new_from_pixmap(source, mask, fg, bg, x, y):
+ return cls.new_from_pixmap(source, mask, fg, bg, x, y)
+
+ _constructor = None
+ if total_len == 1:
+ _constructor = _new
+ elif total_len == 2:
+ _constructor = _new_for_display
+ elif total_len == 4:
+ _constructor = _new_from_pixbuf
+ elif total_len == 6:
+ if Gdk._version != '2.0':
+ # pixmaps don't exist in Gdk 3.0
+ raise ValueError("Wrong number of parameters")
+ _constructor = _new_from_pixmap
+ else:
+ raise ValueError("Wrong number of parameters")
+
+ return _constructor(*args, **kwds)
+
+ def __init__(self, *args, **kwargs):
+ Gdk.Cursor.__init__(self)
+
+Cursor = override(Cursor)
+__all__.append('Cursor')
+
import sys
initialized, argv = Gdk.init_check(sys.argv)
diff --git a/gi/overrides/Gio.py b/gi/overrides/Gio.py
index 78affa2..20343a2 100644
--- a/gi/overrides/Gio.py
+++ b/gi/overrides/Gio.py
@@ -97,3 +97,102 @@ class Settings(Gio.Settings):
Settings = override(Settings)
__all__.append('Settings')
+
+class _DBusProxyMethodCall:
+ '''Helper class to implement DBusProxy method calls.'''
+
+ def __init__(self, dbus_proxy, method_name):
+ self.dbus_proxy = dbus_proxy
+ self.method_name = method_name
+
+ def __async_result_handler(self, obj, result, user_data):
+ (result_callback, error_callback, real_user_data) = user_data
+ try:
+ ret = obj.call_finish(result)
+ except Exception as e:
+ # return exception as value
+ if error_callback:
+ error_callback(obj, e, real_user_data)
+ else:
+ result_callback(obj, e, real_user_data)
+ return
+
+ result_callback(obj, self._unpack_result(ret), real_user_data)
+
+ def __call__(self, signature, *args, **kwargs):
+ arg_variant = GLib.Variant(signature, tuple(args))
+
+ if 'result_handler' in kwargs:
+ # asynchronous call
+ user_data = (kwargs['result_handler'],
+ kwargs.get('error_handler'), kwargs.get('user_data'))
+ self.dbus_proxy.call(self.method_name, arg_variant,
+ kwargs.get('flags', 0), kwargs.get('timeout', -1), None,
+ self.__async_result_handler, user_data)
+ else:
+ # synchronous call
+ result = self.dbus_proxy.call_sync(self.method_name, arg_variant,
+ kwargs.get('flags', 0), kwargs.get('timeout', -1), None)
+ return self._unpack_result(result)
+
+ @classmethod
+ def _unpack_result(klass, result):
+ '''Convert a D-BUS return variant into an appropriate return value'''
+
+ result = result.unpack()
+
+ # to be compatible with standard Python behaviour, unbox
+ # single-element tuples and return None for empty result tuples
+ if len(result) == 1:
+ result = result[0]
+ elif len(result) == 0:
+ result = None
+
+ return result
+
+class DBusProxy(Gio.DBusProxy):
+ '''Provide comfortable and pythonic method calls.
+
+ This marshalls the method arguments into a GVariant, invokes the
+ call_sync() method on the DBusProxy object, and unmarshalls the result
+ GVariant back into a Python tuple.
+
+ The first argument always needs to be the D-Bus signature tuple of the
+ method call. Example:
+
+ proxy = Gio.DBusProxy.new_sync(...)
+ result = proxy.MyMethod('(is)', 42, 'hello')
+
+ Optional keyword arguments:
+
+ - timeout: timeout for the call in milliseconds (default to D-Bus timeout)
+
+ - flags: Combination of Gio.DBusCallFlags.*
+
+ - result_handler: Do an asynchronous method call and invoke
+ result_handler(proxy_object, result, user_data) when it finishes.
+
+ - error_handler: If the asynchronous call raises an exception,
+ error_handler(proxy_object, exception, user_data) is called when it
+ finishes. If error_handler is not given, result_handler is called with
+ the exception object as result instead.
+
+ - user_data: Optional user data to pass to result_handler for
+ asynchronous calls.
+
+ Example for asynchronous calls:
+
+ def mymethod_done(proxy, result, user_data):
+ if isinstance(result, Exception):
+ # handle error
+ else:
+ # do something with result
+
+ proxy.MyMethod('(is)', 42, 'hello',
+ result_handler=mymethod_done, user_data='data')
+ '''
+ def __getattr__(self, name):
+ return _DBusProxyMethodCall(self, name)
+
+DBusProxy = override(DBusProxy)
+__all__.append('DBusProxy')
diff --git a/gi/overrides/Gtk.py b/gi/overrides/Gtk.py
index 6c22829..31d1362 100644
--- a/gi/overrides/Gtk.py
+++ b/gi/overrides/Gtk.py
@@ -720,6 +720,9 @@ class TreeModel(Gtk.TreeModel):
for i in range(n_columns):
value = row[i]
+ if value is None:
+ continue # None means skip this row
+
self.set_value(treeiter, i, value)
def _convert_value(self, treeiter, column, value):
@@ -728,9 +731,9 @@ class TreeModel(Gtk.TreeModel):
# we may need to convert to a basic type
type_ = self.get_column_type(column)
- if type_ == gobject.TYPE_PYOBJECT:
+ if type_ == GObject.TYPE_PYOBJECT:
pass # short-circut branching
- elif type_ == gobject.TYPE_STRING:
+ elif type_ == GObject.TYPE_STRING:
if isinstance(value, str):
value = str(value)
elif sys.version_info < (3, 0):
@@ -740,12 +743,12 @@ class TreeModel(Gtk.TreeModel):
raise ValueError('Expected string or unicode for column %i but got %s%s' % (column, value, type(value)))
else:
raise ValueError('Expected a string for column %i but got %s' % (column, type(value)))
- elif type_ == gobject.TYPE_FLOAT or type_ == gobject.TYPE_DOUBLE:
+ elif type_ == GObject.TYPE_FLOAT or type_ == GObject.TYPE_DOUBLE:
if isinstance(value, float):
value = float(value)
else:
raise ValueError('Expected a float for column %i but got %s' % (column, type(value)))
- elif type_ == gobject.TYPE_LONG or type_ == gobject.TYPE_INT:
+ elif type_ == GObject.TYPE_LONG or type_ == GObject.TYPE_INT:
if isinstance(value, int):
value = int(value)
elif sys.version_info < (3, 0):
@@ -755,6 +758,35 @@ class TreeModel(Gtk.TreeModel):
raise ValueError('Expected an long for column %i but got %s' % (column, type(value)))
else:
raise ValueError('Expected an integer for column %i but got %s' % (column, type(value)))
+ elif type_ == GObject.TYPE_BOOLEAN:
+ if isinstance(value, (int, long)):
+ value = bool(value)
+ else:
+ raise ValueError('Expected a bool for column %i but got %s' % (column, type(value)))
+ else:
+ # use GValues directly to marshal to the correct type
+ # standard object checks should take care of validation
+ # so we don't have to do it here
+ value_container = GObject.Value()
+ value_container.init(type_)
+ if type_ == GObject.TYPE_CHAR:
+ value_container.set_char(value)
+ value = value_container
+ elif type_ == GObject.TYPE_UCHAR:
+ value_container.set_uchar(value)
+ value = value_container
+ elif type_ == GObject.TYPE_UINT:
+ value_container.set_uint(value)
+ value = value_container
+ elif type_ == GObject.TYPE_ULONG:
+ value_container.set_ulong(value)
+ value = value_container
+ elif type_ == GObject.TYPE_INT64:
+ value_container.set_int64(value)
+ value = value_container
+ elif type_ == GObject.TYPE_UINT64:
+ value_container.set_uint64(value)
+ value = value_container
return value
@@ -1143,6 +1175,12 @@ __all__.append('Adjustment')
class Table(Gtk.Table, Container):
def __init__(self, rows=1, columns=1, homogeneous=False, **kwds):
+ if 'n_rows' in kwds:
+ rows = kwds.pop('n_rows')
+
+ if 'n_columns' in kwds:
+ columns = kwds.pop('n_columns')
+
Gtk.Table.__init__(self, n_rows=rows, n_columns=columns, homogeneous=homogeneous, **kwds)
def attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL, xpadding=0, ypadding=0):
@@ -1168,6 +1206,13 @@ class Paned(Gtk.Paned):
Paned = override(Paned)
__all__.append('Paned')
+if Gtk._version != '2.0':
+ class Menu(Gtk.Menu):
+ def popup(self, parent_menu_shell, parent_menu_item, func, data, button, activate_time):
+ self.popup_for_device(None, parent_menu_shell, parent_menu_item, func, data, button, activate_time)
+ Menu = override(Menu)
+ __all__.append('Menu')
+
_Gtk_main_quit = Gtk.main_quit
@override(Gtk.main_quit)
def main_quit(*args):
diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c
index 8dd728d..bbbad00 100644
--- a/gi/pygi-argument.c
+++ b/gi/pygi-argument.c
@@ -241,8 +241,46 @@ _pygi_g_type_interface_check_object (GIBaseInfo *info,
case GI_INFO_TYPE_BOXED:
case GI_INFO_TYPE_INTERFACE:
case GI_INFO_TYPE_OBJECT:
+ retval = _pygi_g_registered_type_info_check_object ( (GIRegisteredTypeInfo *) info, TRUE, object);
+ break;
case GI_INFO_TYPE_UNION:
+
+
retval = _pygi_g_registered_type_info_check_object ( (GIRegisteredTypeInfo *) info, TRUE, object);
+
+ /* If not the same type then check to see if the object's type
+ * is the same as one of the union's members
+ */
+ if (retval == 0) {
+ gint i;
+ gint n_fields;
+
+ n_fields = g_union_info_get_n_fields ( (GIUnionInfo *) info);
+
+ for (i = 0; i < n_fields; i++) {
+ gint member_retval;
+ GIFieldInfo *field_info;
+ GITypeInfo *field_type_info;
+
+ field_info =
+ g_union_info_get_field ( (GIUnionInfo *) info, i);
+ field_type_info = g_field_info_get_type (field_info);
+
+ member_retval = _pygi_g_type_info_check_object(
+ field_type_info,
+ object,
+ TRUE);
+
+ g_base_info_unref ( ( GIBaseInfo *) field_type_info);
+ g_base_info_unref ( ( GIBaseInfo *) field_info);
+
+ if (member_retval == 1) {
+ retval = member_retval;
+ break;
+ }
+ }
+ }
+
break;
default:
g_assert_not_reached();
@@ -1009,13 +1047,25 @@ array_success:
g_warn_if_fail (transfer == GI_TRANSFER_NOTHING);
value = g_slice_new0 (GValue);
- g_value_init (value, object_type);
- retval = pyg_value_from_pyobject (value, object);
- if (retval < 0) {
- g_slice_free (GValue, value);
- PyErr_SetString (PyExc_RuntimeError, "PyObject conversion to GValue failed");
- break;
+ /* if already a gvalue, copy, else marshal into gvalue */
+ if (object_type == G_TYPE_VALUE) {
+ /* src GValue's lifecycle is handled by Python
+ * so we have to copy it into the destination's
+ * GValue which is freed during the cleanup of
+ * invoke.
+ */
+ GValue *src = (GValue *)((PyGObject *) object)->obj;
+ g_value_init (value, G_VALUE_TYPE (src));
+ g_value_copy(src, value);
+ } else {
+ g_value_init (value, object_type);
+ retval = pyg_value_from_pyobject (value, object);
+ if (retval < 0) {
+ g_slice_free (GValue, value);
+ PyErr_SetString (PyExc_RuntimeError, "PyObject conversion to GValue failed");
+ break;
+ }
}
arg.v_pointer = value;
@@ -1719,6 +1769,97 @@ _pygi_argument_to_object (GIArgument *arg,
return object;
}
+
+GIArgument
+_pygi_argument_from_g_value(const GValue *value,
+ GITypeInfo *type_info)
+{
+ GIArgument arg = { 0, };
+
+ GITypeTag type_tag = g_type_info_get_tag (type_info);
+ switch (type_tag) {
+ case GI_TYPE_TAG_BOOLEAN:
+ arg.v_boolean = g_value_get_boolean (value);
+ break;
+ case GI_TYPE_TAG_INT8:
+ case GI_TYPE_TAG_INT16:
+ case GI_TYPE_TAG_INT32:
+ case GI_TYPE_TAG_INT64:
+ arg.v_int = g_value_get_int (value);
+ break;
+ case GI_TYPE_TAG_UINT8:
+ case GI_TYPE_TAG_UINT16:
+ case GI_TYPE_TAG_UINT32:
+ case GI_TYPE_TAG_UINT64:
+ arg.v_uint = g_value_get_uint (value);
+ break;
+ case GI_TYPE_TAG_UNICHAR:
+ arg.v_uint32 = g_value_get_char (value);
+ break;
+ case GI_TYPE_TAG_FLOAT:
+ arg.v_float = g_value_get_float (value);
+ break;
+ case GI_TYPE_TAG_DOUBLE:
+ arg.v_double = g_value_get_double (value);
+ break;
+ case GI_TYPE_TAG_GTYPE:
+ arg.v_long = g_value_get_gtype (value);
+ break;
+ case GI_TYPE_TAG_UTF8:
+ case GI_TYPE_TAG_FILENAME:
+ arg.v_string = g_value_dup_string (value);
+ break;
+ case GI_TYPE_TAG_GLIST:
+ case GI_TYPE_TAG_GSLIST:
+ arg.v_pointer = g_value_get_pointer (value);
+ break;
+ case GI_TYPE_TAG_ARRAY:
+ case GI_TYPE_TAG_GHASH:
+ arg.v_pointer = g_value_get_boxed (value);
+ break;
+ case GI_TYPE_TAG_INTERFACE:
+ {
+ GIBaseInfo *info;
+ GIInfoType info_type;
+
+ info = g_type_info_get_interface (type_info);
+ info_type = g_base_info_get_type (info);
+
+ g_base_info_unref (info);
+
+ switch (info_type) {
+ case GI_INFO_TYPE_FLAGS:
+ case GI_INFO_TYPE_ENUM:
+ arg.v_long = g_value_get_enum (value);
+ break;
+ case GI_INFO_TYPE_INTERFACE:
+ case GI_INFO_TYPE_OBJECT:
+ arg.v_pointer = g_value_get_object (value);
+ break;
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_STRUCT:
+ case GI_INFO_TYPE_UNION:
+ if (G_VALUE_HOLDS(value, G_TYPE_BOXED)) {
+ arg.v_pointer = g_value_get_boxed (value);
+ } else {
+ arg.v_pointer = g_value_get_pointer (value);
+ }
+ break;
+ default:
+ g_warning("Converting of type '%s' is not implemented", g_info_type_to_string(info_type));
+ g_assert_not_reached();
+ }
+ break;
+ }
+ case GI_TYPE_TAG_ERROR:
+ case GI_TYPE_TAG_VOID:
+ g_critical("Converting of type '%s' is not implemented", g_type_tag_to_string(type_tag));
+ g_assert_not_reached();
+ }
+
+ return arg;
+}
+
void
_pygi_argument_release (GIArgument *arg,
GITypeInfo *type_info,
diff --git a/gi/pygi-argument.h b/gi/pygi-argument.h
index d932e8f..7224c75 100644
--- a/gi/pygi-argument.h
+++ b/gi/pygi-argument.h
@@ -55,6 +55,8 @@ PyObject* _pygi_argument_to_object (GIArgument *arg,
GITypeInfo *type_info,
GITransfer transfer);
+GIArgument _pygi_argument_from_g_value(const GValue *value,
+ GITypeInfo *type_info);
void _pygi_argument_release (GIArgument *arg,
GITypeInfo *type_info,
diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c
index 33a8d81..56ddc8b 100644
--- a/gi/pygi-closure.c
+++ b/gi/pygi-closure.c
@@ -295,6 +295,14 @@ _pygi_closure_set_out_arguments (GICallableInfo *callable_info,
if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) {
GITransfer transfer = g_arg_info_get_ownership_transfer (arg_info);
+
+ if (g_type_info_get_tag (type_info) == GI_TYPE_TAG_ERROR) {
+ /* TODO: check if an exception has been set and convert it to a GError */
+ out_args[i_out_args].v_pointer = NULL;
+ i_out_args++;
+ continue;
+ }
+
if (PyTuple_Check (py_retval)) {
PyObject *item = PyTuple_GET_ITEM (py_retval, i_py_retval);
_pygi_closure_assign_pyobj_to_out_argument (
diff --git a/gi/pygi-info.c b/gi/pygi-info.c
index f5dd69f..1bfd7d8 100644
--- a/gi/pygi-info.c
+++ b/gi/pygi-info.c
@@ -923,8 +923,23 @@ _wrap_g_enum_info_get_values (PyGIBaseInfo *self)
return infos;
}
+static PyObject *
+_wrap_g_enum_info_is_flags (PyGIBaseInfo *self)
+{
+ GIInfoType info_type = g_base_info_get_type ((GIBaseInfo *) self->info);
+
+ if (info_type == GI_INFO_TYPE_ENUM) {
+ Py_RETURN_FALSE;
+ } else if (info_type == GI_INFO_TYPE_FLAGS) {
+ Py_RETURN_TRUE;
+ } else {
+ g_assert_not_reached();
+ }
+}
+
static PyMethodDef _PyGIEnumInfo_methods[] = {
{ "get_values", (PyCFunction) _wrap_g_enum_info_get_values, METH_NOARGS },
+ { "is_flags", (PyCFunction) _wrap_g_enum_info_is_flags, METH_NOARGS },
{ NULL, NULL, 0 }
};
diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c
index c93442a..78984e5 100644
--- a/gi/pygi-invoke.c
+++ b/gi/pygi-invoke.c
@@ -62,6 +62,12 @@ struct invocation_state
PyObject *return_value;
GType implementor_gtype;
+
+ /* hack to avoid treating C arrays as GArrays during free
+ * due to overly complicated array handling
+ * this will be removed when the new invoke branch is merged
+ */
+ gboolean c_arrays_are_wrapped;
};
static gboolean
@@ -121,6 +127,11 @@ _initialize_invocation_state (struct invocation_state *state,
state->out_values = NULL;
state->backup_args = NULL;
+ /* HACK: this gets marked FALSE whenever a C array in the args is
+ * not wrapped by a GArray
+ */
+ state->c_arrays_are_wrapped = TRUE;
+
return TRUE;
}
@@ -556,6 +567,20 @@ _prepare_invocation_state (struct invocation_state *state,
(g_type_info_get_array_type (state->arg_type_infos[i]) == GI_ARRAY_TYPE_C)) {
state->args[i]->v_pointer = array->data;
+ /* HACK: We have unwrapped a C array so
+ * set the state to reflect this.
+ * If there is an error between now
+ * and when we rewrap the array
+ * we will leak C arrays due to
+ * being in an inconsitant state.
+ * e.g. for interfaces with more
+ * than one C array argument, an
+ * error may occure when not all
+ * C arrays have been rewrapped.
+ * This will be removed once the invoke
+ * rewrite branch is merged.
+ */
+ state->c_arrays_are_wrapped = FALSE;
if (direction != GI_DIRECTION_INOUT || transfer != GI_TRANSFER_NOTHING) {
/* The array hasn't been referenced anywhere, so free it to avoid losing memory. */
g_array_free (array, FALSE);
@@ -851,6 +876,14 @@ _process_invocation_state (struct invocation_state *state,
}
+ /* HACK: We rewrapped any C arrays above in a GArray so they are ok to
+ * free as GArrays. We will always leak C arrays if there is
+ * an error before we reach this state as there is no easy way
+ * to know which arrays were wrapped if there are more than one.
+ * This will be removed with better array handling once merge
+ * the invoke rewrite branch.
+ */
+ state->c_arrays_are_wrapped = TRUE;
g_assert (state->n_return_values <= 1 || return_values_pos == state->n_return_values);
}
@@ -900,13 +933,26 @@ _free_invocation_state (struct invocation_state *state)
backup_args_pos += 1;
}
if (state->args != NULL && state->args[i] != NULL) {
- _pygi_argument_release (state->args[i], state->arg_type_infos[i],
- transfer, direction);
-
type_tag = g_type_info_get_tag (state->arg_type_infos[i]);
+
+ if (type_tag == GI_TYPE_TAG_ARRAY &&
+ (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) &&
+ (g_type_info_get_array_type (state->arg_type_infos[i]) == GI_ARRAY_TYPE_C) &&
+ !state->c_arrays_are_wrapped) {
+ /* HACK: Noop - we are in an inconsitant state due to
+ * complex array handler so leak any C arrays
+ * as we don't know if we can free them safely.
+ * This will be removed when we merge the
+ * invoke rewrite branch.
+ */
+ } else {
+ _pygi_argument_release (state->args[i], state->arg_type_infos[i],
+ transfer, direction);
+ }
+
if (type_tag == GI_TYPE_TAG_ARRAY
&& (direction != GI_DIRECTION_IN && transfer == GI_TRANSFER_NOTHING)) {
- /* We created a #GArray and it has not been released above, so free it. */
+ /* We created an *out* #GArray and it has not been released above, so free it. */
state->args[i]->v_pointer = g_array_free (state->args[i]->v_pointer, FALSE);
}
}
diff --git a/gi/pygi-private.h b/gi/pygi-private.h
index 3a14bc3..efe62c8 100644
--- a/gi/pygi-private.h
+++ b/gi/pygi-private.h
@@ -29,6 +29,7 @@
#include "pygi-callbacks.h"
#include "pygi-invoke.h"
#include "pygi-property.h"
+#include "pygi-signal-closure.h"
G_BEGIN_DECLS
#if PY_VERSION_HEX >= 0x03000000
diff --git a/gi/pygi-repository.c b/gi/pygi-repository.c
index 9b22eae..c48d2ce 100644
--- a/gi/pygi-repository.c
+++ b/gi/pygi-repository.c
@@ -234,6 +234,28 @@ _wrap_g_irepository_get_version (PyGIRepository *self,
return PYGLIB_PyUnicode_FromString (version);
}
+static PyObject *
+_wrap_g_irepository_get_loaded_namespaces (PyGIRepository *self)
+{
+ char **namespaces;
+ PyObject *py_namespaces;
+ gssize i;
+
+ namespaces = g_irepository_get_loaded_namespaces (self->repository);
+
+ py_namespaces = PyList_New (0);
+ for (i = 0; namespaces[i] != NULL; i++) {
+ PyObject *py_namespace = PYGLIB_PyUnicode_FromString (namespaces[i]);
+ PyList_Append (py_namespaces, py_namespace);
+ Py_DECREF(py_namespace);
+ g_free (namespaces[i]);
+ }
+
+ g_free (namespaces);
+
+ return py_namespaces;
+}
+
static PyMethodDef _PyGIRepository_methods[] = {
{ "enumerate_versions", (PyCFunction) _wrap_g_irepository_enumerate_versions, METH_VARARGS | METH_KEYWORDS },
{ "get_default", (PyCFunction) _wrap_g_irepository_get_default, METH_STATIC | METH_NOARGS },
@@ -242,6 +264,7 @@ static PyMethodDef _PyGIRepository_methods[] = {
{ "find_by_name", (PyCFunction) _wrap_g_irepository_find_by_name, METH_VARARGS | METH_KEYWORDS },
{ "get_typelib_path", (PyCFunction) _wrap_g_irepository_get_typelib_path, METH_VARARGS | METH_KEYWORDS },
{ "get_version", (PyCFunction) _wrap_g_irepository_get_version, METH_VARARGS | METH_KEYWORDS },
+ { "get_loaded_namespaces", (PyCFunction) _wrap_g_irepository_get_loaded_namespaces, METH_NOARGS },
{ NULL, NULL, 0 }
};
diff --git a/gi/pygi-signal-closure.c b/gi/pygi-signal-closure.c
new file mode 100644
index 0000000..1482529
--- /dev/null
+++ b/gi/pygi-signal-closure.c
@@ -0,0 +1,245 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2011 Laszlo Pandy <lpandy@src.gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#include "pygi-private.h"
+
+/* Copied from glib */
+static void
+canonicalize_key (gchar *key)
+{
+ gchar *p;
+
+ for (p = key; *p != 0; p++)
+ {
+ gchar c = *p;
+
+ if (c != '-' &&
+ (c < '0' || c > '9') &&
+ (c < 'A' || c > 'Z') &&
+ (c < 'a' || c > 'z'))
+ *p = '-';
+ }
+}
+
+static GISignalInfo *
+_pygi_lookup_signal_from_g_type (GType g_type,
+ const gchar *signal_name)
+{
+ GIRepository *repository;
+ GIBaseInfo *info;
+ gssize n_infos;
+ gssize i;
+ GType parent;
+
+ repository = g_irepository_get_default();
+ info = g_irepository_find_by_gtype (repository, g_type);
+ if (info != NULL) {
+ n_infos = g_object_info_get_n_signals ( (GIObjectInfo *) info);
+ for (i = 0; i < n_infos; i++) {
+ GISignalInfo *signal_info;
+
+ signal_info = g_object_info_get_signal ( (GIObjectInfo *) info, i);
+ g_assert (info != NULL);
+
+ if (strcmp (signal_name, g_base_info_get_name (signal_info)) == 0) {
+ g_base_info_unref (info);
+ return signal_info;
+ }
+
+ g_base_info_unref (signal_info);
+ }
+
+ g_base_info_unref (info);
+ }
+
+ parent = g_type_parent (g_type);
+ if (parent > 0)
+ return _pygi_lookup_signal_from_g_type (parent, signal_name);
+
+ return NULL;
+}
+
+static void
+pygi_signal_closure_invalidate(gpointer data,
+ GClosure *closure)
+{
+ PyGClosure *pc = (PyGClosure *)closure;
+ PyGILState_STATE state;
+
+ state = PyGILState_Ensure();
+ Py_XDECREF(pc->callback);
+ Py_XDECREF(pc->extra_args);
+ Py_XDECREF(pc->swap_data);
+ PyGILState_Release(state);
+
+ pc->callback = NULL;
+ pc->extra_args = NULL;
+ pc->swap_data = NULL;
+
+ g_base_info_unref (((PyGISignalClosure *) pc)->signal_info);
+ ((PyGISignalClosure *) pc)->signal_info = NULL;
+}
+
+static void
+pygi_signal_closure_marshal(GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ PyGILState_STATE state;
+ PyGClosure *pc = (PyGClosure *)closure;
+ PyObject *params, *ret = NULL;
+ guint i;
+ GISignalInfo *signal_info;
+ gint n_sig_info_args;
+ gint sig_info_highest_arg;
+
+ state = PyGILState_Ensure();
+
+ signal_info = ((PyGISignalClosure *)closure)->signal_info;
+ n_sig_info_args = g_callable_info_get_n_args(signal_info);
+ /* the first argument to a signal callback is instance,
+ but instance is not counted in the introspection data */
+ sig_info_highest_arg = n_sig_info_args + 1;
+ g_assert_cmpint(sig_info_highest_arg, ==, n_param_values);
+
+ /* construct Python tuple for the parameter values */
+ params = PyTuple_New(n_param_values);
+ for (i = 0; i < n_param_values; i++) {
+ /* swap in a different initial data for connect_object() */
+ if (i == 0 && G_CCLOSURE_SWAP_DATA(closure)) {
+ g_return_if_fail(pc->swap_data != NULL);
+ Py_INCREF(pc->swap_data);
+ PyTuple_SetItem(params, 0, pc->swap_data);
+
+ } else if (i == 0) {
+ PyObject *item = pyg_value_as_pyobject(&param_values[i], FALSE);
+
+ if (!item) {
+ goto out;
+ }
+ PyTuple_SetItem(params, i, item);
+
+ } else if (i < sig_info_highest_arg) {
+ GIArgInfo arg_info;
+ GITypeInfo type_info;
+ GITransfer transfer;
+ GIArgument arg = { 0, };
+ PyObject *item = NULL;
+
+ g_callable_info_load_arg(signal_info, i - 1, &arg_info);
+ g_arg_info_load_type(&arg_info, &type_info);
+ transfer = g_arg_info_get_ownership_transfer(&arg_info);
+
+ arg = _pygi_argument_from_g_value(&param_values[i], &type_info);
+ item = _pygi_argument_to_object(&arg, &type_info, transfer);
+
+ if (item == NULL) {
+ goto out;
+ }
+ PyTuple_SetItem(params, i, item);
+ }
+ }
+ /* params passed to function may have extra arguments */
+ if (pc->extra_args) {
+ PyObject *tuple = params;
+ params = PySequence_Concat(tuple, pc->extra_args);
+ Py_DECREF(tuple);
+ }
+ ret = PyObject_CallObject(pc->callback, params);
+ if (ret == NULL) {
+ if (pc->exception_handler)
+ pc->exception_handler(return_value, n_param_values, param_values);
+ else
+ PyErr_Print();
+ goto out;
+ }
+
+ if (return_value && pyg_value_from_pyobject(return_value, ret) != 0) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't convert return value to desired type");
+
+ if (pc->exception_handler)
+ pc->exception_handler(return_value, n_param_values, param_values);
+ else
+ PyErr_Print();
+ }
+ Py_DECREF(ret);
+
+ out:
+ Py_DECREF(params);
+ PyGILState_Release(state);
+}
+
+GClosure *
+pygi_signal_closure_new_real (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data)
+{
+ GClosure *closure = NULL;
+ PyGISignalClosure *pygi_closure = NULL;
+ GType g_type;
+ GISignalInfo *signal_info = NULL;
+ char *signal_name = g_strdup (sig_name);
+
+ g_return_val_if_fail(callback != NULL, NULL);
+
+ canonicalize_key(signal_name);
+
+ g_type = pyg_type_from_object ((PyObject *)instance);
+ signal_info = _pygi_lookup_signal_from_g_type (g_type, signal_name);
+
+ if (signal_info == NULL)
+ goto out;
+
+ closure = g_closure_new_simple(sizeof(PyGISignalClosure), NULL);
+ g_closure_add_invalidate_notifier(closure, NULL, pygi_signal_closure_invalidate);
+ g_closure_set_marshal(closure, pygi_signal_closure_marshal);
+
+ pygi_closure = (PyGISignalClosure *)closure;
+
+ pygi_closure->signal_info = signal_info;
+ Py_INCREF(callback);
+ pygi_closure->pyg_closure.callback = callback;
+
+ if (extra_args != NULL && extra_args != Py_None) {
+ Py_INCREF(extra_args);
+ if (!PyTuple_Check(extra_args)) {
+ PyObject *tmp = PyTuple_New(1);
+ PyTuple_SetItem(tmp, 0, extra_args);
+ extra_args = tmp;
+ }
+ pygi_closure->pyg_closure.extra_args = extra_args;
+ }
+ if (swap_data) {
+ Py_INCREF(swap_data);
+ pygi_closure->pyg_closure.swap_data = swap_data;
+ closure->derivative_flag = TRUE;
+ }
+
+out:
+ g_free (signal_name);
+
+ return closure;
+}
diff --git a/gi/pygi-signal-closure.h b/gi/pygi-signal-closure.h
new file mode 100644
index 0000000..4687f3f
--- /dev/null
+++ b/gi/pygi-signal-closure.h
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2011 Laszlo Pandy <lpandy@src.gnome.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef __PYGI_SIGNAL_CLOSURE_H__
+#define __PYGI_SIGNAL_CLOSURE_H__
+
+#include "pygi.h"
+
+G_BEGIN_DECLS
+
+/* Private */
+typedef struct _PyGISignalClosure
+{
+ PyGClosure pyg_closure;
+ GISignalInfo *signal_info;
+} PyGISignalClosure;
+
+GClosure * pygi_signal_closure_new_real (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data);
+
+G_END_DECLS
+
+#endif /* __PYGI_SIGNAL_CLOSURE_H__ */
diff --git a/gi/pygi.h b/gi/pygi.h
index 2765b40..1c3bf5e 100644
--- a/gi/pygi.h
+++ b/gi/pygi.h
@@ -71,6 +71,11 @@ struct PyGI_API {
gint (*set_property_value) (PyGObject *instance,
const gchar *attr_name,
PyObject *value);
+ GClosure * (*signal_closure_new) (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data);
void (*register_foreign_struct) (const char* namespace_,
const char* name,
PyGIArgOverrideToGIArgumentFunc to_func,
@@ -83,14 +88,20 @@ static struct PyGI_API *PyGI_API = NULL;
static int
_pygi_import (void)
{
+ PyObject *modules_dict;
+
if (PyGI_API != NULL) {
return 1;
}
+
+ modules_dict = PyImport_GetModuleDict(); /* borrowed reference -- don't unref */
+ if (PyMapping_HasKeyString(modules_dict, "gi")) {
#if PY_VERSION_HEX >= 0x03000000
- PyGI_API = (struct PyGI_API*) PyCapsule_Import("gi._API", FALSE);
+ PyGI_API = (struct PyGI_API*) PyCapsule_Import("gi._API", FALSE);
#else
- PyGI_API = (struct PyGI_API*) PyCObject_Import("gi", "_API");
+ PyGI_API = (struct PyGI_API*) PyCObject_Import("gi", "_API");
#endif
+ }
if (PyGI_API == NULL) {
return -1;
}
@@ -128,6 +139,19 @@ pygi_set_property_value (PyGObject *instance,
return PyGI_API->set_property_value(instance, attr_name, value);
}
+static inline GClosure *
+pygi_signal_closure_new (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data)
+{
+ if (_pygi_import() < 0) {
+ return NULL;
+ }
+ return PyGI_API->signal_closure_new(instance, sig_name, callback, extra_args, swap_data);
+}
+
static inline PyObject *
pygi_register_foreign_struct (const char* namespace_,
const char* name,
@@ -169,6 +193,16 @@ pygi_set_property_value (PyGObject *instance,
return -1;
}
+static inline GClosure *
+pygi_signal_closure_new (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data)
+{
+ return NULL;
+}
+
#endif /* ENABLE_INTROSPECTION */
#endif /* __PYGI_H__ */
diff --git a/gi/types.py b/gi/types.py
index 37cf499..ed568d1 100644
--- a/gi/types.py
+++ b/gi/types.py
@@ -229,6 +229,46 @@ class GObjectMeta(gobject.GObjectMeta, MetaClassHelper):
elif isinstance(cls.__info__, InterfaceInfo):
register_interface_info(cls.__info__.get_g_type())
+ def mro(cls):
+ return mro(cls)
+
+
+def mro(C):
+ """Compute the class precedence list (mro) according to C3
+
+ Based on http://www.python.org/download/releases/2.3/mro/
+ Modified to consider that interfaces don't create the diamond problem
+ """
+ # TODO: If this turns out being too slow, consider using generators
+ bases = []
+ bases_of_subclasses = [[C]]
+
+ if C.__bases__:
+ bases_of_subclasses += map(mro, C.__bases__) + [list(C.__bases__)]
+
+ while bases_of_subclasses:
+ for subclass_bases in bases_of_subclasses:
+ candidate = subclass_bases[0]
+ not_head = [s for s in bases_of_subclasses if candidate in s[1:]]
+ if not_head and gobject.GInterface not in candidate.__bases__:
+ candidate = None # conflict, reject candidate
+ else:
+ break
+
+ if candidate is None:
+ raise TypeError('Cannot create a consistent method resolution '
+ 'order (MRO)')
+
+ bases.append(candidate)
+
+ for subclass_bases in bases_of_subclasses[:]: # remove candidate
+ if subclass_bases and subclass_bases[0] == candidate:
+ del subclass_bases[0]
+ if not subclass_bases:
+ bases_of_subclasses.remove(subclass_bases)
+
+ return bases
+
class StructMeta(type, MetaClassHelper):
@@ -250,19 +290,3 @@ class StructMeta(type, MetaClassHelper):
not method_info.get_arguments():
cls.__new__ = staticmethod(Constructor(method_info))
break
-
-class Enum(int):
- # Only subclasses of this type should be instantiated.
- # Each subclass requires an __info__ attribute,
- # which is not declared here because enums do not share the same gi type.
- def __init__(self, value):
- int.__init__(value)
-
- def __repr__(self):
- value_name = str(self)
- for value_info in self.__info__.get_values():
- if self == value_info.get_value():
- value_name = value_info.get_name().upper()
- return "<enum %s of type %s.%s>" % (value_name,
- self.__info__.get_namespace(),
- self.__info__.get_name())