Imported Upstream version 3.31.1 upstream/3.31.1
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 25 Nov 2020 05:48:08 +0000 (14:48 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 25 Nov 2020 05:48:08 +0000 (14:48 +0900)
36 files changed:
.gitlab-ci.yml
.gitlab-ci/Dockerfile.gtk4 [new file with mode: 0644]
.gitlab-ci/run-docker-gtk4.sh [new file with mode: 0755]
.gitlab-ci/test-docker-gtk4.sh [new file with mode: 0755]
.gitlab-ci/test-docker.sh
.gitlab-ci/test-msys2.sh
MANIFEST.in
NEWS
PKG-INFO.in
docs/devguide/dev_environ.rst
docs/index.rst
examples/demo/demos/TreeView/treemodel_filelist.py
examples/demo/demos/TreeView/treemodel_filetree.py
gi/gimodule.c
gi/overrides/GObject.py
gi/overrides/Gdk.py
gi/overrides/Gtk.py
gi/overrides/Pango.py
gi/overrides/__init__.py
gi/pygi-array.c
gi/pygi-error.c
gi/pygobject-object.c
meson.build
pyproject.toml [new file with mode: 0644]
setup.py
tests/__init__.py
tests/gimarshallingtestsextra.c
tests/gimarshallingtestsextra.h
tests/test_atoms.py
tests/test_cairo.py
tests/test_gdbus.py
tests/test_gtk_template.py
tests/test_overrides_gdk.py
tests/test_overrides_gtk.py
tests/test_overrides_pango.py
tests/test_repository.py

index 64ba37a..a1a569b 100644 (file)
@@ -117,8 +117,15 @@ xenial-i386-py2:
   script:
    - bash -x ./.gitlab-ci/test-docker-old.sh
 
-gnome-runtime:
+gtk4:
   stage: build_and_test
-  image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:3.30
+  image: registry.gitlab.gnome.org/gnome/pygobject/gtk4:v1
   script:
-    - xvfb-run -a flatpak run --filesystem=host --share=network --socket=x11 --command=bash org.gnome.Sdk//3.30 -x .gitlab-ci/test-flatpak.sh
+   - bash -x ./.gitlab-ci/test-docker-gtk4.sh
+
+gnome-master:
+  allow_failure: true
+  stage: build_and_test
+  image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:master
+  script:
+    - xvfb-run -a flatpak run --filesystem=host --share=network --socket=x11 --command=bash org.gnome.Sdk//master -x .gitlab-ci/test-flatpak.sh
diff --git a/.gitlab-ci/Dockerfile.gtk4 b/.gitlab-ci/Dockerfile.gtk4
new file mode 100644 (file)
index 0000000..297a70d
--- /dev/null
@@ -0,0 +1,36 @@
+FROM registry.gitlab.gnome.org/gnome/pygobject/main:v8
+
+USER root
+
+RUN python3 -m pip install meson
+
+ENV DEBIAN_FRONTEND noninteractive
+RUN apt-get update && apt-get install -y \
+    libfribidi-dev \
+    libgraphene-1.0-dev \
+    libgstreamer-plugins-bad1.0-dev \
+    libgtk-3-dev \
+    libwayland-dev \
+    libxml2-dev \
+    wayland-protocols \
+    && rm -rf /var/lib/apt/lists/*
+
+RUN git clone https://gitlab.freedesktop.org/wayland/wayland.git \
+    && cd wayland \
+    && git checkout 1.16.0 \
+    && ./autogen.sh --disable-documentation \
+    && make -j8 && make install \
+    && cd .. \
+    && rm -Rf wayland
+
+RUN git clone https://gitlab.gnome.org/GNOME/gtk.git \
+    && cd gtk \
+    && git checkout 833442e1e29e5 \
+    &&  meson -Dprefix=/usr -Dbuild-tests=false -Ddemos=false -Dbuild-examples=false -Dprint-backends=none _build \
+    && ninja -C _build \
+    && ninja -C _build install \
+    && cd .. \
+    && rm -Rf gtk
+
+USER user
+ENV PYENV_VERSION 3.7.0-debug
diff --git a/.gitlab-ci/run-docker-gtk4.sh b/.gitlab-ci/run-docker-gtk4.sh
new file mode 100755 (executable)
index 0000000..f6c979d
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -e
+
+TAG="registry.gitlab.gnome.org/gnome/pygobject/gtk4:v1"
+
+sudo docker build --tag "${TAG}" --file "Dockerfile.gtk4" .
+sudo docker run --rm --security-opt label=disable \
+    --volume "$(pwd)/..:/home/user/app" --workdir "/home/user/app" \
+    --tty --interactive "${TAG}" bash
diff --git a/.gitlab-ci/test-docker-gtk4.sh b/.gitlab-ci/test-docker-gtk4.sh
new file mode 100755 (executable)
index 0000000..e36c715
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -e
+
+# ccache setup
+mkdir -p _ccache
+export CCACHE_BASEDIR="$(pwd)"
+export CCACHE_DIR="${CCACHE_BASEDIR}/_ccache"
+
+# test
+python -m pip install git+https://github.com/pygobject/pycairo.git
+python -m pip install pytest pytest-faulthandler
+g-ir-inspect Gtk --version=4.0 --print-typelibs
+export TEST_GTK_VERSION=4.0
+xvfb-run -a python setup.py test
index 4837d51..141d654 100755 (executable)
@@ -37,7 +37,7 @@ else
     meson _build -Dpython="$(which python)"
 fi
 ninja -C _build
-xvfb-run -a meson test --timeout-multiplier 4 -C _build -v
+xvfb-run -a meson test -C _build -v
 rm -Rf _build
 
 # CODE QUALITY
index 38d4fbc..5e92370 100755 (executable)
@@ -31,6 +31,9 @@ pacman --noconfirm -S --needed \
     git \
     perl
 
+# https://github.com/Alexpux/MINGW-packages/issues/4333
+pacman --noconfirm -S --needed mingw-w64-$MSYS2_ARCH-$PYTHON-pathlib2
+
 # ccache setup
 export PATH="$MSYSTEM/lib/ccache/bin:$PATH"
 mkdir -p _ccache
@@ -51,11 +54,10 @@ export PYTHONDEVMODE=1
 $PYTHON setup.py build_tests
 MSYSTEM= $PYTHON -m coverage run tests/runtests.py
 
-# FIXME: lcov doesn't support gcc9
-#~ curl -O -J -L "https://github.com/linux-test-project/lcov/archive/master.tar.gz"
-#~ tar -xvzf lcov-master.tar.gz
+curl -O -J -L "https://github.com/linux-test-project/lcov/archive/master.tar.gz"
+tar -xvzf lcov-master.tar.gz
 
-#~ ./lcov-master/bin/lcov \
-    #~ --rc lcov_branch_coverage=1 --no-external \
-    #~ --directory . --capture --output-file \
-    #~ "${COV_DIR}/${COV_KEY}.lcov"
+./lcov-master/bin/lcov \
+    --rc lcov_branch_coverage=1 --no-external \
+    --directory . --capture --output-file \
+    "${COV_DIR}/${COV_KEY}.lcov"
index 25de999..72cb04c 100644 (file)
@@ -1,4 +1,5 @@
 include setup.cfg
+include pyproject.toml
 include COPYING
 include *.in
 include NEWS
diff --git a/NEWS b/NEWS
index 6235c56..1520fba 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,30 +1,16 @@
-3.30.5 - 2019-06-16
--------------------
-
-* tests/gimarshallingtestsextra.c/h: relicense to LGPLv2.1+ :issue:`320`
-* Fix a crash when marshalling a GError to Python fails :mr:`115`
-* Fix leak of transfer-full/container C arrays :mr:`117` (:user:`Tomasz Miąsko <tmiasko>`)
-* Python 3.8b1 compatibility fixes
-
-
-3.30.4 - 2018-11-30
--------------------
-
-* gtk overrides: Fix rows getting inserted on the wrong level with
-  TreeStore.insert_before/inster_after if parent=None.
-  :issue:`281` (3.30 regression, thanks to :user:`Cian Wilson <cianwilson>`
-  for the report)
-
-
-3.30.3 - 2018-11-27
--------------------
-
-* GValue: fall back to the custom C marshaller to support fundamental types.
-  This makes GValue work with GstFraction. :issue:`280`
-* GValue: Work around wrong annotations for GVariant
-* Fix GObject attribute access during instance init which can lead to errors
-  with __getattr__ implementations of subclasses. This lead to criticals when
-  instantiating Gio.DBusProxy. :issue:`267`
+3.31.1 - 2018-11-17
+-------------------
+
+* Changes included in 3.30.2
+* overrides: add Pango.Layout.set_text() override. :issue:`259` :mr:`89`
+* docs: link updates :mr:`93` (:user:`tijder`)
+* overrides: Use functools.wraps instead of custom version.
+  :issue:`271` :mr:`95` (:user:`Kai Willadsen <kaiw>`)
+* tests: Make tests run with current gtk4 master
+* Add (again) a pyproject.toml for specifying the pycairo build dep
+  (requires pip >=18.0)
+* setup.py: Make it possible to build without cairo support through the
+  PYGOBJECT_WITHOUT_PYCAIRO env var. :issue:`250`
 
 
 3.30.2 - 2018-11-11
index 34891d7..c357cc3 100644 (file)
@@ -12,7 +12,7 @@ Description: Python bindings for GObject Introspection
 Platform: POSIX, Windows
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)
+Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
 Classifier: Operating System :: POSIX
 Classifier: Operating System :: Microsoft :: Windows
 Classifier: Programming Language :: C
index db31678..ff84857 100644 (file)
@@ -72,7 +72,7 @@ your operating system.
 
 .. code:: console
 
-    sudo zypper install -y python3-venv python3-wheel gobject-introspection \
+    sudo zypper install -y python3-venv python3-wheel gobject-introspection-devel \
       python3-cairo-devel openssl zlib git
     sudo zypper install --type pattern devel_basis
 
index 81fba33..7b383c1 100644 (file)
@@ -72,7 +72,7 @@ Who Is Using PyGObject?
 * `GNOME Music <https://wiki.gnome.org/Apps/Music>`__ - a music player for GNOME
 * `GNOME Tweak Tool <https://wiki.gnome.org/action/show/Apps/GnomeTweakTool>`__ - a tool to customize advanced GNOME 3 options
 * `Gramps <https://gramps-project.org/>`__ - a genealogy program
-* `Lollypop <https://gnumdk.github.io/lollypop-web/>`__ - a modern music player
+* `Lollypop <https://wiki.gnome.org/Apps/Lollypop>`__ - a modern music player
 * `Meld <http://meldmerge.org/>`__ - a visual diff and merge tool
 * `MyPaint <http://mypaint.org/>`__ - a nimble, distraction-free, and easy tool for digital painters
 * `Orca <https://wiki.gnome.org/Projects/Orca>`__ - a flexible and extensible screen reader
index 82e6d95..f011a47 100644 (file)
@@ -125,16 +125,16 @@ class FileListModel(gtk.GenericTreeModel):
         except OSError:
             return None
         mode = filestat.st_mode
-        if column == 0:
+        if column is 0:
             if stat.S_ISDIR(mode):
                 return folderpb
             else:
                 return filepb
-        elif column == 1:
+        elif column is 1:
             return rowref
-        elif column == 2:
+        elif column is 2:
             return filestat.st_size
-        elif column == 3:
+        elif column is 3:
             return oct(stat.S_IMODE(mode))
         return time.ctime(filestat.st_mtime)
 
index eb8e2b9..3b43190 100644 (file)
@@ -157,16 +157,16 @@ class FileTreeModel(gtk.GenericTreeModel):
         except OSError:
             return None
         mode = filestat.st_mode
-        if column == 0:
+        if column is 0:
             if stat.S_ISDIR(mode):
                 return folderpb
             else:
                 return filepb
-        elif column == 1:
+        elif column is 1:
             return os.path.basename(relpath)
-        elif column == 2:
+        elif column is 2:
             return filestat.st_size
-        elif column == 3:
+        elif column is 3:
             return oct(stat.S_IMODE(mode))
         return time.ctime(filestat.st_mtime)
 
index e25583e..3a61616 100644 (file)
@@ -1066,7 +1066,7 @@ pygobject__g_instance_init(GTypeInstance   *instance,
     }
 
     /* XXX: used for Gtk.Template */
-    if (PyObject_HasAttrString ((PyObject*) Py_TYPE (wrapper), "__dontuse_ginstance_init__")) {
+    if (PyObject_HasAttrString (wrapper, "__dontuse_ginstance_init__")) {
         result = PyObject_CallMethod (wrapper, "__dontuse_ginstance_init__", NULL);
         if (result == NULL)
             PyErr_Print ();
index fb548d8..4a6102d 100644 (file)
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 # USA
 
+import functools
 import warnings
 from collections import namedtuple
 
-import gi.overrides
 import gi.module
 from gi.overrides import override, deprecated_attr
 from gi.repository import GLib
@@ -297,9 +297,7 @@ class Value(GObjectModule.Value):
         elif gtype == TYPE_PYOBJECT:
             self.set_boxed(py_value)
         else:
-            # Fall back to _gvalue_set which handles some more cases
-            # like fundamentals for which a converter is registered
-            _gi._gvalue_set(self, py_value)
+            raise TypeError("Unknown value type %s" % gtype)
 
     def get_value(self):
         gtype = self.g_type
@@ -345,15 +343,11 @@ class Value(GObjectModule.Value):
         elif gtype == TYPE_GTYPE:
             return self.get_gtype()
         elif gtype == TYPE_VARIANT:
-            # get_variant was missing annotations
-            # https://gitlab.gnome.org/GNOME/glib/merge_requests/492
-            return self.dup_variant()
+            return self.get_variant()
         elif gtype == TYPE_PYOBJECT:
-            return self.get_boxed()
-        elif gtype == _gi.TYPE_INVALID:
-            return None
+            pass
         else:
-            return _gi._gvalue_get(self)
+            return None
 
     def __repr__(self):
         return '<Value (%s) %s>' % (self.g_type.name, self.get_value())
@@ -555,7 +549,7 @@ def _signalmethod(func):
     # Function wrapper for signal functions used as instance methods.
     # This is needed when the signal functions come directly from GI.
     # (they are not already wrapped)
-    @gi.overrides.wraps(func)
+    @functools.wraps(func)
     def meth(*args, **kwargs):
         return func(*args, **kwargs)
     return meth
index a1ef5f9..44914db 100644 (file)
@@ -161,7 +161,7 @@ if Gdk._version == '2.0':
 
     Drawable = override(Drawable)
     __all__.append('Drawable')
-else:
+elif Gdk._version == '3.0':
     class Window(Gdk.Window):
         def __new__(cls, parent, attributes, attributes_mask):
             # Gdk.Window had to be made abstract,
@@ -181,33 +181,25 @@ if Gdk._version in ("2.0", "3.0"):
     Gdk.EventType._2BUTTON_PRESS = getattr(Gdk.EventType, "2BUTTON_PRESS")
     Gdk.EventType._3BUTTON_PRESS = getattr(Gdk.EventType, "3BUTTON_PRESS")
 
-
-class Event(Gdk.Event):
-    _UNION_MEMBERS = {
-        Gdk.EventType.DELETE: 'any',
-        Gdk.EventType.DESTROY: 'any',
-        Gdk.EventType.EXPOSE: 'expose',
-        Gdk.EventType.MOTION_NOTIFY: 'motion',
-        Gdk.EventType.BUTTON_PRESS: 'button',
-        Gdk.EventType.BUTTON_RELEASE: 'button',
-        Gdk.EventType.KEY_PRESS: 'key',
-        Gdk.EventType.KEY_RELEASE: 'key',
-        Gdk.EventType.ENTER_NOTIFY: 'crossing',
-        Gdk.EventType.LEAVE_NOTIFY: 'crossing',
-        Gdk.EventType.FOCUS_CHANGE: 'focus_change',
-        Gdk.EventType.CONFIGURE: 'configure',
-        Gdk.EventType.MAP: 'any',
-        Gdk.EventType.UNMAP: 'any',
-        Gdk.EventType.PROXIMITY_IN: 'proximity',
-        Gdk.EventType.PROXIMITY_OUT: 'proximity',
-        Gdk.EventType.DRAG_ENTER: 'dnd',
-        Gdk.EventType.DRAG_LEAVE: 'dnd',
-        Gdk.EventType.DRAG_MOTION: 'dnd',
-        Gdk.EventType.DROP_START: 'dnd',
-    }
-
-    if Gdk._version in ("2.0", "3.0"):
-        _UNION_MEMBERS.update({
+    class Event(Gdk.Event):
+        _UNION_MEMBERS = {
+            Gdk.EventType.DELETE: 'any',
+            Gdk.EventType.DESTROY: 'any',
+            Gdk.EventType.MOTION_NOTIFY: 'motion',
+            Gdk.EventType.BUTTON_PRESS: 'button',
+            Gdk.EventType.BUTTON_RELEASE: 'button',
+            Gdk.EventType.KEY_PRESS: 'key',
+            Gdk.EventType.KEY_RELEASE: 'key',
+            Gdk.EventType.ENTER_NOTIFY: 'crossing',
+            Gdk.EventType.LEAVE_NOTIFY: 'crossing',
+            Gdk.EventType.FOCUS_CHANGE: 'focus_change',
+            Gdk.EventType.CONFIGURE: 'configure',
+            Gdk.EventType.PROXIMITY_IN: 'proximity',
+            Gdk.EventType.PROXIMITY_OUT: 'proximity',
+            Gdk.EventType.DRAG_ENTER: 'dnd',
+            Gdk.EventType.DRAG_LEAVE: 'dnd',
+            Gdk.EventType.DRAG_MOTION: 'dnd',
+            Gdk.EventType.DROP_START: 'dnd',
             Gdk.EventType._2BUTTON_PRESS: 'button',
             Gdk.EventType._3BUTTON_PRESS: 'button',
             Gdk.EventType.PROPERTY_NOTIFY: 'property',
@@ -218,116 +210,110 @@ class Event(Gdk.Event):
             Gdk.EventType.DROP_FINISHED: 'dnd',
             Gdk.EventType.CLIENT_EVENT: 'client',
             Gdk.EventType.VISIBILITY_NOTIFY: 'visibility',
-        })
-
-    if Gdk._version == '2.0':
-        _UNION_MEMBERS[Gdk.EventType.NO_EXPOSE] = 'no_expose'
-
-    if hasattr(Gdk.EventType, 'TOUCH_BEGIN'):
-        _UNION_MEMBERS.update(
-            {
-                Gdk.EventType.TOUCH_BEGIN: 'touch',
-                Gdk.EventType.TOUCH_UPDATE: 'touch',
-                Gdk.EventType.TOUCH_END: 'touch',
-                Gdk.EventType.TOUCH_CANCEL: 'touch',
-            })
-
-    def __getattr__(self, name):
-        real_event = getattr(self, '_UNION_MEMBERS').get(self.type)
-        if real_event:
-            return getattr(getattr(self, real_event), name)
-        else:
-            raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name))
-
-    def __setattr__(self, name, value):
-        real_event = getattr(self, '_UNION_MEMBERS').get(self.type)
-        if real_event:
-            setattr(getattr(self, real_event), name, value)
-        else:
-            Gdk.Event.__setattr__(self, name, value)
-
-    def __repr__(self):
-        base_repr = Gdk.Event.__repr__(self).strip("><")
-        return "<%s type=%r>" % (base_repr, self.type)
-
-
-Event = override(Event)
-__all__.append('Event')
-
-# manually bind GdkEvent members to GdkEvent
-
-modname = globals()['__name__']
-module = sys.modules[modname]
-
-# right now we can't get the type_info from the
-# field info so manually list the class names
-event_member_classes = ['EventAny',
-                        'EventExpose',
-                        'EventMotion',
-                        'EventButton',
-                        'EventScroll',
-                        'EventKey',
-                        'EventCrossing',
-                        'EventFocus',
-                        'EventConfigure',
-                        'EventProximity',
-                        'EventDND',
-                        'EventSetting',
-                        'EventGrabBroken']
-
-if Gdk._version in ("2.0", "3.0"):
-    event_member_classes.extend([
-        'EventVisibility',
-        'EventProperty',
-        'EventSelection',
-        'EventOwnerChange',
-        'EventWindowState',
-        'EventVisibility',
-    ])
-
-if Gdk._version == '2.0':
-    event_member_classes.append('EventNoExpose')
-
-if hasattr(Gdk, 'EventTouch'):
-    event_member_classes.append('EventTouch')
-
-
-# whitelist all methods that have a success return we want to mask
-gsuccess_mask_funcs = ['get_state',
-                       'get_axis',
-                       'get_coords',
-                       'get_root_coords']
-
-
-for event_class in event_member_classes:
-    override_class = type(event_class, (getattr(Gdk, event_class),), {})
-    # add the event methods
-    for method_info in Gdk.Event.__info__.get_methods():
-        name = method_info.get_name()
-        event_method = getattr(Gdk.Event, name)
-        # python2 we need to use the __func__ attr to avoid internal
-        # instance checks
-        event_method = getattr(event_method, '__func__', event_method)
-
-        # use the _gsuccess_mask decorator if this method is whitelisted
-        if name in gsuccess_mask_funcs:
-            event_method = strip_boolean_result(event_method)
-        setattr(override_class, name, event_method)
-
-    setattr(module, event_class, override_class)
-    __all__.append(event_class)
-
-# end GdkEvent overrides
-
-
-class DragContext(Gdk.DragContext):
-    def finish(self, success, del_, time):
-        Gtk = get_introspection_module('Gtk')
-        Gtk.drag_finish(self, success, del_, time)
+            Gdk.EventType.EXPOSE: 'expose',
+            Gdk.EventType.MAP: 'any',
+            Gdk.EventType.UNMAP: 'any',
+        }
+
+        if Gdk._version == '2.0':
+            _UNION_MEMBERS[Gdk.EventType.NO_EXPOSE] = 'no_expose'
+
+        if hasattr(Gdk.EventType, 'TOUCH_BEGIN'):
+            _UNION_MEMBERS.update(
+                {
+                    Gdk.EventType.TOUCH_BEGIN: 'touch',
+                    Gdk.EventType.TOUCH_UPDATE: 'touch',
+                    Gdk.EventType.TOUCH_END: 'touch',
+                    Gdk.EventType.TOUCH_CANCEL: 'touch',
+                })
+
+        def __getattr__(self, name):
+            real_event = getattr(self, '_UNION_MEMBERS').get(self.type)
+            if real_event:
+                return getattr(getattr(self, real_event), name)
+            else:
+                raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name))
+
+        def __setattr__(self, name, value):
+            real_event = getattr(self, '_UNION_MEMBERS').get(self.type)
+            if real_event:
+                setattr(getattr(self, real_event), name, value)
+            else:
+                Gdk.Event.__setattr__(self, name, value)
 
+        def __repr__(self):
+            base_repr = Gdk.Event.__repr__(self).strip("><")
+            return "<%s type=%r>" % (base_repr, self.type)
+
+    Event = override(Event)
+    __all__.append('Event')
+
+    # manually bind GdkEvent members to GdkEvent
+
+    modname = globals()['__name__']
+    module = sys.modules[modname]
+
+    # right now we can't get the type_info from the
+    # field info so manually list the class names
+    event_member_classes = ['EventAny',
+                            'EventExpose',
+                            'EventMotion',
+                            'EventButton',
+                            'EventScroll',
+                            'EventKey',
+                            'EventCrossing',
+                            'EventFocus',
+                            'EventConfigure',
+                            'EventProximity',
+                            'EventDND',
+                            'EventSetting',
+                            'EventGrabBroken',
+                            'EventVisibility',
+                            'EventProperty',
+                            'EventSelection',
+                            'EventOwnerChange',
+                            'EventWindowState',
+                            'EventVisibility']
 
-DragContext = override(DragContext)
-__all__.append('DragContext')
+    if Gdk._version == '2.0':
+        event_member_classes.append('EventNoExpose')
+
+    if hasattr(Gdk, 'EventTouch'):
+        event_member_classes.append('EventTouch')
+
+    # whitelist all methods that have a success return we want to mask
+    gsuccess_mask_funcs = ['get_state',
+                           'get_axis',
+                           'get_coords',
+                           'get_root_coords']
+
+    for event_class in event_member_classes:
+        override_class = type(event_class, (getattr(Gdk, event_class),), {})
+        # add the event methods
+        for method_info in Gdk.Event.__info__.get_methods():
+            name = method_info.get_name()
+            event_method = getattr(Gdk.Event, name)
+            # python2 we need to use the __func__ attr to avoid internal
+            # instance checks
+            event_method = getattr(event_method, '__func__', event_method)
+
+            # use the _gsuccess_mask decorator if this method is whitelisted
+            if name in gsuccess_mask_funcs:
+                event_method = strip_boolean_result(event_method)
+            setattr(override_class, name, event_method)
+
+        setattr(module, event_class, override_class)
+        __all__.append(event_class)
+
+    # end GdkEvent overrides
+
+    class DragContext(Gdk.DragContext):
+        def finish(self, success, del_, time):
+            Gtk = get_introspection_module('Gtk')
+            Gtk.drag_finish(self, success, del_, time)
+
+    DragContext = override(DragContext)
+    __all__.append('DragContext')
 
 
 class Cursor(Gdk.Cursor):
index 9cb92c0..7e00c61 100644 (file)
@@ -141,15 +141,17 @@ class Widget(Gtk.Widget):
         super(Widget, self).freeze_child_notify()
         return _FreezeNotifyManager(self)
 
-    def drag_dest_set_target_list(self, target_list):
-        if (target_list is not None) and (not isinstance(target_list, Gtk.TargetList)):
-            target_list = Gtk.TargetList.new(_construct_target_list(target_list))
-        super(Widget, self).drag_dest_set_target_list(target_list)
-
-    def drag_source_set_target_list(self, target_list):
-        if (target_list is not None) and (not isinstance(target_list, Gtk.TargetList)):
-            target_list = Gtk.TargetList.new(_construct_target_list(target_list))
-        super(Widget, self).drag_source_set_target_list(target_list)
+    if Gtk._version != "4.0":
+        def drag_dest_set_target_list(self, target_list):
+            if (target_list is not None) and (not isinstance(target_list, Gtk.TargetList)):
+                target_list = Gtk.TargetList.new(_construct_target_list(target_list))
+            super(Widget, self).drag_dest_set_target_list(target_list)
+
+    if Gtk._version != "4.0":
+        def drag_source_set_target_list(self, target_list):
+            if (target_list is not None) and (not isinstance(target_list, Gtk.TargetList)):
+                target_list = Gtk.TargetList.new(_construct_target_list(target_list))
+            super(Widget, self).drag_source_set_target_list(target_list)
 
     def style_get_property(self, property_name, value=None):
         if value is None:
@@ -184,8 +186,6 @@ class Container(Gtk.Container, Widget):
     # alias for Python 2.x object protocol
     __nonzero__ = __bool__
 
-    get_focus_chain = strip_boolean_result(Gtk.Container.get_focus_chain)
-
     def child_get_property(self, child, property_name, value=None):
         if value is None:
             prop = self.find_child_property(property_name)
@@ -197,15 +197,18 @@ class Container(Gtk.Container, Widget):
         Gtk.Container.child_get_property(self, child, property_name, value)
         return value.get_value()
 
-    def child_get(self, child, *prop_names):
-        """Returns a list of child property values for the given names."""
-        return [self.child_get_property(child, name) for name in prop_names]
+    if Gtk._version in ("2.0", "3.0"):
+        def child_get(self, child, *prop_names):
+            """Returns a list of child property values for the given names."""
+            return [self.child_get_property(child, name) for name in prop_names]
+
+        def child_set(self, child, **kwargs):
+            """Set a child properties on the given child to key/value pairs."""
+            for name, value in kwargs.items():
+                name = name.replace('_', '-')
+                self.child_set_property(child, name, value)
 
-    def child_set(self, child, **kwargs):
-        """Set a child properties on the given child to key/value pairs."""
-        for name, value in kwargs.items():
-            name = name.replace('_', '-')
-            self.child_set_property(child, name, value)
+        get_focus_chain = strip_boolean_result(Gtk.Container.get_focus_chain)
 
 
 Container = override(Container)
@@ -1282,8 +1285,6 @@ class TreeStore(Gtk.TreeStore, TreeModel, TreeSortable):
             if sibling is None:
                 position = -1
             else:
-                if parent is None:
-                    parent = self.iter_parent(sibling)
                 position = self.get_path(sibling).get_indices()[-1]
             return self._do_insert(parent, position, row)
 
@@ -1294,8 +1295,6 @@ class TreeStore(Gtk.TreeStore, TreeModel, TreeSortable):
             if sibling is None:
                 position = 0
             else:
-                if parent is None:
-                    parent = self.iter_parent(sibling)
                 position = self.get_path(sibling).get_indices()[-1] + 1
             return self._do_insert(parent, position, row)
 
index 067a628..7d0d8cf 100644 (file)
@@ -50,6 +50,9 @@ class Layout(Pango.Layout):
     def set_markup(self, text, length=-1):
         super(Layout, self).set_markup(text, length)
 
+    def set_text(self, text, length=-1):
+        super(Layout, self).set_text(text, length)
+
 
 Layout = override(Layout)
 __all__.append('Layout')
index e262b6c..1572d25 100644 (file)
@@ -1,3 +1,4 @@
+import functools
 import types
 import warnings
 import importlib
@@ -19,14 +20,6 @@ __path__ = extend_path(__path__, __name__)
 _deprecated_attrs = {}
 
 
-def wraps(wrapped):
-    def assign(wrapper):
-        wrapper.__name__ = wrapped.__name__
-        wrapper.__module__ = wrapped.__module__
-        return wrapper
-    return assign
-
-
 class OverridesProxyModule(types.ModuleType):
     """Wraps a introspection module and contains all overrides"""
 
@@ -216,7 +209,7 @@ overridefunc = override
 
 def deprecated(fn, replacement):
     """Decorator for marking methods and classes as deprecated"""
-    @wraps(fn)
+    @functools.wraps(fn)
     def wrapped(*args, **kwargs):
         warnings.warn('%s is deprecated; use %s instead' % (fn.__name__, replacement),
                       PyGIDeprecationWarning, stacklevel=2)
@@ -335,7 +328,7 @@ def strip_boolean_result(method, exc_type=None, exc_str=None, fail_ret=None):
     several out arguments. Translate such a method to return the out arguments
     on success and None on failure.
     """
-    @wraps(method)
+    @functools.wraps(method)
     def wrapped(*args, **kwargs):
         ret = method(*args, **kwargs)
         if ret[0]:
index 890e7c5..073e143 100644 (file)
@@ -766,7 +766,7 @@ _pygi_marshal_cleanup_to_py_array (PyGIInvokeState *state,
             return;
 
         free_array = TRUE;
-        free_array_full = arg_cache->transfer != GI_TRANSFER_NOTHING;
+        free_array_full = FALSE;
     } else if (array_cache->array_type == GI_ARRAY_TYPE_PTR_ARRAY) {
         ptr_array_ = (GPtrArray *) data;
     } else {
index b0eeccd..8e4a793 100644 (file)
@@ -36,10 +36,8 @@ PyObject *PyGError = NULL;
  *
  * Checks to see if @error has been set.  If @error has been set, then a
  * GLib.GError Python exception object is returned (but not raised).
- * If not error is set returns Py_None.
  *
- * Returns: a GLib.GError Python exception object, or Py_None,
- *     or NULL and sets an error if creating the exception object fails.
+ * Returns: a GLib.GError Python exception object, or NULL.
  */
 PyObject *
 pygi_error_marshal_to_py (GError **error)
@@ -52,7 +50,7 @@ pygi_error_marshal_to_py (GError **error)
     g_return_val_if_fail(error != NULL, NULL);
 
     if (*error == NULL)
-        Py_RETURN_NONE;
+        return NULL;
 
     state = PyGILState_Ensure();
 
@@ -95,13 +93,8 @@ pygi_error_check (GError **error)
     state = PyGILState_Ensure();
 
     exc_instance = pygi_error_marshal_to_py (error);
-    if (exc_instance != NULL) {
-        PyErr_SetObject(PyGError, exc_instance);
-        Py_DECREF(exc_instance);
-    } else {
-        PyErr_Print ();
-        PyErr_SetString (PyExc_RuntimeError, "Converting the GError failed");
-    }
+    PyErr_SetObject(PyGError, exc_instance);
+    Py_DECREF(exc_instance);
     g_clear_error(error);
 
     PyGILState_Release(state);
@@ -273,7 +266,11 @@ _pygi_marshal_to_py_gerror (PyGIInvokeState   *state,
         g_error_free (error);
     }
 
-    return py_obj;
+    if (py_obj != NULL) {
+        return py_obj;
+    } else {
+        Py_RETURN_NONE;
+    }
 }
 
 static gboolean
@@ -333,7 +330,11 @@ pygerror_from_gvalue (const GValue *value)
 {
     GError *gerror = (GError *) g_value_get_boxed (value);
     PyObject *pyerr = pygi_error_marshal_to_py (&gerror);
-    return pyerr;
+    if (pyerr == NULL) {
+        Py_RETURN_NONE;
+    } else {
+        return pyerr;
+    }
 }
 
 static int
index 39c03ca..dbf46e1 100644 (file)
@@ -878,10 +878,7 @@ pygobject_inherit_slots(PyTypeObject *type, PyObject *bases, gboolean check_for_
                                   offsetof(PyTypeObject, tp_iter),
                                   offsetof(PyTypeObject, tp_repr),
                                   offsetof(PyTypeObject, tp_str),
-#if PY_VERSION_HEX < 0x03000000
-                                  offsetof(PyTypeObject, tp_print),
-#endif
-    };
+                                  offsetof(PyTypeObject, tp_print) };
     gsize i;
 
     /* Happens when registering gobject.GObject itself, at least. */
index 1b30bc1..db9bca3 100644 (file)
@@ -1,5 +1,5 @@
 project('pygobject', 'c',
-  version : '3.30.5',
+  version : '3.31.1',
   meson_version : '>= 0.46.0',
   default_options : [ 'warning_level=1',
                       'buildtype=debugoptimized'])
@@ -72,6 +72,7 @@ else
     '-Wall',
     '-Warray-bounds',
     '-Wcast-align',
+    '-Wdeclaration-after-statement',
     '-Wduplicated-branches',
     '-Wextra',
     '-Wformat=2',
@@ -97,18 +98,11 @@ else
     '-Wsign-compare',
     '-Wstrict-aliasing',
     '-Wstrict-prototypes',
-    '-Wswitch-default',
     '-Wundef',
     '-Wunused-but-set-variable',
     '-Wwrite-strings',
   ]
 
-  if python.language_version().split('.')[0] == '2'
-    main_c_args += [
-        '-Wdeclaration-after-statement',
-    ]
-  endif
-
   main_c_args += [
     '-Wno-incompatible-pointer-types-discards-qualifiers',
     '-Wno-missing-field-initializers',
@@ -124,6 +118,12 @@ else
     '-fvisibility=hidden',
   ]
 
+  if not ['3.3', '3.4'].contains(python.language_version())
+    main_c_args += [
+      '-Wswitch-default',
+    ]
+  endif
+
   main_c_args = cc.get_supported_arguments(main_c_args)
 endif
 
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644 (file)
index 0000000..830e77a
--- /dev/null
@@ -0,0 +1,2 @@
+[build-system]
+requires = ["setuptools", "wheel", "pycairo"]
index fc46a4c..b4aa14c 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -41,15 +41,20 @@ from distutils import dir_util, log
 from distutils.spawn import find_executable
 
 
-PYGOBJECT_VERSION = "3.30.5"
+PYGOBJECT_VERISON = "3.31.1"
 GLIB_VERSION_REQUIRED = "2.38.0"
 GI_VERSION_REQUIRED = "1.46.0"
 PYCAIRO_VERSION_REQUIRED = "1.11.1"
 LIBFFI_VERSION_REQUIRED = "3.0"
 
+WITH_CAIRO = not bool(os.environ.get("PYGOBJECT_WITHOUT_PYCAIRO"))
+"""Set PYGOBJECT_WITHOUT_PYCAIRO if you don't want to build with
+cairo/pycairo support. Note that this option might get removed in the future.
+"""
+
 
 def is_dev_version():
-    version = tuple(map(int, PYGOBJECT_VERSION.split(".")))
+    version = tuple(map(int, PYGOBJECT_VERISON.split(".")))
     return version[1] % 2 != 0
 
 
@@ -79,7 +84,7 @@ def get_version_requirement(pkg_config_name):
 
 
 def get_versions():
-    version = PYGOBJECT_VERSION.split(".")
+    version = PYGOBJECT_VERISON.split(".")
     assert len(version) == 3
 
     versions = {
@@ -275,7 +280,7 @@ class sdist_gnome(Command):
 
     def run(self):
         # Don't use PEP 440 pre-release versions for GNOME releases
-        self.distribution.metadata.version = PYGOBJECT_VERSION
+        self.distribution.metadata.version = PYGOBJECT_VERISON
 
         dist_dir = tempfile.mkdtemp()
         try:
@@ -379,7 +384,6 @@ class build_tests(Command):
         self.build_temp = None
         self.build_base = None
         self.force = False
-        self.extra_defines = []
 
     def finalize_options(self):
         self.set_undefined_options(
@@ -468,13 +472,15 @@ class build_tests(Command):
                 # MSVC: We need to define _GI_EXTERN explcitly so that
                 #       symbols get exported properly
                 if compiler.compiler_type == "msvc":
-                    self.extra_defines = [('_GI_EXTERN',
-                                           '__declspec(dllexport)extern')]
+                    extra_defines = [('_GI_EXTERN',
+                                      '__declspec(dllexport)extern')]
+                else:
+                    extra_defines = []
                 objects = compiler.compile(
                     ext.sources,
                     output_dir=self.build_temp,
                     include_dirs=ext.include_dirs,
-                    macros=self.extra_defines)
+                    macros=ext.define_macros + extra_defines)
 
                 if os.name == "nt":
                     if compiler.compiler_type == "msvc":
@@ -552,6 +558,10 @@ class build_tests(Command):
                 "--output=%s" % typelib_path,
             ])
 
+        regress_macros = []
+        if not WITH_CAIRO:
+            regress_macros.append(("_GI_DISABLE_CAIRO", "1"))
+
         ext = Extension(
             name='libregress',
             sources=[
@@ -565,11 +575,14 @@ class build_tests(Command):
                 os.path.join(gi_tests_dir, "regress.h"),
                 os.path.join(tests_dir, "regressextra.h"),
             ],
+            define_macros=regress_macros,
         )
         add_ext_pkg_config_dep(ext, compiler.compiler_type, "glib-2.0")
         add_ext_pkg_config_dep(ext, compiler.compiler_type, "gio-2.0")
-        add_ext_pkg_config_dep(ext, compiler.compiler_type, "cairo")
-        add_ext_pkg_config_dep(ext, compiler.compiler_type, "cairo-gobject")
+        if WITH_CAIRO:
+            add_ext_pkg_config_dep(ext, compiler.compiler_type, "cairo")
+            add_ext_pkg_config_dep(
+                ext, compiler.compiler_type, "cairo-gobject")
         ext_paths = build_ext(ext)
 
         # We want to always use POSIX-style paths for g-ir-compiler
@@ -579,7 +592,6 @@ class build_tests(Command):
         typelib_path = posixpath.join(tests_dir, "Regress-1.0.typelib")
         regress_g_ir_scanner_cmd = g_ir_scanner_cmd + [
             "--no-libtool",
-            "--include=cairo-1.0",
             "--include=Gio-2.0",
             "--namespace=Regress",
             "--nsversion=1.0",
@@ -591,19 +603,23 @@ class build_tests(Command):
             "--pkg=gio-2.0"]
 
         if self._newer_group(ext_paths, gir_path):
-            # MSVC: We don't normally have the pkg-config files for
-            # cairo and cairo-gobject, so use --extra-library
-            # instead of --pkg to pass those to the linker, so that
-            # g-ir-scanner won't fail due to linker errors
-            if compiler.compiler_type == "msvc":
-                regress_g_ir_scanner_cmd += [
-                    "--extra-library=cairo",
-                    "--extra-library=cairo-gobject"]
+            if WITH_CAIRO:
+                regress_g_ir_scanner_cmd += ["--include=cairo-1.0"]
+                # MSVC: We don't normally have the pkg-config files for
+                # cairo and cairo-gobject, so use --extra-library
+                # instead of --pkg to pass those to the linker, so that
+                # g-ir-scanner won't fail due to linker errors
+                if compiler.compiler_type == "msvc":
+                    regress_g_ir_scanner_cmd += [
+                        "--extra-library=cairo",
+                        "--extra-library=cairo-gobject"]
 
+                else:
+                    regress_g_ir_scanner_cmd += [
+                        "--pkg=cairo",
+                        "--pkg=cairo-gobject"]
             else:
-                regress_g_ir_scanner_cmd += [
-                    "--pkg=cairo",
-                    "--pkg=cairo-gobject"]
+                regress_g_ir_scanner_cmd += ["-D_GI_DISABLE_CAIRO"]
 
             regress_g_ir_scanner_cmd += ["--output=%s" % gir_path]
 
@@ -634,7 +650,6 @@ class build_tests(Command):
         )
         add_ext_pkg_config_dep(ext, compiler.compiler_type, "glib-2.0")
         add_ext_pkg_config_dep(ext, compiler.compiler_type, "gio-2.0")
-        add_ext_pkg_config_dep(ext, compiler.compiler_type, "cairo")
         add_ext_compiler_flags(ext, compiler)
 
         dist = Distribution({"ext_modules": [ext]})
@@ -937,6 +952,7 @@ def add_ext_compiler_flags(ext, compiler, _cache={}):
                 "-Wall",
                 "-Warray-bounds",
                 "-Wcast-align",
+                "-Wdeclaration-after-statement",
                 "-Wduplicated-branches",
                 "-Wextra",
                 "-Wformat=2",
@@ -962,15 +978,14 @@ def add_ext_compiler_flags(ext, compiler, _cache={}):
                 "-Wsign-compare",
                 "-Wstrict-aliasing",
                 "-Wstrict-prototypes",
-                "-Wswitch-default",
                 "-Wundef",
                 "-Wunused-but-set-variable",
                 "-Wwrite-strings",
             ]
 
-            if sys.version_info[0] == 2:
+            if sys.version_info[:2] != (3, 4):
                 args += [
-                    "-Wdeclaration-after-statement",
+                    "-Wswitch-default",
                 ]
 
             args += [
@@ -1061,15 +1076,16 @@ class build_ext(du_build_ext):
         add_dependency(gi_ext, "libffi")
         add_ext_compiler_flags(gi_ext, compiler)
 
-        gi_cairo_ext = ext["gi._gi_cairo"]
-        add_dependency(gi_cairo_ext, "glib-2.0")
-        add_dependency(gi_cairo_ext, "gio-2.0")
-        add_dependency(gi_cairo_ext, "gobject-introspection-1.0")
-        add_dependency(gi_cairo_ext, "libffi")
-        add_dependency(gi_cairo_ext, "cairo")
-        add_dependency(gi_cairo_ext, "cairo-gobject")
-        add_pycairo(gi_cairo_ext)
-        add_ext_compiler_flags(gi_cairo_ext, compiler)
+        if WITH_CAIRO:
+            gi_cairo_ext = ext["gi._gi_cairo"]
+            add_dependency(gi_cairo_ext, "glib-2.0")
+            add_dependency(gi_cairo_ext, "gio-2.0")
+            add_dependency(gi_cairo_ext, "gobject-introspection-1.0")
+            add_dependency(gi_cairo_ext, "libffi")
+            add_dependency(gi_cairo_ext, "cairo")
+            add_dependency(gi_cairo_ext, "cairo-gobject")
+            add_pycairo(gi_cairo_ext)
+            add_ext_compiler_flags(gi_cairo_ext, compiler)
 
     def run(self):
         self._write_config_h()
@@ -1130,7 +1146,7 @@ class install_pkgconfig(Command):
             "includedir": "${prefix}/include",
             "datarootdir": "${prefix}/share",
             "datadir": "${datarootdir}",
-            "VERSION": PYGOBJECT_VERSION,
+            "VERSION": PYGOBJECT_VERISON,
         }
         for key, value in config.items():
             content = content.replace("@%s@" % key, value)
@@ -1171,6 +1187,9 @@ def main():
     with io.open(readme, encoding="utf-8") as h:
         long_description = h.read()
 
+    ext_modules = []
+    install_requires = []
+
     gi_ext = Extension(
         name='gi._gi',
         sources=sorted(sources),
@@ -1178,14 +1197,20 @@ def main():
         depends=list_headers(script_dir) + list_headers(gi_dir),
         define_macros=[("PY_SSIZE_T_CLEAN", None)],
     )
-
-    gi_cairo_ext = Extension(
-        name='gi._gi_cairo',
-        sources=cairo_sources,
-        include_dirs=[script_dir, gi_dir],
-        depends=list_headers(script_dir) + list_headers(gi_dir),
-        define_macros=[("PY_SSIZE_T_CLEAN", None)],
-    )
+    ext_modules.append(gi_ext)
+
+    if WITH_CAIRO:
+        gi_cairo_ext = Extension(
+            name='gi._gi_cairo',
+            sources=cairo_sources,
+            include_dirs=[script_dir, gi_dir],
+            depends=list_headers(script_dir) + list_headers(gi_dir),
+            define_macros=[("PY_SSIZE_T_CLEAN", None)],
+        )
+        ext_modules.append(gi_cairo_ext)
+        install_requires.append(
+            "pycairo>=%s" % get_version_requirement(
+                get_pycairo_pkg_config_name()))
 
     version = pkginfo["Version"]
     if is_dev_version():
@@ -1212,10 +1237,7 @@ def main():
             "gi.repository",
             "gi.overrides",
         ],
-        ext_modules=[
-            gi_ext,
-            gi_cairo_ext,
-        ],
+        ext_modules=ext_modules,
         cmdclass={
             "build_ext": build_ext,
             "distcheck": distcheck,
@@ -1226,10 +1248,7 @@ def main():
             "install": install,
             "install_pkgconfig": install_pkgconfig,
         },
-        install_requires=[
-            "pycairo>=%s" % get_version_requirement(
-                get_pycairo_pkg_config_name()),
-        ],
+        install_requires=install_requires,
         data_files=[
             ('include/pygobject-3.0', ['gi/pygobject.h']),
         ],
index 1ab73f0..3723b07 100644 (file)
@@ -128,6 +128,19 @@ def init_test_environ():
     # always on for the tests.
     warnings.simplefilter('default', gi.PyGIDeprecationWarning)
 
+    # Otherwise we crash on the first gtk use when e.g. DISPLAY isn't set
+    try:
+        from gi.repository import Gtk
+    except ImportError:
+        pass
+    else:
+        if Gtk._version == "4.0":
+            res = Gtk.init_check()
+        else:
+            res = Gtk.init_check([])[0]
+        if not res:
+            raise RuntimeError("Gtk available, but Gtk.init_check() failed")
+
 
 init_test_environ()
 
index d4d6189..4debaf5 100644 (file)
@@ -2,18 +2,18 @@
  *
  * Copyright (C) 2016 Thibault Saunier <tsaunier@gnome.org>
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
  *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "gimarshallingtestsextra.h"
index 68ce1d1..bc5f8fe 100644 (file)
@@ -2,18 +2,18 @@
  *
  * Copyright (C) 2016 Thibault Saunier <tsaunier@gnome.org>
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
  *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #ifndef EXTRA_TESTS
index 0081a02..0068723 100644 (file)
@@ -24,6 +24,7 @@ def is_X11():
 
 
 @unittest.skipUnless(Gdk, 'Gdk not available')
+@unittest.skipIf(Gdk._version == "4.0", 'Gdk4 doesn\'t have GdkAtom')
 class TestGdkAtom(unittest.TestCase):
     def test_create(self):
         atom = Gdk.Atom.intern('my_string', False)
index 18e5d62..8ccbe15 100644 (file)
@@ -180,9 +180,16 @@ class TestRegion(unittest.TestCase):
 @unittest.skipUnless(has_cairo, 'built without cairo support')
 @unittest.skipUnless(Gtk, 'Gtk not available')
 class TestPango(unittest.TestCase):
+
     def test_cairo_font_options(self):
-        screen = Gtk.Window().get_screen()
-        font_opts = screen.get_font_options()
+        window = Gtk.Window()
+        if Gtk._version == "4.0":
+            window.set_font_options(cairo.FontOptions())
+            font_opts = window.get_font_options()
+        else:
+            screen = window.get_screen()
+            font_opts = screen.get_font_options()
+        assert font_opts is not None
         self.assertTrue(isinstance(font_opts.get_subpixel_order(), int))
 
 
index 6e38abe..18315af 100644 (file)
@@ -248,10 +248,3 @@ class TestGDBusClient(unittest.TestCase):
 
         self.assertTrue(isinstance(data['error'], Exception))
         self.assertTrue('InvalidArgs' in str(data['error']), str(data['error']))
-
-    def test_instantiate_custom_proxy(self):
-        class SomeProxy(Gio.DBusProxy):
-            def __init__(self):
-                Gio.DBusProxy.__init__(self)
-
-        SomeProxy()
index f0cc963..ee197d0 100644 (file)
@@ -390,6 +390,9 @@ def test_check_decorated_class():
     with pytest.raises(TypeError, match=".*on Widgets.*"):
         Gtk.Template.from_string("")(object())
 
+
+@pytest.mark.skipif(Gtk._version == "4.0", reason="errors out first with gtk4")
+def test_subclass_fail():
     @Gtk.Template.from_string("")
     class Base(Gtk.Widget):
         __gtype_name__ = new_gtype_name()
index 1dfe8e3..9c36674 100644 (file)
@@ -73,6 +73,7 @@ class TestGdk(unittest.TestCase):
         self.assertEqual(tuple(Gdk.RGBA(0.1, 0.2, 0.3, 0.4)),
                          (0.1, 0.2, 0.3, 0.4))
 
+    @unittest.skipIf(Gdk_version == "4.0", "not in gdk4")
     def test_event(self):
         event = Gdk.Event.new(Gdk.EventType.CONFIGURE)
         self.assertEqual(event.type, Gdk.EventType.CONFIGURE)
@@ -82,6 +83,7 @@ class TestGdk(unittest.TestCase):
         event.type = Gdk.EventType.SCROLL
         self.assertRaises(AttributeError, lambda: getattr(event, 'foo_bar'))
 
+    @unittest.skipIf(Gdk_version == "4.0", "not in gdk4")
     def test_event_touch(self):
         event = Gdk.Event.new(Gdk.EventType.TOUCH_BEGIN)
         self.assertEqual(event.type, Gdk.EventType.TOUCH_BEGIN)
@@ -94,6 +96,7 @@ class TestGdk(unittest.TestCase):
         self.assertTrue(event.emulating_pointer)
         self.assertTrue(event.touch.emulating_pointer)
 
+    @unittest.skipIf(Gdk_version == "4.0", "not in gdk4")
     def test_event_setattr(self):
         event = Gdk.Event.new(Gdk.EventType.DRAG_MOTION)
         event.x_root, event.y_root = 0, 5
@@ -106,6 +109,7 @@ class TestGdk(unittest.TestCase):
         self.assertFalse(hasattr(event, "foo_bar"))
         event.foo_bar = 42
 
+    @unittest.skipIf(Gdk_version == "4.0", "not in gdk4")
     def test_event_repr(self):
         event = Gdk.Event.new(Gdk.EventType.CONFIGURE)
         self.assertTrue("CONFIGURE" in repr(event))
index d83fa45..887a4f3 100644 (file)
@@ -6,10 +6,10 @@ from __future__ import absolute_import
 
 import contextlib
 import unittest
+import time
 import sys
 import gc
 import warnings
-import timeit
 
 from .helper import ignore_gi_deprecation_warnings, capture_glib_warnings
 
@@ -37,6 +37,9 @@ def gtkver():
             Gtk.get_micro_version())
 
 
+GTK4 = (Gtk._version == "4.0")
+
+
 @contextlib.contextmanager
 def realized(widget):
     """Makes sure the widget is realized.
@@ -49,7 +52,10 @@ def realized(widget):
     if isinstance(widget, Gtk.Window):
         toplevel = widget
     else:
-        toplevel = widget.get_parent_window()
+        if Gtk._version == "4.0":
+            toplevel = widget.get_parent_surface()
+        else:
+            toplevel = widget.get_parent_window()
 
     if toplevel is None:
         window = Gtk.Window()
@@ -81,18 +87,21 @@ def test_freeze_child_notif():
     c = Gtk.Button()
     c.connect("child-notify", on_notify)
     c.freeze_child_notify()
-    b.pack_start(c, True, True, 0)
-    b.child_set_property(c, "expand", False)
-    b.child_set_property(c, "expand", True)
+    if GTK4:
+        b.pack_start(c)
+    else:
+        b.pack_start(c, True, True, 0)
+    b.child_set_property(c, "pack-type", Gtk.PackType.END)
+    b.child_set_property(c, "pack-type", Gtk.PackType.START)
     c.thaw_child_notify()
-    assert events.count("expand") == 1
+    assert events.count("pack-type") == 1
     del events[:]
 
     with c.freeze_child_notify():
-        b.child_set_property(c, "expand", True)
-        b.child_set_property(c, "expand", False)
+        b.child_set_property(c, "pack-type", Gtk.PackType.END)
+        b.child_set_property(c, "pack-type", Gtk.PackType.START)
 
-    assert events.count("expand") == 1
+    assert events.count("pack-type") == 1
 
 
 @unittest.skipUnless(Gtk, 'Gtk not available')
@@ -102,6 +111,12 @@ def test_wrapper_toggle_refs():
             Gtk.Button.__init__(self)
             self._height = height
 
+        def do_measure(self, orientation, for_size):
+            if orientation == Gtk.Orientation.VERTICAL:
+                return (self._height, self._height, -1, -1)
+            else:
+                return (0, 0, -1, -1)
+
         def do_get_preferred_height(self):
             return (self._height, self._height)
 
@@ -109,11 +124,16 @@ def test_wrapper_toggle_refs():
     w = Gtk.Window()
     b = MyButton(height)
     w.add(b)
-    b.show_all()
+    if not GTK4:
+        b.show_all()
     del b
     gc.collect()
     gc.collect()
-    assert w.get_preferred_size().minimum_size.height == height
+    if GTK4:
+        # XXX: Why?
+        assert w.get_preferred_size().minimum_size.height == height + 10
+    else:
+        assert w.get_preferred_size().minimum_size.height == height
 
 
 @unittest.skipUnless(Gtk, 'Gtk not available')
@@ -257,8 +277,8 @@ class TestGtk(unittest.TestCase):
     def test_dialog_classes(self):
         self.assertEqual(Gtk.Dialog, gi.overrides.Gtk.Dialog)
         self.assertEqual(Gtk.FileChooserDialog, gi.overrides.Gtk.FileChooserDialog)
-        self.assertEqual(Gtk.RecentChooserDialog, gi.overrides.Gtk.RecentChooserDialog)
-        if Gtk_version != "4.0":
+        if not GTK4:
+            self.assertEqual(Gtk.RecentChooserDialog, gi.overrides.Gtk.RecentChooserDialog)
             self.assertEqual(Gtk.ColorSelectionDialog, gi.overrides.Gtk.ColorSelectionDialog)
             self.assertEqual(Gtk.FontSelectionDialog, gi.overrides.Gtk.FontSelectionDialog)
 
@@ -401,6 +421,7 @@ class TestGtk(unittest.TestCase):
         self.assertTrue(isinstance(dialog, Gtk.Window))
         self.assertEqual('font selection dialog test', dialog.get_title())
 
+    @unittest.skipIf(GTK4, "not in gtk4")
     def test_recent_chooser_dialog(self):
         test_manager = Gtk.RecentManager()
         dialog = Gtk.RecentChooserDialog(title='recent chooser dialog test',
@@ -578,7 +599,10 @@ class TestGtk(unittest.TestCase):
         widget.drag_dest_set_track_motion(True)
         widget.drag_dest_get_target_list()
         widget.drag_dest_set_target_list(None)
-        widget.drag_dest_set_target_list(Gtk.TargetList.new([Gtk.TargetEntry.new('test', 0, 0)]))
+        if GTK4:
+            widget.drag_dest_set_target_list(Gdk.ContentFormats.new([]))
+        else:
+            widget.drag_dest_set_target_list(Gtk.TargetList.new([Gtk.TargetEntry.new('test', 0, 0)]))
         widget.drag_dest_unset()
 
         widget.drag_highlight()
@@ -590,22 +614,26 @@ class TestGtk(unittest.TestCase):
         widget.drag_source_add_text_targets()
         widget.drag_source_add_uri_targets()
         widget.drag_source_set_icon_name("_About")
-        widget.drag_source_set_icon_pixbuf(GdkPixbuf.Pixbuf())
-        if Gtk_version != "4.0":
+        if not GTK4:
+            widget.drag_source_set_icon_pixbuf(GdkPixbuf.Pixbuf())
             widget.drag_source_set_icon_stock(Gtk.STOCK_ABOUT)
         widget.drag_source_get_target_list()
         widget.drag_source_set_target_list(None)
-        widget.drag_source_set_target_list(Gtk.TargetList.new([Gtk.TargetEntry.new('test', 0, 0)]))
+        if GTK4:
+            widget.drag_source_set_target_list(Gdk.ContentFormats.new([]))
+        else:
+            widget.drag_source_set_target_list(Gtk.TargetList.new([Gtk.TargetEntry.new('test', 0, 0)]))
         widget.drag_source_unset()
 
         # these methods cannot be called because they require a valid drag on
         # a real GdkWindow. So we only check that they exist and are callable.
-        if Gtk_version != "4.0":
+        if not GTK4:
             self.assertTrue(hasattr(widget, 'drag_dest_set_proxy'))
         self.assertTrue(hasattr(widget, 'drag_get_data'))
 
     @unittest.skipIf(sys.platform == "darwin", "crashes")
-    def test_drag_target_list(self):
+    @unittest.skipIf(GTK4, "uses lots of gtk3 only api")
+    def test_drag_target_list_gtk3(self):
         mixed_target_list = [Gtk.TargetEntry.new('test0', 0, 0),
                              ('test1', 1, 1),
                              Gtk.TargetEntry.new('test2', 2, 2),
@@ -765,12 +793,12 @@ class TestSignals(unittest.TestCase):
                 self._alloc_value = None
                 self._alloc_error = None
 
-            def do_size_allocate(self, alloc):
+            def do_size_allocate(self, *args):
                 self._alloc_called = True
-                self._alloc_value = alloc
+                self._alloc_value = args[0]
 
                 try:
-                    Gtk.ScrolledWindow.do_size_allocate(self, alloc)
+                    Gtk.ScrolledWindow.do_size_allocate(self, *args)
                 except Exception as e:
                     self._alloc_error = e
 
@@ -782,7 +810,10 @@ class TestSignals(unittest.TestCase):
         with realized(win):
             win.show()
             win.get_preferred_size()
-            win.size_allocate(rect)
+            if GTK4:
+                win.size_allocate(rect, 0)
+            else:
+                win.size_allocate(rect)
             self.assertTrue(win._alloc_called)
             self.assertIsInstance(win._alloc_value, Gdk.Rectangle)
             self.assertTrue(win._alloc_error is None, win._alloc_error)
@@ -1596,26 +1627,6 @@ class TestTreeModel(unittest.TestCase):
             ([0], [-1]), ([0, 0], [0]), ([0, 1], [None]), ([0, 2], [None]),
             ([0, 3], [4321]), ([0, 4], [1234])]
 
-    def test_tree_store_insert_before_none(self):
-        store = Gtk.TreeStore(object)
-        root = store.append(None)
-        sub = store.append(root)
-
-        iter_ = store.insert_before(None, None, [1])
-        assert store.get_path(iter_).get_indices() == [1]
-
-        iter_ = store.insert_before(root, None, [1])
-        assert store.get_path(iter_).get_indices() == [0, 1]
-
-        iter_ = store.insert_before(sub, None, [1])
-        assert store.get_path(iter_).get_indices() == [0, 0, 0]
-
-        iter_ = store.insert_before(None, root, [1])
-        assert store.get_path(iter_).get_indices() == [0]
-
-        iter_ = store.insert_before(None, sub, [1])
-        assert store.get_path(iter_).get_indices() == [1, 0]
-
     def test_tree_store_insert_after(self):
         store = Gtk.TreeStore(object)
         signals = []
@@ -1677,26 +1688,6 @@ class TestTreeModel(unittest.TestCase):
             ([0], [-1]), ([0, 0], [1234]), ([0, 1], [4321]),
             ([0, 2], [None]), ([0, 3], [None]), ([0, 4], [0])]
 
-    def test_tree_store_insert_after_none(self):
-        store = Gtk.TreeStore(object)
-        root = store.append(None)
-        sub = store.append(root)
-
-        iter_ = store.insert_after(None, None, [1])
-        assert store.get_path(iter_).get_indices() == [0]
-
-        iter_ = store.insert_after(root, None, [1])
-        assert store.get_path(iter_).get_indices() == [1, 0]
-
-        iter_ = store.insert_after(sub, None, [1])
-        assert store.get_path(iter_).get_indices() == [1, 1, 0]
-
-        iter_ = store.insert_after(None, root, [1])
-        assert store.get_path(iter_).get_indices() == [2]
-
-        iter_ = store.insert_after(None, sub, [1])
-        assert store.get_path(iter_).get_indices() == [1, 2]
-
     def test_tree_path(self):
         p1 = Gtk.TreePath()
         p2 = Gtk.TreePath.new_first()
@@ -2069,12 +2060,12 @@ class TestTreeModel(unittest.TestCase):
         model = Gtk.ListStore(int, str)
 
         iterations = 2000
-        start = timeit.default_timer()
+        start = time.clock()
         i = iterations
         while i > 0:
             model.append([1, 'hello'])
             i -= 1
-        end = timeit.default_timer()
+        end = time.clock()
         sys.stderr.write('[%.0f µs/append] ' % ((end - start) * 1000000 / iterations))
 
     def test_filter_new_default(self):
@@ -2393,7 +2384,7 @@ class TestContainer(unittest.TestCase):
         box = Gtk.Box()
         child = Gtk.Button()
         if Gtk_version == "4.0":
-            box.pack_start(child, expand=False, fill=True)
+            box.pack_start(child)
         else:
             box.pack_start(child, expand=False, fill=True, padding=42)
         with self.assertRaises(ValueError):
@@ -2415,19 +2406,3 @@ class TestContainer(unittest.TestCase):
         self.assertEqual(expand, False)
         self.assertEqual(fill, False)
         self.assertEqual(padding, 21)
-
-    @unittest.skipIf(Gtk_version != "4.0", "only in gtk4")
-    def test_child_get_and_set_gtk4(self):
-        # padding got removed in gtk4
-        box = Gtk.Box()
-        child = Gtk.Button()
-        box.pack_start(child, expand=True, fill=True)
-
-        expand, fill = box.child_get(child, 'expand', 'fill')
-        self.assertEqual(expand, True)
-        self.assertEqual(fill, True)
-
-        box.child_set(child, expand=False, fill=False, pack_type=1)
-        expand, fill, pack_type = box.child_get(child, 'expand', 'fill', 'pack-type')
-        self.assertEqual(expand, False)
-        self.assertEqual(fill, False)
index a789715..a08880b 100644 (file)
@@ -34,6 +34,26 @@ class TestPango(unittest.TestCase):
         layout.set_markup("Foobar")
         self.assertEqual(layout.get_text(), "Foobar")
 
+    def test_layout_set_markup(self):
+        context = Pango.Context()
+        layout = Pango.Layout(context)
+        layout.set_markup("abc")
+        assert layout.get_text() == "abc"
+        layout.set_markup("abc", -1)
+        assert layout.get_text() == "abc"
+        layout.set_markup("abc", 2)
+        assert layout.get_text() == "ab"
+
+    def test_layout_set_test(self):
+        context = Pango.Context()
+        layout = Pango.Layout(context)
+        layout.set_text("abc")
+        assert layout.get_text() == "abc"
+        layout.set_text("abc", -1)
+        assert layout.get_text() == "abc"
+        layout.set_text("abc", 2)
+        assert layout.get_text() == "ab"
+
     def test_break_keyword_escape(self):
         # https://bugzilla.gnome.org/show_bug.cgi?id=697363
         self.assertTrue(hasattr(Pango, 'break_'))
index de2e5c9..d324dfc 100644 (file)
 from __future__ import absolute_import
 
 import unittest
-try:
-    from collections import abc
-except ImportError:
-    import collections as abc
+import collections
 
 import gi._gi as GIRepository
 from gi.module import repository as repo
@@ -116,12 +113,12 @@ class Test(unittest.TestCase):
     def test_object_info(self):
         info = repo.find_by_name('GIMarshallingTests', 'Object')
         self.assertEqual(info.get_parent(), repo.find_by_name('GObject', 'Object'))
-        self.assertTrue(isinstance(info.get_methods(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_fields(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_interfaces(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_constants(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_vfuncs(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_properties(), abc.Iterable))
+        self.assertTrue(isinstance(info.get_methods(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_fields(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_interfaces(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_constants(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_vfuncs(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_properties(), collections.Iterable))
         self.assertFalse(info.get_abstract())
         self.assertEqual(info.get_class_struct(), repo.find_by_name('GIMarshallingTests', 'ObjectClass'))
         self.assertEqual(info.get_type_name(), 'GIMarshallingTestsObject')
@@ -161,12 +158,12 @@ class Test(unittest.TestCase):
 
     def test_interface_info(self):
         info = repo.find_by_name('GIMarshallingTests', 'Interface')
-        self.assertTrue(isinstance(info.get_methods(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_vfuncs(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_constants(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_prerequisites(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_properties(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_signals(), abc.Iterable))
+        self.assertTrue(isinstance(info.get_methods(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_vfuncs(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_constants(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_prerequisites(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_properties(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_signals(), collections.Iterable))
 
         method = info.find_method('test_int8_in')
         vfunc = info.find_vfunc('test_int8_in')
@@ -182,8 +179,8 @@ class Test(unittest.TestCase):
     def test_struct_info(self):
         info = repo.find_by_name('GIMarshallingTests', 'InterfaceIface')
         self.assertTrue(isinstance(info, GIRepository.StructInfo))
-        self.assertTrue(isinstance(info.get_fields(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_methods(), abc.Iterable))
+        self.assertTrue(isinstance(info.get_fields(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_methods(), collections.Iterable))
         self.assertTrue(isinstance(info.get_size(), int))
         self.assertTrue(isinstance(info.get_alignment(), int))
         self.assertTrue(info.is_gtype_struct())
@@ -192,16 +189,16 @@ class Test(unittest.TestCase):
     def test_enum_info(self):
         info = repo.find_by_name('GIMarshallingTests', 'Enum')
         self.assertTrue(isinstance(info, GIRepository.EnumInfo))
-        self.assertTrue(isinstance(info.get_values(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_methods(), abc.Iterable))
+        self.assertTrue(isinstance(info.get_values(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_methods(), collections.Iterable))
         self.assertFalse(info.is_flags())
         self.assertTrue(info.get_storage_type() > 0)  # might be platform dependent
 
     def test_union_info(self):
         info = repo.find_by_name('GIMarshallingTests', 'Union')
         self.assertTrue(isinstance(info, GIRepository.UnionInfo))
-        self.assertTrue(isinstance(info.get_fields(), abc.Iterable))
-        self.assertTrue(isinstance(info.get_methods(), abc.Iterable))
+        self.assertTrue(isinstance(info.get_fields(), collections.Iterable))
+        self.assertTrue(isinstance(info.get_methods(), collections.Iterable))
         self.assertTrue(isinstance(info.get_size(), int))
 
     def test_type_info(self):
@@ -248,7 +245,7 @@ class Test(unittest.TestCase):
     def test_callable_info(self):
         func_info = repo.find_by_name('GIMarshallingTests', 'array_fixed_out_struct')
         self.assertTrue(hasattr(func_info, 'invoke'))
-        self.assertTrue(isinstance(func_info.get_arguments(), abc.Iterable))
+        self.assertTrue(isinstance(func_info.get_arguments(), collections.Iterable))
         self.assertEqual(func_info.get_caller_owns(), GIRepository.Transfer.NOTHING)
         self.assertFalse(func_info.may_return_null())
         self.assertEqual(func_info.get_return_type().get_tag(), GIRepository.TypeTag.VOID)