Imported Upstream version 3.27.4 upstream/3.27.4
authorDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 30 Oct 2018 01:30:01 +0000 (10:30 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 30 Oct 2018 01:30:01 +0000 (10:30 +0900)
71 files changed:
ChangeLog
NEWS
PKG-INFO
configure
configure.ac
docs/devguide/building_testing.rst
docs/guide/api/signals.rst
gi/gimodule.c
gi/overrides/GLib.py
gi/overrides/GObject.py
gi/pygi-array.c
gi/pygi-basictype.c
gi/pygi-basictype.h
gi/pygi-boxed.c
gi/pygi-boxed.h
gi/pygi-cache.h
gi/pygi-closure.c
gi/pygi-enum-marshal.c
gi/pygi-error.c
gi/pygi-hashtable.c
gi/pygi-invoke-state-struct.h
gi/pygi-invoke.c
gi/pygi-list.c
gi/pygi-marshal-cleanup.c
gi/pygi-object.c
gi/pygi-property.c
gi/pygi-source.c
gi/pygi-struct-marshal.c
pygtkcompat/pygtkcompat.py
setup.py
tests/Makefile.am
tests/Makefile.in
tests/__init__.py [new file with mode: 0644]
tests/helper.py
tests/runtests.py
tests/test_atoms.py
tests/test_cairo.py
tests/test_docstring.py
tests/test_error.py
tests/test_everything.py
tests/test_fields.py
tests/test_gdbus.py
tests/test_generictreemodel.py
tests/test_gi.py
tests/test_gio.py
tests/test_glib.py
tests/test_gobject.py
tests/test_gtype.py
tests/test_import_machinery.py
tests/test_interface.py
tests/test_internal_api.py
tests/test_iochannel.py
tests/test_mainloop.py
tests/test_object_marshaling.py
tests/test_option.py
tests/test_ossig.py
tests/test_overrides_gdk.py
tests/test_overrides_glib.py
tests/test_overrides_gtk.py
tests/test_overrides_pango.py
tests/test_properties.py
tests/test_pygtkcompat.py [moved from tests/compat_test_pygtk.py with 77% similarity]
tests/test_repository.py
tests/test_resulttuple.py
tests/test_signal.py
tests/test_source.py
tests/test_subprocess.py
tests/test_thread.py
tests/test_typeclass.py
tests/test_unknown.py [new file with mode: 0644]
tests/testmodule.py

index e5c7e6c..91bbad9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,249 @@
+commit 9c1d66be67008604206e336a0433c5cdf824b837
+Author: Christoph Reiter <reiter.christoph@gmail.com>
+Date:   Wed Feb 14 01:47:12 2018 +0100
+
+    release
+
+ NEWS | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+commit 86fb783d0f00a49ea7e66ad73be7e150eb0162db
+Author: Christoph Reiter <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
+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 <reiter.christoph@gmail.com>
 Date:   Sat Feb 10 13:22:17 2018 +0100
diff --git a/NEWS b/NEWS
index e0901a4..6a57419 100644 (file)
--- 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
 -------------------
 
index 311ae08..6e6f47d 100644 (file)
--- 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
index 047905d..1dc4a67 100755 (executable)
--- 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 <http://bugzilla.gnome.org/enter_bug.cgi?product=pygobject>.
 #
@@ -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\\"
 
index 004573c..5c9d3f7 100644 (file)
@@ -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 ...
index 79c73f5..f2c9624 100644 (file)
@@ -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
index 0001862..4353b21 100644 (file)
@@ -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
 
 
index 48ddee2..5f8853c 100644 (file)
@@ -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;
 }
index b1ff24f..f47a338 100644 (file)
@@ -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
index bfcf7cc..c252bfa 100644 (file)
@@ -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
index 0f6c6d8..e55f9f6 100644 (file)
@@ -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)
index ddb02f9..6d4e64e 100644 (file)
@@ -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)
 {
index 62f11c6..466c7d4 100644 (file)
@@ -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 */
index 599d6d3..e9014f2 100644 (file)
@@ -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);
index 5c04b5c..8679322 100644 (file)
@@ -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,
index 574563b..4dfabd8 100644 (file)
@@ -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;
 
index 42144e0..b51c04c 100644 (file)
@@ -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;
index fe6c7d7..44eb009 100644 (file)
@@ -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;
index 4827e9f..e3d8838 100644 (file)
@@ -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;
index f0cda78..647bf04 100644 (file)
@@ -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)
 {
index dbf4e66..64711cb 100644 (file)
@@ -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.
index fca48ba..fd9e474 100644 (file)
@@ -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)
index e9fc92c..72a3d20 100644 (file)
@@ -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
index ddda594..906be58 100644 (file)
@@ -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;
     }
 }
index 80c9055..8fd8ee0 100644 (file)
@@ -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)
 {
index c4f3e4a..9978585 100644 (file)
@@ -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);
index 2ed38d3..5305260 100644 (file)
@@ -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;
 }
index 44797e7..60d2585 100644 (file)
@@ -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;
index 6884ef1..299e0d1 100644 (file)
@@ -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)
index c76bc9f..8635dbc 100755 (executable)
--- 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):
index 6c0cedf..216aca5 100644 (file)
@@ -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
index d0b51c7..8230e02 100644 (file)
@@ -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 (file)
index 0000000..4d9b383
--- /dev/null
@@ -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__)
index 0d2f204..b683a4a 100644 (file)
@@ -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"])
index 457553f..652dc99 100755 (executable)
 #!/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 <testfiles>")
-    sys.exit(0)
+def main(argv):
+    if '--help' in argv:
+        print("Usage: ./runtests.py <testfiles>")
+        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))
index a2c2d5b..e1c5a89 100644 (file)
@@ -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()
index 06289e2..8ba5553 100644 (file)
@@ -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()
index 29b7e5e..adee174 100644 (file)
@@ -1,3 +1,5 @@
+from __future__ import absolute_import
+
 import unittest
 
 import gi.docstring
index 5702490..3f48326 100644 (file)
@@ -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()
index e206954..ce79cc2 100644 (file)
@@ -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):
index ac09949..0181d28 100644 (file)
@@ -1,6 +1,8 @@
 # -*- Mode: Python; py-indent-offset: 4 -*-
 # coding=utf-8
 
+from __future__ import absolute_import
+
 import math
 import unittest
 
index 5fb4f5d..18315af 100644 (file)
@@ -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("""
+<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+    'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+    <interface name='org.freedesktop.DBus.Introspectable'>
+        <method name='Introspect'>
+            <arg name='data' direction='out' type='s'/>
+        </method>
+    </interface>
+</node>
+""")
+
+        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):
index 2430110..d99c056 100644 (file)
@@ -17,6 +17,7 @@
 # You should have received a copy of the GNU Lesser General Public
 # License along with this library; if not, see <http://www.gnu.org/licenses/>.
 
+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()
index 9be28db..3108520 100644 (file)
@@ -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"
index c7239ce..92159c1 100644 (file)
@@ -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):
index fa48cdb..7a782e9 100644 (file)
@@ -1,6 +1,8 @@
 # -*- Mode: Python -*-
 # encoding: UTF-8
 
+from __future__ import absolute_import
+
 import os
 import sys
 import unittest
index c380d72..a72c41a 100644 (file)
@@ -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()
index 8099101..26b7ff5 100644 (file)
@@ -1,3 +1,5 @@
+from __future__ import absolute_import
+
 import unittest
 
 from gi.repository import GObject
index f1a5f30..27377cd 100644 (file)
@@ -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
 
index ba20cb4..bed37f3 100644 (file)
@@ -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")
index eb66fdd..0cf3e6f 100644 (file)
@@ -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()
index 95b8d1f..56a0aea 100644 (file)
@@ -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()
index fda6787..1c1b122 100644 (file)
@@ -1,5 +1,7 @@
 # -*- Mode: Python -*-
 
+from __future__ import absolute_import
+
 import os
 import sys
 import select
index c881a9a..4fce561 100644 (file)
@@ -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
index fe25746..33a1288 100644 (file)
@@ -1,5 +1,7 @@
 #!/usr/bin/env python
 
+from __future__ import absolute_import
+
 import unittest
 import sys
 
index 0b6eea6..b59f2f5 100644 (file)
@@ -14,6 +14,8 @@
 # You should have received a copy of the GNU Lesser General Public
 # License along with this library; if not, see <http://www.gnu.org/licenses/>.
 
+from __future__ import absolute_import
+
 import os
 import signal
 import unittest
index 1452737..1dfe8e3 100644 (file)
@@ -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')
index a9d5b58..6dcfd58 100644 (file)
@@ -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):
index 338ca72..aa55564 100644 (file)
@@ -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
index 5c09a6a..a789715 100644 (file)
@@ -1,6 +1,8 @@
 # -*- Mode: Python; py-indent-offset: 4 -*-
 # vim: tabstop=4 shiftwidth=4 expandtab
 
+from __future__ import absolute_import
+
 import unittest
 
 try:
index ce035cd..268ee93 100644 (file)
@@ -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()
similarity index 77%
rename from tests/compat_test_pygtk.py
rename to tests/test_pygtkcompat.py
index 6552256..da220b5 100644 (file)
@@ -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()
index d7b6d2e..a074a35 100644 (file)
@@ -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()
index 20f80f3..1a4aadf 100644 (file)
@@ -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
 
index 642708b..35fc8ef 100644 (file)
@@ -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()
index b544734..5828fa9 100644 (file)
@@ -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()
index deea58f..3ffdf93 100644 (file)
@@ -1,5 +1,7 @@
 # -*- Mode: Python -*-
 
+from __future__ import absolute_import
+
 import sys
 import os
 import unittest
index 3da3310..e2bbda0 100644 (file)
@@ -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):
index 3ece684..b584fdd 100644 (file)
@@ -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 (file)
index 0000000..e483003
--- /dev/null
@@ -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')
index c083f62..f79eed8 100644 (file)
@@ -1,3 +1,5 @@
+from __future__ import absolute_import
+
 from gi.repository import GObject