From 7447e4c02524c21cd0c3160a427dbe8cff83a7e1 Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Wed, 12 Jul 2017 08:43:44 +0900 Subject: [PATCH] Imported Upstream version 3.17.90 Change-Id: Iad22098f78874f80422829788d71b474602eda4e Signed-off-by: DongHun Kwak --- ChangeLog | 173 ++++++++++++++++++++++++++++++++++++++++++++ NEWS | 14 ++++ PKG-INFO | 4 +- configure | 24 +++--- configure.ac | 2 +- gi/overrides/__init__.py | 17 ++++- gi/pygi-argument.c | 5 ++ gi/pygi-foreign-cairo.c | 2 +- gi/pygi-value.c | 45 +++++++++--- gi/pygobject.c | 4 +- tests/Makefile.am | 1 + tests/Makefile.in | 1 + tests/compat_test_pygtk.py | 27 ++----- tests/compathelper.py | 8 ++ tests/helper.py | 132 +++++++++++++++++++++++++++++++++ tests/test_everything.py | 34 ++++++--- tests/test_gi.py | 6 +- tests/test_gio.py | 3 + tests/test_glib.py | 2 +- tests/test_iochannel.py | 20 ++--- tests/test_overrides_gdk.py | 18 +++-- tests/test_overrides_gtk.py | 20 ++--- tests/test_properties.py | 29 +++++--- tests/test_repository.py | 13 +--- tests/test_signal.py | 24 +++--- tests/test_source.py | 8 +- 26 files changed, 506 insertions(+), 130 deletions(-) create mode 100644 tests/helper.py diff --git a/ChangeLog b/ChangeLog index 5d17a82..d228eee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,176 @@ +commit 1da98892cf505b35ce0280db22e1e8ba31c095e0 +Author: Simon Feltman +Date: Wed Aug 19 20:57:37 2015 -0700 + + configure.ac: pre-release version bump to 3.17.90 + + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 79d23e9accd331d954007dcae6b6b7fa2837cfa7 +Author: Christoph Reiter +Date: Wed Feb 18 19:30:30 2015 +0100 + + Allow passing unicode lists to GStrv properties on Python 2 + + https://bugzilla.gnome.org/show_bug.cgi?id=744745 + + gi/pygi-value.c | 41 ++++++++++++++++++++++++++++++++--------- + tests/test_properties.py | 8 +++++++- + 2 files changed, 39 insertions(+), 10 deletions(-) + +commit 8aa3d5935b4541be6e76e8792e58bb301fa4f7d1 +Author: Rui Matos +Date: Thu May 21 17:53:17 2015 +0200 + + Avoid a silent long to int truncation + + If the python object contains a value bigger than MAXUINT we'd + silently truncate it when assigning to 'val' and the if condition + would always be true. + + This was caught by a coverity scan. + + https://bugzilla.gnome.org/show_bug.cgi?id=749698 + + gi/pygi-value.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 5af6c722e5b7db90a3ca0832c46efe0c9142a0d1 +Author: Christoph Reiter +Date: Sun Jul 5 11:58:50 2015 +0200 + + tests: add a test for test_glist_gtype_container_in() + + https://bugzilla.gnome.org/show_bug.cgi?id=749696 + + tests/test_everything.py | 6 ++++++ + 1 file changed, 6 insertions(+) + +commit f69ce9f5f6cd81b057ed0006d1fe7f71fa294943 +Author: Mathieu Bridon +Date: Sun Jul 5 11:07:00 2015 +0200 + + Handle gtype marshalling + + https://bugzilla.gnome.org/show_bug.cgi?id=749696 + + gi/pygi-argument.c | 5 +++++ + 1 file changed, 5 insertions(+) + +commit 0ee1f562c975df51ce93578d35678ef1e915e202 +Author: Daniel Hahler +Date: Wed Mar 25 14:37:29 2015 +0100 + + pygi-foreign-cairo.c: fix include for py3cairo.h + + The pkg-config info includes the "pycairo" folder already. + + https://bugzilla.gnome.org/show_bug.cgi?id=746742 + + gi/pygi-foreign-cairo.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit fea15145c2a3e6aac73350241a982a095e16c7d8 +Author: Christoph Reiter +Date: Thu Jun 18 13:36:52 2015 +0200 + + tests: Silence various error messages and warnings. + + This silences glib warnings which are due to testing + of error handling, deprecation warnings which we ignore + since we want to continue testing deprecated code and + other error output of code which is supposed to fail. + + To reduce code duplication and make things easier + this introduces a shared helper module containing + various context managers and decorators which allow + testing and silencing of warnings and errors. + + https://bugzilla.gnome.org/show_bug.cgi?id=751156 + + tests/Makefile.am | 1 + + tests/compat_test_pygtk.py | 27 +++------ + tests/compathelper.py | 8 +++ + tests/helper.py | 132 + ++++++++++++++++++++++++++++++++++++++++++++ + tests/test_everything.py | 28 ++++++---- + tests/test_gi.py | 6 +- + tests/test_gio.py | 3 + + tests/test_iochannel.py | 20 +++---- + tests/test_overrides_gdk.py | 18 +++--- + tests/test_overrides_gtk.py | 20 +++---- + tests/test_properties.py | 21 +++---- + tests/test_repository.py | 13 ++--- + tests/test_signal.py | 24 +++----- + tests/test_source.py | 8 +-- + 14 files changed, 231 insertions(+), 98 deletions(-) + +commit 619777730891b42b98da556c3aa9ca5a1b3f617b +Author: Christoph Reiter +Date: Thu Jun 18 13:30:03 2015 +0200 + + Fix gcc warning regarding uninitialized use of variable + + https://bugzilla.gnome.org/show_bug.cgi?id=751156 + + gi/pygobject.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +commit 1ed8200abefc3e51e4d2083b1372695aaf4163fb +Author: Christoph Reiter +Date: Sun Jun 21 23:55:02 2015 +0200 + + Fix test regression when xdg-user-dirs is not installed. + + GLib.get_user_special_dir is only guaranteed to always return + a path in case GLib.UserDirectory.DIRECTORY_DESKTOP is passed. + This was unintentionally changed to DIRECTORY_MUSIC + in 9948a67e677c8a351f2de1708. + + https://bugzilla.gnome.org/show_bug.cgi?id=751299 + + tests/test_glib.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 79cf1f70d247b5a4d33d1e60107e47903ca76055 +Author: Garrett Regier +Date: Mon May 18 02:32:18 2015 -0700 + + Explicitly check if an override exists instead of ImportError + + If an override depended on another module and it did not + exist then the raised ImportError was consumed and assumed + to mean that the override did not exist. This makes it + difficult to diagnose issues with overrides. + + This uses pkgutil.get_loader() as it is the easier way to + determine if a module exists in both Python 2 and 3 + and avoid deprecated functions. + + https://bugzilla.gnome.org/show_bug.cgi?id=749532 + + gi/overrides/__init__.py | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +commit d2faa619f5e204b75315a42fec99dd48fe7fb31c +Author: Simon Feltman +Date: Mon Jun 15 01:24:16 2015 -0700 + + configure.ac: post-release version bump to 3.17.2 + + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 6fa54fe868d0e2a9a9d4dc300a61cbfa079681a8 +Author: Simon Feltman +Date: Mon Jun 15 01:20:47 2015 -0700 + + release 3.17.1 + + NEWS | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + commit 2048dc8d1d708abce7037f96483c6d776567d6b5 Author: Christoph Reiter Date: Mon Mar 2 20:58:04 2015 +0100 diff --git a/NEWS b/NEWS index e9b37e3..7a863a8 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,17 @@ +3.17.90 19-Aug-2015 + - Allow passing unicode lists to GStrv properties on Python 2 + (Christoph Reiter) (#744745) + - Avoid a silent long to int truncation (Rui Matos) (#749698) + - Handle gtype marshalling (Mathieu Bridon) (#749696) + - pygi-foreign-cairo.c: fix include for py3cairo.h + (Daniel Hahler) (#746742) + - tests: Silence various error messages and warnings + (Christoph Reiter) (#751156) + - Fix test regression when xdg-user-dirs is not installed + (Christoph Reiter) (#751299) + - Explicitly check if an override exists instead of ImportError + (Garrett Regier) (#749532) + 3.17.1 15-Jun-2015 - Add gi.PyGIWarning used when import version is not specified (Christoph Reiter) (#727379) diff --git a/PKG-INFO b/PKG-INFO index 8c4e160..43942de 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: PyGObject -Version: 3.17.1 +Version: 3.17.90 Summary: Python bindings for GObject Home-page: http://www.pygtk.org/ Author: James Henstridge @@ -8,7 +8,7 @@ Author-email: james@daa.com.au Maintainer: Simon Feltman Maintainer-email: sfeltman@src.gnome.org License: GNU LGPL -Download-url: ftp://ftp.gnome.org/pub/GNOME/sources/pygobject/3.17/pygobject-3.17.1.tar.gz +Download-url: ftp://ftp.gnome.org/pub/GNOME/sources/pygobject/3.17/pygobject-3.17.90.tar.gz Description: Python bindings for GLib and GObject Platform: POSIX, Windows Classifier: Development Status :: 5 - Production/Stable diff --git a/configure b/configure index c6da85f..b06269f 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for pygobject 3.17.1. +# Generated by GNU Autoconf 2.69 for pygobject 3.17.90. # # Report bugs to . # @@ -591,8 +591,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='pygobject' PACKAGE_TARNAME='pygobject' -PACKAGE_VERSION='3.17.1' -PACKAGE_STRING='pygobject 3.17.1' +PACKAGE_VERSION='3.17.90' +PACKAGE_STRING='pygobject 3.17.90' PACKAGE_BUGREPORT='http://bugzilla.gnome.org/enter_bug.cgi?product=pygobject' PACKAGE_URL='https://wiki.gnome.org/Projects/PyGObject/' @@ -1395,7 +1395,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.17.1 to adapt to many kinds of systems. +\`configure' configures pygobject 3.17.90 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1465,7 +1465,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of pygobject 3.17.1:";; + short | recursive ) echo "Configuration of pygobject 3.17.90:";; esac cat <<\_ACEOF @@ -1603,7 +1603,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -pygobject configure 3.17.1 +pygobject configure 3.17.90 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1881,7 +1881,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.17.1, which was +It was created by pygobject $as_me 3.17.90, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2245,9 +2245,9 @@ $as_echo "#define PYGOBJECT_MINOR_VERSION 17" >>confdefs.h PYGOBJECT_MINOR_VERSION=17 -$as_echo "#define PYGOBJECT_MICRO_VERSION 1" >>confdefs.h +$as_echo "#define PYGOBJECT_MICRO_VERSION 90" >>confdefs.h -PYGOBJECT_MICRO_VERSION=1 +PYGOBJECT_MICRO_VERSION=90 ac_config_headers="$ac_config_headers config.h" @@ -2767,7 +2767,7 @@ fi # Define the identity of the package. PACKAGE='pygobject' - VERSION='3.17.1' + VERSION='3.17.90' cat >>confdefs.h <<_ACEOF @@ -15377,7 +15377,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.17.1, which was +This file was extended by pygobject $as_me 3.17.90, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -15444,7 +15444,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.17.1 +pygobject config.status 3.17.90 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index c66ba35..897b053 100644 --- a/configure.ac +++ b/configure.ac @@ -18,7 +18,7 @@ m4_define(python3_min_ver, 3.1) dnl the pygobject version number m4_define(pygobject_major_version, 3) m4_define(pygobject_minor_version, 17) -m4_define(pygobject_micro_version, 1) +m4_define(pygobject_micro_version, 90) m4_define(pygobject_version, pygobject_major_version.pygobject_minor_version.pygobject_micro_version) dnl versions of packages we require ... diff --git a/gi/overrides/__init__.py b/gi/overrides/__init__.py index bc915b2..942e6ed 100644 --- a/gi/overrides/__init__.py +++ b/gi/overrides/__init__.py @@ -2,6 +2,7 @@ import types import warnings import importlib import sys +from pkgutil import get_loader from gi import PyGIDeprecationWarning from gi._gi import CallableInfo @@ -107,10 +108,22 @@ def load_overrides(introspection_module): modules[namespace] = proxy try: + override_package_name = 'gi.overrides.' + namespace + + # http://bugs.python.org/issue14710 try: - override_mod = importlib.import_module('gi.overrides.' + namespace) - except ImportError: + override_loader = get_loader(override_package_name) + + except AttributeError: + override_loader = None + + # Avoid checking for an ImportError, an override might + # depend on a missing module thus causing an ImportError + if override_loader is None: return introspection_module + + override_mod = importlib.import_module(override_package_name) + finally: del modules[namespace] del sys.modules[module_key] diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c index 0c322e8..fdd461a 100644 --- a/gi/pygi-argument.c +++ b/gi/pygi-argument.c @@ -95,6 +95,9 @@ _pygi_hash_pointer_to_arg (GIArgument *arg, case GI_TYPE_TAG_UINT32: arg->v_uint32 = GPOINTER_TO_UINT (arg->v_pointer); break; + case GI_TYPE_TAG_GTYPE: + arg->v_size = GPOINTER_TO_SIZE (arg->v_pointer); + break; case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_INTERFACE: @@ -121,6 +124,8 @@ _pygi_arg_to_hash_pointer (const GIArgument *arg, return GINT_TO_POINTER (arg->v_int32); case GI_TYPE_TAG_UINT32: return GINT_TO_POINTER (arg->v_uint32); + case GI_TYPE_TAG_GTYPE: + return GSIZE_TO_POINTER (arg->v_size); case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: case GI_TYPE_TAG_INTERFACE: diff --git a/gi/pygi-foreign-cairo.c b/gi/pygi-foreign-cairo.c index 5937759..8e76529 100644 --- a/gi/pygi-foreign-cairo.c +++ b/gi/pygi-foreign-cairo.c @@ -28,7 +28,7 @@ #include static Pycairo_CAPI_t *Pycairo_CAPI; #else -#include +#include #endif #include diff --git a/gi/pygi-value.c b/gi/pygi-value.c index 9d5d0ca..79ee596 100644 --- a/gi/pygi-value.c +++ b/gi/pygi-value.c @@ -382,7 +382,7 @@ pyg_value_from_pyobject_with_error(GValue *value, PyObject *obj) case G_TYPE_UINT: { if (PYGLIB_PyLong_Check(obj)) { - guint val; + gulong val; /* check that number is not negative */ if (PyLong_AsLongLong(obj) < 0) @@ -390,7 +390,7 @@ pyg_value_from_pyobject_with_error(GValue *value, PyObject *obj) val = PyLong_AsUnsignedLong(obj); if (val <= G_MAXUINT) - g_value_set_uint(value, val); + g_value_set_uint(value, (guint) val); else return -1; } else { @@ -933,17 +933,40 @@ pyg_strv_to_gvalue(GValue *value, PyObject *obj) Py_ssize_t argc, i; gchar **argv; - if (!(PyTuple_Check(obj) || PyList_Check(obj))) + if (!(PyTuple_Check (obj) || PyList_Check (obj))) return -1; - argc = PySequence_Length(obj); - for (i = 0; i < argc; ++i) - if (!PYGLIB_PyUnicode_Check(PySequence_Fast_GET_ITEM(obj, i))) - return -1; - argv = g_new(gchar *, argc + 1); - for (i = 0; i < argc; ++i) - argv[i] = g_strdup(PYGLIB_PyUnicode_AsString(PySequence_Fast_GET_ITEM(obj, i))); + argc = PySequence_Length (obj); + argv = g_new (gchar *, argc + 1); + for (i = 0; i < argc; ++i) { + PyObject* item = PySequence_Fast_GET_ITEM (obj, i); + /* same as _pygi_marshal_from_py_utf8 */ + if (PyUnicode_Check (item)) { + PyObject *pystr_obj = PyUnicode_AsUTF8String (item); + if (!pystr_obj) { + goto error; + } + argv[i] = g_strdup (PYGLIB_PyBytes_AsString (pystr_obj)); + Py_DECREF (pystr_obj); + } +#if PY_VERSION_HEX < 0x03000000 + else if (PyString_Check (item)) { + argv[i] = g_strdup (PyString_AsString (item)); + } +#endif + else { + goto error; + } + } + argv[i] = NULL; - g_value_take_boxed(value, argv); + g_value_take_boxed (value, argv); return 0; + +error: + for (i = i - 1; i >= 0; i--) { + g_free (argv[i]); + } + g_free (argv); + return -1; } diff --git a/gi/pygobject.c b/gi/pygobject.c index 83e460a..9839f49 100644 --- a/gi/pygobject.c +++ b/gi/pygobject.c @@ -82,10 +82,12 @@ pygobject_data_free(PyGObjectData *data) * free the memory. */ PyGILState_STATE state; PyThreadState *_save = NULL; + gboolean state_saved = FALSE; GSList *closures, *tmp; if (Py_IsInitialized()) { + state_saved = TRUE; state = pyglib_gil_state_ensure(); Py_DECREF(data->type); /* We cannot use Py_BEGIN_ALLOW_THREADS here because this is inside @@ -112,7 +114,7 @@ pygobject_data_free(PyGObjectData *data) g_free(data); - if (Py_IsInitialized()) { + if (state_saved && Py_IsInitialized ()) { Py_BLOCK_THREADS; /* Restores _save */ pyglib_gil_state_release(state); } diff --git a/tests/Makefile.am b/tests/Makefile.am index 07eae97..b36ff51 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -75,6 +75,7 @@ testhelper.la: $(testhelper_la_OBJECTS) $(testhelper_la_DEPENDENCIES) all: $(check_LTLIBRARIES:.la=.$(OS_EXT)) EXTRA_DIST = \ + helper.py \ compathelper.py \ runtests.py \ runtests-windows.py \ diff --git a/tests/Makefile.in b/tests/Makefile.in index 9f99c72..71d191f 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -376,6 +376,7 @@ testhelper_la_SOURCES = \ test-unknown.c EXTRA_DIST = \ + helper.py \ compathelper.py \ runtests.py \ runtests-windows.py \ diff --git a/tests/compat_test_pygtk.py b/tests/compat_test_pygtk.py index b2e7a11..dbe3c9a 100644 --- a/tests/compat_test_pygtk.py +++ b/tests/compat_test_pygtk.py @@ -2,11 +2,9 @@ # vim: tabstop=4 shiftwidth=4 expandtab import unittest -import contextlib import base64 import gi -from gi.repository import GLib try: try: @@ -32,14 +30,7 @@ try: except ImportError: Gtk = None - -@contextlib.contextmanager -def ignore_glib_warnings(): - """Temporarily change GLib logging to not bail on warnings.""" - old_mask = GLib.log_set_always_fatal( - GLib.LogLevelFlags.LEVEL_CRITICAL | GLib.LogLevelFlags.LEVEL_ERROR) - yield - GLib.log_set_always_fatal(old_mask) +from helper import capture_gi_deprecation_warnings, capture_glib_warnings @unittest.skipUnless(Gtk, 'Gtk not available') @@ -81,12 +72,13 @@ class TestGTKCompat(unittest.TestCase): def test_style(self): widget = gtk.Button() - self.assertTrue(isinstance(widget.style.base[gtk.STATE_NORMAL], - gtk.gdk.Color)) + with capture_gi_deprecation_warnings(): + self.assertTrue(isinstance(widget.style.base[gtk.STATE_NORMAL], + gtk.gdk.Color)) def test_alignment(self): # Creation of pygtk.Alignment causes hard warnings, ignore this in testing. - with ignore_glib_warnings(): + with capture_glib_warnings(allow_warnings=True): a = gtk.Alignment() self.assertEqual(a.props.xalign, 0.0) @@ -119,12 +111,8 @@ class TestGTKCompat(unittest.TestCase): liststore.append((2, 'Two')) liststore.append((3, 'Three')) # might cause a Pango warning, do not break on this - old_mask = GLib.log_set_always_fatal( - GLib.LogLevelFlags.LEVEL_CRITICAL | GLib.LogLevelFlags.LEVEL_ERROR) - try: + with capture_glib_warnings(allow_warnings=True): combo = gtk.ComboBoxEntry(model=liststore) - finally: - GLib.log_set_always_fatal(old_mask) combo.set_text_column(1) combo.set_active(0) self.assertEqual(combo.get_text_column(), 1) @@ -143,7 +131,8 @@ class TestGTKCompat(unittest.TestCase): def test_size_request(self): box = gtk.Box() - self.assertEqual(box.size_request(), [0, 0]) + with capture_gi_deprecation_warnings(): + self.assertEqual(box.size_request(), [0, 0]) def test_pixbuf(self): gtk.gdk.Pixbuf() diff --git a/tests/compathelper.py b/tests/compathelper.py index 668e60a..e5de550 100644 --- a/tests/compathelper.py +++ b/tests/compathelper.py @@ -1,4 +1,5 @@ import sys +import collections if sys.version_info >= (3, 0): ''' @@ -62,8 +63,15 @@ if sys.version_info >= (3, 0): ''' _unicode = lambda s: str(s) + + callable = lambda x: isinstance(x, collections.Callable) + from io import StringIO + StringIO else: _long = long _basestring = basestring _bytes = str _unicode = lambda s: unicode(s, 'UTF-8') + callable = callable + from StringIO import StringIO + StringIO diff --git a/tests/helper.py b/tests/helper.py new file mode 100644 index 0000000..c4308fe --- /dev/null +++ b/tests/helper.py @@ -0,0 +1,132 @@ +import contextlib +import unittest +import inspect +import warnings +import functools +import sys +from collections import namedtuple + +import gi +from gi import PyGIDeprecationWarning +from gi.repository import GLib + +from compathelper import callable, StringIO + + +ExceptionInfo = namedtuple("ExceptionInfo", ["type", "value", "traceback"]) +"""The type used for storing exceptions used by capture_exceptions()""" + + +@contextlib.contextmanager +def capture_exceptions(): + """Installs a temporary sys.excepthook which records all exceptions + instead of printing them. + """ + + exceptions = [] + + def custom_excepthook(*args): + exceptions.append(ExceptionInfo(*args)) + + old_hook = sys.excepthook + sys.excepthook = custom_excepthook + try: + yield exceptions + finally: + sys.excepthook = old_hook + + +def ignore_gi_deprecation_warnings(func_or_class): + """A unittest class and function decorator which makes them ignore + PyGIDeprecationWarning. + """ + + if inspect.isclass(func_or_class): + assert issubclass(func_or_class, unittest.TestCase) + cls = func_or_class + for name, value in cls.__dict__.items(): + if callable(value) and name.startswith("test_"): + new_value = ignore_gi_deprecation_warnings(value) + setattr(cls, name, new_value) + return cls + else: + func = func_or_class + + @functools.wraps(func) + def wrapper(*args, **kwargs): + with capture_gi_deprecation_warnings(): + return func(*args, **kwargs) + + return wrapper + + +@contextlib.contextmanager +def capture_gi_deprecation_warnings(): + """Temporarily suppress PyGIDeprecationWarning output and record them""" + + with warnings.catch_warnings(record=True) as warn: + warnings.simplefilter('always', category=PyGIDeprecationWarning) + yield warn + + +@contextlib.contextmanager +def capture_glib_warnings(allow_warnings=False, allow_criticals=False): + """Temporarily suppress glib warning output and record them. + + The test suite is run with G_DEBUG="fatal-warnings fatal-criticals" + by default. Setting allow_warnings and allow_criticals will temporarily + allow warnings or criticals without terminating the test run. + """ + + old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags(0)) + + new_mask = old_mask + if allow_warnings: + new_mask &= ~GLib.LogLevelFlags.LEVEL_WARNING + if allow_criticals: + new_mask &= ~GLib.LogLevelFlags.LEVEL_CRITICAL + + GLib.log_set_always_fatal(GLib.LogLevelFlags(new_mask)) + + GLibWarning = gi._gi._gobject.Warning + try: + with warnings.catch_warnings(record=True) as warn: + warnings.filterwarnings('always', category=GLibWarning) + yield warn + finally: + GLib.log_set_always_fatal(old_mask) + + +@contextlib.contextmanager +def capture_glib_deprecation_warnings(): + """Temporarily suppress glib deprecation warning output and record them""" + + GLibWarning = gi._gi._gobject.Warning + with warnings.catch_warnings(record=True) as warn: + warnings.filterwarnings( + 'always', category=GLibWarning, + message=".+ is deprecated and shouldn't be used anymore\. " + "It will be removed in a future version\.") + yield warn + + +@contextlib.contextmanager +def capture_output(): + """ + with capture_output as (stdout, stderr): + some_action() + print(stdout.getvalue(), stderr.getvalue()) + """ + + err = StringIO() + out = StringIO() + old_err = sys.stderr + old_out = sys.stdout + sys.stderr = err + sys.stdout = out + + try: + yield (out, err) + finally: + sys.stderr = old_err + sys.stdout = old_out diff --git a/tests/test_everything.py b/tests/test_everything.py index 5be9ce4..0b5cf6d 100644 --- a/tests/test_everything.py +++ b/tests/test_everything.py @@ -26,6 +26,9 @@ try: except: Gtk = None +from helper import capture_exceptions + + if sys.version_info < (3, 0): UNICHAR = "\xe2\x99\xa5" PY2_UNICODE_UNICHAR = unicode(UNICHAR, 'UTF-8') @@ -462,6 +465,12 @@ class TestEverything(unittest.TestCase): Everything.test_glist_nothing_in(['1', '2', '3']) Everything.test_glist_nothing_in2(['1', '2', '3']) + @unittest.skipUnless(hasattr(Everything, 'test_glist_gtype_container_in'), + 'Requires newer version of GI') + def test_glist_gtype(self): + Everything.test_glist_gtype_container_in( + [Everything.TestObj, Everything.TestSubObj]) + def test_gslist(self): self.assertEqual(Everything.test_gslist_nothing_return(), ['1', '2', '3']) self.assertEqual(Everything.test_gslist_nothing_return2(), ['1', '2', '3']) @@ -624,7 +633,10 @@ class TestCallbacks(unittest.TestCase): # note that we do NOT expect the ZeroDivisionError to be propagated # through from the callback, as it crosses the Python<->C boundary # twice. (See GNOME #616279) - Everything.test_simple_callback(callback) + with capture_exceptions() as exc: + Everything.test_simple_callback(callback) + self.assertTrue(exc) + self.assertEqual(exc[0].type, ZeroDivisionError) def test_double_callback_exception(self): """ @@ -643,7 +655,10 @@ class TestCallbacks(unittest.TestCase): # note that we do NOT expect the ZeroDivisionError to be propagated # through from the callback, as it crosses the Python<->C boundary # twice. (See GNOME #616279) - Everything.test_simple_callback(callback) + with capture_exceptions() as exc: + Everything.test_simple_callback(callback) + self.assertTrue(exc) + self.assertEqual(exc[0].type, ZeroDivisionError) def test_return_value_callback(self): TestCallbacks.called = False @@ -1050,17 +1065,16 @@ class TestClosures(unittest.TestCase): def callback(variant): return 'no_variant' - # reset last error - sys.last_type = None - - # this does not directly raise an exception (see - # https://bugzilla.gnome.org/show_bug.cgi?id=616279) - result = Everything.test_closure_variant(callback, GLib.Variant('i', 42)) + with capture_exceptions() as exc: + # this does not directly raise an exception (see + # https://bugzilla.gnome.org/show_bug.cgi?id=616279) + result = Everything.test_closure_variant(callback, GLib.Variant('i', 42)) # ... but the result shouldn't be a string self.assertEqual(result, None) # and the error should be shown - self.assertEqual(sys.last_type, TypeError) - self.assertTrue('return value' in str(sys.last_value), sys.last_value) + self.assertEqual(len(exc), 1) + self.assertEqual(exc[0].type, TypeError) + self.assertTrue('return value' in str(exc[0].value), exc[0].value) @unittest.skipUnless(has_cairo, 'built without cairo support') diff --git a/tests/test_gi.py b/tests/test_gi.py index f69a61c..4cdd1a4 100644 --- a/tests/test_gi.py +++ b/tests/test_gi.py @@ -24,6 +24,7 @@ from gi.repository import GObject, GLib, Gio from gi.repository import GIMarshallingTests from compathelper import _bytes, _unicode +from helper import capture_exceptions if sys.version_info < (3, 0): CONSTANT_UTF8 = "const \xe2\x99\xa5 utf8" @@ -2305,7 +2306,10 @@ class TestPythonGObject(unittest.TestCase): def test_exception_in_vfunc_return_value(self): obj = self.ErrorObject() - self.assertEqual(obj.vfunc_return_value_only(), 0) + with capture_exceptions() as exc: + self.assertEqual(obj.vfunc_return_value_only(), 0) + self.assertEqual(len(exc), 1) + self.assertEqual(exc[0].type, ValueError) @unittest.skipUnless(hasattr(GIMarshallingTests, 'callback_owned_boxed'), 'requires newer version of GI') diff --git a/tests/test_gio.py b/tests/test_gio.py index 0c773bc..05ab008 100644 --- a/tests/test_gio.py +++ b/tests/test_gio.py @@ -6,6 +6,8 @@ import unittest import gi.overrides from gi.repository import GLib, Gio +from helper import ignore_gi_deprecation_warnings + class TestGio(unittest.TestCase): def test_file_enumerator(self): @@ -120,6 +122,7 @@ class TestGSettings(unittest.TestCase): self.assertEqual(bool(empty), True) self.assertEqual(empty.keys(), []) + @ignore_gi_deprecation_warnings def test_change_event(self): changed_log = [] change_event_log = [] diff --git a/tests/test_glib.py b/tests/test_glib.py index 5ede849..6331d11 100644 --- a/tests/test_glib.py +++ b/tests/test_glib.py @@ -38,7 +38,7 @@ class TestGLib(unittest.TestCase): def test_xdg_dirs(self): d = GLib.get_user_data_dir() self.assertTrue('/' in d, d) - d = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_MUSIC) + d = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP) self.assertTrue('/' in d, d) with warnings.catch_warnings(): warnings.simplefilter('ignore', PyGIDeprecationWarning) diff --git a/tests/test_iochannel.py b/tests/test_iochannel.py index 02277e1..aa130ec 100644 --- a/tests/test_iochannel.py +++ b/tests/test_iochannel.py @@ -49,7 +49,7 @@ second line self.assertEqual(_unicode(ch.readline()), 'À demain!') self.assertEqual(ch.get_buffer_condition(), 0) self.assertEqual(ch.readline(), '') - ch.close() + ch.shutdown(True) def test_file_readline_latin1(self): ch = GLib.IOChannel(filename=self.testlatin1, mode='r') @@ -59,7 +59,7 @@ second line self.assertEqual(ch.readline(), 'second line\n') self.assertEqual(ch.readline(), '\n') self.assertEqual(_unicode(ch.readline()), 'À demain!') - ch.close() + ch.shutdown(True) def test_file_iter(self): items = [] @@ -68,7 +68,7 @@ second line items.append(item) self.assertEqual(len(items), 4) self.assertEqual(_unicode(items[0]), 'hello ♥ world\n') - ch.close() + ch.shutdown(True) def test_file_readlines(self): ch = GLib.IOChannel(filename=self.testutf8) @@ -117,11 +117,11 @@ second line ch = GLib.IOChannel(filename=self.testout, mode='w') ch.set_encoding('latin1') ch.write('hellø world\n') - ch.close() + ch.shutdown(True) ch = GLib.IOChannel(filename=self.testout, mode='a') ch.set_encoding('latin1') ch.write('À demain!') - ch.close() + ch.shutdown(True) with open(self.testout, 'rb') as f: self.assertEqual(f.read().decode('latin1'), 'hellø world\nÀ demain!') @@ -129,7 +129,7 @@ second line def test_file_writelines(self): ch = GLib.IOChannel(filename=self.testout, mode='w') ch.writelines(['foo', 'bar\n', 'baz\n', 'end']) - ch.close() + ch.shutdown(True) with open(self.testout, 'r') as f: self.assertEqual(f.read(), 'foobar\nbaz\nend') @@ -163,9 +163,9 @@ second line # closing flushes writer.set_buffered(True) writer.write('ghi') - writer.close() + writer.shutdown(True) self.assertEqual(reader.read(), b'ghi') - reader.close() + reader.shutdown(True) def test_fd_read(self): (r, w) = os.pipe() @@ -184,7 +184,7 @@ second line os.close(w) self.assertEqual(ch.read(), b'\x03\x04') - ch.close() + ch.shutdown(True) def test_fd_write(self): (r, w) = os.pipe() @@ -199,7 +199,7 @@ second line # now test blocking case, after closing the write end fcntl.fcntl(r, fcntl.F_SETFL, fcntl.fcntl(r, fcntl.F_GETFL) & ~os.O_NONBLOCK) ch.write(b'\x03\x04') - ch.close() + ch.shutdown(True) self.assertEqual(os.read(r, 10), b'\x03\x04') os.close(r) diff --git a/tests/test_overrides_gdk.py b/tests/test_overrides_gdk.py index da96855..eff2c54 100644 --- a/tests/test_overrides_gdk.py +++ b/tests/test_overrides_gdk.py @@ -2,7 +2,6 @@ # vim: tabstop=4 shiftwidth=4 expandtab import unittest -import warnings import gi.overrides from gi import PyGIDeprecationWarning @@ -13,6 +12,8 @@ try: except ImportError: Gdk = None +from helper import capture_glib_deprecation_warnings + @unittest.skipUnless(Gdk, 'Gdk not available') class TestGdk(unittest.TestCase): @@ -29,7 +30,8 @@ class TestGdk(unittest.TestCase): self.assertEqual(color.red, 100) self.assertEqual(color.green, 200) self.assertEqual(color.blue, 300) - self.assertEqual(color, Gdk.Color(100, 200, 300)) + with capture_glib_deprecation_warnings(): + self.assertEqual(color, Gdk.Color(100, 200, 300)) self.assertNotEqual(color, Gdk.Color(1, 2, 3)) def test_color_floats(self): @@ -122,9 +124,11 @@ class TestGdk(unittest.TestCase): def test_cursor(self): self.assertEqual(Gdk.Cursor, gi.overrides.Gdk.Cursor) - c = Gdk.Cursor(Gdk.CursorType.WATCH) + with capture_glib_deprecation_warnings(): + c = Gdk.Cursor(Gdk.CursorType.WATCH) self.assertNotEqual(c, None) - c = Gdk.Cursor(cursor_type=Gdk.CursorType.WATCH) + with capture_glib_deprecation_warnings(): + c = Gdk.Cursor(cursor_type=Gdk.CursorType.WATCH) self.assertNotEqual(c, None) display_manager = Gdk.DisplayManager.get() @@ -136,8 +140,7 @@ class TestGdk(unittest.TestCase): 5, 10) - with warnings.catch_warnings(record=True) as warn: - warnings.simplefilter('always') + with capture_glib_deprecation_warnings() as warn: c = Gdk.Cursor(display, test_pixbuf, y=0, x=0) @@ -166,7 +169,8 @@ class TestGdk(unittest.TestCase): '') def test_color_parse(self): - c = Gdk.color_parse('#00FF80') + with capture_glib_deprecation_warnings(): + c = Gdk.color_parse('#00FF80') self.assertEqual(c.red, 0) self.assertEqual(c.green, 65535) self.assertEqual(c.blue, 32896) diff --git a/tests/test_overrides_gtk.py b/tests/test_overrides_gtk.py index bd477bf..44f2f6e 100644 --- a/tests/test_overrides_gtk.py +++ b/tests/test_overrides_gtk.py @@ -9,6 +9,7 @@ import sys import warnings from compathelper import _unicode, _bytes +from helper import ignore_gi_deprecation_warnings, capture_glib_warnings import gi import gi.overrides @@ -58,16 +59,8 @@ def realized(widget): Gtk.main_iteration() -@contextlib.contextmanager -def ignore_glib_warnings(): - """Temporarily change GLib logging to not bail on warnings.""" - old_mask = GLib.log_set_always_fatal( - GLib.LogLevelFlags.LEVEL_CRITICAL | GLib.LogLevelFlags.LEVEL_ERROR) - yield - GLib.log_set_always_fatal(old_mask) - - @unittest.skipUnless(Gtk, 'Gtk not available') +@ignore_gi_deprecation_warnings class TestGtk(unittest.TestCase): def test_container(self): box = Gtk.Box() @@ -319,7 +312,7 @@ class TestGtk(unittest.TestCase): def test_file_chooser_dialog(self): # might cause a GVFS warning, do not break on this - with ignore_glib_warnings(): + with capture_glib_warnings(allow_warnings=True): dialog = Gtk.FileChooserDialog(title='file chooser dialog test', action=Gtk.FileChooserAction.SAVE) @@ -332,7 +325,7 @@ class TestGtk(unittest.TestCase): def test_file_chooser_dialog_default_action(self): # might cause a GVFS warning, do not break on this - with ignore_glib_warnings(): + with capture_glib_warnings(allow_warnings=True): dialog = Gtk.FileChooserDialog(title='file chooser dialog test') action = dialog.get_property('action') @@ -375,7 +368,7 @@ class TestGtk(unittest.TestCase): self.assertTrue(isinstance(button, Gtk.Widget)) # Using stock items causes hard warning in devel versions of GTK+. - with ignore_glib_warnings(): + with capture_glib_warnings(allow_warnings=True): button = Gtk.Button.new_from_stock(Gtk.STOCK_CLOSE) self.assertEqual(Gtk.STOCK_CLOSE, button.get_label()) @@ -594,7 +587,7 @@ class TestGtk(unittest.TestCase): # PyGTK compat # Using stock items causes hard warning in devel versions of GTK+. - with ignore_glib_warnings(): + with capture_glib_warnings(allow_warnings=True): button = Gtk.ToolButton() self.assertEqual(button.props.stock_id, None) @@ -858,6 +851,7 @@ class TestBuilder(unittest.TestCase): self.assertEqual(signal_checker.after_sentinel, 2) +@ignore_gi_deprecation_warnings @unittest.skipUnless(Gtk, 'Gtk not available') class TestTreeModel(unittest.TestCase): def test_tree_model_sort(self): diff --git a/tests/test_properties.py b/tests/test_properties.py index 6010bc2..d8d5a10 100644 --- a/tests/test_properties.py +++ b/tests/test_properties.py @@ -40,7 +40,8 @@ else: TEST_UTF8 = "♥" UNICODE_UTF8 = TEST_UTF8 -from compathelper import _long +from compathelper import _long, _unicode +from helper import capture_glib_warnings, capture_output class PropertyObject(GObject.GObject): @@ -550,14 +551,17 @@ class TestProperty(unittest.TestCase): raise ValueError('something bad happend') o = C() - with self.assertRaisesRegex(ValueError, 'something bad happend'): - o.prop - with self.assertRaisesRegex(ValueError, 'something bad happend'): - o.get_property('prop') + # silence exception printed to stderr + with capture_output(): + with self.assertRaisesRegex(ValueError, 'something bad happend'): + o.prop - with self.assertRaisesRegex(ValueError, 'something bad happend'): - o.props.prop + with self.assertRaisesRegex(ValueError, 'something bad happend'): + o.get_property('prop') + + with self.assertRaisesRegex(ValueError, 'something bad happend'): + o.props.prop def test_custom_setter(self): class C(GObject.GObject): @@ -690,8 +694,7 @@ class TestProperty(unittest.TestCase): # we test known-bad values here which cause Gtk-WARNING logs. # Explicitly allow these for this test. - old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_CRITICAL) - try: + with capture_glib_warnings(allow_warnings=True): o = C() self.assertEqual(o.prop_int, 1) @@ -714,8 +717,6 @@ class TestProperty(unittest.TestCase): o.prop_float = 10.51 self.assertEqual(o.prop_float, 7.75) - finally: - GLib.log_set_always_fatal(old_mask) def test_multiple_instances(self): class C(GObject.GObject): @@ -1108,6 +1109,12 @@ class CPropertiesTestBase(object): obj = GIMarshallingTests.PropertiesObject(some_strv=['hello', 'world']) self.assertEqual(self.get_prop(obj, 'some-strv'), ['hello', 'world']) + # unicode on py2 + obj = GIMarshallingTests.PropertiesObject(some_strv=[_unicode('foo')]) + self.assertEqual(self.get_prop(obj, 'some-strv'), [_unicode('foo')]) + self.assertRaises(TypeError, self.set_prop, self.obj, 'some-strv', + [_unicode('foo'), 1]) + def test_boxed_struct(self): self.assertEqual(self.get_prop(self.obj, 'some-boxed-struct'), None) diff --git a/tests/test_repository.py b/tests/test_repository.py index 3d7cf68..8710ce7 100644 --- a/tests/test_repository.py +++ b/tests/test_repository.py @@ -30,7 +30,6 @@ gi.require_version('GIRepository', '2.0') import gi._gi as GIRepository from gi.module import repository as repo from gi.repository import GObject -from gi.repository import GLib from gi.repository import GIMarshallingTests from gi.repository import GIRepository as IntrospectedRepository @@ -41,6 +40,8 @@ try: except ImportError: has_cairo = False +from helper import capture_glib_warnings + def find_child_info(info, getter_name, name): getter = getattr(info, getter_name) @@ -339,26 +340,20 @@ class Test(unittest.TestCase): # also raise a RuntimeError. GIMarshallingTests.NoTypeFlags # cause flags registration info = repo.find_by_name('GIMarshallingTests', 'NoTypeFlags') - old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_ERROR) - try: + with capture_glib_warnings(allow_warnings=True): self.assertRaises(RuntimeError, GIRepository.flags_register_new_gtype_and_add, info) - finally: - GLib.log_set_always_fatal(old_mask) def test_enum_double_registration_error(self): # a warning is printed for double registration and pygobject will # also raise a RuntimeError. GIMarshallingTests.Enum # cause enum registration info = repo.find_by_name('GIMarshallingTests', 'Enum') - old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_ERROR) - try: + with capture_glib_warnings(allow_warnings=True): self.assertRaises(RuntimeError, GIRepository.enum_register_new_gtype_and_add, info) - finally: - GLib.log_set_always_fatal(old_mask) def test_enums(self): self.assertTrue(hasattr(GIRepository, 'Direction')) diff --git a/tests/test_signal.py b/tests/test_signal.py index 74ec745..01e4b00 100644 --- a/tests/test_signal.py +++ b/tests/test_signal.py @@ -9,6 +9,7 @@ from gi.repository import GObject, GLib from gi import _signalhelper as signalhelper import testhelper from compathelper import _long +from helper import capture_glib_warnings, capture_gi_deprecation_warnings try: import cairo @@ -83,13 +84,9 @@ class TestGSignalsError(unittest.TestCase): def foo(): class Foo(GObject.GObject): __gsignals__ = {'not-exists': 'override'} - # do not stumble over the warning thrown by GLib - old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_CRITICAL | - GLib.LogLevelFlags.LEVEL_ERROR) - try: + + with capture_glib_warnings(allow_warnings=True): self.assertRaises(TypeError, foo) - finally: - GLib.log_set_always_fatal(old_mask) gc.collect() @@ -373,15 +370,10 @@ class TestClosures(unittest.TestCase): self.count += 1 def _callback_invalid_stop_emission_name(self, obj, prop): - # We expect a GLib warning but there currently is no way to test that - # This can at least make sure we don't crash - old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_CRITICAL | - GLib.LogLevelFlags.LEVEL_ERROR) - try: + with capture_glib_warnings(allow_warnings=True) as warn: obj.stop_emission_by_name('notasignal::baddetail') - finally: - GLib.log_set_always_fatal(old_mask) self.emission_error = True + self.assertTrue(warn) def test_disconnect_by_func(self): e = E() @@ -416,7 +408,8 @@ class TestClosures(unittest.TestCase): e = E() e.connect('notify::prop', self._callback_invalid_stop_emission_name) - e.set_property('prop', 1234) + with capture_glib_warnings(): + e.set_property('prop', 1234) self.assertTrue(self.emission_error) def test_handler_block(self): @@ -1256,7 +1249,8 @@ class _ConnectObjectTestBase(object): else: connect_func = obj.connect_object - connect_func('sig-with-int64-prop', callback, swap_obj, *user_data) + with capture_gi_deprecation_warnings(): + connect_func('sig-with-int64-prop', callback, swap_obj, *user_data) obj.emit('sig-with-int64-prop', *emit_args) self.assertEqual(len(callback_args), 1) return callback_args[0] diff --git a/tests/test_source.py b/tests/test_source.py index 362e5cd..f13ab6b 100644 --- a/tests/test_source.py +++ b/tests/test_source.py @@ -7,6 +7,8 @@ import warnings from gi.repository import GLib from gi import PyGIDeprecationWarning +from helper import capture_glib_warnings + class Idle(GLib.Idle): def __init__(self, loop): @@ -132,8 +134,8 @@ class TestSource(unittest.TestCase): self.assertEqual(GLib.source_remove(s), True) # Removing sources not found cause critical - old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_ERROR) - try: + with capture_glib_warnings(allow_criticals=True): + # s is now removed, should fail now self.assertEqual(GLib.source_remove(s), False) @@ -141,8 +143,6 @@ class TestSource(unittest.TestCase): self.assertEqual(GLib.source_remove(GLib.MAXINT32), False) self.assertEqual(GLib.source_remove(GLib.MAXINT32 + 1), False) self.assertEqual(GLib.source_remove(GLib.MAXUINT32), False) - finally: - GLib.log_set_always_fatal(old_mask) def test_recurse_property(self): s = GLib.Idle() -- 2.7.4