diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2017-07-12 08:35:42 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2017-07-12 08:35:43 +0900 |
commit | ee96b8b4ed303ec374e37237f86555754cf2ae60 (patch) | |
tree | 6ad8344f0cf7f15b105be722ca87f77f16ee4c31 /gi | |
parent | f29005349e245e146d416b2dc7a8f8a89a2e8a61 (diff) | |
download | pygobject2-ee96b8b4ed303ec374e37237f86555754cf2ae60.tar.gz pygobject2-ee96b8b4ed303ec374e37237f86555754cf2ae60.tar.bz2 pygobject2-ee96b8b4ed303ec374e37237f86555754cf2ae60.zip |
Imported Upstream version 2.27.90
Change-Id: I23ecc791685b18ea66500f2af94287da6bec1436
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
Diffstat (limited to 'gi')
-rw-r--r-- | gi/Makefile.am | 11 | ||||
-rw-r--r-- | gi/Makefile.in | 19 | ||||
-rw-r--r-- | gi/gimodule.c | 144 | ||||
-rw-r--r-- | gi/module.py | 66 | ||||
-rw-r--r-- | gi/overrides/GIMarshallingTests.py | 2 | ||||
-rw-r--r-- | gi/overrides/GLib.py | 322 | ||||
-rw-r--r-- | gi/overrides/Gdk.py | 50 | ||||
-rw-r--r-- | gi/overrides/Gio.py | 99 | ||||
-rw-r--r-- | gi/overrides/Gtk.py | 335 | ||||
-rw-r--r-- | gi/overrides/Makefile.am | 6 | ||||
-rw-r--r-- | gi/overrides/Makefile.in | 9 | ||||
-rw-r--r-- | gi/overrides/Pango.py | 51 | ||||
-rw-r--r-- | gi/overrides/__init__.py | 27 | ||||
-rw-r--r-- | gi/pygi-argument.c | 180 | ||||
-rw-r--r-- | gi/pygi-closure.c | 69 | ||||
-rw-r--r-- | gi/pygi-foreign-cairo.c | 72 | ||||
-rw-r--r-- | gi/pygi-foreign-gvariant.c | 4 | ||||
-rw-r--r-- | gi/pygi-foreign-gvariant.h | 2 | ||||
-rw-r--r-- | gi/pygi-foreign.c | 2 | ||||
-rw-r--r-- | gi/pygi-info.c | 46 | ||||
-rw-r--r-- | gi/pygi-invoke.c | 118 | ||||
-rw-r--r-- | gi/pygi-invoke.h | 3 | ||||
-rw-r--r-- | gi/pygi-property.c | 8 | ||||
-rw-r--r-- | gi/pygi.h | 2 | ||||
-rw-r--r-- | gi/repository/Makefile.am | 4 | ||||
-rw-r--r-- | gi/repository/Makefile.in | 7 | ||||
-rw-r--r-- | gi/types.py | 149 |
27 files changed, 1406 insertions, 401 deletions
diff --git a/gi/Makefile.am b/gi/Makefile.am index 2fbb4ac..a98993b 100644 --- a/gi/Makefile.am +++ b/gi/Makefile.am @@ -1,7 +1,7 @@ PLATFORM_VERSION = 2.0 pkgincludedir = $(includedir)/pygtk-$(PLATFORM_VERSION) -pkgpyexecdir = $(pyexecdir)/gtk-2.0 +pkgpyexecdir = $(pyexecdir) SUBDIRS = \ repository \ @@ -24,7 +24,9 @@ _gi_la_LDFLAGS = \ -avoid-version \ -export-symbols-regex "init_gi|PyInit__gi" _gi_la_LIBADD = \ - $(GI_LIBS) + $(GI_LIBS) \ + $(PYTHON_LIBS) \ + $(top_builddir)/glib/libpyglib-2.0-@PYTHON_BASENAME@.la _gi_la_SOURCES = \ pygi-repository.c \ pygi-repository.h \ @@ -65,7 +67,8 @@ _gi_cairo_la_LDFLAGS = \ -export-symbols-regex "init_gi_cairo|PyInit__gi_cairo" _gi_cairo_la_LIBADD = \ $(GI_LIBS) \ - $(PYCAIRO_LIBS) + $(PYCAIRO_LIBS) \ + $(PYTHON_LIBS) _gi_cairo_la_SOURCES = pygi-foreign-cairo.c @@ -87,3 +90,5 @@ check-local: $(LTLIBRARIES:.la=.so) clean-local: rm -f $(LTLIBRARIES:.la=.so) + +-include $(top_srcdir)/git.mk diff --git a/gi/Makefile.in b/gi/Makefile.in index f0dd624..173abf6 100644 --- a/gi/Makefile.in +++ b/gi/Makefile.in @@ -73,7 +73,8 @@ am__base_list = \ am__installdirs = "$(DESTDIR)$(pygidir)" "$(DESTDIR)$(pygidir)" LTLIBRARIES = $(pygi_LTLIBRARIES) am__DEPENDENCIES_1 = -_gi_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +_gi_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(top_builddir)/glib/libpyglib-2.0-@PYTHON_BASENAME@.la am__gi_la_OBJECTS = _gi_la-pygi-repository.lo _gi_la-pygi-info.lo \ _gi_la-pygi-invoke.lo _gi_la-pygi-foreign.lo \ _gi_la-pygi-foreign-gvariant.lo _gi_la-pygi-struct.lo \ @@ -89,7 +90,7 @@ _gi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(_gi_la_CFLAGS) $(CFLAGS) \ $(_gi_la_LDFLAGS) $(LDFLAGS) -o $@ _gi_cairo_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am__gi_cairo_la_OBJECTS = _gi_cairo_la-pygi-foreign-cairo.lo _gi_cairo_la_OBJECTS = $(am__gi_cairo_la_OBJECTS) _gi_cairo_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ @@ -228,6 +229,7 @@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ @@ -256,6 +258,7 @@ PYTHON = @PYTHON@ PYTHON_BASENAME = @PYTHON_BASENAME@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_INCLUDES = @PYTHON_INCLUDES@ +PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ @@ -271,6 +274,7 @@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ @@ -307,7 +311,7 @@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ -pkgpyexecdir = $(pyexecdir)/gtk-2.0 +pkgpyexecdir = $(pyexecdir) pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ @@ -346,7 +350,9 @@ _gi_la_LDFLAGS = \ -export-symbols-regex "init_gi|PyInit__gi" _gi_la_LIBADD = \ - $(GI_LIBS) + $(GI_LIBS) \ + $(PYTHON_LIBS) \ + $(top_builddir)/glib/libpyglib-2.0-@PYTHON_BASENAME@.la _gi_la_SOURCES = \ pygi-repository.c \ @@ -390,7 +396,8 @@ _gi_cairo_la_LDFLAGS = \ _gi_cairo_la_LIBADD = \ $(GI_LIBS) \ - $(PYCAIRO_LIBS) + $(PYCAIRO_LIBS) \ + $(PYTHON_LIBS) _gi_cairo_la_SOURCES = pygi-foreign-cairo.c pygi_LTLIBRARIES = _gi.la $(am__append_1) @@ -995,6 +1002,8 @@ check-local: $(LTLIBRARIES:.la=.so) clean-local: rm -f $(LTLIBRARIES:.la=.so) +-include $(top_srcdir)/git.mk + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/gi/gimodule.c b/gi/gimodule.c index f7624ae..f70d0f2 100644 --- a/gi/gimodule.c +++ b/gi/gimodule.c @@ -131,39 +131,25 @@ _wrap_pyg_register_interface_info (PyObject *self, PyObject *args) Py_RETURN_NONE; } -static PyObject * -_wrap_pyg_hook_up_vfunc_implementation (PyObject *self, PyObject *args) +static void +find_vfunc_info (GIBaseInfo *vfunc_info, + GType implementor_gtype, + gpointer *implementor_class_ret, + gpointer *implementor_vtable_ret, + GIFieldInfo **field_info_ret) { - PyGIBaseInfo *py_info; - PyObject *py_type; - PyObject *py_function; - gpointer implementor_class = NULL; GType ancestor_g_type = 0; - GType implementor_gtype = 0; - gpointer *method_ptr = NULL; int length, i; - GIBaseInfo *vfunc_info; GIBaseInfo *ancestor_info; GIStructInfo *struct_info; + gpointer implementor_class = NULL; gboolean is_interface = FALSE; - PyGICClosure *closure = NULL; - if (!PyArg_ParseTuple (args, "O!O!O:hook_up_vfunc_implementation", - &PyGIBaseInfo_Type, &py_info, - &PyGTypeWrapper_Type, &py_type, - &py_function)) - return NULL; - - implementor_gtype = pyg_type_from_object (py_type); - g_assert (G_TYPE_IS_CLASSED (implementor_gtype)); - - vfunc_info = py_info->info; ancestor_info = g_base_info_get_container (vfunc_info); is_interface = g_base_info_get_type (ancestor_info) == GI_INFO_TYPE_INTERFACE; ancestor_g_type = g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *) ancestor_info); - implementor_class = g_type_class_ref (implementor_gtype); if (is_interface) { GTypeInstance *implementor_iface_class; @@ -175,23 +161,23 @@ _wrap_pyg_hook_up_vfunc_implementation (PyObject *self, PyObject *args) "Couldn't find GType of implementor of interface %s. " "Forgot to set __gtype_name__?", g_type_name (ancestor_g_type)); - return NULL; + return; } - g_type_class_unref (implementor_class); - implementor_class = implementor_iface_class; + *implementor_vtable_ret = implementor_iface_class; struct_info = g_interface_info_get_iface_struct ( (GIInterfaceInfo*) ancestor_info); - } else + } else { struct_info = g_object_info_get_class_struct ( (GIObjectInfo*) ancestor_info); + *implementor_vtable_ret = implementor_class; + } + + *implementor_class_ret = implementor_class; length = g_struct_info_get_n_fields (struct_info); for (i = 0; i < length; i++) { GIFieldInfo *field_info; GITypeInfo *type_info; - GIBaseInfo *interface_info; - GICallbackInfo *callback_info; - gint offset; field_info = g_struct_info_get_field (struct_info, i); @@ -202,18 +188,56 @@ _wrap_pyg_hook_up_vfunc_implementation (PyObject *self, PyObject *args) } type_info = g_field_info_get_type (field_info); - if (g_type_info_get_tag (type_info) != GI_TYPE_TAG_INTERFACE) { + if (g_type_info_get_tag (type_info) == GI_TYPE_TAG_INTERFACE) { g_base_info_unref (type_info); - g_base_info_unref (field_info); - continue; + *field_info_ret = field_info; + break; } + g_base_info_unref (type_info); + g_base_info_unref (field_info); + } + + g_base_info_unref (struct_info); +} + +static PyObject * +_wrap_pyg_hook_up_vfunc_implementation (PyObject *self, PyObject *args) +{ + PyGIBaseInfo *py_info; + PyObject *py_type; + PyObject *py_function; + GType implementor_gtype = 0; + gpointer implementor_class = NULL; + gpointer implementor_vtable = NULL; + GIFieldInfo *field_info = NULL; + gpointer *method_ptr = NULL; + PyGICClosure *closure = NULL; + + if (!PyArg_ParseTuple (args, "O!O!O:hook_up_vfunc_implementation", + &PyGIBaseInfo_Type, &py_info, + &PyGTypeWrapper_Type, &py_type, + &py_function)) + return NULL; + + implementor_gtype = pyg_type_from_object (py_type); + g_assert (G_TYPE_IS_CLASSED (implementor_gtype)); + + find_vfunc_info (py_info->info, implementor_gtype, &implementor_class, &implementor_vtable, &field_info); + if (field_info != NULL) { + GITypeInfo *type_info; + GIBaseInfo *interface_info; + GICallbackInfo *callback_info; + gint offset; + + type_info = g_field_info_get_type (field_info); + interface_info = g_type_info_get_interface (type_info); g_assert (g_base_info_get_type (interface_info) == GI_INFO_TYPE_CALLBACK); callback_info = (GICallbackInfo*) interface_info; offset = g_field_info_get_offset (field_info); - method_ptr = G_STRUCT_MEMBER_P (implementor_class, offset); + method_ptr = G_STRUCT_MEMBER_P (implementor_vtable, offset); closure = _pygi_make_native_closure ( (GICallableInfo*) callback_info, GI_SCOPE_TYPE_NOTIFIED, py_function, NULL); @@ -223,17 +247,53 @@ _wrap_pyg_hook_up_vfunc_implementation (PyObject *self, PyObject *args) g_base_info_unref (interface_info); g_base_info_unref (type_info); g_base_info_unref (field_info); - - break; } + g_type_class_unref (implementor_class); - g_base_info_unref (struct_info); + Py_RETURN_NONE; +} - if (!is_interface) - g_type_class_unref (implementor_class); +#if 0 +/* Not used, left around for future reference */ +static PyObject * +_wrap_pyg_has_vfunc_implementation (PyObject *self, PyObject *args) +{ + PyGIBaseInfo *py_info; + PyObject *py_type; + PyObject *py_ret; + gpointer implementor_class = NULL; + gpointer implementor_vtable = NULL; + GType implementor_gtype = 0; + GIFieldInfo *field_info = NULL; - Py_RETURN_NONE; + if (!PyArg_ParseTuple (args, "O!O!:has_vfunc_implementation", + &PyGIBaseInfo_Type, &py_info, + &PyGTypeWrapper_Type, &py_type)) + return NULL; + + implementor_gtype = pyg_type_from_object (py_type); + g_assert (G_TYPE_IS_CLASSED (implementor_gtype)); + + py_ret = Py_False; + find_vfunc_info (py_info->info, implementor_gtype, &implementor_class, &implementor_vtable, &field_info); + if (field_info != NULL) { + gpointer *method_ptr; + gint offset; + + offset = g_field_info_get_offset (field_info); + method_ptr = G_STRUCT_MEMBER_P (implementor_vtable, offset); + if (*method_ptr != NULL) { + py_ret = Py_True; + } + + g_base_info_unref (field_info); + } + g_type_class_unref (implementor_class); + + Py_INCREF(py_ret); + return py_ret; } +#endif static PyObject * _wrap_pyg_variant_new_tuple (PyObject *self, PyObject *args) @@ -258,7 +318,7 @@ _wrap_pyg_variant_new_tuple (PyObject *self, PyObject *args) PyObject *value = PyTuple_GET_ITEM (py_values, i); if (!PyObject_IsInstance (value, py_type)) { - PyErr_Format (PyExc_TypeError, "argument %d is not a GLib.Variant", i); + PyErr_Format (PyExc_TypeError, "argument %" G_GSSIZE_FORMAT " is not a GLib.Variant", i); return NULL; } @@ -315,11 +375,11 @@ PYGLIB_MODULE_START(_gi, "_gi") PyObject *api; if (pygobject_init (-1, -1, -1) == NULL) { - return; + return PYGLIB_MODULE_ERROR_RETURN; } if (_pygobject_import() < 0) { - return; + return PYGLIB_MODULE_ERROR_RETURN; } _pygi_repository_register_types (module); @@ -330,7 +390,7 @@ PYGLIB_MODULE_START(_gi, "_gi") api = PYGLIB_CPointer_WrapPointer ( (void *) &CAPI, "gi._API"); if (api == NULL) { - return; + return PYGLIB_MODULE_ERROR_RETURN; } PyModule_AddObject (module, "_API", api); } diff --git a/gi/module.py b/gi/module.py index c7b6557..9b935ed 100644 --- a/gi/module.py +++ b/gi/module.py @@ -82,14 +82,14 @@ class IntrospectionModule(object): def __init__(self, namespace, version=None): repository.require(namespace, version) self._namespace = namespace - self.version = version + self._version = version self.__name__ = 'gi.repository.' + namespace - repository.require(self._namespace, self.version) + repository.require(self._namespace, self._version) self.__path__ = repository.get_typelib_path(self._namespace) - if self.version is None: - self.version = repository.get_version(self._namespace) + if self._version is None: + self._version = repository.get_version(self._namespace) def __getattr__(self, name): info = repository.find_by_name(self._namespace, name) @@ -106,7 +106,7 @@ class IntrospectionModule(object): 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 = Enum + wrapper = type(info.get_name(), (Enum,), {}) else: wrapper = flags_add(g_type) @@ -114,8 +114,11 @@ class IntrospectionModule(object): wrapper.__module__ = 'gi.repository.' + info.get_namespace() for value_info in info.get_values(): - name = value_info.get_name().upper() - setattr(wrapper, name, wrapper(value_info.get_value())) + value_name = value_info.get_name().upper() + setattr(wrapper, value_name, wrapper(value_info.get_value())) + + if g_type != gobject.TYPE_NONE: + g_type.pytype = wrapper elif isinstance(info, RegisteredTypeInfo): g_type = info.get_g_type() @@ -175,8 +178,17 @@ class IntrospectionModule(object): return "<IntrospectionModule %r from %r>" % (self._namespace, path) def __dir__ (self): - attribs_list = repository.get_infos(self._namespace) - return list(map(lambda x: x.get_name(), attribs_list)) + # Python's default dir() is just dir(self.__class__) + self.__dict__.keys() + result = set(dir(self.__class__)) + result.update(self.__dict__.keys()) + + # update *set* because some repository attributes have already been + # wrapped by __getattr__() and included in self.__dict__ + namespace_infos = repository.get_infos(self._namespace) + result.update(info.get_name() for info in namespace_infos) + + return list(result) + class DynamicGObjectModule(IntrospectionModule): """Wrapper for the GObject module @@ -211,26 +223,27 @@ class DynamicGObjectModule(IntrospectionModule): class DynamicModule(object): def __init__(self, namespace): self._namespace = namespace - self.introspection_module = None + 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: + 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): - self.introspection_module = IntrospectionModule(self._namespace, - self._version) + self._introspection_module = IntrospectionModule(self._namespace, + self._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: + if self._introspection_module is None: self._import() if self._overrides_module is not None: @@ -247,9 +260,26 @@ class DynamicModule(object): if key in registry: return registry[key] - return getattr(self.introspection_module, name) + 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()) + + result.update(dir(self._introspection_module)) + override_exports = getattr(self._overrides_module, '__all__', ()) + result.update(override_exports) + return list(result) + + def __repr__(self): repository.require(self._namespace, self._version) - attribs_list = repository.get_infos(self._namespace) - return list(map(lambda x: x.get_name(), attribs_list)) + + path = repository.get_typelib_path(self._namespace) + return "<%s.%s %r from %r>" % (self.__class__.__module__, + self.__class__.__name__, + self._namespace, + path) diff --git a/gi/overrides/GIMarshallingTests.py b/gi/overrides/GIMarshallingTests.py index 25a882f..aac8883 100644 --- a/gi/overrides/GIMarshallingTests.py +++ b/gi/overrides/GIMarshallingTests.py @@ -21,7 +21,7 @@ from ..overrides import override from ..importer import modules -GIMarshallingTests = modules['GIMarshallingTests'].introspection_module +GIMarshallingTests = modules['GIMarshallingTests']._introspection_module __all__ = [] diff --git a/gi/overrides/GLib.py b/gi/overrides/GLib.py index 78d8c35..ac783be 100644 --- a/gi/overrides/GLib.py +++ b/gi/overrides/GLib.py @@ -21,10 +21,21 @@ from ..importer import modules from .._gi import variant_new_tuple, variant_type_from_string -GLib = modules['GLib'].introspection_module +GLib = modules['GLib']._introspection_module __all__ = [] +def _create_variant(value): + '''Create a variant containing the variant "value". + + This is usually done with the GLib.Variant.new_variant() leaf + constructor, but this is currently broken, see GNOME#639952. + ''' + builder = GLib.VariantBuilder() + builder.init(variant_type_from_string('v')) + builder.add_value(value) + return builder.end() + class _VariantCreator(object): _LEAF_CONSTRUCTORS = { @@ -41,115 +52,242 @@ class _VariantCreator(object): 's': GLib.Variant.new_string, 'o': GLib.Variant.new_object_path, 'g': GLib.Variant.new_signature, - 'v': GLib.Variant.new_variant, + #'v': GLib.Variant.new_variant, + 'v': _create_variant, } - def __init__(self, format_string, args): - self._format_string = format_string - self._args = args + def _create(self, format, args): + '''Create a GVariant object from given format and argument list. - def create(self): - if self._format_string_is_leaf(): - return self._new_variant_leaf() + This method recursively calls itself for complex structures (arrays, + dictionaries, boxed). - format_char = self._pop_format_char() - arg = self._pop_arg() + Return a tuple (variant, rest_format, rest_args) with the generated + GVariant, the remainder of the format string, and the remainder of the + arguments. - if format_char == 'm': - raise NotImplementedError() - else: - builder = GLib.VariantBuilder() - if format_char == '(': - builder.init(variant_type_from_string('r')) - elif format_char == '{': - builder.init(variant_type_from_string('{?*}')) + If args is None, then this won't actually consume any arguments, and + just parse the format string and generate empty GVariant structures. + This is required for creating empty dictionaries or arrays. + ''' + # leaves (simple types) + constructor = self._LEAF_CONSTRUCTORS.get(format[0]) + if constructor: + if args is not None: + if not args: + raise TypeError('not enough arguments for GVariant format string') + v = constructor(args[0]) + return (constructor(args[0]), format[1:], args[1:]) else: - raise NotImplementedError() - format_char = self._pop_format_char() - while format_char not in [')', '}']: - builder.add_value(Variant(format_char, arg)) - format_char = self._pop_format_char() - if self._args: - arg = self._pop_arg() - return builder.end() - - def _format_string_is_leaf(self): - format_char = self._format_string[0] - return not format_char in ['m', '(', '{'] - - def _format_string_is_nnp(self): - format_char = self._format_string[0] - return format_char in ['a', 's', 'o', 'g', '^', '@', '*', '?', 'r', - 'v', '&'] - - def _new_variant_leaf(self): - if self._format_string_is_nnp(): - return self._new_variant_nnp() - - format_char = self._pop_format_char() - arg = self._pop_arg() - - return _VariantCreator._LEAF_CONSTRUCTORS[format_char](arg) - - def _new_variant_nnp(self): - format_char = self._pop_format_char() - arg = self._pop_arg() - - if format_char == '&': - format_char = self._pop_format_char() - - if format_char == 'a': - builder = GLib.VariantBuilder() - builder.init(variant_type_from_string('a*')) + return (None, format[1:], None) - element_format_string = self._pop_leaf_format_string() + if format[0] == '(': + return self._create_tuple(format, args) - if isinstance(arg, dict): - for element in arg.items(): - value = Variant(element_format_string, *element) - builder.add_value(value) - else: - for element in arg: - value = Variant(element_format_string, element) - builder.add_value(value) - return builder.end() - elif format_char == '^': - raise NotImplementedError() - elif format_char == '@': - raise NotImplementedError() - elif format_char == '*': - raise NotImplementedError() - elif format_char == 'r': - raise NotImplementedError() - elif format_char == '?': - raise NotImplementedError() + if format.startswith('a{'): + return self._create_dict(format, args) + + if format[0] == 'a': + return self._create_array(format, args) + + raise NotImplementedError('cannot handle GVariant type ' + format) + + def _create_tuple(self, format, args): + '''Handle the case where the outermost type of format is a tuple.''' + + format = format[1:] # eat the '(' + builder = GLib.VariantBuilder() + builder.init(variant_type_from_string('r')) + if args is not None: + if not args or type(args[0]) != type(()): + raise (TypeError, 'expected tuple argument') + + for i in range(len(args[0])): + if format.startswith(')'): + raise (TypeError, 'too many arguments for tuple signature') + + (v, format, _) = self._create(format, args[0][i:]) + builder.add_value(v) + args = args[1:] + return (builder.end(), format[1:], args) + + def _create_dict(self, format, args): + '''Handle the case where the outermost type of format is a dict.''' + + builder = GLib.VariantBuilder() + if args is None or not args[0]: + # empty value: we need to call _create() to parse the subtype, + # and specify the element type precisely + rest_format = self._create(format[2:], None)[1] + rest_format = self._create(rest_format, None)[1] + if not rest_format.startswith('}'): + raise ValueError('dictionary type string not closed with }') + rest_format = rest_format[1:] # eat the } + element_type = format[:len(format) - len(rest_format)] + builder.init(variant_type_from_string(element_type)) else: - return _VariantCreator._LEAF_CONSTRUCTORS[format_char](arg) + builder.init(variant_type_from_string('a{?*}')) + for k, v in args[0].items(): + (key_v, rest_format, _) = self._create(format[2:], [k]) + (val_v, rest_format, _) = self._create(rest_format, [v]) - def _pop_format_char(self): - format_char = self._format_string[0] - self._format_string = self._format_string[1:] - return format_char + if not rest_format.startswith('}'): + raise ValueError('dictionary type string not closed with }') + rest_format = rest_format[1:] # eat the } - def _pop_leaf_format_string(self): - # FIXME: This will break when the leaf is inside a tuple or dict entry - format_string = self._format_string - self._format_string = '' - return format_string + entry = GLib.VariantBuilder() + entry.init(variant_type_from_string('{?*}')) + entry.add_value(key_v) + entry.add_value(val_v) + builder.add_value(entry.end()) - def _pop_arg(self): - arg = self._args[0] - self._args = self._args[1:] - return arg + if args is not None: + args = args[1:] + return (builder.end(), rest_format, args) + + def _create_array(self, format, args): + '''Handle the case where the outermost type of format is an array.''' + + builder = GLib.VariantBuilder() + if args is None or not args[0]: + # empty value: we need to call _create() to parse the subtype, + # and specify the element type precisely + rest_format = self._create(format[1:], None)[1] + element_type = format[:len(format) - len(rest_format)] + builder.init(variant_type_from_string(element_type)) + else: + builder.init(variant_type_from_string('a*')) + for i in range(len(args[0])): + (v, rest_format, _) = self._create(format[1:], args[0][i:]) + builder.add_value(v) + if args is not None: + args = args[1:] + return (builder.end(), rest_format, args) class Variant(GLib.Variant): - def __new__(cls, format_string, *args): - creator = _VariantCreator(format_string, args) - return creator.create() + def __new__(cls, format_string, value): + '''Create a GVariant from a native Python object. + + format_string is a standard GVariant type signature, value is a Python + object whose structure has to match the signature. + + Examples: + GLib.Variant('i', 1) + GLib.Variant('(is)', (1, 'hello')) + GLib.Variant('(asa{sv})', ([], {'foo': GLib.Variant('b', True), + 'bar': GLib.Variant('i', 2)})) + ''' + creator = _VariantCreator() + (v, rest_format, _) = creator._create(format_string, [value]) + if rest_format: + raise TypeError('invalid remaining format string: "%s"' % rest_format) + return v def __repr__(self): return '<GLib.Variant(%s)>' % getattr(self, 'print')(True) + def unpack(self): + '''Decompose a GVariant into a native Python object.''' + + LEAF_ACCESSORS = { + 'b': self.get_boolean, + 'y': self.get_byte, + 'n': self.get_int16, + 'q': self.get_uint16, + 'i': self.get_int32, + 'u': self.get_uint32, + 'x': self.get_int64, + 't': self.get_uint64, + 'h': self.get_handle, + 'd': self.get_double, + 's': self.get_string, + 'o': self.get_string, # object path + 'g': self.get_string, # signature + } + + # simple values + la = LEAF_ACCESSORS.get(self.get_type_string()) + if la: + return la() + + # tuple + if self.get_type_string().startswith('('): + res = [self.get_child_value(i).unpack() + for i in range(self.n_children())] + return tuple(res) + + # dictionary + if self.get_type_string().startswith('a{'): + res = {} + for i in range(self.n_children()): + v = self.get_child_value(i) + res[v.get_child_value(0).unpack()] = v.get_child_value(1).unpack() + return res + + # array + if self.get_type_string().startswith('a'): + return [self.get_child_value(i).unpack() + for i in range(self.n_children())] + + # variant (just unbox transparently) + if self.get_type_string().startswith('v'): + return self.get_variant().unpack() + + raise NotImplementedError('unsupported GVariant type ' + self.get_type_string()) + + # + # Pythonic iterators + # + def __len__(self): + if self.get_type_string() in ['s', 'o', 'g']: + return len(self.get_string()) + if self.get_type_string().startswith('a') or self.get_type_string().startswith('('): + return self.n_children() + raise TypeError('GVariant type %s does not have a length' % self.get_type_string()) + + def __getitem__(self, key): + # dict + if self.get_type_string().startswith('a{'): + try: + val = self.lookup_value(key, variant_type_from_string('*')) + if val is None: + raise KeyError(key) + return val.unpack() + except TypeError: + # lookup_value() only works for string keys, which is certainly + # the common case; we have to do painful iteration for other + # key types + for i in range(self.n_children()): + v = self.get_child_value(i) + if v.get_child_value(0).unpack() == key: + return v.get_child_value(1).unpack() + raise KeyError(key) + + # array/tuple + if self.get_type_string().startswith('a') or self.get_type_string().startswith('('): + key = int(key) + if key < 0: + key = self.n_children() + key + if key < 0 or key >= self.n_children(): + raise IndexError('list index out of range') + return self.get_child_value(key).unpack() + + # string + if self.get_type_string() in ['s', 'o', 'g']: + return self.get_string().__getitem__(key) + + raise TypeError('GVariant type %s is not a container' % self.get_type_string()) + + def keys(self): + if not self.get_type_string().startswith('a{'): + return TypeError, 'GVariant type %s is not a dictionary' % self.get_type_string() + + res = [] + for i in range(self.n_children()): + v = self.get_child_value(i) + res.append(v.get_child_value(0).unpack()) + return res + @classmethod def new_tuple(cls, *elements): return variant_new_tuple(elements) diff --git a/gi/overrides/Gdk.py b/gi/overrides/Gdk.py index 08141d7..4ed71a6 100644 --- a/gi/overrides/Gdk.py +++ b/gi/overrides/Gdk.py @@ -22,7 +22,7 @@ from ..overrides import override from ..importer import modules -Gdk = modules['Gdk'].introspection_module +Gdk = modules['Gdk']._introspection_module __all__ = [] @@ -43,7 +43,7 @@ class Color(Gdk.Color): Color = override(Color) __all__.append('Color') -if Gdk.version == '2.0': +if Gdk._version == '2.0': class Rectangle(Gdk.Rectangle): def __init__(self, x, y, width, height): @@ -62,12 +62,29 @@ if Gdk.version == '2.0': Rectangle = override(Rectangle) __all__.append('Rectangle') -class Drawable(Gdk.Drawable): - def cairo_create(self): - return Gdk.cairo_create(self) - -Drawable = override(Drawable) -__all__.append('Drawable') +if Gdk._version == '2.0': + class Drawable(Gdk.Drawable): + def cairo_create(self): + return Gdk.cairo_create(self) + + Drawable = override(Drawable) + __all__.append('Drawable') +else: + class Window(Gdk.Window): + def __new__(cls, parent, attributes, attributes_mask): + # Gdk.Window had to be made abstract, + # this override allows using the standard constructor + return Gdk.Window.new(parent, attributes, attributes_mask) + def __init__(self, parent, attributes, attributes_mask): + pass + def cairo_create(self): + return Gdk.cairo_create(self) + + Window = override(Window) + __all__.append('Window') + +Gdk.EventType._2BUTTON_PRESS = getattr(Gdk.EventType, "2BUTTON_PRESS") +Gdk.EventType._3BUTTON_PRESS = getattr(Gdk.EventType, "3BUTTON_PRESS") class Event(Gdk.Event): _UNION_MEMBERS = { @@ -76,8 +93,8 @@ class Event(Gdk.Event): Gdk.EventType.EXPOSE: 'expose', Gdk.EventType.MOTION_NOTIFY: 'motion', Gdk.EventType.BUTTON_PRESS: 'button', - #Gdk.EventType.2BUTTON_PRESS: 'button', - #Gdk.EventType.3BUTTON_PRESS: 'button', + Gdk.EventType._2BUTTON_PRESS: 'button', + Gdk.EventType._3BUTTON_PRESS: 'button', Gdk.EventType.BUTTON_RELEASE: 'button', Gdk.EventType.KEY_PRESS: 'key', Gdk.EventType.KEY_RELEASE: 'key', @@ -101,9 +118,11 @@ class Event(Gdk.Event): Gdk.EventType.DROP_FINISHED: 'dnd', Gdk.EventType.CLIENT_EVENT: 'client', Gdk.EventType.VISIBILITY_NOTIFY: 'visibility', - Gdk.EventType.NO_EXPOSE: 'no_expose' } + if Gdk._version == '2.0': + _UNION_MEMBERS[Gdk.EventType.NO_EXPOSE] = 'no_expose' + def __new__(cls, *args, **kwargs): return Gdk.Event.__new__(cls) @@ -112,11 +131,18 @@ class Event(Gdk.Event): if real_event: return getattr(getattr(self, real_event), name) else: - return getattr(self, name) + raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name)) Event = override(Event) __all__.append('Event') +class DragContext(Gdk.DragContext): + def finish(self, success, del_, time): + Gtk = modules['Gtk']._introspection_module + Gtk.drag_finish(self, success, del_, time) + +DragContext = override(DragContext) +__all__.append('DragContext') import sys diff --git a/gi/overrides/Gio.py b/gi/overrides/Gio.py new file mode 100644 index 0000000..78affa2 --- /dev/null +++ b/gi/overrides/Gio.py @@ -0,0 +1,99 @@ +# -*- Mode: Python; py-indent-offset: 4 -*- +# vim: tabstop=4 shiftwidth=4 expandtab +# +# Copyright (C) 2010 Ignacio Casal Quinteiro <icq@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 St, Fifth Floor, Boston, MA 02110-1301 +# USA + +from ..overrides import override +from ..importer import modules + +from gi.repository import GLib + +Gio = modules['Gio']._introspection_module + +__all__ = [] + +class FileEnumerator(Gio.FileEnumerator): + def __iter__(self): + return self + + def __next__(self): + file_info = self.next_file(None) + + if file_info is not None: + return file_info + else: + raise StopIteration + + # python 2 compat for the iter protocol + next = __next__ + + +FileEnumerator = override(FileEnumerator) +__all__.append('FileEnumerator') + +class Settings(Gio.Settings): + '''Provide dictionary-like access to GLib.Settings.''' + + def __init__(self, schema, path=None, backend=None): + Gio.Settings.__init__(self, schema=schema, backend=backend, path=path) + + def __contains__(self, key): + return key in self.list_keys() + + def __len__(self): + return len(self.list_keys()) + + def __bool__(self): + # for "if mysettings" we don't want a dictionary-like test here, just + # if the object isn't None + return True + + # alias for Python 2.x object protocol + __nonzero__ = __bool__ + + def __getitem__(self, key): + # get_value() aborts the program on an unknown key + if not key in self: + raise KeyError('unknown key: %r' % (key,)) + + return self.get_value(key).unpack() + + def __setitem__(self, key, value): + # set_value() aborts the program on an unknown key + if not key in self: + raise KeyError('unknown key: %r' % (key,)) + + # determine type string of this key + range = self.get_range(key) + type_ = range.get_child_value(0).get_string() + v = range.get_child_value(1) + if type_ == 'type': + # v is boxed empty array, type of its elements is the allowed value type + type_str = v.get_child_value(0).get_type_string() + assert type_str.startswith('a') + type_str = type_str[1:] + else: + raise NotImplementedError('Cannot handle allowed type range class' + str(type_)) + + self.set_value(key, GLib.Variant(type_str, value)) + + def keys(self): + return self.list_keys() + +Settings = override(Settings) +__all__.append('Settings') diff --git a/gi/overrides/Gtk.py b/gi/overrides/Gtk.py index a2f38ac..6c22829 100644 --- a/gi/overrides/Gtk.py +++ b/gi/overrides/Gtk.py @@ -21,7 +21,6 @@ import sys import gobject -from gi.repository import Gdk from gi.repository import GObject from ..overrides import override from ..importer import modules @@ -33,7 +32,7 @@ else: _basestring = basestring _callable = callable -Gtk = modules['Gtk'].introspection_module +Gtk = modules['Gtk']._introspection_module __all__ = [] class Widget(Gtk.Widget): @@ -49,6 +48,21 @@ __all__.append('Widget') class Container(Gtk.Container, Widget): + def __len__(self): + return len(self.get_children()) + + def __contains__(self, child): + return child in self.get_children() + + def __iter__(self): + return iter(self.get_children()) + + def __bool__(self): + return True + + # alias for Python 2.x object protocol + __nonzero__ = __bool__ + def get_focus_chain(self): success, widgets = super(Container, self).get_focus_chain() if success: @@ -74,7 +88,24 @@ class Editable(Gtk.Editable): Editable = override(Editable) __all__.append("Editable") +class Action(Gtk.Action): + def __init__(self, name, label, tooltip, stock_id, **kwds): + Gtk.Action.__init__(self, name=name, label=label, tooltip=tooltip, stock_id=stock_id, **kwds) + +Action = override(Action) +__all__.append("Action") + +class RadioAction(Gtk.RadioAction): + def __init__(self, name, label, tooltip, stock_id, value, **kwds): + Gtk.RadioAction.__init__(self, name=name, label=label, tooltip=tooltip, stock_id=stock_id, value=value, **kwds) + +RadioAction = override(RadioAction) +__all__.append("RadioAction") + class ActionGroup(Gtk.ActionGroup): + def __init__(self, name, **kwds): + super(ActionGroup, self).__init__(name = name, **kwds) + def add_actions(self, entries, user_data=None): """ The add_actions() method is a convenience method that creates a number @@ -107,7 +138,7 @@ class ActionGroup(Gtk.ActionGroup): raise TypeError('entries must be iterable') def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None): - action = Gtk.Action(name=name, label=label, tooltip=tooltip, stock_id=stock_id) + action = Action(name, label, tooltip, stock_id) if callback is not None: action.connect('activate', callback, user_data) @@ -152,7 +183,7 @@ class ActionGroup(Gtk.ActionGroup): raise TypeError('entries must be iterable') def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None, is_active=False): - action = Gtk.ToggleAction(name=name, label=label, tooltip=tooltip, stock_id=stock_id) + action = Gtk.ToggleAction(name, label, tooltip, stock_id) action.set_active(is_active) if callback is not None: action.connect('activate', callback, user_data) @@ -200,7 +231,7 @@ class ActionGroup(Gtk.ActionGroup): first_action = None def _process_action(group_source, name, stock_id=None, label=None, accelerator=None, tooltip=None, entry_value=0): - action = Gtk.RadioAction(name=name, label=label, tooltip=tooltip, stock_id=stock_id, value=entry_value) + action = RadioAction(name, label, tooltip, stock_id, entry_value) # FIXME: join_group is a patch to Gtk+ 3.0 # otherwise we can't effectively add radio actions to a @@ -237,6 +268,9 @@ class UIManager(Gtk.UIManager): return Gtk.UIManager.add_ui_from_string(self, buffer, length) + def insert_action_group(self, buffer, length=-1): + return Gtk.UIManager.insert_action_group(self, buffer, length) + UIManager = override(UIManager) __all__.append('UIManager') @@ -278,8 +312,7 @@ class Builder(Gtk.Builder): else: gobj.connect(signal_name, handler) - self.connect_signals_full(_full_callback, - obj_or_map); + self.connect_signals_full(_full_callback, obj_or_map) def add_from_string(self, buffer): if not isinstance(buffer, _basestring): @@ -316,7 +349,7 @@ class Dialog(Gtk.Dialog, Container): # keyword to work around this if _buttons_property is not None: kwds['buttons'] = _buttons_property - + Gtk.Dialog.__init__(self, **kwds) if title: self.set_title(title) @@ -380,6 +413,14 @@ class MessageDialog(Gtk.MessageDialog, Dialog): **kwds) Dialog.__init__(self, parent=parent, flags=flags) + def format_secondary_text(self, message_format): + self.set_property('secondary-use-markup', False) + self.set_property('secondary-text', message_format) + + def format_secondary_markup(self, message_format): + self.set_property('secondary-use-markup', True) + self.set_property('secondary-text', message_format) + MessageDialog = override(MessageDialog) __all__.append('MessageDialog') @@ -409,9 +450,9 @@ class FileChooserDialog(Gtk.FileChooserDialog, Dialog): Gtk.FileChooserDialog.__init__(self, action=action, **kwds) - Dialog.__init__(self, - title=title, - parent=parent, + Dialog.__init__(self, + title=title, + parent=parent, buttons=buttons) FileChooserDialog = override(FileChooserDialog) @@ -516,6 +557,12 @@ class TextBuffer(Gtk.TextBuffer): self._get_or_create_tag_table().add(tag) return tag + def create_mark(self, mark_name, where, left_gravity=False): + return Gtk.TextBuffer.create_mark(self, mark_name, where, left_gravity) + + def set_text(self, text, length=-1): + Gtk.TextBuffer.set_text(self, text, length) + def insert(self, iter, text): if not isinstance(text , _basestring): raise TypeError('text must be a string, not %s' % type(text)) @@ -523,6 +570,32 @@ class TextBuffer(Gtk.TextBuffer): length = len(text) Gtk.TextBuffer.insert(self, iter, text, length) + def insert_with_tags(self, iter, text, *tags): + start_offset = iter.get_offset() + self.insert(iter, text) + + if not tags: + return + + start = self.get_iter_at_offset(start_offset) + + for tag in tags: + self.apply_tag(tag, start, iter) + + def insert_with_tags_by_name(self, iter, text, *tags): + if not tags: + return + + tag_objs = [] + + for tag in tags: + tag_obj = self.get_tag_table().lookup(tag) + if not tag_obj: + raise ValueError('unknown text tag: %s' % tag) + tag_objs.append(tag_obj) + + self.insert_with_tags(iter, text, *tag_objs) + def insert_at_cursor(self, text): if not isinstance(text , _basestring): raise TypeError('text must be a string, not %s' % type(text)) @@ -531,9 +604,11 @@ class TextBuffer(Gtk.TextBuffer): Gtk.TextBuffer.insert_at_cursor(self, text, length) def get_selection_bounds(self): - success, start, end = super(TextBuffer, self).get_selection_bounds(string, - flags, limit) - return (start, end) + success, start, end = super(TextBuffer, self).get_selection_bounds() + if success: + return (start, end) + else: + return () TextBuffer = override(TextBuffer) __all__.append('TextBuffer') @@ -550,6 +625,15 @@ class TextIter(Gtk.TextIter): flags, limit) return (match_start, match_end,) + def begins_tag(self, tag=None): + return super(TextIter, self).begins_tag(tag) + + def ends_tag(self, tag=None): + return super(TextIter, self).ends_tag(tag) + + def toggles_tag(self, tag=None): + return super(TextIter, self).toggles_tag(tag) + TextIter = override(TextIter) __all__.append('TextIter') @@ -586,30 +670,14 @@ class TreeModel(Gtk.TreeModel): return TreeModelRowIter(self, self.get_iter_first()) def get_iter(self, path): - if isinstance(path, Gtk.TreePath): - pass - elif isinstance(path, (int, str,)): - path = self._tree_path_from_string(str(path)) - elif isinstance(path, tuple): - path_str = ":".join(str(val) for val in path) - path = self._tree_path_from_string(path_str) - else: - raise TypeError("tree path must be one of Gtk.TreeIter, Gtk.TreePath, \ - int, str or tuple, not %s" % type(path).__name__) + if not isinstance(path, Gtk.TreePath): + path = TreePath(path) success, aiter = super(TreeModel, self).get_iter(path) if not success: raise ValueError("invalid tree path '%s'" % path) return aiter - def _tree_path_from_string(self, path): - if len(path) == 0: - raise TypeError("could not parse subscript '%s' as a tree path" % path) - try: - return TreePath.new_from_string(path) - except TypeError: - raise TypeError("could not parse subscript '%s' as a tree path" % path) - def get_iter_first(self): success, aiter = super(TreeModel, self).get_iter_first() if success: @@ -646,13 +714,64 @@ class TreeModel(Gtk.TreeModel): # TODO: Accept a dictionary for row # model.append(None,{COLUMN_ICON: icon, COLUMN_NAME: name}) - n_columns = self.get_n_columns(); + n_columns = self.get_n_columns() if len(row) != n_columns: raise ValueError('row sequence has the incorrect number of elements') for i in range(n_columns): - if row[i] is not None: - self.set_value(treeiter, i, row[i]) + value = row[i] + self.set_value(treeiter, i, value) + + def _convert_value(self, treeiter, column, value): + if value is None: + return + + # we may need to convert to a basic type + type_ = self.get_column_type(column) + if type_ == gobject.TYPE_PYOBJECT: + pass # short-circut branching + elif type_ == gobject.TYPE_STRING: + if isinstance(value, str): + value = str(value) + elif sys.version_info < (3, 0): + if isinstance(value, unicode): + value = value.encode('UTF-8') + else: + 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: + 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: + if isinstance(value, int): + value = int(value) + elif sys.version_info < (3, 0): + if isinstance(value, long): + value = long(value) + else: + 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))) + + return value + + def get(self, treeiter, *columns): + n_columns = self.get_n_columns() + + values = [] + for col in columns: + if not isinstance(col, int): + raise TypeError("column numbers must be ints") + + if col < 0 or col >= n_columns: + raise ValueError("column number is out of range") + + values.append(self.get_value(treeiter, col)) + + return tuple(values) TreeModel = override(TreeModel) __all__.append('TreeModel') @@ -666,6 +785,12 @@ class TreeSortable(Gtk.TreeSortable, ): else: return (None, None,) + def set_sort_func(self, sort_column_id, sort_func, user_data=None): + super(TreeSortable, self).set_sort_func(sort_column_id, sort_func, user_data) + + def set_default_sort_func(self, sort_func, user_data=None): + super(TreeSortable, self).set_default_sort_func(sort_func, user_data) + TreeSortable = override(TreeSortable) __all__.append('TreeSortable') @@ -698,7 +823,6 @@ class ListStore(Gtk.ListStore, TreeModel, TreeSortable): return treeiter - def insert_after(self, sibling, row=None): treeiter = Gtk.ListStore.insert_after(self, sibling) @@ -707,6 +831,10 @@ class ListStore(Gtk.ListStore, TreeModel, TreeSortable): return treeiter + def set_value(self, treeiter, column, value): + value = self._convert_value(treeiter, column, value) + Gtk.ListStore.set_value(self, treeiter, column, value) + ListStore = override(ListStore) __all__.append('ListStore') @@ -802,26 +930,39 @@ __all__.append('TreeModelRowIter') class TreePath(Gtk.TreePath): + def __new__(cls, path=0): + if isinstance(path, int): + path = str(path) + elif isinstance(path, tuple): + path = ":".join(str(val) for val in path) + + if len(path) == 0: + raise TypeError("could not parse subscript '%s' as a tree path" % path) + try: + return TreePath.new_from_string(path) + except TypeError: + raise TypeError("could not parse subscript '%s' as a tree path" % path) + def __str__(self): return self.to_string() def __lt__(self, other): - return self.compare(other) < 0 + return not other is None and self.compare(other) < 0 def __le__(self, other): - return self.compare(other) <= 0 + return not other is None and self.compare(other) <= 0 def __eq__(self, other): - return self.compare(other) == 0 + return not other is None and self.compare(other) == 0 def __ne__(self, other): - return self.compare(other) != 0 + return other is None or self.compare(other) != 0 def __gt__(self, other): - return self.compare(other) > 0 + return other is None or self.compare(other) > 0 def __ge__(self, other): - return self.compare(other) >= 0 + return other is None or self.compare(other) >= 0 TreePath = override(TreePath) __all__.append('TreePath') @@ -856,7 +997,6 @@ class TreeStore(Gtk.TreeStore, TreeModel, TreeSortable): return treeiter - def insert_after(self, parent, sibling, row=None): treeiter = Gtk.TreeStore.insert_after(self, parent, sibling) @@ -865,6 +1005,10 @@ class TreeStore(Gtk.TreeStore, TreeModel, TreeSortable): return treeiter + def set_value(self, treeiter, column, value): + value = self._convert_value(treeiter, column, value) + Gtk.TreeStore.set_value(self, treeiter, column, value) + TreeStore = override(TreeStore) __all__.append('TreeStore') @@ -881,6 +1025,32 @@ class TreeView(Gtk.TreeView, Container): if success: return (path, pos,) + def _construct_target_list(self, targets): + # FIXME: this should most likely be part of Widget or a global helper + # function + target_entries = [] + for t in targets: + entry = Gtk.TargetEntry.new(*t) + target_entries.append(entry) + return target_entries + + def enable_model_drag_source(self, start_button_mask, targets, actions): + target_entries = self._construct_target_list(targets) + super(TreeView, self).enable_model_drag_source(start_button_mask, + target_entries, + actions) + + def enable_model_drag_dest(self, targets, actions): + target_entries = self._construct_target_list(targets) + super(TreeView, self).enable_model_drag_dest(target_entries, + actions) + + def scroll_to_cell(self, path, column=None, use_align=False, row_align=0.0, col_align=0.0): + if not isinstance(path, Gtk.TreePath): + path = TreePath(path) + super(TreeView, self).scroll_to_cell(path, column, use_align, row_align, col_align) + + TreeView = override(TreeView) __all__.append('TreeView') @@ -900,11 +1070,17 @@ class TreeViewColumn(Gtk.TreeViewColumn): if success: return (start_pos, width,) + TreeViewColumn = override(TreeViewColumn) __all__.append('TreeViewColumn') class TreeSelection(Gtk.TreeSelection): + def select_path(self, path): + if not isinstance(path, Gtk.TreePath): + path = TreePath(path) + super(TreeSelection, self).select_path(path) + def get_selected(self): success, model, aiter = super(TreeSelection, self).get_selected() if success: @@ -912,11 +1088,17 @@ class TreeSelection(Gtk.TreeSelection): else: return (model, None) + # for compatibility with PyGtk + def get_selected_rows(self): + rows, model = super(TreeSelection, self).get_selected_rows() + return (model, rows) + + TreeSelection = override(TreeSelection) __all__.append('TreeSelection') class Button(Gtk.Button, Container): - def __init__(self, label=None, stock=None, use_underline=False): + def __init__(self, label=None, stock=None, use_underline=False, **kwds): if stock: label = stock use_stock = True @@ -924,11 +1106,72 @@ class Button(Gtk.Button, Container): else: use_stock = False Gtk.Button.__init__(self, label=label, use_stock=use_stock, - use_underline=use_underline) + use_underline=use_underline, **kwds) Button = override(Button) __all__.append('Button') -import sys +class LinkButton(Gtk.LinkButton): + def __init__(self, uri, label=None, **kwds): + Gtk.LinkButton.__init__(self, uri=uri, label=label, **kwds) + +LinkButton = override(LinkButton) +__all__.append('LinkButton') + +class Label(Gtk.Label): + def __init__(self, label=None, **kwds): + Gtk.Label.__init__(self, label=label, **kwds) + +Label = override(Label) +__all__.append('Label') + +class Adjustment(Gtk.Adjustment): + def __init__(self, *args, **kwds): + arg_names = ('value', 'lower', 'upper', + 'step_increment', 'page_increment', 'page_size') + new_args = dict(zip(arg_names, args)) + new_args.update(kwds) + Gtk.Adjustment.__init__(self, **new_args) + + # The value property is set between lower and (upper - page_size). + # Just in case lower, upper or page_size was still 0 when value + # was set, we set it again here. + if 'value' in new_args: + self.set_value(new_args['value']) + +Adjustment = override(Adjustment) +__all__.append('Adjustment') + +class Table(Gtk.Table, Container): + def __init__(self, rows=1, columns=1, homogeneous=False, **kwds): + 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): + Gtk.Table.attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions, yoptions, xpadding, ypadding) + +Table = override(Table) +__all__.append('Table') + +class ScrolledWindow(Gtk.ScrolledWindow): + def __init__(self, hadjustment=None, vadjustment=None, **kwds): + Gtk.ScrolledWindow.__init__(self, hadjustment=hadjustment, vadjustment=vadjustment, **kwds) + +ScrolledWindow = override(ScrolledWindow) +__all__.append('ScrolledWindow') + +class Paned(Gtk.Paned): + def pack1(self, child, resize=False, shrink=True): + super(Paned, self).pack1(child, resize, shrink) + + def pack2(self, child, resize=True, shrink=True): + super(Paned, self).pack2(child, resize, shrink) + +Paned = override(Paned) +__all__.append('Paned') + +_Gtk_main_quit = Gtk.main_quit +@override(Gtk.main_quit) +def main_quit(*args): + _Gtk_main_quit() initialized, argv = Gtk.init_check(sys.argv) sys.argv = list(argv) diff --git a/gi/overrides/Makefile.am b/gi/overrides/Makefile.am index b36c7a5..7a15b46 100644 --- a/gi/overrides/Makefile.am +++ b/gi/overrides/Makefile.am @@ -1,13 +1,17 @@ PLATFORM_VERSION = 2.0 -pkgpyexecdir = $(pyexecdir)/gtk-2.0/gi +pkgpyexecdir = $(pyexecdir)/gi pygioverridesdir = $(pkgpyexecdir)/overrides pygioverrides_PYTHON = \ GLib.py \ Gtk.py \ Gdk.py \ + Gio.py \ GIMarshallingTests.py \ + Pango.py \ keysyms.py \ __init__.py + +-include $(top_srcdir)/git.mk diff --git a/gi/overrides/Makefile.in b/gi/overrides/Makefile.in index d0a78be..7ba6f5e 100644 --- a/gi/overrides/Makefile.in +++ b/gi/overrides/Makefile.in @@ -141,6 +141,7 @@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ @@ -169,6 +170,7 @@ PYTHON = @PYTHON@ PYTHON_BASENAME = @PYTHON_BASENAME@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_INCLUDES = @PYTHON_INCLUDES@ +PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ @@ -184,6 +186,7 @@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ @@ -220,7 +223,7 @@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ -pkgpyexecdir = $(pyexecdir)/gtk-2.0/gi +pkgpyexecdir = $(pyexecdir)/gi pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ @@ -242,7 +245,9 @@ pygioverrides_PYTHON = \ GLib.py \ Gtk.py \ Gdk.py \ + Gio.py \ GIMarshallingTests.py \ + Pango.py \ keysyms.py \ __init__.py @@ -474,6 +479,8 @@ uninstall-am: uninstall-pygioverridesPYTHON uninstall-pygioverridesPYTHON +-include $(top_srcdir)/git.mk + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/gi/overrides/Pango.py b/gi/overrides/Pango.py new file mode 100644 index 0000000..3269806 --- /dev/null +++ b/gi/overrides/Pango.py @@ -0,0 +1,51 @@ +# -*- Mode: Python; py-indent-offset: 4 -*- +# vim: tabstop=4 shiftwidth=4 expandtab +# +# Copyright (C) 2010 Paolo Borelli <pborelli@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 St, Fifth Floor, Boston, MA 02110-1301 +# USA + +from ..overrides import override +from ..importer import modules + +Pango = modules['Pango']._introspection_module + +__all__ = [] + +class FontDescription(Pango.FontDescription): + + def __new__(cls, string=None): + if string is not None: + return Pango.font_description_from_string (string) + else: + return Pango.FontDescription.__new__(cls) + +FontDescription = override(FontDescription) +__all__.append('FontDescription') + +class Layout(Pango.Layout): + + def __new__(cls, context): + return Pango.Layout.new(context) + + def __init__(self, context, **kwds): + # simply discard 'context', since it was set by + # __new__ and it is not a PangoLayout property + super(Layout, self).__init__(**kwds) + +Layout = override(Layout) +__all__.append('Layout') + diff --git a/gi/overrides/__init__.py b/gi/overrides/__init__.py index d4cd80b..a98974f 100644 --- a/gi/overrides/__init__.py +++ b/gi/overrides/__init__.py @@ -1,3 +1,6 @@ +import sys +import types + import gobject registry = None @@ -34,10 +37,30 @@ class _Registry(dict): def register(self, override_class): self[override_class] = override_class + +class overridefunc(object): + '''decorator for overriding a function''' + def __init__(self, func): + if not hasattr(func, '__info__'): + raise TypeError("func must be an gi function") + from ..importer import modules + self.module = modules[func.__module__]._introspection_module + + def __call__(self, func): + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + wrapper.__name__ = func.__name__ + setattr(self.module, func.__name__, wrapper) + return wrapper + registry = _Registry() def override(type_): '''Decorator for registering an override''' - registry.register(type_) - return type_ + if type(type_) == types.FunctionType: + return overridefunc(type_) + else: + registry.register(type_) + return type_ + diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c index b768e9e..8dd728d 100644 --- a/gi/pygi-argument.c +++ b/gi/pygi-argument.c @@ -188,6 +188,7 @@ _pygi_g_type_interface_check_object (GIBaseInfo *info, for (i = 0; i < g_enum_info_get_n_values (info); i++) { GIValueInfo *value_info = g_enum_info_get_value (info, i); glong enum_value = g_value_info_get_value (value_info); + g_base_info_unref (value_info); if (value == enum_value) { retval = 1; break; @@ -271,8 +272,18 @@ _pygi_g_type_info_check_object (GITypeInfo *type_info, case GI_TYPE_TAG_BOOLEAN: /* No check; every Python object has a truth value. */ break; - case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_UINT8: + /* UINT8 types can be characters */ + if (PYGLIB_PyBytes_Check(object)) { + if (PYGLIB_PyBytes_Size(object) != 1) { + PyErr_Format (PyExc_TypeError, "Must be a single character"); + retval = 0; + break; + } + + break; + } + case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_INT32: @@ -368,9 +379,36 @@ check_number_release: } break; } + case GI_TYPE_TAG_UNICHAR: + { + Py_ssize_t size; + if (PyUnicode_Check (object)) { + size = PyUnicode_GET_SIZE (object); +#if PY_VERSION_HEX < 0x03000000 + } else if (PyString_Check (object)) { + PyObject *pyuni = PyUnicode_FromEncodedObject (object, "UTF-8", "strict"); + size = PyUnicode_GET_SIZE (pyuni); + Py_DECREF(pyuni); +#endif + } else { + PyErr_Format (PyExc_TypeError, "Must be string, not %s", + object->ob_type->tp_name); + retval = 0; + break; + } + + if (size != 1) { + PyErr_Format (PyExc_TypeError, "Must be a one character string, not %ld characters", + size); + retval = 0; + break; + } + + break; + } case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: - if (!PYGLIB_PyUnicode_Check (object)) { + if (!PYGLIB_PyBaseString_Check (object) ) { PyErr_Format (PyExc_TypeError, "Must be string, not %s", object->ob_type->tp_name); retval = 0; @@ -407,6 +445,10 @@ check_number_release: item_type_info = g_type_info_get_param_type (type_info, 0); g_assert (item_type_info != NULL); + /* FIXME: This is insain. We really should only check the first + * object and perhaps have a debugging mode. Large arrays + * will cause apps to slow to a crawl. + */ for (i = 0; i < length; i++) { PyObject *item; @@ -640,8 +682,13 @@ _pygi_argument_from_object (PyObject *object, arg.v_boolean = PyObject_IsTrue (object); break; } - case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_UINT8: + if (PYGLIB_PyBytes_Check(object)) { + arg.v_long = (long)(PYGLIB_PyBytes_AsString(object)[0]); + break; + } + + case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_INT32: @@ -675,8 +722,7 @@ _pygi_argument_from_object (PyObject *object, value = PyInt_AS_LONG (number); } else #endif - if (PyLong_Check (number)) - value = PyLong_AsUnsignedLongLong (number); + value = PyLong_AsUnsignedLongLong (number); arg.v_uint64 = value; @@ -697,10 +743,9 @@ _pygi_argument_from_object (PyObject *object, #if PY_VERSION_HEX < 0x03000000 if (PyInt_Check (number)) { value = PyInt_AS_LONG (number); - } else -#endif - if (PyLong_Check (number)) - value = PyLong_AsLongLong (number); + } else +#endif + value = PyLong_AsLongLong (number); arg.v_int64 = value; @@ -742,6 +787,42 @@ _pygi_argument_from_object (PyObject *object, break; } + case GI_TYPE_TAG_UNICHAR: + { + gchar *string; + + if (object == Py_None) { + arg.v_uint32 = 0; + break; + } + +#if PY_VERSION_HEX < 0x03000000 + if (PyUnicode_Check(object)) { + PyObject *pystr_obj = PyUnicode_AsUTF8String (object); + + if (!pystr_obj) + break; + + string = g_strdup(PyString_AsString (pystr_obj)); + Py_DECREF(pystr_obj); + } else { + string = g_strdup(PyString_AsString (object)); + } +#else + { + PyObject *pybytes_obj = PyUnicode_AsUTF8String (object); + if (!pybytes_obj) + break; + + string = g_strdup(PyBytes_AsString (pybytes_obj)); + Py_DECREF (pybytes_obj); + } +#endif + + arg.v_uint32 = g_utf8_get_char (string); + + break; + } case GI_TYPE_TAG_UTF8: { gchar *string; @@ -751,7 +832,17 @@ _pygi_argument_from_object (PyObject *object, break; } #if PY_VERSION_HEX < 0x03000000 - string = g_strdup(PyString_AsString (object)); + if (PyUnicode_Check(object)) { + PyObject *pystr_obj = PyUnicode_AsUTF8String (object); + + if (!pystr_obj) + break; + + string = g_strdup(PyString_AsString (pystr_obj)); + Py_DECREF(pystr_obj); + } else { + string = g_strdup(PyString_AsString (object)); + } #else { PyObject *pybytes_obj = PyUnicode_AsUTF8String (object); @@ -830,6 +921,15 @@ _pygi_argument_from_object (PyObject *object, break; } + if (g_type_info_get_tag (item_type_info) == GI_TYPE_TAG_UINT8 && + PYGLIB_PyBytes_Check(object)) { + + memcpy(array->data, PYGLIB_PyBytes_AsString(object), length); + array->len = length; + goto array_success; + } + + item_transfer = transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer; for (i = 0; i < length; i++) { @@ -862,6 +962,7 @@ array_item_error: break; } +array_success: arg.v_pointer = array; g_base_info_unref ( (GIBaseInfo *) item_type_info); @@ -1241,6 +1342,27 @@ _pygi_argument_to_object (GIArgument *arg, object = pyg_type_wrapper_new ( (GType) arg->v_long); break; } + case GI_TYPE_TAG_UNICHAR: + { + /* Preserve the bidirectional mapping between 0 and "" */ + if (arg->v_uint32 == 0) { + object = PYGLIB_PyUnicode_FromString (""); + } else if (g_unichar_validate (arg->v_uint32)) { + gchar utf8[6]; + gint bytes; + + bytes = g_unichar_to_utf8 (arg->v_uint32, utf8); + object = PYGLIB_PyUnicode_FromStringAndSize ((char*)utf8, bytes); + } else { + /* TODO: Convert the error to an exception. */ + PyErr_Format (PyExc_TypeError, + "Invalid unicode codepoint %" G_GUINT32_FORMAT, + arg->v_uint32); + object = Py_None; + Py_INCREF (object); + } + break; + } case GI_TYPE_TAG_UTF8: if (arg->v_string == NULL) { object = Py_None; @@ -1282,23 +1404,36 @@ _pygi_argument_to_object (GIArgument *arg, GITransfer item_transfer; gsize i, item_size; - if (arg->v_pointer == NULL) { - object = PyList_New (0); - break; - } - array = arg->v_pointer; - object = PyList_New (array->len); - if (object == NULL) { - break; - } - item_type_info = g_type_info_get_param_type (type_info, 0); g_assert (item_type_info != NULL); item_type_tag = g_type_info_get_tag (item_type_info); item_transfer = transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer; + + if (item_type_tag == GI_TYPE_TAG_UINT8) { + /* Return as a byte array */ + if (arg->v_pointer == NULL) { + object = PYGLIB_PyBytes_FromString (""); + break; + } + + object = PYGLIB_PyBytes_FromStringAndSize(array->data, array->len); + break; + + } else { + if (arg->v_pointer == NULL) { + object = PyList_New (0); + break; + } + + object = PyList_New (array->len); + if (object == NULL) { + break; + } + + } item_size = g_array_get_element_size (array); for (i = 0; i < array->len; i++) { @@ -1375,6 +1510,8 @@ _pygi_argument_to_object (GIArgument *arg, type = g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *) info); if (g_type_is_a (type, G_TYPE_VALUE)) { object = pyg_value_as_pyobject (arg->v_pointer, FALSE); + } else if (g_struct_info_is_foreign (info)) { + object = pygi_struct_foreign_convert_from_g_argument (type_info, arg->v_pointer); } else if (g_type_is_a (type, G_TYPE_BOXED)) { PyObject *py_type; @@ -1398,8 +1535,6 @@ _pygi_argument_to_object (GIArgument *arg, } Py_XDECREF (py_type); - } else if ( (type == G_TYPE_NONE) && (g_struct_info_is_foreign (info))) { - object = pygi_struct_foreign_convert_from_g_argument (type_info, arg->v_pointer); } else if (type == G_TYPE_NONE) { PyObject *py_type; @@ -1611,6 +1746,7 @@ _pygi_argument_release (GIArgument *arg, case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: case GI_TYPE_TAG_GTYPE: + case GI_TYPE_TAG_UNICHAR: break; case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_UTF8: diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c index 1e2ce05..33a8d81 100644 --- a/gi/pygi-closure.c +++ b/gi/pygi-closure.c @@ -27,6 +27,53 @@ */ static GSList* async_free_list; +static void +_pygi_closure_assign_pyobj_to_out_argument (gpointer out_arg, PyObject *object, + GITypeInfo *type_info, + GITransfer transfer) +{ + GIArgument arg = _pygi_argument_from_object (object, type_info, transfer); + GITypeTag type_tag = g_type_info_get_tag (type_info); + + switch (type_tag) { + case GI_TYPE_TAG_BOOLEAN: + *((gboolean *) out_arg) = arg.v_boolean; + break; + case GI_TYPE_TAG_INT8: + *((gint8 *) out_arg) = arg.v_int8; + break; + case GI_TYPE_TAG_UINT8: + *((guint8 *) out_arg) = arg.v_uint8; + break; + case GI_TYPE_TAG_INT16: + *((gint16 *) out_arg) = arg.v_int16; + break; + case GI_TYPE_TAG_UINT16: + *((guint16 *) out_arg) = arg.v_uint16; + break; + case GI_TYPE_TAG_INT32: + *((gint32 *) out_arg) = arg.v_int32; + break; + case GI_TYPE_TAG_UINT32: + *((guint32 *) out_arg) = arg.v_uint32; + break; + case GI_TYPE_TAG_INT64: + *((gint64 *) out_arg) = arg.v_int64; + break; + case GI_TYPE_TAG_UINT64: + *((glong *) out_arg) = arg.v_uint64; + break; + case GI_TYPE_TAG_FLOAT: + *((gfloat *) out_arg) = arg.v_float; + break; + case GI_TYPE_TAG_DOUBLE: + *((gdouble *) out_arg) = arg.v_double; + break; + default: + *((GIArgument *) out_arg) = arg; + break; + } +} static GIArgument * _pygi_closure_convert_ffi_arguments (GICallableInfo *callable_info, void **args) @@ -198,6 +245,7 @@ _pygi_closure_convert_arguments (GICallableInfo *callable_info, void **args, if (_PyTuple_Resize (py_args, n_in_args) == -1) goto error; + g_free (g_args); return TRUE; error: @@ -225,15 +273,14 @@ _pygi_closure_set_out_arguments (GICallableInfo *callable_info, return_type_info = g_callable_info_get_return_type (callable_info); return_type_tag = g_type_info_get_tag (return_type_info); if (return_type_tag != GI_TYPE_TAG_VOID) { - GIArgument arg; GITransfer transfer = g_callable_info_get_caller_owns (callable_info); if (PyTuple_Check (py_retval)) { PyObject *item = PyTuple_GET_ITEM (py_retval, 0); - arg = _pygi_argument_from_object (item, return_type_info, transfer); - * ( (GIArgument*) resp) = arg; + _pygi_closure_assign_pyobj_to_out_argument (resp, item, + return_type_info, transfer); } else { - arg = _pygi_argument_from_object (py_retval, return_type_info, transfer); - * ( (GIArgument*) resp) = arg; + _pygi_closure_assign_pyobj_to_out_argument (resp, py_retval, + return_type_info, transfer); } i_py_retval++; } @@ -248,14 +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); - GIArgument arg; if (PyTuple_Check (py_retval)) { PyObject *item = PyTuple_GET_ITEM (py_retval, i_py_retval); - arg = _pygi_argument_from_object (item, type_info, transfer); - * ( (GIArgument*) out_args[i_out_args].v_pointer) = arg; + _pygi_closure_assign_pyobj_to_out_argument ( + out_args[i_out_args].v_pointer, item, type_info, transfer); } else if (i_py_retval == 0) { - arg = _pygi_argument_from_object (py_retval, type_info, transfer); - * ( (GIArgument*) out_args[i_out_args].v_pointer) = arg; + _pygi_closure_assign_pyobj_to_out_argument ( + out_args[i_out_args].v_pointer, py_retval, type_info, + transfer); } else g_assert_not_reached(); @@ -309,6 +356,8 @@ _pygi_closure_handle (ffi_cif *cif, _pygi_closure_set_out_arguments (closure->info, retval, out_args, result); end: + if (out_args != NULL) + g_free (out_args); g_base_info_unref ( (GIBaseInfo*) return_type); PyGILState_Release (state); diff --git a/gi/pygi-foreign-cairo.c b/gi/pygi-foreign-cairo.c index 6326397..0264b71 100644 --- a/gi/pygi-foreign-cairo.c +++ b/gi/pygi-foreign-cairo.c @@ -56,9 +56,9 @@ cairo_context_to_arg (PyObject *value, } PyObject * -cairo_context_from_arg (GITypeInfo *type_info, GIArgument *arg) +cairo_context_from_arg (GITypeInfo *type_info, gpointer data) { - cairo_t *context = (cairo_t*) arg; + cairo_t *context = (cairo_t*) data; cairo_reference (context); @@ -95,9 +95,9 @@ cairo_surface_to_arg (PyObject *value, } PyObject * -cairo_surface_from_arg (GITypeInfo *type_info, GIArgument *arg) +cairo_surface_from_arg (GITypeInfo *type_info, gpointer data) { - cairo_surface_t *surface = (cairo_surface_t*) arg; + cairo_surface_t *surface = (cairo_surface_t*) data; cairo_surface_reference (surface); @@ -112,65 +112,12 @@ cairo_surface_release (GIBaseInfo *base_info, Py_RETURN_NONE; } -#ifdef PycairoRectangleInt_FromRectangleInt -PyObject * -cairo_rectangle_int_to_arg (PyObject *value, - GITypeInfo *type_info, - GITransfer transfer, - GIArgument *arg) -{ - cairo_rectangle_int_t *rect; - - rect = ( (PycairoRectangleInt *) value)->rectangle_int; - if (!rect) { - PyErr_SetString (PyExc_ValueError, "RectangleInt instance wrapping a NULL pointer"); - return NULL; - } - - if (transfer == GI_TRANSFER_EVERYTHING) { - unsigned int size = sizeof(cairo_rectangle_int_t); - cairo_rectangle_int_t *transfer = malloc(size); - if (!transfer) { - PyErr_NoMemory(); - return NULL; - } - - memcpy(transfer, rect, size); - rect = transfer; - } - - arg->v_pointer = rect; - Py_RETURN_NONE; -} - -PyObject * -cairo_rectangle_int_from_arg (GITypeInfo *type_info, GIArgument *arg) -{ - cairo_rectangle_int_t *rect = (cairo_rectangle_int_t*) arg; - - if (rect) - return PycairoRectangleInt_FromRectangleInt (rect); - else { - cairo_rectangle_int_t temp = {}; - return PycairoRectangleInt_FromRectangleInt (&temp); - } -} - -PyObject * -cairo_rectangle_int_release (GIBaseInfo *base_info, - gpointer struct_) -{ - g_free (struct_); - Py_RETURN_NONE; -} -#endif - static PyMethodDef _gi_cairo_functions[] = {}; PYGLIB_MODULE_START(_gi_cairo, "_gi_cairo") { Pycairo_IMPORT; if (Pycairo_CAPI == NULL) - return 0; + return PYGLIB_MODULE_ERROR_RETURN; pygi_register_foreign_struct ("cairo", "Context", @@ -183,14 +130,5 @@ PYGLIB_MODULE_START(_gi_cairo, "_gi_cairo") cairo_surface_to_arg, cairo_surface_from_arg, cairo_surface_release); - -#ifdef PycairoRectangleInt_FromRectangleInt - pygi_register_foreign_struct ("cairo", - "RectangleInt", - cairo_rectangle_int_to_arg, - cairo_rectangle_int_from_arg, - cairo_rectangle_int_release); -#endif - } PYGLIB_MODULE_END; diff --git a/gi/pygi-foreign-gvariant.c b/gi/pygi-foreign-gvariant.c index ac16395..3c8ae8d 100644 --- a/gi/pygi-foreign-gvariant.c +++ b/gi/pygi-foreign-gvariant.c @@ -42,9 +42,9 @@ g_variant_to_arg (PyObject *value, PyObject * g_variant_from_arg (GITypeInfo *type_info, - GIArgument *arg) + gpointer data) { - GVariant *variant = (GVariant *) arg; + GVariant *variant = (GVariant *) data; GITypeInfo *interface_info = g_type_info_get_interface (type_info); PyObject *type = _pygi_type_import_by_gi_info (interface_info); diff --git a/gi/pygi-foreign-gvariant.h b/gi/pygi-foreign-gvariant.h index 6de8c57..b0c9781 100644 --- a/gi/pygi-foreign-gvariant.h +++ b/gi/pygi-foreign-gvariant.h @@ -32,7 +32,7 @@ PyObject *g_variant_to_arg(PyObject *value, GIArgument *arg); PyObject *g_variant_from_arg(GITypeInfo *type_info, - GIArgument *arg); + gpointer data); PyObject *g_variant_release_foreign (GIBaseInfo *base_info, gpointer struct_); diff --git a/gi/pygi-foreign.c b/gi/pygi-foreign.c index 75d5bb9..5c9a88a 100644 --- a/gi/pygi-foreign.c +++ b/gi/pygi-foreign.c @@ -165,7 +165,7 @@ pygi_register_foreign_struct_real (const char* namespace_, PyGIArgOverrideFromGIArgumentFunc from_func, PyGIArgOverrideReleaseFunc release_func) { - PyGIForeignStruct *new_struct = g_slice_new0 (PyGIForeignStruct); + PyGIForeignStruct *new_struct = g_slice_new (PyGIForeignStruct); new_struct->namespace = namespace_; new_struct->name = name; new_struct->to_func = to_func; diff --git a/gi/pygi-info.c b/gi/pygi-info.c index 33f71c1..f5dd69f 100644 --- a/gi/pygi-info.c +++ b/gi/pygi-info.c @@ -234,7 +234,45 @@ out: /* CallableInfo */ PYGLIB_DEFINE_TYPE ("gi.CallableInfo", PyGICallableInfo_Type, PyGIBaseInfo); +static PyObject * +_wrap_g_callable_info_get_arguments (PyGIBaseInfo *self) +{ + gssize n_infos; + PyObject *infos; + gssize i; + + n_infos = g_callable_info_get_n_args ( (GICallableInfo *) self->info); + + infos = PyTuple_New (n_infos); + if (infos == NULL) { + return NULL; + } + + for (i = 0; i < n_infos; i++) { + GIBaseInfo *info; + PyObject *py_info; + + info = (GIBaseInfo *) g_callable_info_get_arg ( (GICallableInfo *) self->info, i); + g_assert (info != NULL); + + py_info = _pygi_info_new (info); + + g_base_info_unref (info); + + if (py_info == NULL) { + Py_CLEAR (infos); + break; + } + + PyTuple_SET_ITEM (infos, i, py_info); + } + + return infos; +} + static PyMethodDef _PyGICallableInfo_methods[] = { + { "invoke", (PyCFunction) _wrap_g_callable_info_invoke, METH_VARARGS | METH_KEYWORDS }, + { "get_arguments", (PyCFunction) _wrap_g_callable_info_get_arguments, METH_NOARGS }, { NULL, NULL, 0 } }; @@ -349,6 +387,9 @@ _pygi_g_type_tag_size (GITypeTag type_tag) case GI_TYPE_TAG_GTYPE: size = sizeof (GType); break; + case GI_TYPE_TAG_UNICHAR: + size = sizeof (gunichar); + break; case GI_TYPE_TAG_VOID: case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: @@ -388,6 +429,7 @@ _pygi_g_type_info_size (GITypeInfo *type_info) case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: case GI_TYPE_TAG_GTYPE: + case GI_TYPE_TAG_UNICHAR: if (g_type_info_is_pointer (type_info)) { size = sizeof (gpointer); } else { @@ -472,7 +514,6 @@ _pygi_g_type_info_size (GITypeInfo *type_info) static PyMethodDef _PyGIFunctionInfo_methods[] = { { "is_constructor", (PyCFunction) _wrap_g_function_info_is_constructor, METH_NOARGS }, { "is_method", (PyCFunction) _wrap_g_function_info_is_method, METH_NOARGS }, - { "invoke", (PyCFunction) _wrap_g_function_info_invoke, METH_VARARGS }, { NULL, NULL, 0 } }; @@ -767,6 +808,7 @@ pygi_g_struct_info_is_simple (GIStructInfo *struct_info) case GI_TYPE_TAG_UINT64: case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: + case GI_TYPE_TAG_UNICHAR: if (g_type_info_is_pointer (field_type_info)) { is_simple = FALSE; } @@ -1539,7 +1581,7 @@ _pygi_info_register_types (PyObject *m) _PyGI_REGISTER_TYPE (m, PyGIFieldInfo_Type, FieldInfo, PyGIBaseInfo_Type); _PyGI_REGISTER_TYPE (m, PyGIVFuncInfo_Type, VFuncInfo, - PyGIBaseInfo_Type); + PyGICallableInfo_Type); _PyGI_REGISTER_TYPE (m, PyGIUnionInfo_Type, UnionInfo, PyGIRegisteredTypeInfo_Type); _PyGI_REGISTER_TYPE (m, PyGIBoxedInfo_Type, BoxedInfo, diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c index 71d5859..c93442a 100644 --- a/gi/pygi-invoke.c +++ b/gi/pygi-invoke.c @@ -21,6 +21,7 @@ * USA */ +#include <pyglib.h> #include "pygi-invoke.h" struct invocation_state @@ -58,19 +59,40 @@ struct invocation_state GIArgument *backup_args; GIArgument return_arg; - PyObject *return_value; + PyObject *return_value; + + GType implementor_gtype; }; -static void +static gboolean _initialize_invocation_state (struct invocation_state *state, GIFunctionInfo *info, - PyObject *py_args) + PyObject *py_args, + PyObject *kwargs) { - GIFunctionInfoFlags flags; + if (g_base_info_get_type (info) == GI_INFO_TYPE_FUNCTION) { + GIFunctionInfoFlags flags = g_function_info_get_flags (info); - flags = g_function_info_get_flags (info); - state->is_method = (flags & GI_FUNCTION_IS_METHOD) != 0; - state->is_constructor = (flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0; + state->is_method = (flags & GI_FUNCTION_IS_METHOD) != 0; + state->is_constructor = (flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0; + state->implementor_gtype = 0; + } else { + PyObject *obj; + + state->is_method = TRUE; + state->is_constructor = FALSE; + + obj = PyDict_GetItemString (kwargs, "gtype"); + if (obj == NULL) { + PyErr_SetString (PyExc_TypeError, + "need the GType of the implementor class"); + return FALSE; + } + + state->implementor_gtype = pyg_type_from_object (obj); + if (state->implementor_gtype == 0) + return FALSE; + } /* Count arguments. */ state->n_args = g_callable_info_get_n_args ( (GICallableInfo *) info); @@ -98,6 +120,8 @@ _initialize_invocation_state (struct invocation_state *state, state->out_args = NULL; state->out_values = NULL; state->backup_args = NULL; + + return TRUE; } static gboolean @@ -552,7 +576,7 @@ _prepare_invocation_state (struct invocation_state *state, static gboolean _invoke_function (struct invocation_state *state, - GIFunctionInfo *function_info, PyObject *py_args) + GICallableInfo *callable_info, PyObject *py_args) { GError *error; gint retval; @@ -560,20 +584,28 @@ _invoke_function (struct invocation_state *state, error = NULL; pyg_begin_allow_threads; - retval = g_function_info_invoke ( (GIFunctionInfo *) function_info, - state->in_args, - state->n_in_args, - state->out_args, - state->n_out_args, - &state->return_arg, - &error); + if (g_base_info_get_type (callable_info) == GI_INFO_TYPE_FUNCTION) { + retval = g_function_info_invoke ( (GIFunctionInfo *) callable_info, + state->in_args, + state->n_in_args, + state->out_args, + state->n_out_args, + &state->return_arg, + &error); + } else { + retval = g_vfunc_info_invoke ( (GIVFuncInfo *) callable_info, + state->implementor_gtype, + state->in_args, + state->n_in_args, + state->out_args, + state->n_out_args, + &state->return_arg, + &error); + } pyg_end_allow_threads; if (!retval) { - g_assert (error != NULL); - /* TODO: raise the right error, out of the error domain. */ - PyErr_SetString (PyExc_RuntimeError, error->message); - g_error_free (error); + pyglib_error_check(&error); /* TODO: release input arguments. */ @@ -585,11 +617,7 @@ _invoke_function (struct invocation_state *state, error = state->args[state->error_arg_pos]->v_pointer; - if (*error != NULL) { - /* TODO: raise the right error, out of the error domain, if applicable. */ - PyErr_SetString (PyExc_Exception, (*error)->message); - g_error_free (*error); - + if (pyglib_error_check(error)) { /* TODO: release input arguments. */ return FALSE; @@ -672,6 +700,10 @@ _process_invocation_state (struct invocation_state *state, /* The new wrapper increased the reference count, so decrease it. */ g_object_unref (state->return_arg.v_pointer); } + if (state->is_constructor && G_IS_INITIALLY_UNOWNED (state->return_arg.v_pointer)) { + /* GInitiallyUnowned constructors always end up with one extra reference, so decrease it. */ + g_object_unref (state->return_arg.v_pointer); + } break; default: /* Other types don't have neither methods nor constructors. */ @@ -772,13 +804,16 @@ _process_invocation_state (struct invocation_state *state, if (type_tag == GI_TYPE_TAG_INTERFACE) { GIBaseInfo *info; GIInfoType info_type; + GType type; info = g_type_info_get_interface (state->arg_type_infos[i]); g_assert (info != NULL); info_type = g_base_info_get_type (info); + type = g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *) info); if ( (info_type == GI_INFO_TYPE_STRUCT) && - !g_struct_info_is_foreign((GIStructInfo *) info) ) { + !g_struct_info_is_foreign((GIStructInfo *) info) && + !g_type_is_a (type, G_TYPE_BOXED)) { if (g_arg_info_is_caller_allocates (state->arg_infos[i])) { transfer = GI_TRANSFER_EVERYTHING; } else if (transfer == GI_TRANSFER_EVERYTHING) { @@ -846,9 +881,7 @@ _free_invocation_state (struct invocation_state *state) continue; } - if (state->args != NULL - && state->args[i] != NULL - && state->arg_infos[i] != NULL + if (state->arg_infos[i] != NULL && state->arg_type_infos[i] != NULL) { GIDirection direction; GITypeTag type_tag; @@ -857,20 +890,25 @@ _free_invocation_state (struct invocation_state *state) direction = g_arg_info_get_direction (state->arg_infos[i]); transfer = g_arg_info_get_ownership_transfer (state->arg_infos[i]); - type_tag = g_type_info_get_tag (state->arg_type_infos[i]); - /* Release the argument. */ if (direction == GI_DIRECTION_INOUT) { - _pygi_argument_release (&state->backup_args[backup_args_pos], state->arg_type_infos[i], - transfer, GI_DIRECTION_IN); + if (state->args != NULL) { + _pygi_argument_release (&state->backup_args[backup_args_pos], + state->arg_type_infos[i], + transfer, GI_DIRECTION_IN); + } backup_args_pos += 1; } - _pygi_argument_release (state->args[i], state->arg_type_infos[i], transfer, direction); + if (state->args != NULL && state->args[i] != NULL) { + _pygi_argument_release (state->args[i], state->arg_type_infos[i], + transfer, direction); - if (type_tag == GI_TYPE_TAG_ARRAY + type_tag = g_type_info_get_tag (state->arg_type_infos[i]); + 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. */ - state->args[i]->v_pointer = g_array_free (state->args[i]->v_pointer, FALSE); + /* We created a #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); + } } } @@ -913,11 +951,15 @@ _free_invocation_state (struct invocation_state *state) PyObject * -_wrap_g_function_info_invoke (PyGIBaseInfo *self, PyObject *py_args) +_wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args, + PyObject *kwargs) { struct invocation_state state = { 0, }; - _initialize_invocation_state (&state, self->info, py_args); + if (!_initialize_invocation_state (&state, self->info, py_args, kwargs)) { + _free_invocation_state (&state); + return NULL; + } if (!_prepare_invocation_state (&state, self->info, py_args)) { _free_invocation_state (&state); diff --git a/gi/pygi-invoke.h b/gi/pygi-invoke.h index 0d07b21..dc1ce18 100644 --- a/gi/pygi-invoke.h +++ b/gi/pygi-invoke.h @@ -30,7 +30,8 @@ G_BEGIN_DECLS -PyObject *_wrap_g_function_info_invoke (PyGIBaseInfo *self, PyObject *py_args); +PyObject *_wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args, + PyObject *kwargs); G_END_DECLS diff --git a/gi/pygi-property.c b/gi/pygi-property.c index 7b6a508..2f8970d 100644 --- a/gi/pygi-property.c +++ b/gi/pygi-property.c @@ -139,7 +139,7 @@ pygi_get_property_value_real (PyGObject *instance, arg.v_double = g_value_get_double (&value); break; case GI_TYPE_TAG_GTYPE: - arg.v_size = g_value_get_uint (&value); + arg.v_size = g_value_get_gtype (&value); break; case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: @@ -155,6 +155,8 @@ pygi_get_property_value_real (PyGObject *instance, type = g_registered_type_info_get_g_type (info); info_type = g_base_info_get_type (info); + g_base_info_unref (info); + switch (info_type) { case GI_INFO_TYPE_ENUM: arg.v_int32 = g_value_get_enum (&value); @@ -261,6 +263,8 @@ pygi_set_property_value_real (PyGObject *instance, type = g_registered_type_info_get_g_type (info); info_type = g_base_info_get_type (info); + g_base_info_unref (info); + switch (info_type) { case GI_INFO_TYPE_ENUM: g_value_set_enum (&value, arg.v_int32); @@ -310,7 +314,7 @@ pygi_set_property_value_real (PyGObject *instance, g_value_set_double (&value, arg.v_double); break; case GI_TYPE_TAG_GTYPE: - g_value_set_uint (&value, arg.v_size); + g_value_set_gtype (&value, arg.v_size); break; case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: @@ -60,7 +60,7 @@ typedef PyObject * (*PyGIArgOverrideToGIArgumentFunc) (PyObject *value, GITransfer transfer, GIArgument *arg); typedef PyObject * (*PyGIArgOverrideFromGIArgumentFunc) (GITypeInfo *type_info, - GIArgument *arg); + gpointer data); typedef PyObject * (*PyGIArgOverrideReleaseFunc) (GITypeInfo *type_info, gpointer struct_); diff --git a/gi/repository/Makefile.am b/gi/repository/Makefile.am index c9138ce..ece9c4f 100644 --- a/gi/repository/Makefile.am +++ b/gi/repository/Makefile.am @@ -1,8 +1,10 @@ PLATFORM_VERSION = 2.0 -pkgpyexecdir = $(pyexecdir)/gtk-2.0/gi +pkgpyexecdir = $(pyexecdir)/gi pygirepositorydir = $(pkgpyexecdir)/repository pygirepository_PYTHON = \ __init__.py + +-include $(top_srcdir)/git.mk diff --git a/gi/repository/Makefile.in b/gi/repository/Makefile.in index 7f0ef44..cb6de2a 100644 --- a/gi/repository/Makefile.in +++ b/gi/repository/Makefile.in @@ -141,6 +141,7 @@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ @@ -169,6 +170,7 @@ PYTHON = @PYTHON@ PYTHON_BASENAME = @PYTHON_BASENAME@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_INCLUDES = @PYTHON_INCLUDES@ +PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ @@ -184,6 +186,7 @@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ @@ -220,7 +223,7 @@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ -pkgpyexecdir = $(pyexecdir)/gtk-2.0/gi +pkgpyexecdir = $(pyexecdir)/gi pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ @@ -469,6 +472,8 @@ uninstall-am: uninstall-pygirepositoryPYTHON uninstall-pygirepositoryPYTHON +-include $(top_srcdir)/git.mk + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/gi/types.py b/gi/types.py index 18b4013..37cf499 100644 --- a/gi/types.py +++ b/gi/types.py @@ -29,10 +29,14 @@ from ._gi import \ InterfaceInfo, \ ObjectInfo, \ StructInfo, \ + VFuncInfo, \ set_object_has_new_constructor, \ register_interface_info, \ hook_up_vfunc_implementation +if sys.version_info > (3, 0): + def callable(obj): + return hasattr(obj, '__call__') def Function(info): @@ -45,6 +49,16 @@ def Function(info): return function +def NativeVFunc(info, cls): + + def native_vfunc(*args): + return info.invoke(*args, gtype=cls.__gtype__) + native_vfunc.__info__ = info + native_vfunc.__name__ = info.get_name() + native_vfunc.__module__ = info.get_namespace() + + return native_vfunc + def Constructor(info): def constructor(cls, *args): @@ -93,40 +107,106 @@ class MetaClassHelper(object): setattr(cls, name, value) def _setup_vfuncs(cls): - for base in cls.__bases__: - if not hasattr(base, '__info__') or \ - not hasattr(base.__info__, 'get_vfuncs'): + for vfunc_name, py_vfunc in cls.__dict__.items(): + if not vfunc_name.startswith("do_") or not callable(py_vfunc): continue - for vfunc_info in base.__info__.get_vfuncs(): - vfunc = getattr(cls, 'do_' + vfunc_info.get_name(), None) - if vfunc is None and isinstance(base.__info__, InterfaceInfo) and \ - (not hasattr(cls, vfunc_info.get_name()) and not vfunc_info.get_invoker()): - raise TypeError('Class implementing %s.%s should implement ' - 'the method do_%s()' % (base.__info__.get_namespace(), - base.__info__.get_name(), - vfunc_info.get_name())) - elif vfunc is not None: - function = vfunc - if sys.version_info < (3, 0): - function = vfunc.im_func - - if not is_function_in_classes(function, cls.__bases__): - hook_up_vfunc_implementation(vfunc_info, cls.__gtype__, - vfunc) - -def is_function_in_classes(function, classes): - for klass in classes: - if function in klass.__dict__.values(): - return True - elif is_function_in_classes(function, klass.__bases__): - return True - return False + + # If a method name starts with "do_" assume it is a vfunc, and search + # in the base classes for a method with the same name to override. + # Recursion is not necessary here because getattr() searches all + # super class attributes as well. + vfunc_info = None + for base in cls.__bases__: + method = getattr(base, vfunc_name, None) + if method is not None and hasattr(method, '__info__') and \ + isinstance(method.__info__, VFuncInfo): + vfunc_info = method.__info__ + break + + # If we did not find a matching method name in the bases, we might + # be overriding an interface virtual method. Since interfaces do not + # provide implementations, there will be no method attribute installed + # on the object. Instead we have to search through + # InterfaceInfo.get_vfuncs(). Note that the infos returned by + # get_vfuncs() use the C vfunc name (ie. there is no "do_" prefix). + if vfunc_info is None: + vfunc_info = find_vfunc_info_in_interface(cls.__bases__, vfunc_name[len("do_"):]) + + if vfunc_info is not None: + assert vfunc_name == ('do_' + vfunc_info.get_name()) + # Check to see if there are vfuncs with the same name in the bases. + # We have no way of specifying which one we are supposed to override. + ambiguous_base = find_vfunc_conflict_in_bases(vfunc_info, cls.__bases__) + if ambiguous_base is not None: + base_info = vfunc_info.get_container() + raise TypeError('Method %s() on class %s.%s is ambiguous ' + 'with methods in base classes %s.%s and %s.%s' % + (vfunc_name, + cls.__info__.get_namespace(), + cls.__info__.get_name(), + base_info.get_namespace(), + base_info.get_name(), + ambiguous_base.__info__.get_namespace(), + ambiguous_base.__info__.get_name())) + + hook_up_vfunc_implementation(vfunc_info, cls.__gtype__, + py_vfunc) + + def _setup_native_vfuncs(cls): + # Only InterfaceInfo and ObjectInfo have the get_vfuncs() method. + # We skip InterfaceInfo because interfaces have no implementations for vfuncs. + # Also check if __info__ in __dict__, not hasattr('__info__', ...) + # because we do not want to accidentally retrieve __info__ from a base class. + class_info = cls.__dict__.get('__info__') + if class_info is None or not isinstance(class_info, ObjectInfo): + return + + for vfunc_info in class_info.get_vfuncs(): + name = 'do_%s' % vfunc_info.get_name() + value = NativeVFunc(vfunc_info, cls) + setattr(cls, name, value) + +def find_vfunc_info_in_interface(bases, vfunc_name): + for base in bases: + # All wrapped interfaces inherit from GInterface. + # This can be seen in IntrospectionModule.__getattr__() in module.py. + # We do not need to search regular classes here, only wrapped interfaces. + # We also skip GInterface, because it is not wrapped and has no __info__ attr. + if base is gobject.GInterface or\ + not issubclass(base, gobject.GInterface) or\ + not isinstance(base.__info__, InterfaceInfo): + continue + + for vfunc in base.__info__.get_vfuncs(): + if vfunc.get_name() == vfunc_name: + return vfunc + + vfunc = find_vfunc_info_in_interface(base.__bases__, vfunc_name) + if vfunc is not None: + return vfunc + + return None + +def find_vfunc_conflict_in_bases(vfunc, bases): + for klass in bases: + if not hasattr(klass, '__info__') or \ + not hasattr(klass.__info__, 'get_vfuncs'): + continue + vfuncs = klass.__info__.get_vfuncs() + vfunc_name = vfunc.get_name() + for v in vfuncs: + if v.get_name() == vfunc_name and v != vfunc: + return klass + + aklass = find_vfunc_conflict_in_bases(vfunc, klass.__bases__) + if aklass is not None: + return aklass + return None class GObjectMeta(gobject.GObjectMeta, MetaClassHelper): def __init__(cls, name, bases, dict_): super(GObjectMeta, cls).__init__(name, bases, dict_) - is_gi_defined = False if cls.__module__ == 'gi.repository.' + cls.__info__.get_namespace(): is_gi_defined = True @@ -140,6 +220,7 @@ class GObjectMeta(gobject.GObjectMeta, MetaClassHelper): elif is_gi_defined: cls._setup_methods() cls._setup_constants() + cls._setup_native_vfuncs() if isinstance(cls.__info__, ObjectInfo): cls._setup_fields() @@ -148,6 +229,7 @@ class GObjectMeta(gobject.GObjectMeta, MetaClassHelper): elif isinstance(cls.__info__, InterfaceInfo): register_interface_info(cls.__info__.get_g_type()) + class StructMeta(type, MetaClassHelper): def __init__(cls, name, bases, dict_): @@ -162,8 +244,17 @@ class StructMeta(type, MetaClassHelper): cls._setup_methods() cls._setup_constructors() + for method_info in cls.__info__.get_methods(): + if method_info.is_constructor() and \ + method_info.get_name() == 'new' and \ + not method_info.get_arguments(): + cls.__new__ = staticmethod(Constructor(method_info)) + break + class Enum(int): - __info__ = None + # 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) |