diff options
Diffstat (limited to 'gi')
-rw-r--r-- | gi/Makefile.am | 2 | ||||
-rw-r--r-- | gi/Makefile.in | 13 | ||||
-rw-r--r-- | gi/__init__.py | 29 | ||||
-rw-r--r-- | gi/gimodule.c | 132 | ||||
-rw-r--r-- | gi/importer.py | 1 | ||||
-rw-r--r-- | gi/module.py | 45 | ||||
-rw-r--r-- | gi/overrides/Gdk.py | 116 | ||||
-rw-r--r-- | gi/overrides/Gio.py | 99 | ||||
-rw-r--r-- | gi/overrides/Gtk.py | 53 | ||||
-rw-r--r-- | gi/pygi-argument.c | 153 | ||||
-rw-r--r-- | gi/pygi-argument.h | 2 | ||||
-rw-r--r-- | gi/pygi-closure.c | 8 | ||||
-rw-r--r-- | gi/pygi-info.c | 15 | ||||
-rw-r--r-- | gi/pygi-invoke.c | 54 | ||||
-rw-r--r-- | gi/pygi-private.h | 1 | ||||
-rw-r--r-- | gi/pygi-repository.c | 23 | ||||
-rw-r--r-- | gi/pygi-signal-closure.c | 245 | ||||
-rw-r--r-- | gi/pygi-signal-closure.h | 46 | ||||
-rw-r--r-- | gi/pygi.h | 38 | ||||
-rw-r--r-- | gi/types.py | 56 |
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(¶m_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(¶m_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__ */ @@ -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()) |