From a0cd68d2ceb3753c7efea7f8a049a7f77afaabfb Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Tue, 30 Oct 2018 10:30:01 +0900 Subject: [PATCH] Imported Upstream version 3.27.4 --- ChangeLog | 246 +++++++++++++++++++ NEWS | 9 + PKG-INFO | 2 +- configure | 24 +- configure.ac | 2 +- docs/devguide/building_testing.rst | 6 + docs/guide/api/signals.rst | 3 +- gi/gimodule.c | 6 +- gi/overrides/GLib.py | 4 - gi/overrides/GObject.py | 3 + gi/pygi-array.c | 24 +- gi/pygi-basictype.c | 8 +- gi/pygi-basictype.h | 3 +- gi/pygi-boxed.c | 40 +-- gi/pygi-boxed.h | 4 +- gi/pygi-cache.h | 13 +- gi/pygi-closure.c | 9 +- gi/pygi-enum-marshal.c | 6 +- gi/pygi-error.c | 3 +- gi/pygi-hashtable.c | 13 +- gi/pygi-invoke-state-struct.h | 3 - gi/pygi-invoke.c | 17 +- gi/pygi-list.c | 38 +-- gi/pygi-marshal-cleanup.c | 13 +- gi/pygi-object.c | 8 +- gi/pygi-property.c | 6 +- gi/pygi-source.c | 6 +- gi/pygi-struct-marshal.c | 113 ++++++--- pygtkcompat/pygtkcompat.py | 273 ++++++++++++--------- setup.py | 52 ++-- tests/Makefile.am | 19 +- tests/Makefile.in | 19 +- tests/__init__.py | 102 ++++++++ tests/helper.py | 4 +- tests/runtests.py | 143 ++--------- tests/test_atoms.py | 21 +- tests/test_cairo.py | 6 +- tests/test_docstring.py | 2 + tests/test_error.py | 6 +- tests/test_everything.py | 6 +- tests/test_fields.py | 2 + tests/test_gdbus.py | 31 +++ tests/test_generictreemodel.py | 5 +- tests/test_gi.py | 6 +- tests/test_gio.py | 4 +- tests/test_glib.py | 2 + tests/test_gobject.py | 8 +- tests/test_gtype.py | 2 + tests/test_import_machinery.py | 2 + tests/test_interface.py | 4 +- tests/test_internal_api.py | 10 +- tests/test_iochannel.py | 6 +- tests/test_mainloop.py | 2 + tests/test_object_marshaling.py | 2 + tests/test_option.py | 2 + tests/test_ossig.py | 2 + tests/test_overrides_gdk.py | 4 +- tests/test_overrides_glib.py | 4 +- tests/test_overrides_gtk.py | 4 +- tests/test_overrides_pango.py | 2 + tests/test_properties.py | 10 +- .../{compat_test_pygtk.py => test_pygtkcompat.py} | 98 ++++++-- tests/test_repository.py | 8 +- tests/test_resulttuple.py | 2 + tests/test_signal.py | 13 +- tests/test_source.py | 16 +- tests/test_subprocess.py | 2 + tests/test_thread.py | 5 +- tests/test_typeclass.py | 6 +- tests/test_unknown.py | 31 +++ tests/testmodule.py | 2 + 71 files changed, 1027 insertions(+), 555 deletions(-) create mode 100644 tests/__init__.py rename tests/{compat_test_pygtk.py => test_pygtkcompat.py} (77%) create mode 100644 tests/test_unknown.py diff --git a/ChangeLog b/ChangeLog index e5c7e6c..91bbad9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,249 @@ +commit 9c1d66be67008604206e336a0433c5cdf824b837 +Author: Christoph Reiter +Date: Wed Feb 14 01:47:12 2018 +0100 + + release + + NEWS | 9 +++++++++ + 1 file changed, 9 insertions(+) + +commit 86fb783d0f00a49ea7e66ad73be7e150eb0162db +Author: Christoph Reiter +Date: Wed Feb 14 02:07:13 2018 +0100 + + Fix make distcheck + + tests/Makefile.am | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +commit 618ccf06ad75bc481dec59b7da86a550571968c5 +Author: Christoph Reiter +Date: Wed Feb 14 01:34:02 2018 +0100 + + setup.py: cache pkg-config calls + + This saves 300ms for a noop build_tests command + + setup.py | 29 +++++++++++++++++------------ + 1 file changed, 17 insertions(+), 12 deletions(-) + +commit ad9fc2d9cd8d9a0f3fd07a46e7056a182f18d399 +Author: Christoph Reiter +Date: Wed Feb 14 01:29:09 2018 +0100 + + gitlab-ci: Install pytest-faulthandler + + This makes pytest print a Python stack trace in case of a crash. + + .gitlab-ci/test-docker.sh | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 0d3fbc2a8d10c9eea4e3eef905d52c946f8ab7e9 +Author: Christoph Reiter +Date: Wed Feb 14 01:16:51 2018 +0100 + + setup.py: set the same env vars for testing as autotools + + Also don't print a Python stack trace of the calling process on + error, just return the error status. + + setup.py | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +commit 0fcd57846b0053319dc5a28503d3eab173927fc6 +Author: Christoph Reiter +Date: Wed Feb 14 00:56:47 2018 +0100 + + tests: add a test for Gio.DBusNodeInfo.new_for_xml, see #164 + + This was fixed by the recent reverts + + tests/test_gdbus.py | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +commit a506d5e3c64321c43a4ce7c2a72ca8d36e985999 +Author: Christoph Reiter +Date: Tue Feb 13 23:26:21 2018 +0100 + + Revert "Revert "Refactor boxed wrapper memory management strategy"" + + This reverts commit daefdfa3e4dc97b4ae38250358d722f09764cc9b. + + gi/gimodule.c | 6 +++- + gi/overrides/GLib.py | 4 --- + gi/overrides/GObject.py | 3 ++ + gi/pygi-boxed.c | 32 +++++++++++++++----- + gi/pygi-boxed.h | 4 +-- + gi/pygi-property.c | 6 +--- + gi/pygi-source.c | 6 ++-- + gi/pygi-struct-marshal.c | 78 + ++++++++++++++++++++++++++++++++++++++++++++---- + tests/test_gi.py | 1 - + tests/test_source.py | 8 +++-- + 10 files changed, 119 insertions(+), 29 deletions(-) + +commit 700c785367975995d24a8ffd00b2aa028ccf77e8 +Author: Christoph Reiter +Date: Tue Feb 13 23:26:15 2018 +0100 + + Revert "pygi-boxed: make in-place copy safer" + + This reverts commit 9d96df1ea3ad533885fbcae4de693a3cd81e765a. + + gi/pygi-boxed.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +commit fa555767464fbe551b14cfbc121febc5290b1371 +Author: Christoph Reiter +Date: Tue Feb 13 23:26:03 2018 +0100 + + Revert "to python marshalling: collect cleanup data" + + This reverts commit 80eab029ce4e181624d7b845a4b517051797080d. + + gi/pygi-array.c | 24 ++++++------------------ + gi/pygi-basictype.c | 8 +++----- + gi/pygi-basictype.h | 3 +-- + gi/pygi-cache.h | 13 +++---------- + gi/pygi-closure.c | 9 ++------- + gi/pygi-enum-marshal.c | 6 ++---- + gi/pygi-error.c | 3 +-- + gi/pygi-hashtable.c | 13 ++++--------- + gi/pygi-invoke-state-struct.h | 3 --- + gi/pygi-invoke.c | 17 ++++------------- + gi/pygi-list.c | 38 +++++++------------------------------- + gi/pygi-marshal-cleanup.c | 13 +++++-------- + gi/pygi-object.c | 8 +++----- + gi/pygi-struct-marshal.c | 5 ++--- + 14 files changed, 43 insertions(+), 120 deletions(-) + +commit 10c062001ddd948b0a70545cbf613b38ed6fa46c +Author: Christoph Reiter +Date: Tue Feb 13 23:09:49 2018 +0100 + + Revert "to python struct marshalling: copy boxed during cleanup" + + This reverts commit 7ed8191818733b9130bce84f782dc6f8f734abf7. + + gi/pygi-struct-marshal.c | 34 +++++++--------------------------- + tests/test_gi.py | 1 + + 2 files changed, 8 insertions(+), 27 deletions(-) + +commit e427d1a3a44e882c998543bb2c189700409df873 +Author: Christoph Reiter +Date: Mon Feb 12 20:35:09 2018 +0100 + + tests: Fix tests under Wayland. Fixes #163 + + These tests try to test Gdk.Atom with the exsting API in libgdk + but under Wayland they either don't return Gdk.Atom or block. + + Since they are disabled on other platforms just change them to only + run under X11. + + tests/test_atoms.py | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +commit 3e91df3dca3691635b978d1dc661506213e0efe3 +Author: Christoph Reiter +Date: Mon Feb 12 15:39:41 2018 +0100 + + docs: fix build with sphinx 1.7.0 + + 1.7.0 no longer allows non-class references as param types + + docs/guide/api/signals.rst | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 5a25c98cb6d387791e41c7dc240c55814988519a +Author: Christoph Reiter +Date: Sun Feb 11 18:08:07 2018 +0100 + + tests: Make it possible to use pytest directly + + pytest will just import the files passed to it and try to run tests. + Since we need to run some setup code convert the tests directory to + a Python package and do the initialization in __init__.py. This makes + the init code (env vars, typelib search path, dbus) always run when + something from the package gets imported. + + python3 setup.py build_tests # build pygobject and tests + py.test-3 tests/test_gi.py # run tests in test_gi.py only + + docs/devguide/building_testing.rst | 6 ++ + setup.py | 10 +-- + tests/Makefile.am | 8 ++- + tests/__init__.py | 102 ++++++++++++++++++++++++++ + tests/helper.py | 4 +- + tests/runtests.py | 143 + +++++++------------------------------ + tests/test_atoms.py | 4 +- + tests/test_cairo.py | 6 +- + tests/test_docstring.py | 2 + + tests/test_error.py | 6 +- + tests/test_everything.py | 6 +- + tests/test_fields.py | 2 + + tests/test_gdbus.py | 2 + + tests/test_generictreemodel.py | 5 +- + tests/test_gi.py | 6 +- + tests/test_gio.py | 4 +- + tests/test_glib.py | 2 + + tests/test_gobject.py | 8 +-- + tests/test_gtype.py | 2 + + tests/test_import_machinery.py | 2 + + tests/test_interface.py | 4 +- + tests/test_internal_api.py | 10 ++- + tests/test_iochannel.py | 6 +- + tests/test_mainloop.py | 2 + + tests/test_object_marshaling.py | 2 + + tests/test_option.py | 2 + + tests/test_ossig.py | 2 + + tests/test_overrides_gdk.py | 4 +- + tests/test_overrides_glib.py | 4 +- + tests/test_overrides_gtk.py | 4 +- + tests/test_overrides_pango.py | 2 + + tests/test_properties.py | 10 ++- + tests/test_pygtkcompat.py | 4 +- + tests/test_repository.py | 8 +-- + tests/test_resulttuple.py | 2 + + tests/test_signal.py | 13 ++-- + tests/test_source.py | 8 +-- + tests/test_subprocess.py | 2 + + tests/test_thread.py | 5 +- + tests/test_typeclass.py | 6 +- + tests/test_unknown.py | 5 +- + tests/testmodule.py | 2 + + 42 files changed, 244 insertions(+), 193 deletions(-) + +commit 21cee6cc4fbc7fb1a28a15840924b0da52b49fca +Author: Christoph Reiter +Date: Sun Feb 11 16:08:18 2018 +0100 + + tests: Make patching in pygtkcompat reversible + + Record all the attribute and sys.modules changes and add an API + for reverting them after tests are completed. + + This allows us to run the pygtkcompat tests in the same test process + as other tests. + + pygtkcompat/pygtkcompat.py | 273 + ++++++++++++--------- + setup.py | 7 - + tests/Makefile.am | 4 +- + tests/runtests.py | 2 +- + .../{compat_test_pygtk.py => test_pygtkcompat.py} | 96 ++++++-- + 5 files changed, 237 insertions(+), 145 deletions(-) + +commit 7f5aedb6e1f317b33a96d0c77641b95f182b2a47 +Author: Christoph Reiter +Date: Sat Feb 10 13:31:23 2018 +0100 + + version bump + + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + commit c65efcb0bb4a73c9fb1ffa10225795adc489a081 Author: Christoph Reiter Date: Sat Feb 10 13:22:17 2018 +0100 diff --git a/NEWS b/NEWS index e0901a4..6a57419 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,12 @@ +3.27.4 - 2018-02-14 +------------------- + +* tests: Fix tests under Wayland. :issue:`163` +* tests: Make it possible to use pytest directly. +* Reverted transfer-none boxed copy changes (:mr:`10`) due to regressions in + gnome-music. :issue:`164` :mr:`23` + + 3.27.3 - 2018-02-10 ------------------- diff --git a/PKG-INFO b/PKG-INFO index 311ae08..6e6f47d 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: PyGObject -Version: 3.27.3 +Version: 3.27.4 Summary: Python bindings for GObject Introspection Home-page: https://pygobject.readthedocs.io Author: James Henstridge diff --git a/configure b/configure index 047905d..1dc4a67 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for pygobject 3.27.3. +# Generated by GNU Autoconf 2.69 for pygobject 3.27.4. # # Report bugs to . # @@ -591,8 +591,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='pygobject' PACKAGE_TARNAME='pygobject' -PACKAGE_VERSION='3.27.3' -PACKAGE_STRING='pygobject 3.27.3' +PACKAGE_VERSION='3.27.4' +PACKAGE_STRING='pygobject 3.27.4' PACKAGE_BUGREPORT='http://bugzilla.gnome.org/enter_bug.cgi?product=pygobject' PACKAGE_URL='https://wiki.gnome.org/Projects/PyGObject/' @@ -1418,7 +1418,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures pygobject 3.27.3 to adapt to many kinds of systems. +\`configure' configures pygobject 3.27.4 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1489,7 +1489,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of pygobject 3.27.3:";; + short | recursive ) echo "Configuration of pygobject 3.27.4:";; esac cat <<\_ACEOF @@ -1634,7 +1634,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -pygobject configure 3.27.3 +pygobject configure 3.27.4 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1912,7 +1912,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by pygobject $as_me 3.27.3, which was +It was created by pygobject $as_me 3.27.4, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2290,9 +2290,9 @@ $as_echo "#define PYGOBJECT_MINOR_VERSION 27" >>confdefs.h PYGOBJECT_MINOR_VERSION=27 -$as_echo "#define PYGOBJECT_MICRO_VERSION 3" >>confdefs.h +$as_echo "#define PYGOBJECT_MICRO_VERSION 4" >>confdefs.h -PYGOBJECT_MICRO_VERSION=3 +PYGOBJECT_MICRO_VERSION=4 ac_config_headers="$ac_config_headers config.h" @@ -2812,7 +2812,7 @@ fi # Define the identity of the package. PACKAGE='pygobject' - VERSION='3.27.3' + VERSION='3.27.4' cat >>confdefs.h <<_ACEOF @@ -16931,7 +16931,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by pygobject $as_me 3.27.3, which was +This file was extended by pygobject $as_me 3.27.4, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -16998,7 +16998,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -pygobject config.status 3.27.3 +pygobject config.status 3.27.4 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 004573c..5c9d3f7 100644 --- a/configure.ac +++ b/configure.ac @@ -18,7 +18,7 @@ m4_define(python3_min_ver, 3.4) dnl the pygobject version number m4_define(pygobject_major_version, 3) m4_define(pygobject_minor_version, 27) -m4_define(pygobject_micro_version, 3) +m4_define(pygobject_micro_version, 4) m4_define(pygobject_version, pygobject_major_version.pygobject_minor_version.pygobject_micro_version) dnl versions of packages we require ... diff --git a/docs/devguide/building_testing.rst b/docs/devguide/building_testing.rst index 79c73f5..f2c9624 100644 --- a/docs/devguide/building_testing.rst +++ b/docs/devguide/building_testing.rst @@ -53,6 +53,9 @@ Using Setuptools # Build in-tree python3 setup.py build_ext --inplace + # Build in-tree including tests + python3 setup.py build_tests + # Executing some code after the build PYTHONPATH=. python3 foo.py @@ -64,5 +67,8 @@ Using Setuptools TEST_NAMES=test_gi.TestUtf8 python3 setup.py test TEST_NAMES=test_gi.TestUtf8.test_utf8_full_return python3 setup.py test + # using pytest directly + py.test-3 tests/test_gi.py + # Running flake8 tests python3 setup.py quality diff --git a/docs/guide/api/signals.rst b/docs/guide/api/signals.rst index 0001862..4353b21 100644 --- a/docs/guide/api/signals.rst +++ b/docs/guide/api/signals.rst @@ -63,7 +63,8 @@ You can define your own signals using the :obj:`GObject.Signal` decorator: :param GObject.SignalFlags flags: Signal flags :param GObject.GType return_type: Return type :param list arg_types: List of :class:`GObject.GType` argument types - :param GObject.SignalAccumulator accumulator: Accumulator function + :param accumulator: Accumulator function + :type accumulator: :obj:`GObject.SignalAccumulator` :param object accu_data: User data for the accumulator diff --git a/gi/gimodule.c b/gi/gimodule.c index 48ddee2..5f8853c 100644 --- a/gi/gimodule.c +++ b/gi/gimodule.c @@ -535,7 +535,11 @@ _wrap_pyg_variant_type_from_string (PyObject *self, PyObject *args) py_type = _pygi_type_import_by_name ("GLib", "VariantType"); - py_variant = _pygi_boxed_new ( (PyTypeObject *) py_type, type_string, FALSE, 0); + /* Pass the string directly and force a boxed copy. This works because + * GVariantType is just a char pointer. */ + py_variant = _pygi_boxed_new ( (PyTypeObject *) py_type, type_string, + TRUE, /* copy_boxed */ + 0); /* slice_allocated */ return py_variant; } diff --git a/gi/overrides/GLib.py b/gi/overrides/GLib.py index b1ff24f..f47a338 100644 --- a/gi/overrides/GLib.py +++ b/gi/overrides/GLib.py @@ -522,10 +522,6 @@ class Source(GLib.Source): def __init__(self, *args, **kwargs): return super(Source, self).__init__() - def __del__(self): - if hasattr(self, '__pygi_custom_source'): - self.unref() - def set_callback(self, fn, user_data=None): if hasattr(self, '__pygi_custom_source'): # use our custom pyg_source_set_callback() if for a GSource object diff --git a/gi/overrides/GObject.py b/gi/overrides/GObject.py index bfcf7cc..c252bfa 100644 --- a/gi/overrides/GObject.py +++ b/gi/overrides/GObject.py @@ -221,6 +221,9 @@ class Value(GObjectModule.Value): if self._free_on_dealloc and self.g_type != TYPE_INVALID: self.unset() + # We must call base class __del__() after unset. + super(Value, self).__del__() + def set_boxed(self, boxed): # Workaround the introspection marshalers inability to know # these methods should be marshaling boxed types. This is because diff --git a/gi/pygi-array.c b/gi/pygi-array.c index 0f6c6d8..e55f9f6 100644 --- a/gi/pygi-array.c +++ b/gi/pygi-array.c @@ -505,8 +505,7 @@ static PyObject * _pygi_marshal_to_py_array (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data) + GIArgument *arg) { GArray *array_; PyObject *py_obj = NULL; @@ -576,14 +575,11 @@ _pygi_marshal_to_py_array (PyGIInvokeState *state, gsize item_size; PyGIMarshalToPyFunc item_to_py_marshaller; PyGIArgCache *item_arg_cache; - GPtrArray *item_cleanups; py_obj = PyList_New (array_->len); if (py_obj == NULL) goto err; - item_cleanups = g_ptr_array_sized_new (array_->len); - *cleanup_data = item_cleanups; item_arg_cache = seq_cache->item_cache; item_to_py_marshaller = item_arg_cache->to_py_marshaller; @@ -593,7 +589,6 @@ _pygi_marshal_to_py_array (PyGIInvokeState *state, for (i = 0; i < array_->len; i++) { GIArgument item_arg = {0}; PyObject *py_item; - gpointer item_cleanup_data = NULL; /* If we are receiving an array of pointers, simply assign the pointer * and move on, letting the per-item marshaler deal with the @@ -637,10 +632,7 @@ _pygi_marshal_to_py_array (PyGIInvokeState *state, py_item = item_to_py_marshaller ( state, callable_cache, item_arg_cache, - &item_arg, - &item_cleanup_data); - - g_ptr_array_index (item_cleanups, i) = item_cleanup_data; + &item_arg); if (py_item == NULL) { Py_CLEAR (py_obj); @@ -648,8 +640,6 @@ _pygi_marshal_to_py_array (PyGIInvokeState *state, if (array_cache->array_type == GI_ARRAY_TYPE_C) g_array_unref (array_); - g_ptr_array_unref (item_cleanups); - goto err; } PyList_SET_ITEM (py_obj, i, py_item); @@ -670,7 +660,7 @@ err: /* clean up unprocessed items */ if (seq_cache->item_cache->to_py_cleanup != NULL) { guint j; - PyGIMarshalToPyCleanupFunc cleanup_func = seq_cache->item_cache->to_py_cleanup; + PyGIMarshalCleanupFunc cleanup_func = seq_cache->item_cache->to_py_cleanup; for (j = processed_items; j < array_->len; j++) { cleanup_func (state, seq_cache->item_cache, @@ -721,7 +711,7 @@ _wrap_c_array (PyGIInvokeState *state, static void _pygi_marshal_cleanup_to_py_array (PyGIInvokeState *state, PyGIArgCache *arg_cache, - gpointer cleanup_data, + PyObject *dummy, gpointer data, gboolean was_processed) { @@ -747,19 +737,17 @@ _pygi_marshal_cleanup_to_py_array (PyGIInvokeState *state, } if (sequence_cache->item_cache->to_py_cleanup != NULL) { - GPtrArray *item_cleanups = (GPtrArray *) cleanup_data; gsize i; guint len = (array_ != NULL) ? array_->len : ptr_array_->len; - PyGIMarshalToPyCleanupFunc cleanup_func = sequence_cache->item_cache->to_py_cleanup; + PyGIMarshalCleanupFunc cleanup_func = sequence_cache->item_cache->to_py_cleanup; for (i = 0; i < len; i++) { cleanup_func (state, sequence_cache->item_cache, - g_ptr_array_index(item_cleanups, i), + NULL, (array_ != NULL) ? g_array_index (array_, gpointer, i) : g_ptr_array_index (ptr_array_, i), was_processed); } - g_ptr_array_unref (item_cleanups); } if (array_ != NULL) diff --git a/gi/pygi-basictype.c b/gi/pygi-basictype.c index ddb02f9..6d4e64e 100644 --- a/gi/pygi-basictype.c +++ b/gi/pygi-basictype.c @@ -684,8 +684,7 @@ static PyObject * _pygi_marshal_to_py_void (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data) + GIArgument *arg) { if (arg_cache->is_pointer) { return PyLong_FromVoidPtr (arg->v_pointer); @@ -829,8 +828,7 @@ PyObject * _pygi_marshal_to_py_basic_type_cache_adapter (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data) + GIArgument *arg) { return _pygi_marshal_to_py_basic_type (arg, arg_cache->type_tag, @@ -840,7 +838,7 @@ _pygi_marshal_to_py_basic_type_cache_adapter (PyGIInvokeState *state, static void _pygi_marshal_cleanup_to_py_utf8 (PyGIInvokeState *state, PyGIArgCache *arg_cache, - gpointer cleanup_data, + PyObject *dummy, gpointer data, gboolean was_processed) { diff --git a/gi/pygi-basictype.h b/gi/pygi-basictype.h index 62f11c6..466c7d4 100644 --- a/gi/pygi-basictype.h +++ b/gi/pygi-basictype.h @@ -43,8 +43,7 @@ PyObject *_pygi_marshal_to_py_basic_type (GIArgument *arg, PyObject *_pygi_marshal_to_py_basic_type_cache_adapter (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data); + GIArgument *arg); PyGIArgCache *pygi_arg_basic_type_new_from_info (GITypeInfo *type_info, GIArgInfo *arg_info, /* may be null */ diff --git a/gi/pygi-boxed.c b/gi/pygi-boxed.c index 599d6d3..e9014f2 100644 --- a/gi/pygi-boxed.c +++ b/gi/pygi-boxed.c @@ -42,8 +42,6 @@ boxed_del (PyGIBoxed *self) if ( ( (PyGBoxed *) self)->free_on_dealloc && boxed != NULL) { if (self->slice_allocated) { g_slice_free1 (self->size, boxed); - self->slice_allocated = FALSE; - self->size = 0; } else { g_type = pyg_type_from_object ( (PyObject *) self); g_boxed_free (g_type, boxed); @@ -115,7 +113,7 @@ _boxed_new (PyTypeObject *type, goto out; } - self = (PyGIBoxed *) _pygi_boxed_new (type, boxed, TRUE, size); + self = (PyGIBoxed *) _pygi_boxed_new (type, boxed, FALSE, size); if (self == NULL) { g_slice_free1 (size, boxed); goto out; @@ -151,30 +149,48 @@ _boxed_init (PyObject *self, PYGLIB_DEFINE_TYPE("gi.Boxed", PyGIBoxed_Type, PyGIBoxed); PyObject * -_pygi_boxed_new (PyTypeObject *type, +_pygi_boxed_new (PyTypeObject *pytype, gpointer boxed, - gboolean free_on_dealloc, + gboolean copy_boxed, gsize allocated_slice) { PyGIBoxed *self; + GType gtype; if (!boxed) { Py_RETURN_NONE; } - if (!PyType_IsSubtype (type, &PyGIBoxed_Type)) { + if (!PyType_IsSubtype (pytype, &PyGIBoxed_Type)) { PyErr_SetString (PyExc_TypeError, "must be a subtype of gi.Boxed"); return NULL; } - self = (PyGIBoxed *) type->tp_alloc (type, 0); + gtype = pyg_type_from_object ((PyObject *)pytype); + + /* Boxed objects with slice allocation means they come from caller allocated + * out arguments. In this case copy_boxed does not make sense because we + * already own the slice allocated memory and we should be receiving full + * ownership transfer. */ + if (copy_boxed) { + g_assert (allocated_slice == 0); + boxed = g_boxed_copy (gtype, boxed); + } + + self = (PyGIBoxed *) pytype->tp_alloc (pytype, 0); if (self == NULL) { return NULL; } - ( (PyGBoxed *) self)->gtype = pyg_type_from_object ( (PyObject *) type); - ( (PyGBoxed *) self)->free_on_dealloc = free_on_dealloc; + /* We always free on dealloc because we always own the memory due to: + * 1) copy_boxed == TRUE + * 2) allocated_slice > 0 + * 3) otherwise the mode is assumed "transfer everything". + */ + ((PyGBoxed *)self)->free_on_dealloc = TRUE; + ((PyGBoxed *)self)->gtype = gtype; pyg_boxed_set_ptr (self, boxed); + if (allocated_slice > 0) { self->size = allocated_slice; self->slice_allocated = TRUE; @@ -205,11 +221,7 @@ void _pygi_boxed_copy_in_place (PyGIBoxed *self) { PyGBoxed *pygboxed = (PyGBoxed *)self; - gpointer ptr = pyg_boxed_get_ptr (self); - gpointer copy = NULL; - - if (ptr) - copy = g_boxed_copy (pygboxed->gtype, ptr); + gpointer copy = g_boxed_copy (pygboxed->gtype, pyg_boxed_get_ptr (self)); boxed_del (self); pyg_boxed_set_ptr (pygboxed, copy); diff --git a/gi/pygi-boxed.h b/gi/pygi-boxed.h index 5c04b5c..8679322 100644 --- a/gi/pygi-boxed.h +++ b/gi/pygi-boxed.h @@ -34,9 +34,9 @@ typedef struct { extern PyTypeObject PyGIBoxed_Type; -PyObject * _pygi_boxed_new (PyTypeObject *type, +PyObject * _pygi_boxed_new (PyTypeObject *pytype, gpointer boxed, - gboolean free_on_dealloc, + gboolean copy_boxed, gsize allocated_slice); void * _pygi_boxed_alloc (GIBaseInfo *info, diff --git a/gi/pygi-cache.h b/gi/pygi-cache.h index 574563b..4dfabd8 100644 --- a/gi/pygi-cache.h +++ b/gi/pygi-cache.h @@ -50,21 +50,14 @@ typedef gboolean (*PyGIMarshalFromPyFunc) (PyGIInvokeState *state, typedef PyObject *(*PyGIMarshalToPyFunc) (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data); + GIArgument *arg); typedef void (*PyGIMarshalCleanupFunc) (PyGIInvokeState *state, PyGIArgCache *arg_cache, - PyObject *py_arg, + PyObject *py_arg, /* always NULL for to_py cleanup */ gpointer data, gboolean was_processed); -typedef void (*PyGIMarshalToPyCleanupFunc) (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - gpointer cleanup_data, - gpointer data, - gboolean was_processed); - /* Argument meta types denote how we process the argument: * - PYGI_META_ARG_TYPE_PARENT - parents may or may not have children * but are always processed via the normal marshaller for their @@ -125,7 +118,7 @@ struct _PyGIArgCache PyGIMarshalToPyFunc to_py_marshaller; PyGIMarshalCleanupFunc from_py_cleanup; - PyGIMarshalToPyCleanupFunc to_py_cleanup; + PyGIMarshalCleanupFunc to_py_cleanup; GDestroyNotify destroy_notify; diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c index 42144e0..b51c04c 100644 --- a/gi/pygi-closure.c +++ b/gi/pygi-closure.c @@ -393,14 +393,10 @@ _pygi_closure_convert_arguments (PyGIInvokeState *state, } else if (arg_cache->meta_type != PYGI_META_ARG_TYPE_PARENT) { continue; } else { - gpointer cleanup_data = NULL; - value = arg_cache->to_py_marshaller (state, cache, arg_cache, - &state->args[i].arg_value, - &cleanup_data); - state->args[i].to_py_arg_cleanup_data = cleanup_data; + &state->args[i].arg_value); if (value == NULL) { pygi_marshal_cleanup_args_to_py_parameter_fail (state, @@ -804,8 +800,7 @@ static PyObject * _pygi_marshal_to_py_interface_callback (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *arg_cleanup_data) + GIArgument *arg) { PyGICallbackCache *callback_cache = (PyGICallbackCache *) arg_cache; gssize user_data_index; diff --git a/gi/pygi-enum-marshal.c b/gi/pygi-enum-marshal.c index fe6c7d7..44eb009 100644 --- a/gi/pygi-enum-marshal.c +++ b/gi/pygi-enum-marshal.c @@ -225,8 +225,7 @@ static PyObject * _pygi_marshal_to_py_interface_enum (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data) + GIArgument *arg) { PyObject *py_obj = NULL; PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; @@ -254,8 +253,7 @@ static PyObject * _pygi_marshal_to_py_interface_flags (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data) + GIArgument *arg) { PyObject *py_obj = NULL; PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; diff --git a/gi/pygi-error.c b/gi/pygi-error.c index 4827e9f..e3d8838 100644 --- a/gi/pygi-error.c +++ b/gi/pygi-error.c @@ -275,8 +275,7 @@ static PyObject * _pygi_marshal_to_py_gerror (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data) + GIArgument *arg) { GError *error = arg->v_pointer; PyObject *py_obj = NULL; diff --git a/gi/pygi-hashtable.c b/gi/pygi-hashtable.c index f0cda78..647bf04 100644 --- a/gi/pygi-hashtable.c +++ b/gi/pygi-hashtable.c @@ -220,8 +220,7 @@ static PyObject * _pygi_marshal_to_py_ghash (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data) + GIArgument *arg) { GHashTable *hash_; GHashTableIter hash_table_iter; @@ -260,8 +259,6 @@ _pygi_marshal_to_py_ghash (PyGIInvokeState *state, while (g_hash_table_iter_next (&hash_table_iter, &key_arg.v_pointer, &value_arg.v_pointer)) { - gpointer key_cleanup_data = NULL; - gpointer value_cleanup_data = NULL; PyObject *py_key; PyObject *py_value; int retval; @@ -271,8 +268,7 @@ _pygi_marshal_to_py_ghash (PyGIInvokeState *state, py_key = key_to_py_marshaller ( state, callable_cache, key_arg_cache, - &key_arg, - &key_cleanup_data); + &key_arg); if (py_key == NULL) { Py_CLEAR (py_obj); @@ -283,8 +279,7 @@ _pygi_marshal_to_py_ghash (PyGIInvokeState *state, py_value = value_to_py_marshaller ( state, callable_cache, value_arg_cache, - &value_arg, - &value_cleanup_data); + &value_arg); if (py_value == NULL) { Py_CLEAR (py_obj); @@ -309,7 +304,7 @@ _pygi_marshal_to_py_ghash (PyGIInvokeState *state, static void _pygi_marshal_cleanup_to_py_ghash (PyGIInvokeState *state, PyGIArgCache *arg_cache, - gpointer cleanup_data, + PyObject *dummy, gpointer data, gboolean was_processed) { diff --git a/gi/pygi-invoke-state-struct.h b/gi/pygi-invoke-state-struct.h index dbf4e66..64711cb 100644 --- a/gi/pygi-invoke-state-struct.h +++ b/gi/pygi-invoke-state-struct.h @@ -20,8 +20,6 @@ typedef struct _PyGIInvokeArgState /* Holds from_py marshaler cleanup data. */ gpointer arg_cleanup_data; - /* Holds to_py marshaler cleanup data. */ - gpointer to_py_arg_cleanup_data; } PyGIInvokeArgState; @@ -47,7 +45,6 @@ typedef struct _PyGIInvokeState /* Memory to receive the result of the C ffi function call. */ GIArgument return_arg; - gpointer to_py_return_arg_cleanup_data; /* A GError exception which is indirectly bound into the last position of * the "args" array if the callable caches "throws" member is set. diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c index fca48ba..fd9e474 100644 --- a/gi/pygi-invoke.c +++ b/gi/pygi-invoke.c @@ -565,13 +565,10 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGIFunctionCache *function_ca if (cache->return_cache) { if (!cache->return_cache->is_skipped) { - gpointer cleanup_data = NULL; py_return = cache->return_cache->to_py_marshaller ( state, cache, cache->return_cache, - &state->return_arg, - &cleanup_data); - state->to_py_return_arg_cleanup_data = cleanup_data; + &state->return_arg); if (py_return == NULL) { pygi_marshal_cleanup_args_return_fail (state, cache); @@ -579,7 +576,7 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGIFunctionCache *function_ca } } else { if (cache->return_cache->transfer == GI_TRANSFER_EVERYTHING) { - PyGIMarshalToPyCleanupFunc to_py_cleanup = + PyGIMarshalCleanupFunc to_py_cleanup = cache->return_cache->to_py_cleanup; if (to_py_cleanup != NULL) @@ -607,13 +604,10 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGIFunctionCache *function_ca } else if (!cache->has_return && n_out_args == 1) { /* if we get here there is one out arg an no return */ PyGIArgCache *arg_cache = (PyGIArgCache *)cache->to_py_args->data; - gpointer cleanup_data = NULL; py_out = arg_cache->to_py_marshaller (state, cache, arg_cache, - state->args[arg_cache->c_arg_index].arg_pointer.v_pointer, - &cleanup_data); - state->args[arg_cache->c_arg_index].to_py_arg_cleanup_data = cleanup_data; + state->args[arg_cache->c_arg_index].arg_pointer.v_pointer); if (py_out == NULL) { pygi_marshal_cleanup_args_to_py_parameter_fail (state, cache, @@ -643,13 +637,10 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGIFunctionCache *function_ca for (; py_arg_index < tuple_len; py_arg_index++) { PyGIArgCache *arg_cache = (PyGIArgCache *)cache_item->data; - gpointer cleanup_data = NULL; PyObject *py_obj = arg_cache->to_py_marshaller (state, cache, arg_cache, - state->args[arg_cache->c_arg_index].arg_pointer.v_pointer, - &cleanup_data); - state->args[arg_cache->c_arg_index].to_py_arg_cleanup_data = cleanup_data; + state->args[arg_cache->c_arg_index].arg_pointer.v_pointer); if (py_obj == NULL) { if (cache->has_return) diff --git a/gi/pygi-list.c b/gi/pygi-list.c index e9fc92c..72a3d20 100644 --- a/gi/pygi-list.c +++ b/gi/pygi-list.c @@ -234,13 +234,11 @@ static PyObject * _pygi_marshal_to_py_glist (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data) + GIArgument *arg) { GList *list_; gsize length; gsize i; - GPtrArray *item_cleanups; PyGIMarshalToPyFunc item_to_py_marshaller; PyGIArgCache *item_arg_cache; @@ -255,31 +253,23 @@ _pygi_marshal_to_py_glist (PyGIInvokeState *state, if (py_obj == NULL) return NULL; - item_cleanups = g_ptr_array_sized_new (length); - *cleanup_data = item_cleanups; - item_arg_cache = seq_cache->item_cache; item_to_py_marshaller = item_arg_cache->to_py_marshaller; for (i = 0; list_ != NULL; list_ = g_list_next (list_), i++) { GIArgument item_arg; PyObject *py_item; - gpointer item_cleanup_data = NULL; item_arg.v_pointer = list_->data; _pygi_hash_pointer_to_arg (&item_arg, item_arg_cache->type_info); py_item = item_to_py_marshaller (state, callable_cache, item_arg_cache, - &item_arg, - &item_cleanup_data); - - g_ptr_array_index (item_cleanups, i) = item_cleanup_data; + &item_arg); if (py_item == NULL) { Py_CLEAR (py_obj); _PyGI_ERROR_PREFIX ("Item %zu: ", i); - g_ptr_array_unref (item_cleanups); return NULL; } @@ -293,13 +283,11 @@ static PyObject * _pygi_marshal_to_py_gslist (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data) + GIArgument *arg) { GSList *list_; gsize length; gsize i; - GPtrArray *item_cleanups; PyGIMarshalToPyFunc item_to_py_marshaller; PyGIArgCache *item_arg_cache; @@ -314,30 +302,23 @@ _pygi_marshal_to_py_gslist (PyGIInvokeState *state, if (py_obj == NULL) return NULL; - item_cleanups = g_ptr_array_sized_new (length); - *cleanup_data = item_cleanups; - item_arg_cache = seq_cache->item_cache; item_to_py_marshaller = item_arg_cache->to_py_marshaller; for (i = 0; list_ != NULL; list_ = g_slist_next (list_), i++) { GIArgument item_arg; PyObject *py_item; - gpointer item_cleanup_data = NULL; item_arg.v_pointer = list_->data; _pygi_hash_pointer_to_arg (&item_arg, item_arg_cache->type_info); py_item = item_to_py_marshaller (state, callable_cache, item_arg_cache, - &item_arg, - &item_cleanup_data); + &item_arg); - g_ptr_array_index (item_cleanups, i) = item_cleanup_data; if (py_item == NULL) { Py_CLEAR (py_obj); _PyGI_ERROR_PREFIX ("Item %zu: ", i); - g_ptr_array_unref (item_cleanups); return NULL; } @@ -350,30 +331,27 @@ _pygi_marshal_to_py_gslist (PyGIInvokeState *state, static void _pygi_marshal_cleanup_to_py_glist (PyGIInvokeState *state, PyGIArgCache *arg_cache, - gpointer cleanup_data, + PyObject *dummy, gpointer data, gboolean was_processed) { - GPtrArray *item_cleanups = (GPtrArray *) cleanup_data; PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache; if (arg_cache->transfer == GI_TRANSFER_EVERYTHING || arg_cache->transfer == GI_TRANSFER_CONTAINER) { GSList *list_ = (GSList *)data; if (sequence_cache->item_cache->to_py_cleanup != NULL) { - PyGIMarshalToPyCleanupFunc cleanup_func = + PyGIMarshalCleanupFunc cleanup_func = sequence_cache->item_cache->to_py_cleanup; GSList *node = list_; - guint i = 0; while (node != NULL) { cleanup_func (state, sequence_cache->item_cache, - g_ptr_array_index(item_cleanups, i), + NULL, node->data, was_processed); node = node->next; - i++; } } @@ -385,8 +363,6 @@ _pygi_marshal_cleanup_to_py_glist (PyGIInvokeState *state, g_assert_not_reached(); } } - - g_ptr_array_unref (item_cleanups); } static void diff --git a/gi/pygi-marshal-cleanup.c b/gi/pygi-marshal-cleanup.c index ddda594..906be58 100644 --- a/gi/pygi-marshal-cleanup.c +++ b/gi/pygi-marshal-cleanup.c @@ -119,15 +119,13 @@ pygi_marshal_cleanup_args_to_py_marshal_success (PyGIInvokeState *state, PyGICallableCache *cache) { GSList *cache_item; - guint i = 0; - /* clean up the return if available */ if (cache->return_cache != NULL) { - PyGIMarshalToPyCleanupFunc cleanup_func = cache->return_cache->to_py_cleanup; + PyGIMarshalCleanupFunc cleanup_func = cache->return_cache->to_py_cleanup; if (cleanup_func && state->return_arg.v_pointer != NULL) cleanup_func (state, cache->return_cache, - state->to_py_return_arg_cleanup_data, + NULL, state->return_arg.v_pointer, TRUE); } @@ -136,24 +134,23 @@ pygi_marshal_cleanup_args_to_py_marshal_success (PyGIInvokeState *state, cache_item = cache->to_py_args; while (cache_item) { PyGIArgCache *arg_cache = (PyGIArgCache *) cache_item->data; - PyGIMarshalToPyCleanupFunc cleanup_func = arg_cache->to_py_cleanup; + PyGIMarshalCleanupFunc cleanup_func = arg_cache->to_py_cleanup; gpointer data = state->args[arg_cache->c_arg_index].arg_value.v_pointer; if (cleanup_func != NULL && data != NULL) cleanup_func (state, arg_cache, - state->args[arg_cache->c_arg_index].to_py_arg_cleanup_data, + NULL, data, TRUE); else if (arg_cache->is_caller_allocates && data != NULL) { _cleanup_caller_allocates (state, arg_cache, - state->args[arg_cache->c_arg_index].to_py_arg_cleanup_data, + NULL, data, TRUE); } - i++; cache_item = cache_item->next; } } diff --git a/gi/pygi-object.c b/gi/pygi-object.c index 80c9055..8fd8ee0 100644 --- a/gi/pygi-object.c +++ b/gi/pygi-object.c @@ -279,8 +279,7 @@ static PyObject * _pygi_marshal_to_py_called_from_c_interface_object_cache_adapter (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data) + GIArgument *arg) { return pygi_arg_gobject_to_py_called_from_c (arg, arg_cache->transfer); } @@ -289,8 +288,7 @@ static PyObject * _pygi_marshal_to_py_called_from_py_interface_object_cache_adapter (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data) + GIArgument *arg) { return pygi_arg_gobject_to_py (arg, arg_cache->transfer); } @@ -298,7 +296,7 @@ _pygi_marshal_to_py_called_from_py_interface_object_cache_adapter (PyGIInvokeSta static void _pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *state, PyGIArgCache *arg_cache, - gpointer cleanup_data, + PyObject *dummy, gpointer data, gboolean was_processed) { diff --git a/gi/pygi-property.c b/gi/pygi-property.c index c4f3e4a..9978585 100644 --- a/gi/pygi-property.c +++ b/gi/pygi-property.c @@ -160,7 +160,6 @@ pygi_get_property_value (PyGObject *instance, GParamSpec *pspec) GITypeInfo *type_info = NULL; gboolean free_array = FALSE; GIArgument arg = { 0, }; - GITransfer transfer = GI_TRANSFER_NOTHING; type_info = g_property_info_get_type (property_info); arg = _pygi_argument_from_g_value (&value, type_info); @@ -169,12 +168,9 @@ pygi_get_property_value (PyGObject *instance, GParamSpec *pspec) if (g_type_info_get_tag (type_info) == GI_TYPE_TAG_ARRAY) { arg.v_pointer = _pygi_argument_to_array (&arg, NULL, NULL, NULL, type_info, &free_array); - } else if (g_type_is_a (pspec->value_type, G_TYPE_BOXED)) { - arg.v_pointer = g_value_dup_boxed (&value); - transfer = GI_TRANSFER_EVERYTHING; } - py_value = _pygi_argument_to_object (&arg, type_info, transfer); + py_value = _pygi_argument_to_object (&arg, type_info, GI_TRANSFER_NOTHING); if (free_array) { g_array_free (arg.v_pointer, FALSE); diff --git a/gi/pygi-source.c b/gi/pygi-source.c index 2ed38d3..5305260 100644 --- a/gi/pygi-source.c +++ b/gi/pygi-source.c @@ -241,8 +241,10 @@ pyg_source_new (void) source = (PyGRealSource*) g_source_new (&pyg_source_funcs, sizeof (PyGRealSource)); py_type = _pygi_type_import_by_name ("GLib", "Source"); - /* g_source_new uses malloc, not slices */ - source->obj = _pygi_boxed_new ( (PyTypeObject *) py_type, source, FALSE, 0); + /* Full ownership transfer of the source, this will be free'd with g_boxed_free. */ + source->obj = _pygi_boxed_new ( (PyTypeObject *) py_type, source, + FALSE, /* copy_boxed */ + 0); /* slice_allocated */ return source->obj; } diff --git a/gi/pygi-struct-marshal.c b/gi/pygi-struct-marshal.c index 44797e7..60d2585 100644 --- a/gi/pygi-struct-marshal.c +++ b/gi/pygi-struct-marshal.c @@ -388,9 +388,11 @@ pygi_arg_struct_to_py_marshal (GIArgument *arg, arg->v_pointer); } else if (g_type_is_a (g_type, G_TYPE_BOXED)) { if (py_type) { + /* Force a boxed copy if we are not transfered ownership and the + * memory is not caller allocated. */ py_obj = _pygi_boxed_new ((PyTypeObject *) py_type, arg->v_pointer, - transfer == GI_TRANSFER_EVERYTHING || is_allocated, + transfer == GI_TRANSFER_NOTHING && !is_allocated, is_allocated ? g_struct_info_get_size(interface_info) : 0); } @@ -435,29 +437,53 @@ static PyObject * arg_struct_to_py_marshal_adapter (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, - GIArgument *arg, - gpointer *cleanup_data) + GIArgument *arg) { PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; - PyObject *ret; - ret = pygi_arg_struct_to_py_marshal (arg, - iface_cache->interface_info, - iface_cache->g_type, - iface_cache->py_type, - arg_cache->transfer, - arg_cache->is_caller_allocates, - iface_cache->is_foreign); + return pygi_arg_struct_to_py_marshal (arg, + iface_cache->interface_info, + iface_cache->g_type, + iface_cache->py_type, + arg_cache->transfer, + arg_cache->is_caller_allocates, + iface_cache->is_foreign); +} + +static PyObject * +arg_boxed_to_py_marshal_pass_by_ref (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; - *cleanup_data = ret; + if (arg->v_pointer == NULL) { + Py_RETURN_NONE; + } + + if (g_type_is_a (iface_cache->g_type, G_TYPE_BOXED)) { + if (iface_cache->py_type) { + py_obj = _pygi_boxed_new ((PyTypeObject *) iface_cache->py_type, + arg->v_pointer, + FALSE, /* copy_boxed */ + 0); /* slice_alloc */ + ((PyGBoxed *)py_obj)->free_on_dealloc = FALSE; + } + } else { + PyErr_Format (PyExc_NotImplementedError, + "expected boxed type but got %s", + g_type_name (iface_cache->g_type)); + } - return ret; + return py_obj; } static void arg_foreign_to_py_cleanup (PyGIInvokeState *state, PyGIArgCache *arg_cache, - gpointer cleanup_data, + PyObject *dummy, gpointer data, gboolean was_processed) { @@ -468,17 +494,6 @@ arg_foreign_to_py_cleanup (PyGIInvokeState *state, } } -static void -arg_boxed_to_py_cleanup (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - gpointer cleanup_data, - gpointer data, - gboolean was_processed) -{ - if (arg_cache->transfer == GI_TRANSFER_NOTHING) - _pygi_boxed_copy_in_place ((PyGIBoxed *) cleanup_data); -} - static gboolean arg_type_class_from_py_marshal (PyGIInvokeState *state, PyGICallableCache *callable_cache, @@ -546,22 +561,54 @@ arg_struct_from_py_setup (PyGIArgCache *arg_cache, static void arg_struct_to_py_setup (PyGIArgCache *arg_cache, GIInterfaceInfo *iface_info, - GITransfer transfer) + GITransfer transfer, + GIArgInfo *arg_info) { PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + /* HACK to force GtkTreeModel:iter_next() and iter_previous() vfunc implementations + * to receive their Gtk.TreeIter argument as pass-by-reference. We create a new + * PyGIBoxed wrapper which does not copy the memory and also does not free it. + * This is needed to hack the noted vfunc implementations so they can continue + * working with bug https://bugzilla.gnome.org/show_bug.cgi?id=722899 + * being fixed. This hack should be removed once GTK+ has fixed bug + * https://bugzilla.gnome.org/show_bug.cgi?id=734465 + * and we've moved to a new major version. + */ + if (arg_info && g_strcmp0 (iface_cache->type_name, "Gtk.TreeIter") == 0) { + + /* GICallbackInfo */ + GIBaseInfo *info = g_base_info_get_container (arg_info); + if (info && g_base_info_get_type (info) == GI_INFO_TYPE_CALLBACK && + (g_strcmp0 (g_base_info_get_name (info), "iter_next") == 0 || + g_strcmp0 (g_base_info_get_name (info), "iter_previous") == 0)) { + + /* GITypeInfo */ + info = g_base_info_get_container (info); + if (info && g_base_info_get_type (info) == GI_INFO_TYPE_TYPE && + g_type_info_get_tag ((GITypeInfo *)info) == GI_TYPE_TAG_INTERFACE) { + + /* GIFieldInfo */ + info = g_base_info_get_container (info); + if (info && g_base_info_get_type (info) == GI_INFO_TYPE_FIELD) { + + /* GIStructInfo */ + info = g_base_info_get_container (info); + if (info && g_base_info_get_type (info) == GI_INFO_TYPE_STRUCT && + g_strcmp0 (g_base_info_get_name (info), "TreeModelIface") == 0) { + arg_cache->to_py_marshaller = arg_boxed_to_py_marshal_pass_by_ref; + } + } + } + } + } + if (arg_cache->to_py_marshaller == NULL) { arg_cache->to_py_marshaller = arg_struct_to_py_marshal_adapter; } - iface_cache->is_foreign = g_struct_info_is_foreign ( (GIStructInfo*)iface_info); - if (iface_cache->is_foreign) arg_cache->to_py_cleanup = arg_foreign_to_py_cleanup; - else if (!g_type_is_a (iface_cache->g_type, G_TYPE_VALUE) && - iface_cache->py_type && - g_type_is_a (iface_cache->g_type, G_TYPE_BOXED)) - arg_cache->to_py_cleanup = arg_boxed_to_py_cleanup; } PyGIArgCache * @@ -591,7 +638,7 @@ pygi_arg_struct_new_from_info (GITypeInfo *type_info, } if (direction & PYGI_DIRECTION_TO_PYTHON) { - arg_struct_to_py_setup (cache, iface_info, transfer); + arg_struct_to_py_setup (cache, iface_info, transfer, arg_info); } return cache; diff --git a/pygtkcompat/pygtkcompat.py b/pygtkcompat/pygtkcompat.py index 6884ef1..299e0d1 100644 --- a/pygtkcompat/pygtkcompat.py +++ b/pygtkcompat/pygtkcompat.py @@ -51,6 +51,24 @@ import gi from gi.repository import GObject +_patches = [] +_module_patches = [] +_unset = object() +_enabled_registry = {} + + +def _patch(obj, name, new_value): + old_value = getattr(obj, name, _unset) + setattr(obj, name, new_value) + _patches.append((obj, name, old_value)) + + +def _patch_module(name, new_value): + old_value = sys.modules.get(name, _unset) + sys.modules[name] = new_value + _module_patches.append((name, old_value)) + + def _install_enums(module, dest=None, strip=''): if dest is None: dest = module @@ -67,7 +85,7 @@ def _install_enums(module, dest=None, strip=''): name = name.replace(modname + '_', '') if strip and name.startswith(strip): name = name[len(strip):] - setattr(dest, name, enum) + _patch(dest, name, enum) except TypeError: continue try: @@ -79,14 +97,11 @@ def _install_enums(module, dest=None, strip=''): # FIXME: this happens for some large flags which do not # fit into a long on 32 bit systems continue - setattr(dest, name, flag) + _patch(dest, name, flag) except TypeError: continue -_enabled_registry = {} - - def _check_enabled(name, version=None): """Returns True in case it is already enabled""" @@ -108,20 +123,46 @@ def enable(): # gobject from gi.repository import GLib - sys.modules['glib'] = GLib + _patch_module('glib', GLib) # gobject from gi.repository import GObject - sys.modules['gobject'] = GObject + _patch_module('gobject', GObject) from gi import _propertyhelper - sys.modules['gobject.propertyhelper'] = _propertyhelper + _patch_module('gobject.propertyhelper', _propertyhelper) # gio from gi.repository import Gio - sys.modules['gio'] = Gio + _patch_module('gio', Gio) -_unset = object() +def _disable_all(): + """Reverse all effects of the enable_xxx() calls except for + require_version() calls and imports. + """ + + _enabled_registry.clear() + + for obj, name, old_value in reversed(_patches): + if old_value is _unset: + delattr(obj, name) + else: + # try if deleting is enough (for override proxies) + delattr(obj, name) + if getattr(obj, name, _unset) is not old_value: + setattr(obj, name, old_value) + del _patches[:] + + for name, old_value in reversed(_module_patches): + if old_value is _unset: + del sys.modules[name] + else: + sys.modules[name] = old_value + del _module_patches[:] + + reload(sys) + if sys.version_info < (3, 0): + sys.setdefaultencoding('ascii') def enable_gtk(version='3.0'): @@ -139,46 +180,46 @@ def enable_gtk(version='3.0'): # atk gi.require_version('Atk', '1.0') from gi.repository import Atk - sys.modules['atk'] = Atk + _patch_module('atk', Atk) _install_enums(Atk) # pango gi.require_version('Pango', '1.0') from gi.repository import Pango - sys.modules['pango'] = Pango + _patch_module('pango', Pango) _install_enums(Pango) # pangocairo gi.require_version('PangoCairo', '1.0') from gi.repository import PangoCairo - sys.modules['pangocairo'] = PangoCairo + _patch_module('pangocairo', PangoCairo) # gdk gi.require_version('Gdk', version) gi.require_version('GdkPixbuf', '2.0') from gi.repository import Gdk from gi.repository import GdkPixbuf - sys.modules['gtk.gdk'] = Gdk + _patch_module('gtk.gdk', Gdk) _install_enums(Gdk) _install_enums(GdkPixbuf, dest=Gdk) - Gdk._2BUTTON_PRESS = 5 - Gdk.BUTTON_PRESS = 4 - - Gdk.screen_get_default = Gdk.Screen.get_default - Gdk.Pixbuf = GdkPixbuf.Pixbuf - Gdk.PixbufLoader = GdkPixbuf.PixbufLoader.new_with_type - Gdk.pixbuf_new_from_data = GdkPixbuf.Pixbuf.new_from_data - Gdk.pixbuf_new_from_file = GdkPixbuf.Pixbuf.new_from_file + _patch(Gdk, "_2BUTTON_PRESS", 5) + _patch(Gdk, "BUTTON_PRESS", 4) + + _patch(Gdk, "screen_get_default", Gdk.Screen.get_default) + _patch(Gdk, "Pixbuf", GdkPixbuf.Pixbuf) + _patch(Gdk, "PixbufLoader", GdkPixbuf.PixbufLoader.new_with_type) + _patch(Gdk, "pixbuf_new_from_data", GdkPixbuf.Pixbuf.new_from_data) + _patch(Gdk, "pixbuf_new_from_file", GdkPixbuf.Pixbuf.new_from_file) try: - Gdk.pixbuf_new_from_file_at_scale = GdkPixbuf.Pixbuf.new_from_file_at_scale + _patch(Gdk, "pixbuf_new_from_file_at_scale", GdkPixbuf.Pixbuf.new_from_file_at_scale) except AttributeError: pass - Gdk.pixbuf_new_from_file_at_size = GdkPixbuf.Pixbuf.new_from_file_at_size - Gdk.pixbuf_new_from_inline = GdkPixbuf.Pixbuf.new_from_inline - Gdk.pixbuf_new_from_stream = GdkPixbuf.Pixbuf.new_from_stream - Gdk.pixbuf_new_from_stream_at_scale = GdkPixbuf.Pixbuf.new_from_stream_at_scale - Gdk.pixbuf_new_from_xpm_data = GdkPixbuf.Pixbuf.new_from_xpm_data - Gdk.pixbuf_get_file_info = GdkPixbuf.Pixbuf.get_file_info + _patch(Gdk, "pixbuf_new_from_file_at_size", GdkPixbuf.Pixbuf.new_from_file_at_size) + _patch(Gdk, "pixbuf_new_from_inline", GdkPixbuf.Pixbuf.new_from_inline) + _patch(Gdk, "pixbuf_new_from_stream", GdkPixbuf.Pixbuf.new_from_stream) + _patch(Gdk, "pixbuf_new_from_stream_at_scale", GdkPixbuf.Pixbuf.new_from_stream_at_scale) + _patch(Gdk, "pixbuf_new_from_xpm_data", GdkPixbuf.Pixbuf.new_from_xpm_data) + _patch(Gdk, "pixbuf_get_file_info", GdkPixbuf.Pixbuf.get_file_info) orig_get_formats = GdkPixbuf.Pixbuf.get_formats @@ -198,7 +239,7 @@ def enable_gtk(version='3.0'): result.append(make_dict(format_)) return result - Gdk.pixbuf_get_formats = get_formats + _patch(Gdk, "pixbuf_get_formats", get_formats) orig_get_frame_extents = Gdk.Window.get_frame_extents @@ -212,34 +253,34 @@ def enable_gtk(version='3.0'): except TypeError: rect = orig_get_frame_extents(window) return rect - Gdk.Window.get_frame_extents = get_frame_extents + _patch(Gdk.Window, "get_frame_extents", get_frame_extents) orig_get_origin = Gdk.Window.get_origin def get_origin(self): return orig_get_origin(self)[1:] - Gdk.Window.get_origin = get_origin + _patch(Gdk.Window, "get_origin", get_origin) - Gdk.screen_width = Gdk.Screen.width - Gdk.screen_height = Gdk.Screen.height + _patch(Gdk, "screen_width", Gdk.Screen.width) + _patch(Gdk, "screen_height", Gdk.Screen.height) orig_gdk_window_get_geometry = Gdk.Window.get_geometry def gdk_window_get_geometry(window): return orig_gdk_window_get_geometry(window) + (window.get_visual().get_best_depth(),) - Gdk.Window.get_geometry = gdk_window_get_geometry + _patch(Gdk.Window, "get_geometry", gdk_window_get_geometry) # gtk gi.require_version('Gtk', version) from gi.repository import Gtk - sys.modules['gtk'] = Gtk - Gtk.gdk = Gdk + _patch_module('gtk', Gtk) + _patch(Gtk, "gdk", Gdk) - Gtk.pygtk_version = (2, 99, 0) + _patch(Gtk, "pygtk_version", (2, 99, 0)) - Gtk.gtk_version = (Gtk.MAJOR_VERSION, - Gtk.MINOR_VERSION, - Gtk.MICRO_VERSION) + _patch(Gtk, "gtk_version", (Gtk.MAJOR_VERSION, + Gtk.MINOR_VERSION, + Gtk.MICRO_VERSION)) _install_enums(Gtk) # Action @@ -247,7 +288,7 @@ def enable_gtk(version='3.0'): def set_tool_item_type(menuaction, gtype): warnings.warn('set_tool_item_type() is not supported', gi.PyGIDeprecationWarning, stacklevel=2) - Gtk.Action.set_tool_item_type = classmethod(set_tool_item_type) + _patch(Gtk.Action, "set_tool_item_type", classmethod(set_tool_item_type)) # Alignment @@ -261,7 +302,7 @@ def enable_gtk(version='3.0'): self.props.xscale = xscale self.props.yscale = yscale - Gtk.Alignment = Alignment + _patch(Gtk, "Alignment", Alignment) # Box @@ -269,13 +310,13 @@ def enable_gtk(version='3.0'): def pack_end(self, child, expand=True, fill=True, padding=0): orig_pack_end(self, child, expand, fill, padding) - Gtk.Box.pack_end = pack_end + _patch(Gtk.Box, "pack_end", pack_end) orig_pack_start = Gtk.Box.pack_start def pack_start(self, child, expand=True, fill=True, padding=0): orig_pack_start(self, child, expand, fill, padding) - Gtk.Box.pack_start = pack_start + _patch(Gtk.Box, "pack_start", pack_start) # TreeViewColumn @@ -283,13 +324,13 @@ def enable_gtk(version='3.0'): def tree_view_column_pack_end(self, cell, expand=True): orig_tree_view_column_pack_end(self, cell, expand) - Gtk.TreeViewColumn.pack_end = tree_view_column_pack_end + _patch(Gtk.TreeViewColumn, "pack_end", tree_view_column_pack_end) orig_tree_view_column_pack_start = Gtk.TreeViewColumn.pack_start def tree_view_column_pack_start(self, cell, expand=True): orig_tree_view_column_pack_start(self, cell, expand) - Gtk.TreeViewColumn.pack_start = tree_view_column_pack_start + _patch(Gtk.TreeViewColumn, "pack_start", tree_view_column_pack_start) # CellLayout @@ -297,13 +338,13 @@ def enable_gtk(version='3.0'): def cell_pack_end(self, cell, expand=True): orig_cell_pack_end(self, cell, expand) - Gtk.CellLayout.pack_end = cell_pack_end + _patch(Gtk.CellLayout, "pack_end", cell_pack_end) orig_cell_pack_start = Gtk.CellLayout.pack_start def cell_pack_start(self, cell, expand=True): orig_cell_pack_start(self, cell, expand) - Gtk.CellLayout.pack_start = cell_pack_start + _patch(Gtk.CellLayout, "pack_start", cell_pack_start) orig_set_cell_data_func = Gtk.CellLayout.set_cell_data_func @@ -313,13 +354,13 @@ def enable_gtk(version='3.0'): args = args[:-1] return func(*args) orig_set_cell_data_func(self, cell, callback, user_data) - Gtk.CellLayout.set_cell_data_func = set_cell_data_func + _patch(Gtk.CellLayout, "set_cell_data_func", set_cell_data_func) # CellRenderer class GenericCellRenderer(Gtk.CellRenderer): pass - Gtk.GenericCellRenderer = GenericCellRenderer + _patch(Gtk, "GenericCellRenderer", GenericCellRenderer) # ComboBox @@ -331,7 +372,7 @@ def enable_gtk(version='3.0'): args = args[:-1] return func(*args) orig_combo_row_separator_func(self, callback, user_data) - Gtk.ComboBox.set_row_separator_func = combo_row_separator_func + _patch(Gtk.ComboBox, "set_row_separator_func", combo_row_separator_func) # ComboBoxEntry @@ -344,22 +385,22 @@ def enable_gtk(version='3.0'): def get_text_column(self): return self.get_entry_text_column() - Gtk.ComboBoxEntry = ComboBoxEntry + _patch(Gtk, "ComboBoxEntry", ComboBoxEntry) def combo_box_entry_new(): return Gtk.ComboBoxEntry() - Gtk.combo_box_entry_new = combo_box_entry_new + _patch(Gtk, "combo_box_entry_new", combo_box_entry_new) def combo_box_entry_new_with_model(model): return Gtk.ComboBoxEntry(model=model) - Gtk.combo_box_entry_new_with_model = combo_box_entry_new_with_model + _patch(Gtk, "combo_box_entry_new_with_model", combo_box_entry_new_with_model) # Container def install_child_property(container, flag, pspec): warnings.warn('install_child_property() is not supported', gi.PyGIDeprecationWarning, stacklevel=2) - Gtk.Container.install_child_property = classmethod(install_child_property) + _patch(Gtk.Container, "install_child_property", classmethod(install_child_property)) def new_text(): combo = Gtk.ComboBox() @@ -367,32 +408,32 @@ def enable_gtk(version='3.0'): combo.set_model(model) combo.set_entry_text_column(0) return combo - Gtk.combo_box_new_text = new_text + _patch(Gtk, "combo_box_new_text", new_text) def append_text(self, text): model = self.get_model() model.append([text]) - Gtk.ComboBox.append_text = append_text - Gtk.expander_new_with_mnemonic = Gtk.Expander.new_with_mnemonic - Gtk.icon_theme_get_default = Gtk.IconTheme.get_default - Gtk.image_new_from_pixbuf = Gtk.Image.new_from_pixbuf - Gtk.image_new_from_stock = Gtk.Image.new_from_stock - Gtk.image_new_from_animation = Gtk.Image.new_from_animation - Gtk.image_new_from_icon_set = Gtk.Image.new_from_icon_set - Gtk.image_new_from_file = Gtk.Image.new_from_file - Gtk.settings_get_default = Gtk.Settings.get_default - Gtk.window_set_default_icon = Gtk.Window.set_default_icon + _patch(Gtk.ComboBox, "append_text", append_text) + _patch(Gtk, "expander_new_with_mnemonic", Gtk.Expander.new_with_mnemonic) + _patch(Gtk, "icon_theme_get_default", Gtk.IconTheme.get_default) + _patch(Gtk, "image_new_from_pixbuf", Gtk.Image.new_from_pixbuf) + _patch(Gtk, "image_new_from_stock", Gtk.Image.new_from_stock) + _patch(Gtk, "image_new_from_animation", Gtk.Image.new_from_animation) + _patch(Gtk, "image_new_from_icon_set", Gtk.Image.new_from_icon_set) + _patch(Gtk, "image_new_from_file", Gtk.Image.new_from_file) + _patch(Gtk, "settings_get_default", Gtk.Settings.get_default) + _patch(Gtk, "window_set_default_icon", Gtk.Window.set_default_icon) try: - Gtk.clipboard_get = Gtk.Clipboard.get + _patch(Gtk, "clipboard_get", Gtk.Clipboard.get) except AttributeError: pass # AccelGroup - Gtk.AccelGroup.connect_group = Gtk.AccelGroup.connect + _patch(Gtk.AccelGroup, "connect_group", Gtk.AccelGroup.connect) # StatusIcon - Gtk.status_icon_position_menu = Gtk.StatusIcon.position_menu - Gtk.StatusIcon.set_tooltip = Gtk.StatusIcon.set_tooltip_text + _patch(Gtk, "status_icon_position_menu", Gtk.StatusIcon.position_menu) + _patch(Gtk.StatusIcon, "set_tooltip", Gtk.StatusIcon.set_tooltip_text) # Scale @@ -402,20 +443,20 @@ def enable_gtk(version='3.0'): class HScale(orig_HScale): def __init__(self, adjustment=None): orig_HScale.__init__(self, adjustment=adjustment) - Gtk.HScale = HScale + _patch(Gtk, "HScale", HScale) class VScale(orig_VScale): def __init__(self, adjustment=None): orig_VScale.__init__(self, adjustment=adjustment) - Gtk.VScale = VScale + _patch(Gtk, "VScale", VScale) - Gtk.stock_add = lambda items: None + _patch(Gtk, "stock_add", lambda items: None) # Widget - Gtk.Widget.window = property(fget=Gtk.Widget.get_window) + _patch(Gtk.Widget, "window", property(fget=Gtk.Widget.get_window)) - Gtk.widget_get_default_direction = Gtk.Widget.get_default_direction + _patch(Gtk, "widget_get_default_direction", Gtk.Widget.get_default_direction) orig_size_request = Gtk.Widget.size_request def size_request(widget): @@ -425,8 +466,8 @@ def enable_gtk(version='3.0'): self.width = req.width UserList.__init__(self, [self.width, self.height]) return SizeRequest(orig_size_request(widget)) - Gtk.Widget.size_request = size_request - Gtk.Widget.hide_all = Gtk.Widget.hide + _patch(Gtk.Widget, "size_request", size_request) + _patch(Gtk.Widget, "hide_all", Gtk.Widget.hide) class BaseGetter(object): def __init__(self, context): @@ -447,7 +488,7 @@ def enable_gtk(version='3.0'): class StyleDescriptor(object): def __get__(self, instance, class_): return Styles(instance) - Gtk.Widget.style = StyleDescriptor() + _patch(Gtk.Widget, "style", StyleDescriptor()) # TextView @@ -457,7 +498,7 @@ def enable_gtk(version='3.0'): use_align=False, xalign=0.5, yalign=0.5): return orig_text_view_scroll_to_mark(self, mark, within_margin, use_align, xalign, yalign) - Gtk.TextView.scroll_to_mark = text_view_scroll_to_mark + _patch(Gtk.TextView, "scroll_to_mark", text_view_scroll_to_mark) # Window @@ -501,16 +542,16 @@ def enable_gtk(version='3.0'): return orig_set_geometry_hints(self, geometry_widget, geometry, geom_mask) - Gtk.Window.set_geometry_hints = set_geometry_hints - Gtk.window_list_toplevels = Gtk.Window.list_toplevels - Gtk.window_set_default_icon_name = Gtk.Window.set_default_icon_name + _patch(Gtk.Window, "set_geometry_hints", set_geometry_hints) + _patch(Gtk, "window_list_toplevels", Gtk.Window.list_toplevels) + _patch(Gtk, "window_set_default_icon_name", Gtk.Window.set_default_icon_name) # gtk.unixprint class UnixPrint(object): pass unixprint = UnixPrint() - sys.modules['gtkunixprint'] = unixprint + _patch_module('gtkunixprint', unixprint) # gtk.keysyms @@ -518,11 +559,11 @@ def enable_gtk(version='3.0'): warnings.simplefilter('ignore', category=RuntimeWarning) from gi.overrides import keysyms - sys.modules['gtk.keysyms'] = keysyms - Gtk.keysyms = keysyms + _patch_module('gtk.keysyms', keysyms) + _patch(Gtk, "keysyms", keysyms) from . import generictreemodel - Gtk.GenericTreeModel = generictreemodel.GenericTreeModel + _patch(Gtk, "GenericTreeModel", generictreemodel.GenericTreeModel) def enable_vte(): @@ -531,7 +572,7 @@ def enable_vte(): gi.require_version('Vte', '0.0') from gi.repository import Vte - sys.modules['vte'] = Vte + _patch_module('vte', Vte) def enable_poppler(): @@ -540,8 +581,9 @@ def enable_poppler(): gi.require_version('Poppler', '0.18') from gi.repository import Poppler - sys.modules['poppler'] = Poppler - Poppler.pypoppler_version = (1, 0, 0) + _patch_module('poppler', Poppler) + + _patch(Poppler, "pypoppler_version", (1, 0, 0)) def enable_webkit(version='1.0'): @@ -550,8 +592,9 @@ def enable_webkit(version='1.0'): gi.require_version('WebKit', version) from gi.repository import WebKit - sys.modules['webkit'] = WebKit - WebKit.WebView.get_web_inspector = WebKit.WebView.get_inspector + _patch_module('webkit', WebKit) + + _patch(WebKit.WebView, "get_web_inspector", WebKit.WebView.get_inspector) def enable_gudev(): @@ -560,7 +603,7 @@ def enable_gudev(): gi.require_version('GUdev', '1.0') from gi.repository import GUdev - sys.modules['gudev'] = GUdev + _patch_module('gudev', GUdev) def enable_gst(): @@ -569,40 +612,41 @@ def enable_gst(): gi.require_version('Gst', '0.10') from gi.repository import Gst - sys.modules['gst'] = Gst + _patch_module('gst', Gst) _install_enums(Gst) - Gst.registry_get_default = Gst.Registry.get_default - Gst.element_register = Gst.Element.register - Gst.element_factory_make = Gst.ElementFactory.make - Gst.caps_new_any = Gst.Caps.new_any - Gst.get_pygst_version = lambda: (0, 10, 19) - Gst.get_gst_version = lambda: (0, 10, 40) + + _patch(Gst, "registry_get_default", Gst.Registry.get_default) + _patch(Gst, "element_register", Gst.Element.register) + _patch(Gst, "element_factory_make", Gst.ElementFactory.make) + _patch(Gst, "caps_new_any", Gst.Caps.new_any) + _patch(Gst, "get_pygst_version", lambda: (0, 10, 19)) + _patch(Gst, "get_gst_version", lambda: (0, 10, 40)) from gi.repository import GstInterfaces - sys.modules['gst.interfaces'] = GstInterfaces + _patch_module('gst.interfaces', GstInterfaces) _install_enums(GstInterfaces) from gi.repository import GstAudio - sys.modules['gst.audio'] = GstAudio + _patch_module('gst.audio', GstAudio) _install_enums(GstAudio) from gi.repository import GstVideo - sys.modules['gst.video'] = GstVideo + _patch_module('gst.video', GstVideo) _install_enums(GstVideo) from gi.repository import GstBase - sys.modules['gst.base'] = GstBase + _patch_module('gst.base', GstBase) _install_enums(GstBase) - Gst.BaseTransform = GstBase.BaseTransform - Gst.BaseSink = GstBase.BaseSink + _patch(Gst, "BaseTransform", GstBase.BaseTransform) + _patch(Gst, "BaseSink", GstBase.BaseSink) from gi.repository import GstController - sys.modules['gst.controller'] = GstController + _patch_module('gst.controller', GstController) _install_enums(GstController, dest=Gst) from gi.repository import GstPbutils - sys.modules['gst.pbutils'] = GstPbutils + _patch_module('gst.pbutils', GstPbutils) _install_enums(GstPbutils) @@ -612,10 +656,11 @@ def enable_goocanvas(): gi.require_version('GooCanvas', '2.0') from gi.repository import GooCanvas - sys.modules['goocanvas'] = GooCanvas + _patch_module('goocanvas', GooCanvas) _install_enums(GooCanvas, strip='GOO_CANVAS_') - GooCanvas.ItemSimple = GooCanvas.CanvasItemSimple - GooCanvas.Item = GooCanvas.CanvasItem - GooCanvas.Image = GooCanvas.CanvasImage - GooCanvas.Group = GooCanvas.CanvasGroup - GooCanvas.Rect = GooCanvas.CanvasRect + + _patch(GooCanvas, "ItemSimple", GooCanvas.CanvasItemSimple) + _patch(GooCanvas, "Item", GooCanvas.CanvasItem) + _patch(GooCanvas, "Image", GooCanvas.CanvasImage) + _patch(GooCanvas, "Group", GooCanvas.CanvasGroup) + _patch(GooCanvas, "Rect", GooCanvas.CanvasRect) diff --git a/setup.py b/setup.py index c76bc9f..8635dbc 100755 --- a/setup.py +++ b/setup.py @@ -102,18 +102,23 @@ def parse_pkg_info(conf_dir): return message -def _run_pkg_config(args): - command = ["pkg-config"] + args +def _run_pkg_config(args, _cache={}): + command = tuple(["pkg-config"] + args) - try: - return subprocess.check_output(command) - except OSError as e: - if e.errno == errno.ENOENT: - raise SystemExit( - "%r not found.\nArguments: %r" % (command[0], command)) - raise SystemExit(e) - except subprocess.CalledProcessError as e: - raise SystemExit(e) + if command not in _cache: + try: + result = subprocess.check_output(command) + except OSError as e: + if e.errno == errno.ENOENT: + raise SystemExit( + "%r not found.\nArguments: %r" % (command[0], command)) + raise SystemExit(e) + except subprocess.CalledProcessError as e: + raise SystemExit(e) + else: + _cache[command] = result + + return _cache[command] def pkg_config_version_check(pkg, version): @@ -237,6 +242,11 @@ class build_tests(Command): return True def run(self): + cmd = self.reinitialize_command("build_ext") + cmd.inplace = True + cmd.ensure_finalized() + cmd.run() + from distutils.ccompiler import new_compiler from distutils.sysconfig import customize_compiler @@ -440,11 +450,6 @@ class test(Command): pass def run(self): - cmd = self.reinitialize_command("build_ext") - cmd.inplace = True - cmd.ensure_finalized() - cmd.run() - cmd = self.reinitialize_command("build_tests") cmd.ensure_finalized() cmd.run() @@ -452,18 +457,15 @@ class test(Command): env = os.environ.copy() env.pop("MSYSTEM", None) + env["MALLOC_PERTURB_"] = "85" + env["MALLOC_CHECK_"] = "3" + env["G_SLICE"] = "debug-blocks" + tests_dir = os.path.join(get_script_dir(), "tests") - subprocess.check_call([ + sys.exit(subprocess.call([ sys.executable, os.path.join(tests_dir, "runtests.py"), - ], env=env) - - if not env.get("TEST_NAMES"): - env["TEST_NAMES"] = "compat_test_pygtk" - subprocess.check_call([ - sys.executable, - os.path.join(tests_dir, "runtests.py"), - ], env=env) + ], env=env)) class quality(Command): diff --git a/tests/Makefile.am b/tests/Makefile.am index 6c0cedf..216aca5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -127,6 +127,7 @@ EXTRA_DIST = \ test_object_marshaling.py \ test_option.py \ test_properties.py \ + test_pygtkcompat.py \ test_signal.py \ test_source.py \ test_subprocess.py \ @@ -145,15 +146,19 @@ EXTRA_DIST = \ test_docstring.py \ test_repository.py \ test_resulttuple.py \ + test_unknown.py \ test_ossig.py \ - compat_test_pygtk.py \ + __init__.py \ gi/__init__.py \ gi/overrides/__init__.py \ gi/overrides/Regress.py \ $(NULL) clean-local: - rm -f $(target_libraries) file.txt~ + rm -f $(target_libraries) file.txt~; + @if [ "$(abs_builddir)" != "$(abs_srcdir)" ]; then \ + rm -f __init__.py; \ + fi; # Unsetting MSYSTEM prevents msys2 from changing os.path.sep to "/" RUN_TESTS_ENV_VARS= \ @@ -163,12 +168,14 @@ RUN_TESTS_ENV_VARS= \ MSYSTEM= \ TESTS_BUILDDIR=$(builddir) +# for non-srcdir builds +$(abs_builddir)/__init__.py: $(abs_srcdir)/__init__.py + echo "__path__ = __import__('pkgutil').extend_path(__path__, __name__)" > $@ + # pygtkcompat tests need to be run in a separate process as they # clobber global name space -check-local: $(target_libraries) $(test_typelibs) gschemas.compiled - $(RUN_TESTS_ENV_VARS) $(EXTRA_ENV) $(EXEC_NAME) $(PYTHON) -Wd $(srcdir)/runtests.py; rc=$$?; \ - [ "$$rc" -ne 0 ] || [ -n "$$TEST_NAMES" ] || { TEST_NAMES=compat_test_pygtk $(RUN_TESTS_ENV_VARS) $(EXTRA_ENV) $(EXEC_NAME) $(PYTHON) -Wd $(srcdir)/runtests.py; rc=$$?; }; \ - exit $$rc +check-local: $(target_libraries) $(test_typelibs) gschemas.compiled $(abs_builddir)/__init__.py + $(RUN_TESTS_ENV_VARS) $(EXTRA_ENV) $(EXEC_NAME) $(PYTHON) $(srcdir)/runtests.py; check.gdb: EXEC_NAME="gdb --args" $(MAKE) check diff --git a/tests/Makefile.in b/tests/Makefile.in index d0b51c7..8230e02 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -477,6 +477,7 @@ EXTRA_DIST = \ test_object_marshaling.py \ test_option.py \ test_properties.py \ + test_pygtkcompat.py \ test_signal.py \ test_source.py \ test_subprocess.py \ @@ -495,8 +496,9 @@ EXTRA_DIST = \ test_docstring.py \ test_repository.py \ test_resulttuple.py \ + test_unknown.py \ test_ossig.py \ - compat_test_pygtk.py \ + __init__.py \ gi/__init__.py \ gi/overrides/__init__.py \ gi/overrides/Regress.py \ @@ -905,14 +907,19 @@ $(target_libraries): test -L $@ || $(LN_S) .libs/$@ $@ clean-local: - rm -f $(target_libraries) file.txt~ + rm -f $(target_libraries) file.txt~; + @if [ "$(abs_builddir)" != "$(abs_srcdir)" ]; then \ + rm -f __init__.py; \ + fi; + +# for non-srcdir builds +$(abs_builddir)/__init__.py: $(abs_srcdir)/__init__.py + echo "__path__ = __import__('pkgutil').extend_path(__path__, __name__)" > $@ # pygtkcompat tests need to be run in a separate process as they # clobber global name space -check-local: $(target_libraries) $(test_typelibs) gschemas.compiled - $(RUN_TESTS_ENV_VARS) $(EXTRA_ENV) $(EXEC_NAME) $(PYTHON) -Wd $(srcdir)/runtests.py; rc=$$?; \ - [ "$$rc" -ne 0 ] || [ -n "$$TEST_NAMES" ] || { TEST_NAMES=compat_test_pygtk $(RUN_TESTS_ENV_VARS) $(EXTRA_ENV) $(EXEC_NAME) $(PYTHON) -Wd $(srcdir)/runtests.py; rc=$$?; }; \ - exit $$rc +check-local: $(target_libraries) $(test_typelibs) gschemas.compiled $(abs_builddir)/__init__.py + $(RUN_TESTS_ENV_VARS) $(EXTRA_ENV) $(EXEC_NAME) $(PYTHON) $(srcdir)/runtests.py; check.gdb: EXEC_NAME="gdb --args" $(MAKE) check diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..4d9b383 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,102 @@ +from __future__ import absolute_import + +import os +import sys +import unittest +import signal +import subprocess +import atexit + + +def init_test_environ(): + # this was renamed in Python 3, provide backwards compatible name + if sys.version_info[:2] == (2, 7): + unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp + + if sys.version_info[0] == 3: + unittest.TestCase.assertRegexpMatches = unittest.TestCase.assertRegex + unittest.TestCase.assertRaisesRegexp = unittest.TestCase.assertRaisesRegex + + def dbus_launch_session(): + if os.name == "nt" or sys.platform == "darwin": + return (-1, "") + + try: + out = subprocess.check_output([ + "dbus-daemon", "--session", "--fork", "--print-address=1", + "--print-pid=1"]) + except (subprocess.CalledProcessError, OSError): + return (-1, "") + else: + if sys.version_info[0] == 3: + out = out.decode("utf-8") + addr, pid = out.splitlines() + return int(pid), addr + + pid, addr = dbus_launch_session() + if pid >= 0: + os.environ["DBUS_SESSION_BUS_ADDRESS"] = addr + atexit.register(os.kill, pid, signal.SIGKILL) + else: + os.environ["DBUS_SESSION_BUS_ADDRESS"] = "." + + tests_builddir = os.path.abspath(os.environ.get('TESTS_BUILDDIR', os.path.dirname(__file__))) + builddir = os.path.dirname(tests_builddir) + tests_srcdir = os.path.abspath(os.path.dirname(__file__)) + srcdir = os.path.dirname(tests_srcdir) + + sys.path.insert(0, tests_srcdir) + sys.path.insert(0, srcdir) + sys.path.insert(0, tests_builddir) + sys.path.insert(0, builddir) + + # force untranslated messages, as we check for them in some tests + os.environ['LC_MESSAGES'] = 'C' + os.environ['G_DEBUG'] = 'fatal-warnings fatal-criticals' + if sys.platform == "darwin": + # gtk 3.22 has warnings and ciriticals on OS X, ignore for now + os.environ['G_DEBUG'] = '' + + # make Gio able to find our gschemas.compiled in tests/. This needs to be set + # before importing Gio. Support a separate build tree, so look in build dir + # first. + os.environ['GSETTINGS_BACKEND'] = 'memory' + os.environ['GSETTINGS_SCHEMA_DIR'] = tests_builddir + os.environ['G_FILENAME_ENCODING'] = 'UTF-8' + + import gi + gi.require_version("GIRepository", "2.0") + from gi.repository import GIRepository + repo = GIRepository.Repository.get_default() + repo.prepend_library_path(os.path.join(tests_builddir)) + repo.prepend_library_path(os.path.join(tests_builddir, ".libs")) + repo.prepend_search_path(tests_builddir) + + def try_require_version(namespace, version): + try: + gi.require_version(namespace, version) + except ValueError: + # prevent tests from running with the wrong version + sys.modules["gi.repository." + namespace] = None + + # Optional + try_require_version("Gtk", os.environ.get("TEST_GTK_VERSION", "3.0")) + try_require_version("Gdk", os.environ.get("TEST_GTK_VERSION", "3.0")) + try_require_version("GdkPixbuf", "2.0") + try_require_version("Pango", "1.0") + try_require_version("PangoCairo", "1.0") + try_require_version("Atk", "1.0") + + # Required + gi.require_versions({ + "GIMarshallingTests": "1.0", + "Regress": "1.0", + "GLib": "2.0", + "Gio": "2.0", + "GObject": "2.0", + }) + + +init_test_environ() + +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/tests/helper.py b/tests/helper.py index 0d2f204..b683a4a 100644 --- a/tests/helper.py +++ b/tests/helper.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + import contextlib import unittest import inspect @@ -10,7 +12,7 @@ import gi from gi import PyGIDeprecationWarning from gi.repository import GLib -from compathelper import StringIO +from .compathelper import StringIO ExceptionInfo = namedtuple("ExceptionInfo", ["type", "value", "traceback"]) diff --git a/tests/runtests.py b/tests/runtests.py index 457553f..652dc99 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -1,134 +1,41 @@ #!/usr/bin/env python # -*- Mode: Python -*- +from __future__ import absolute_import + import os -import glob import sys -import signal -import unittest -import subprocess -import atexit import pytest -# this was renamed in Python 3, provide backwards compatible name -if sys.version_info[:2] == (2, 7): - unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp - -if sys.version_info[0] == 3: - unittest.TestCase.assertRegexpMatches = unittest.TestCase.assertRegex - unittest.TestCase.assertRaisesRegexp = unittest.TestCase.assertRaisesRegex - -if '--help' in sys.argv: - print("Usage: ./runtests.py ") - sys.exit(0) +def main(argv): + if '--help' in argv: + print("Usage: ./runtests.py ") + return + mydir = os.path.dirname(os.path.abspath(__file__)) -def dbus_launch_session(): - if os.name == "nt" or sys.platform == "darwin": - return (-1, "") - - try: - out = subprocess.check_output([ - "dbus-daemon", "--session", "--fork", "--print-address=1", - "--print-pid=1"]) - except (subprocess.CalledProcessError, OSError): - return (-1, "") + if 'TEST_NAMES' in os.environ: + names = os.environ['TEST_NAMES'].split() + elif 'TEST_FILES' in os.environ: + names = [] + for filename in os.environ['TEST_FILES'].split(): + names.append(filename[:-3]) + elif len(argv) > 1: + names = [] + for filename in argv[1:]: + names.append(filename.replace('.py', '')) else: - if sys.version_info[0] == 3: - out = out.decode("utf-8") - addr, pid = out.splitlines() - return int(pid), addr - - -pid, addr = dbus_launch_session() -if pid >= 0: - os.environ["DBUS_SESSION_BUS_ADDRESS"] = addr - atexit.register(os.kill, pid, signal.SIGKILL) -else: - os.environ["DBUS_SESSION_BUS_ADDRESS"] = "." - -mydir = os.path.dirname(os.path.abspath(__file__)) -tests_builddir = os.path.abspath(os.environ.get('TESTS_BUILDDIR', os.path.dirname(__file__))) -builddir = os.path.dirname(tests_builddir) -tests_srcdir = os.path.abspath(os.path.dirname(__file__)) -srcdir = os.path.dirname(tests_srcdir) - -sys.path.insert(0, tests_srcdir) -sys.path.insert(0, srcdir) -sys.path.insert(0, tests_builddir) -sys.path.insert(0, builddir) - -# force untranslated messages, as we check for them in some tests -os.environ['LC_MESSAGES'] = 'C' -os.environ['G_DEBUG'] = 'fatal-warnings fatal-criticals' -if sys.platform == "darwin": - # gtk 3.22 has warnings and ciriticals on OS X, ignore for now - os.environ['G_DEBUG'] = '' - -# make Gio able to find our gschemas.compiled in tests/. This needs to be set -# before importing Gio. Support a separate build tree, so look in build dir -# first. -os.environ['GSETTINGS_BACKEND'] = 'memory' -os.environ['GSETTINGS_SCHEMA_DIR'] = tests_builddir -os.environ['G_FILENAME_ENCODING'] = 'UTF-8' - -import gi -gi.require_version("GIRepository", "2.0") -from gi.repository import GIRepository -repo = GIRepository.Repository.get_default() -repo.prepend_library_path(os.path.join(tests_builddir)) -repo.prepend_library_path(os.path.join(tests_builddir, ".libs")) -repo.prepend_search_path(tests_builddir) - - -def try_require_version(namespace, version): - try: - gi.require_version(namespace, version) - except ValueError: - # prevent tests from running with the wrong version - sys.modules["gi.repository." + namespace] = None - - -# Optional -try_require_version("Gtk", os.environ.get("TEST_GTK_VERSION", "3.0")) -try_require_version("Gdk", os.environ.get("TEST_GTK_VERSION", "3.0")) -try_require_version("GdkPixbuf", "2.0") -try_require_version("Pango", "1.0") -try_require_version("PangoCairo", "1.0") -try_require_version("Atk", "1.0") - -# Required -gi.require_versions({ - "GIMarshallingTests": "1.0", - "Regress": "1.0", - "GLib": "2.0", - "Gio": "2.0", - "GObject": "2.0", -}) - -# Load tests. -if 'TEST_NAMES' in os.environ: - names = os.environ['TEST_NAMES'].split() -elif 'TEST_FILES' in os.environ: - names = [] - for filename in os.environ['TEST_FILES'].split(): - names.append(filename[:-3]) -elif len(sys.argv) > 1: - names = [] - for filename in sys.argv[1:]: - names.append(filename.replace('.py', '')) -else: - names = [] - for filename in glob.iglob(os.path.join(mydir, 'test_*.py')): - names.append(os.path.basename(filename)[:-3]) + return pytest.main([mydir]) + def unittest_to_pytest_name(name): + parts = name.split(".") + parts[0] = os.path.join(mydir, parts[0] + ".py") + return "::".join(parts) -def unittest_to_pytest_name(name): - parts = name.split(".") - parts[0] = os.path.join(mydir, parts[0] + ".py") - return "::".join(parts) + return pytest.main([unittest_to_pytest_name(n) for n in names]) -sys.exit(pytest.main([unittest_to_pytest_name(n) for n in names])) +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/tests/test_atoms.py b/tests/test_atoms.py index a2c2d5b..e1c5a89 100644 --- a/tests/test_atoms.py +++ b/tests/test_atoms.py @@ -1,5 +1,5 @@ -import os -import sys +from __future__ import absolute_import + import unittest try: @@ -9,7 +9,17 @@ except ImportError: Atk = None Gtk = None -from helper import capture_glib_deprecation_warnings +from .helper import capture_glib_deprecation_warnings + + +def is_X11(): + try: + from gi.repository import Gdk, GdkX11 + except ImportError: + return False + + display = Gdk.Display.get_default() + return isinstance(display, GdkX11.X11Display) @unittest.skipUnless(Gdk, 'Gdk not available') @@ -56,7 +66,7 @@ class TestGdkAtom(unittest.TestCase): self.assertTrue(Gtk.targets_include_image([a_jpeg], False)) self.assertTrue(Gtk.targets_include_image([a_jpeg, a_plain], False)) - @unittest.skipIf(sys.platform == "darwin", "fails on OSX") + @unittest.skipUnless(is_X11(), "only on X11") def test_out_array(self): a_selection = Gdk.Atom.intern('my_clipboard', False) clipboard = Gtk.Clipboard.get(a_selection) @@ -76,8 +86,7 @@ class TestGdkAtom(unittest.TestCase): self.assertFalse(None in names, names) self.assertTrue('TEXT' in names, names) - @unittest.skipIf(sys.platform == "darwin" or os.name == "nt", - "fails on OSX/Windows") + @unittest.skipUnless(is_X11(), "only on X11") @unittest.skipIf(not Gdk or Gdk._version == "4.0", "not in gdk4") def test_out_glist(self): display = Gdk.Display.get_default() diff --git a/tests/test_cairo.py b/tests/test_cairo.py index 06289e2..8ba5553 100644 --- a/tests/test_cairo.py +++ b/tests/test_cairo.py @@ -2,6 +2,8 @@ # coding=utf-8 # vim: tabstop=4 shiftwidth=4 expandtab +from __future__ import absolute_import + import unittest import gi @@ -158,7 +160,3 @@ class TestSignalMarshaling(unittest.TestCase): result = self.pass_object_through_signal(pattern, self.tester.sig_pattern) self.assertTrue(isinstance(result, cairo.Pattern)) self.assertTrue(isinstance(result, cairo.SolidPattern)) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_docstring.py b/tests/test_docstring.py index 29b7e5e..adee174 100644 --- a/tests/test_docstring.py +++ b/tests/test_docstring.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + import unittest import gi.docstring diff --git a/tests/test_error.py b/tests/test_error.py index 5702490..3f48326 100644 --- a/tests/test_error.py +++ b/tests/test_error.py @@ -22,6 +22,8 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 # USA +from __future__ import absolute_import + import unittest from gi.repository import GLib @@ -138,7 +140,3 @@ class TestMarshalling(unittest.TestCase): error1 = GLib.Error.new_literal(1, "error", 1) GIMarshallingTests.compare_two_gerrors_in_gvalue(error, error1) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_everything.py b/tests/test_everything.py index e206954..ce79cc2 100644 --- a/tests/test_everything.py +++ b/tests/test_everything.py @@ -2,6 +2,8 @@ # coding=utf-8 # vim: tabstop=4 shiftwidth=4 expandtab +from __future__ import absolute_import + import unittest import traceback import ctypes @@ -20,8 +22,8 @@ try: except: Gtk = None -from compathelper import PY3 -from helper import capture_exceptions +from .compathelper import PY3 +from .helper import capture_exceptions if sys.version_info < (3, 0): diff --git a/tests/test_fields.py b/tests/test_fields.py index ac09949..0181d28 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1,6 +1,8 @@ # -*- Mode: Python; py-indent-offset: 4 -*- # coding=utf-8 +from __future__ import absolute_import + import math import unittest diff --git a/tests/test_gdbus.py b/tests/test_gdbus.py index 5fb4f5d..18315af 100644 --- a/tests/test_gdbus.py +++ b/tests/test_gdbus.py @@ -1,6 +1,8 @@ # -*- Mode: Python; py-indent-offset: 4 -*- # vim: tabstop=4 shiftwidth=4 expandtab +from __future__ import absolute_import + import unittest from gi.repository import GLib @@ -15,6 +17,35 @@ else: has_dbus = True +class TestDBusNodeInfo(unittest.TestCase): + + def test_new_for_xml(self): + info = Gio.DBusNodeInfo.new_for_xml(""" + + + + + + + + +""") + + interfaces = info.interfaces + del info + assert len(interfaces) == 1 + assert interfaces[0].name == "org.freedesktop.DBus.Introspectable" + methods = interfaces[0].methods + del interfaces + assert len(methods) == 1 + assert methods[0].name == "Introspect" + out_args = methods[0].out_args + assert len(out_args) + del methods + assert out_args[0].name == "data" + + @unittest.skipUnless(has_dbus, "no dbus running") class TestGDBusClient(unittest.TestCase): def setUp(self): diff --git a/tests/test_generictreemodel.py b/tests/test_generictreemodel.py index 2430110..d99c056 100644 --- a/tests/test_generictreemodel.py +++ b/tests/test_generictreemodel.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, see . +from __future__ import absolute_import # system import gc @@ -414,7 +415,3 @@ class TestReturnsAfterError(unittest.TestCase): with ExceptHook(NotImplementedError): res = self.model.iter_parent(child) self.assertEqual(res, None) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_gi.py b/tests/test_gi.py index 9be28db..3108520 100644 --- a/tests/test_gi.py +++ b/tests/test_gi.py @@ -2,6 +2,8 @@ # coding=utf-8 # vim: tabstop=4 shiftwidth=4 expandtab +from __future__ import absolute_import + import sys import unittest @@ -21,8 +23,8 @@ from gi.repository import GObject, GLib, Gio from gi.repository import GIMarshallingTests -from compathelper import PY2, PY3 -from helper import capture_exceptions +from .compathelper import PY2, PY3 +from .helper import capture_exceptions CONSTANT_UTF8 = "const ♥ utf8" diff --git a/tests/test_gio.py b/tests/test_gio.py index c7239ce..92159c1 100644 --- a/tests/test_gio.py +++ b/tests/test_gio.py @@ -1,6 +1,8 @@ # -*- Mode: Python; py-indent-offset: 4 -*- # vim: tabstop=4 shiftwidth=4 expandtab +from __future__ import absolute_import + import os import unittest import warnings @@ -9,7 +11,7 @@ import gi.overrides from gi import PyGIWarning from gi.repository import GLib, Gio -from helper import ignore_gi_deprecation_warnings +from .helper import ignore_gi_deprecation_warnings class TestGio(unittest.TestCase): diff --git a/tests/test_glib.py b/tests/test_glib.py index fa48cdb..7a782e9 100644 --- a/tests/test_glib.py +++ b/tests/test_glib.py @@ -1,6 +1,8 @@ # -*- Mode: Python -*- # encoding: UTF-8 +from __future__ import absolute_import + import os import sys import unittest diff --git a/tests/test_gobject.py b/tests/test_gobject.py index c380d72..a72c41a 100644 --- a/tests/test_gobject.py +++ b/tests/test_gobject.py @@ -1,5 +1,7 @@ # -*- Mode: Python -*- +from __future__ import absolute_import + import sys import gc import unittest @@ -10,7 +12,7 @@ from gi import PyGIDeprecationWarning from gi.module import get_introspection_module from gi import _gi -import testhelper +from . import testhelper class TestGObjectAPI(unittest.TestCase): @@ -694,7 +696,3 @@ class TestGValue(unittest.TestCase): value = GObject.Value(GLib.Error) self.assertEqual(value.g_type, GObject.type_from_name('GError')) self.assertEqual(value.get_value(), None) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_gtype.py b/tests/test_gtype.py index 8099101..26b7ff5 100644 --- a/tests/test_gtype.py +++ b/tests/test_gtype.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + import unittest from gi.repository import GObject diff --git a/tests/test_import_machinery.py b/tests/test_import_machinery.py index f1a5f30..27377cd 100644 --- a/tests/test_import_machinery.py +++ b/tests/test_import_machinery.py @@ -1,6 +1,8 @@ # -*- Mode: Python; py-indent-offset: 4 -*- # vim: tabstop=4 shiftwidth=4 expandtab +from __future__ import absolute_import + import sys import unittest diff --git a/tests/test_interface.py b/tests/test_interface.py index ba20cb4..bed37f3 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -1,9 +1,11 @@ # -*- Mode: Python -*- +from __future__ import absolute_import + import unittest from gi.repository import GObject -import testhelper +from . import testhelper GUnknown = GObject.type_from_name("TestUnknown") diff --git a/tests/test_internal_api.py b/tests/test_internal_api.py index eb66fdd..0cf3e6f 100644 --- a/tests/test_internal_api.py +++ b/tests/test_internal_api.py @@ -1,11 +1,13 @@ # -*- Mode: Python -*- +from __future__ import absolute_import + import unittest from gi.repository import GLib, GObject -import testhelper -import testmodule +from . import testhelper +from . import testmodule class TestObject(unittest.TestCase): @@ -67,7 +69,3 @@ class TestErrors(unittest.TestCase): def test_no_gerror(self): callable_ = lambda: GLib.file_get_contents(__file__) self.assertEqual(testhelper.test_gerror_exception(callable_), None) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_iochannel.py b/tests/test_iochannel.py index 95b8d1f..56a0aea 100644 --- a/tests/test_iochannel.py +++ b/tests/test_iochannel.py @@ -1,6 +1,8 @@ # -*- Mode: Python -*- # encoding: UTF-8 +from __future__ import absolute_import + import os import unittest import tempfile @@ -474,7 +476,3 @@ second line self.assertEqual(GLib.IOFlags.NONBLOCK, GLib.IO_FLAG_NONBLOCK) self.assertEqual(GLib.IOFlags.IS_SEEKABLE, GLib.IO_FLAG_IS_SEEKABLE) self.assertEqual(GLib.IOStatus.NORMAL, GLib.IO_STATUS_NORMAL) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_mainloop.py b/tests/test_mainloop.py index fda6787..1c1b122 100644 --- a/tests/test_mainloop.py +++ b/tests/test_mainloop.py @@ -1,5 +1,7 @@ # -*- Mode: Python -*- +from __future__ import absolute_import + import os import sys import select diff --git a/tests/test_object_marshaling.py b/tests/test_object_marshaling.py index c881a9a..4fce561 100644 --- a/tests/test_object_marshaling.py +++ b/tests/test_object_marshaling.py @@ -1,6 +1,8 @@ # -*- Mode: Python; py-indent-offset: 4 -*- # vim: tabstop=4 shiftwidth=4 expandtab +from __future__ import absolute_import + import unittest import weakref import gc diff --git a/tests/test_option.py b/tests/test_option.py index fe25746..33a1288 100644 --- a/tests/test_option.py +++ b/tests/test_option.py @@ -1,5 +1,7 @@ #!/usr/bin/env python +from __future__ import absolute_import + import unittest import sys diff --git a/tests/test_ossig.py b/tests/test_ossig.py index 0b6eea6..b59f2f5 100644 --- a/tests/test_ossig.py +++ b/tests/test_ossig.py @@ -14,6 +14,8 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, see . +from __future__ import absolute_import + import os import signal import unittest diff --git a/tests/test_overrides_gdk.py b/tests/test_overrides_gdk.py index 1452737..1dfe8e3 100644 --- a/tests/test_overrides_gdk.py +++ b/tests/test_overrides_gdk.py @@ -1,6 +1,8 @@ # -*- Mode: Python; py-indent-offset: 4 -*- # vim: tabstop=4 shiftwidth=4 expandtab +from __future__ import absolute_import + import os import sys import unittest @@ -15,7 +17,7 @@ except ImportError: Gdk = None Gdk_version = None -from helper import capture_glib_deprecation_warnings +from .helper import capture_glib_deprecation_warnings @unittest.skipUnless(Gdk, 'Gdk not available') diff --git a/tests/test_overrides_glib.py b/tests/test_overrides_glib.py index a9d5b58..6dcfd58 100644 --- a/tests/test_overrides_glib.py +++ b/tests/test_overrides_glib.py @@ -1,12 +1,14 @@ # -*- Mode: Python; py-indent-offset: 4 -*- # vim: tabstop=4 shiftwidth=4 expandtab +from __future__ import absolute_import + import gc import unittest import gi from gi.repository import GLib -from compathelper import _long +from .compathelper import _long class TestGVariant(unittest.TestCase): diff --git a/tests/test_overrides_gtk.py b/tests/test_overrides_gtk.py index 338ca72..aa55564 100644 --- a/tests/test_overrides_gtk.py +++ b/tests/test_overrides_gtk.py @@ -2,13 +2,15 @@ # coding: UTF-8 # vim: tabstop=4 shiftwidth=4 expandtab +from __future__ import absolute_import + import contextlib import unittest import time import sys import warnings -from helper import ignore_gi_deprecation_warnings, capture_glib_warnings +from .helper import ignore_gi_deprecation_warnings, capture_glib_warnings import gi.overrides import gi.types diff --git a/tests/test_overrides_pango.py b/tests/test_overrides_pango.py index 5c09a6a..a789715 100644 --- a/tests/test_overrides_pango.py +++ b/tests/test_overrides_pango.py @@ -1,6 +1,8 @@ # -*- Mode: Python; py-indent-offset: 4 -*- # vim: tabstop=4 shiftwidth=4 expandtab +from __future__ import absolute_import + import unittest try: diff --git a/tests/test_properties.py b/tests/test_properties.py index ce035cd..268ee93 100644 --- a/tests/test_properties.py +++ b/tests/test_properties.py @@ -1,5 +1,7 @@ # coding=utf-8 +from __future__ import absolute_import + import os import gc import sys @@ -27,8 +29,8 @@ from gi.repository import GIMarshallingTests from gi.repository import Regress from gi import _propertyhelper as propertyhelper -from compathelper import _long -from helper import capture_glib_warnings, capture_output +from .compathelper import _long +from .helper import capture_glib_warnings, capture_output class PropertyObject(GObject.GObject): @@ -1317,7 +1319,3 @@ class TestCGetPropertyMethod(CPropertiesTestBase, unittest.TestCase): def set_prop(self, obj, name, value): obj.set_property(name, value) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/compat_test_pygtk.py b/tests/test_pygtkcompat.py similarity index 77% rename from tests/compat_test_pygtk.py rename to tests/test_pygtkcompat.py index 6552256..da220b5 100644 --- a/tests/compat_test_pygtk.py +++ b/tests/test_pygtkcompat.py @@ -1,35 +1,45 @@ # -*- Mode: Python; py-indent-offset: 4 -*- # vim: tabstop=4 shiftwidth=4 expandtab +from __future__ import absolute_import + import unittest import base64 +import pygtkcompat +from pygtkcompat.pygtkcompat import _disable_all as disable_all + +from .helper import capture_gi_deprecation_warnings, capture_glib_warnings + try: - from gi.repository import Gtk - from gi.repository import Pango - from gi.repository import Atk - from gi.repository import Gdk - (Atk, Gtk, Pango) # pyflakes - - import pygtkcompat - - pygtkcompat.enable() - pygtkcompat.enable_gtk(version=Gtk._version) - - import atk - import pango - import pangocairo - import gtk - import gtk.gdk -except (ValueError, ImportError): + from gi.repository import Gtk, Gdk +except ImportError: Gtk = None +else: + if Gtk._version != "3.0": + Gtk = None + + +class TestGlibCompat(unittest.TestCase): + + def setUp(self): + pygtkcompat.enable() -from helper import capture_gi_deprecation_warnings, capture_glib_warnings + def tearDown(self): + disable_all() + + def test_import(self): + import glib + import gio + glib, gio @unittest.skipUnless(Gtk, 'Gtk not available') class TestMultipleEnable(unittest.TestCase): + def tearDown(self): + disable_all() + def test_main(self): pygtkcompat.enable() pygtkcompat.enable() @@ -37,6 +47,7 @@ class TestMultipleEnable(unittest.TestCase): def test_gtk(self): pygtkcompat.enable_gtk("3.0") pygtkcompat.enable_gtk("3.0") + import gtk # https://bugzilla.gnome.org/show_bug.cgi?id=759009 w = gtk.Window() @@ -44,39 +55,77 @@ class TestMultipleEnable(unittest.TestCase): self.assertEqual(len(w.window.get_origin()), 2) w.destroy() + def test_gtk_no_4(self): + self.assertRaises(ValueError, pygtkcompat.enable_gtk, version='4.0') + def test_gtk_version_conflict(self): + pygtkcompat.enable_gtk("3.0") self.assertRaises(ValueError, pygtkcompat.enable_gtk, version='2.0') @unittest.skipUnless(Gtk, 'Gtk not available') class TestATKCompat(unittest.TestCase): + + def setUp(self): + pygtkcompat.enable_gtk("3.0") + + def tearDown(self): + disable_all() + def test_object(self): + import atk self.assertTrue(hasattr(atk, 'Object')) @unittest.skipUnless(Gtk, 'Gtk not available') class TestPangoCompat(unittest.TestCase): + + def setUp(self): + pygtkcompat.enable_gtk("3.0") + + def tearDown(self): + disable_all() + def test_layout(self): + import pango self.assertTrue(hasattr(pango, 'Layout')) @unittest.skipUnless(Gtk, 'Gtk not available') class TestPangoCairoCompat(unittest.TestCase): + + def setUp(self): + pygtkcompat.enable_gtk("3.0") + + def tearDown(self): + disable_all() + def test_error_underline_path(self): + import pangocairo self.assertTrue(hasattr(pangocairo, 'error_underline_path')) @unittest.skipUnless(Gtk, 'Gtk not available') class TestGTKCompat(unittest.TestCase): + + def setUp(self): + pygtkcompat.enable_gtk("3.0") + + def tearDown(self): + disable_all() + def test_buttons(self): - self.assertEqual(Gdk._2BUTTON_PRESS, 5) - self.assertEqual(Gdk.BUTTON_PRESS, 4) + import gtk.gdk + self.assertEqual(gtk.gdk._2BUTTON_PRESS, 5) + self.assertEqual(gtk.gdk.BUTTON_PRESS, 4) def test_enums(self): + import gtk self.assertEqual(gtk.WINDOW_TOPLEVEL, Gtk.WindowType.TOPLEVEL) self.assertEqual(gtk.PACK_START, Gtk.PackType.START) def test_flags(self): + import gtk self.assertEqual(gtk.EXPAND, Gtk.AttachOptions.EXPAND) self.assertEqual(gtk.gdk.SHIFT_MASK, Gdk.ModifierType.SHIFT_MASK) @@ -86,6 +135,7 @@ class TestGTKCompat(unittest.TestCase): self.assertTrue(gtk.keysyms._0, Gdk.KEY_0) def test_style(self): + import gtk widget = gtk.Button() with capture_gi_deprecation_warnings(): widget.get_style_context().set_state(gtk.STATE_NORMAL) @@ -93,6 +143,7 @@ class TestGTKCompat(unittest.TestCase): gtk.gdk.Color)) def test_alignment(self): + import gtk # Creation of pygtk.Alignment causes hard warnings, ignore this in testing. with capture_glib_warnings(allow_warnings=True): a = gtk.Alignment() @@ -103,6 +154,7 @@ class TestGTKCompat(unittest.TestCase): self.assertEqual(a.props.yscale, 0.0) def test_box(self): + import gtk box = gtk.Box() child = gtk.Button() @@ -122,6 +174,7 @@ class TestGTKCompat(unittest.TestCase): self.assertEqual(pack_type, gtk.PACK_END) def test_combobox_entry(self): + import gtk liststore = gtk.ListStore(int, str) liststore.append((1, 'One')) liststore.append((2, 'Two')) @@ -146,14 +199,17 @@ class TestGTKCompat(unittest.TestCase): self.assertEqual(combo.get_child().get_text(), 'One') def test_size_request(self): + import gtk box = gtk.Box() with capture_gi_deprecation_warnings(): self.assertEqual(box.size_request(), [0, 0]) def test_pixbuf(self): + import gtk.gdk gtk.gdk.Pixbuf() def test_pixbuf_loader(self): + import gtk.gdk # load a 1x1 pixel PNG from memory data = base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP4n8Dw' 'HwAGIAJf85Z3XgAAAABJRU5ErkJggg==') @@ -166,6 +222,7 @@ class TestGTKCompat(unittest.TestCase): self.assertEqual(pixbuf.get_height(), 1) def test_pixbuf_formats(self): + import gtk.gdk formats = gtk.gdk.pixbuf_get_formats() self.assertEqual(type(formats[0]), dict) self.assertTrue('name' in formats[0]) @@ -174,6 +231,7 @@ class TestGTKCompat(unittest.TestCase): self.assertEqual(type(formats[0]['extensions']), list) def test_gdk_window(self): + import gtk w = gtk.Window() w.realize() origin = w.get_window().get_origin() diff --git a/tests/test_repository.py b/tests/test_repository.py index d7b6d2e..a074a35 100644 --- a/tests/test_repository.py +++ b/tests/test_repository.py @@ -20,6 +20,8 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 # USA +from __future__ import absolute_import + import unittest import collections @@ -29,7 +31,7 @@ from gi.repository import GObject from gi.repository import GIMarshallingTests from gi.repository import GIRepository as IntrospectedRepository -from helper import capture_glib_warnings +from .helper import capture_glib_warnings def find_child_info(info, getter_name, name): @@ -384,7 +386,3 @@ class Test(unittest.TestCase): IntrospectedRepository.Argument.__info__ = 'not an info' self.assertRaises(TypeError, IntrospectedRepository.Argument) IntrospectedRepository.Argument.__info__ = old_info - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_resulttuple.py b/tests/test_resulttuple.py index 20f80f3..1a4aadf 100644 --- a/tests/test_resulttuple.py +++ b/tests/test_resulttuple.py @@ -18,6 +18,8 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 # USA +from __future__ import absolute_import + import unittest import pickle diff --git a/tests/test_signal.py b/tests/test_signal.py index 642708b..35fc8ef 100644 --- a/tests/test_signal.py +++ b/tests/test_signal.py @@ -1,5 +1,7 @@ # -*- Mode: Python -*- +from __future__ import absolute_import + import gc import unittest import sys @@ -9,11 +11,12 @@ import time from gi.repository import GObject, GLib, Regress, Gio from gi import _signalhelper as signalhelper -import testhelper -from compathelper import _long -from helper import capture_glib_warnings, capture_gi_deprecation_warnings from gi.module import repository as repo +from . import testhelper +from .compathelper import _long +from .helper import capture_glib_warnings, capture_gi_deprecation_warnings + class C(GObject.GObject): __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_FIRST, None, @@ -1541,7 +1544,3 @@ class TestClosureRefCycle(unittest.TestCase): self.assertEqual(len(called), 1) self.assertTrue(called[0].__grefcount__ > 0) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_source.py b/tests/test_source.py index b544734..5828fa9 100644 --- a/tests/test_source.py +++ b/tests/test_source.py @@ -1,13 +1,16 @@ # -*- Mode: Python -*- +from __future__ import absolute_import + import sys +import gc import unittest import warnings from gi.repository import GLib from gi import PyGIDeprecationWarning -from helper import capture_glib_warnings +from .helper import capture_glib_warnings class Idle(GLib.Idle): @@ -126,6 +129,7 @@ class TestSource(unittest.TestCase): return s s = f() + gc.collect() self.assertTrue(s.is_destroyed()) def test_remove(self): @@ -207,8 +211,9 @@ class TestSource(unittest.TestCase): self.finalized = True source = S() - id = source.attach() - print('source id:', id) + self.assertEqual(source.ref_count, 1) + source.attach() + self.assertEqual(source.ref_count, 2) self.assertFalse(self.finalized) self.assertFalse(source.is_destroyed()) @@ -216,6 +221,7 @@ class TestSource(unittest.TestCase): pass source.destroy() + self.assertEqual(source.ref_count, 1) self.assertTrue(self.dispatched) self.assertFalse(self.finalized) self.assertTrue(source.is_destroyed()) @@ -421,7 +427,3 @@ class TestUserData(unittest.TestCase): GLib.idle_add(self.cb_with_data, data) self.loop.run() self.assertTrue(data['called']) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_subprocess.py b/tests/test_subprocess.py index deea58f..3ffdf93 100644 --- a/tests/test_subprocess.py +++ b/tests/test_subprocess.py @@ -1,5 +1,7 @@ # -*- Mode: Python -*- +from __future__ import absolute_import + import sys import os import unittest diff --git a/tests/test_thread.py b/tests/test_thread.py index 3da3310..e2bbda0 100644 --- a/tests/test_thread.py +++ b/tests/test_thread.py @@ -1,10 +1,13 @@ # -*- Mode: Python -*- +from __future__ import absolute_import + import unittest -import testhelper from gi.repository import GLib +from . import testhelper + class TestThread(unittest.TestCase): def setUp(self): diff --git a/tests/test_typeclass.py b/tests/test_typeclass.py index 3ece684..b584fdd 100644 --- a/tests/test_typeclass.py +++ b/tests/test_typeclass.py @@ -20,6 +20,8 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 # USA +from __future__ import absolute_import + import unittest from gi.repository import GObject @@ -74,7 +76,3 @@ class TestTypeClassMethodsMovedToClass(unittest.TestCase): def test_find_child_property(self): pspec = GIMarshallingTests.PropertiesObject.find_property('some-int') self.assertEqual(pspec.name, 'some-int') - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_unknown.py b/tests/test_unknown.py new file mode 100644 index 0000000..e483003 --- /dev/null +++ b/tests/test_unknown.py @@ -0,0 +1,31 @@ +# -*- Mode: Python -*- + +from __future__ import absolute_import + +import unittest + +from gi.repository import GObject + +from . import testhelper + + +TestInterface = GObject.GType.from_name('TestInterface') + + +class TestUnknown(unittest.TestCase): + def test_unknown_interface(self): + obj = testhelper.get_unknown() + TestUnknownGType = GObject.GType.from_name('TestUnknown') + TestUnknown = GObject.new(TestUnknownGType).__class__ + assert isinstance(obj, testhelper.Interface) + assert isinstance(obj, TestUnknown) + + def test_property(self): + obj = testhelper.get_unknown() + self.assertEqual(obj.get_property('some-property'), None) + obj.set_property('some-property', 'foo') + + def test_unknown_property(self): + obj = testhelper.get_unknown() + self.assertRaises(TypeError, obj.get_property, 'unknown') + self.assertRaises(TypeError, obj.set_property, 'unknown', '1') diff --git a/tests/testmodule.py b/tests/testmodule.py index c083f62..f79eed8 100644 --- a/tests/testmodule.py +++ b/tests/testmodule.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from gi.repository import GObject -- 2.7.4