From: DongHun Kwak Date: Wed, 25 Nov 2020 05:48:08 +0000 (+0900) Subject: Imported Upstream version 3.31.1 X-Git-Tag: upstream/3.31.1^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a4b0487dd4b3293e8b0705baf54c36f8d13d4455;p=platform%2Fupstream%2Fpython-gobject.git Imported Upstream version 3.31.1 --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 64ba37a..a1a569b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -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 index 0000000..297a70d --- /dev/null +++ b/.gitlab-ci/Dockerfile.gtk4 @@ -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 index 0000000..f6c979d --- /dev/null +++ b/.gitlab-ci/run-docker-gtk4.sh @@ -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 index 0000000..e36c715 --- /dev/null +++ b/.gitlab-ci/test-docker-gtk4.sh @@ -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 diff --git a/.gitlab-ci/test-docker.sh b/.gitlab-ci/test-docker.sh index 4837d51..141d654 100755 --- a/.gitlab-ci/test-docker.sh +++ b/.gitlab-ci/test-docker.sh @@ -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 diff --git a/.gitlab-ci/test-msys2.sh b/.gitlab-ci/test-msys2.sh index 38d4fbc..5e92370 100755 --- a/.gitlab-ci/test-msys2.sh +++ b/.gitlab-ci/test-msys2.sh @@ -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" diff --git a/MANIFEST.in b/MANIFEST.in index 25de999..72cb04c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -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 --- 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 `) -* 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 ` - 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 `) +* 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 diff --git a/PKG-INFO.in b/PKG-INFO.in index 34891d7..c357cc3 100644 --- a/PKG-INFO.in +++ b/PKG-INFO.in @@ -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 diff --git a/docs/devguide/dev_environ.rst b/docs/devguide/dev_environ.rst index db31678..ff84857 100644 --- a/docs/devguide/dev_environ.rst +++ b/docs/devguide/dev_environ.rst @@ -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 diff --git a/docs/index.rst b/docs/index.rst index 81fba33..7b383c1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -72,7 +72,7 @@ Who Is Using PyGObject? * `GNOME Music `__ - a music player for GNOME * `GNOME Tweak Tool `__ - a tool to customize advanced GNOME 3 options * `Gramps `__ - a genealogy program -* `Lollypop `__ - a modern music player +* `Lollypop `__ - a modern music player * `Meld `__ - a visual diff and merge tool * `MyPaint `__ - a nimble, distraction-free, and easy tool for digital painters * `Orca `__ - a flexible and extensible screen reader diff --git a/examples/demo/demos/TreeView/treemodel_filelist.py b/examples/demo/demos/TreeView/treemodel_filelist.py index 82e6d95..f011a47 100644 --- a/examples/demo/demos/TreeView/treemodel_filelist.py +++ b/examples/demo/demos/TreeView/treemodel_filelist.py @@ -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) diff --git a/examples/demo/demos/TreeView/treemodel_filetree.py b/examples/demo/demos/TreeView/treemodel_filetree.py index eb8e2b9..3b43190 100644 --- a/examples/demo/demos/TreeView/treemodel_filetree.py +++ b/examples/demo/demos/TreeView/treemodel_filetree.py @@ -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) diff --git a/gi/gimodule.c b/gi/gimodule.c index e25583e..3a61616 100644 --- a/gi/gimodule.c +++ b/gi/gimodule.c @@ -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 (); diff --git a/gi/overrides/GObject.py b/gi/overrides/GObject.py index fb548d8..4a6102d 100644 --- a/gi/overrides/GObject.py +++ b/gi/overrides/GObject.py @@ -21,10 +21,10 @@ # 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 '' % (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 diff --git a/gi/overrides/Gdk.py b/gi/overrides/Gdk.py index a1ef5f9..44914db 100644 --- a/gi/overrides/Gdk.py +++ b/gi/overrides/Gdk.py @@ -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): diff --git a/gi/overrides/Gtk.py b/gi/overrides/Gtk.py index 9cb92c0..7e00c61 100644 --- a/gi/overrides/Gtk.py +++ b/gi/overrides/Gtk.py @@ -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) diff --git a/gi/overrides/Pango.py b/gi/overrides/Pango.py index 067a628..7d0d8cf 100644 --- a/gi/overrides/Pango.py +++ b/gi/overrides/Pango.py @@ -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') diff --git a/gi/overrides/__init__.py b/gi/overrides/__init__.py index e262b6c..1572d25 100644 --- a/gi/overrides/__init__.py +++ b/gi/overrides/__init__.py @@ -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]: diff --git a/gi/pygi-array.c b/gi/pygi-array.c index 890e7c5..073e143 100644 --- a/gi/pygi-array.c +++ b/gi/pygi-array.c @@ -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 { diff --git a/gi/pygi-error.c b/gi/pygi-error.c index b0eeccd..8e4a793 100644 --- a/gi/pygi-error.c +++ b/gi/pygi-error.c @@ -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 diff --git a/gi/pygobject-object.c b/gi/pygobject-object.c index 39c03ca..dbf46e1 100644 --- a/gi/pygobject-object.c +++ b/gi/pygobject-object.c @@ -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. */ diff --git a/meson.build b/meson.build index 1b30bc1..db9bca3 100644 --- a/meson.build +++ b/meson.build @@ -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 index 0000000..830e77a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[build-system] +requires = ["setuptools", "wheel", "pycairo"] diff --git a/setup.py b/setup.py index fc46a4c..b4aa14c 100755 --- 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']), ], diff --git a/tests/__init__.py b/tests/__init__.py index 1ab73f0..3723b07 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -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() diff --git a/tests/gimarshallingtestsextra.c b/tests/gimarshallingtestsextra.c index d4d6189..4debaf5 100644 --- a/tests/gimarshallingtestsextra.c +++ b/tests/gimarshallingtestsextra.c @@ -2,18 +2,18 @@ * * Copyright (C) 2016 Thibault Saunier * - * 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 . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ #include "gimarshallingtestsextra.h" diff --git a/tests/gimarshallingtestsextra.h b/tests/gimarshallingtestsextra.h index 68ce1d1..bc5f8fe 100644 --- a/tests/gimarshallingtestsextra.h +++ b/tests/gimarshallingtestsextra.h @@ -2,18 +2,18 @@ * * Copyright (C) 2016 Thibault Saunier * - * 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 . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ #ifndef EXTRA_TESTS diff --git a/tests/test_atoms.py b/tests/test_atoms.py index 0081a02..0068723 100644 --- a/tests/test_atoms.py +++ b/tests/test_atoms.py @@ -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) diff --git a/tests/test_cairo.py b/tests/test_cairo.py index 18e5d62..8ccbe15 100644 --- a/tests/test_cairo.py +++ b/tests/test_cairo.py @@ -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)) diff --git a/tests/test_gdbus.py b/tests/test_gdbus.py index 6e38abe..18315af 100644 --- a/tests/test_gdbus.py +++ b/tests/test_gdbus.py @@ -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() diff --git a/tests/test_gtk_template.py b/tests/test_gtk_template.py index f0cc963..ee197d0 100644 --- a/tests/test_gtk_template.py +++ b/tests/test_gtk_template.py @@ -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() diff --git a/tests/test_overrides_gdk.py b/tests/test_overrides_gdk.py index 1dfe8e3..9c36674 100644 --- a/tests/test_overrides_gdk.py +++ b/tests/test_overrides_gdk.py @@ -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)) diff --git a/tests/test_overrides_gtk.py b/tests/test_overrides_gtk.py index d83fa45..887a4f3 100644 --- a/tests/test_overrides_gtk.py +++ b/tests/test_overrides_gtk.py @@ -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) diff --git a/tests/test_overrides_pango.py b/tests/test_overrides_pango.py index a789715..a08880b 100644 --- a/tests/test_overrides_pango.py +++ b/tests/test_overrides_pango.py @@ -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_')) diff --git a/tests/test_repository.py b/tests/test_repository.py index de2e5c9..d324dfc 100644 --- a/tests/test_repository.py +++ b/tests/test_repository.py @@ -23,10 +23,7 @@ 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)