Imported Upstream version 3.29.2 upstream/3.29.2
authorDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 30 Oct 2018 01:30:30 +0000 (10:30 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 30 Oct 2018 01:30:30 +0000 (10:30 +0900)
85 files changed:
.gitlab-ci.yml
.gitlab-ci/Dockerfile
.gitlab-ci/README.rst
.gitlab-ci/run-docker-old.sh
.gitlab-ci/run-docker.sh
.gitlab-ci/test-docker.sh
MANIFEST.in
NEWS
PKG-INFO
PyGObject.egg-info/PKG-INFO
PyGObject.egg-info/SOURCES.txt
README.rst
docs/contact.rst
docs/devguide/dev_environ.rst
docs/guide/api/error_handling.rst [new file with mode: 0644]
docs/guide/api/index.rst
gi/_compat.py
gi/_gtktemplate.py
gi/_option.py
gi/_ossighelper.py
gi/_propertyhelper.py
gi/gimodule.c
gi/importer.py
gi/meson.build [new file with mode: 0644]
gi/overrides/GLib.py
gi/overrides/GObject.py
gi/overrides/Gio.py
gi/overrides/Gtk.py
gi/overrides/meson.build [new file with mode: 0644]
gi/pygi-array.c
gi/pygi-basictype.c
gi/pygi-basictype.h
gi/pygi-boxed.c
gi/pygi-closure.c
gi/pygi-enum-marshal.c
gi/pygi-error.c
gi/pygi-hashtable.c
gi/pygi-info.c
gi/pygi-list.c
gi/pygi-object.c
gi/pygi-property.c
gi/pygi-python-compat.h
gi/pygi-resulttuple.c
gi/pygi-signal-closure.c
gi/pygi-source.c
gi/pygi-source.h
gi/pygi-struct-marshal.c
gi/pygi-type.c
gi/pygi-value.c
gi/pygobject-object.c
gi/pygobject.h
gi/pygoptioncontext.c
gi/repository/meson.build [new file with mode: 0644]
meson.build [new file with mode: 0644]
meson_options.txt [new file with mode: 0644]
pygtkcompat/meson.build [new file with mode: 0644]
pyproject.toml [deleted file]
setup.py
subprojects/glib.wrap [new file with mode: 0644]
subprojects/gobject-introspection.wrap [new file with mode: 0644]
subprojects/libffi.wrap [new file with mode: 0644]
tests/__init__.py
tests/conftest.py
tests/gimarshallingtestsextra.h
tests/meson.build [new file with mode: 0644]
tests/org.gnome.test.gschema.xml
tests/regressextra.h
tests/test_everything.py
tests/test_gi.py
tests/test_gio.py
tests/test_gobject.py
tests/test_gtk_template.py
tests/test_interface.py
tests/test_internal_api.py
tests/test_object_marshaling.py
tests/test_option.py
tests/test_overrides_gio.py [new file with mode: 0644]
tests/test_overrides_gtk.py
tests/test_properties.py
tests/test_signal.py
tests/test_source.py
tests/test_thread.py
tests/test_unknown.py
tests/testhelpermodule.c
tests/valgrind.supp [new file with mode: 0644]

index 584d308..f53bddf 100644 (file)
@@ -1,3 +1,5 @@
+image: registry.gitlab.gnome.org/gnome/pygobject/main:v6
+
 stages:
   - build_and_test
   - coverage
@@ -9,7 +11,6 @@ cache:
 
 .defaults: &defaults
   stage: build_and_test
-  image: lazka/pygobject:v3
   artifacts:
     paths:
       - coverage/
@@ -29,7 +30,6 @@ cache:
 
 coverage:
   stage: coverage
-  image: lazka/pygobject:v3
   artifacts:
     paths:
       - coverage/
@@ -40,7 +40,6 @@ coverage:
 
 pages:
   stage: deploy
-  image: lazka/pygobject:v3
   dependencies:
     - coverage
   script:
@@ -60,6 +59,7 @@ python2-mingw32:
   <<: *mingw-defaults
 
 python2-mingw64:
+  when: manual
   variables:
     PYTHON: "python2"
     MSYSTEM: "MINGW64"
@@ -67,6 +67,7 @@ python2-mingw64:
   <<: *mingw-defaults
 
 python3-mingw32:
+  when: manual
   variables:
     PYTHON: "python3"
     MSYSTEM: "MINGW32"
@@ -82,15 +83,11 @@ python3-mingw64:
 
 python2.7:
   variables:
-    PYENV_VERSION: "2.7.14"
-  <<: *defaults
-
-python3.4:
-  variables:
-    PYENV_VERSION: "3.4.8"
+    PYENV_VERSION: "2.7.15"
   <<: *defaults
 
 python3.5:
+  when: manual
   variables:
     PYENV_VERSION: "3.5.5"
   <<: *defaults
@@ -106,17 +103,20 @@ python3.7:
   <<: *defaults
 
 pypy2:
+  allow_failure: true
   variables:
-    PYENV_VERSION: "pypy2.7-5.10.0"
+    PYENV_VERSION: "pypy2.7-6.0.0"
   <<: *defaults
 
 pypy3:
+  when: manual
+  allow_failure: true
   variables:
-    PYENV_VERSION: "pypy3.5-5.10.1"
+    PYENV_VERSION: "pypy3.5-6.0.0"
   <<: *defaults
 
 xenial-i386-py2:
   stage: build_and_test
-  image: lazka/pygobject:pyenv-old
+  image: registry.gitlab.gnome.org/gnome/pygobject/old:v2
   script:
    - bash -x ./.gitlab-ci/test-docker-old.sh
index c1af1ce..cd1aa1b 100644 (file)
@@ -1,7 +1,6 @@
-FROM ubuntu:artful
+FROM ubuntu:bionic
 
 RUN apt-get update && apt-get install -y \
-    autoconf-archive \
     build-essential \
     ccache \
     curl \
@@ -19,8 +18,8 @@ RUN apt-get update && apt-get install -y \
     libreadline-dev \
     libsqlite3-dev \
     libssl-dev \
-    libtool \
-    locales \
+    ninja-build \
+    python3-pip \
     xauth \
     xvfb \
     && rm -rf /var/lib/apt/lists/*
@@ -36,15 +35,15 @@ ENV LANG C.UTF-8
 ENV CI true
 ENV PYENV_ROOT /home/user/.pyenv
 ENV PATH="${PYENV_ROOT}/shims:${PYENV_ROOT}/bin:${PATH}"
+ENV PYTHON_CONFIGURE_OPTS="--enable-shared"
 
 RUN curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
 
-RUN pyenv install 2.7.14
-RUN pyenv install 3.4.8
+RUN pyenv install 2.7.15
 RUN pyenv install 3.5.5
 RUN pyenv install 3.6.5
 RUN pyenv install 3.7.0b3
-RUN pyenv install pypy2.7-5.10.0
-RUN pyenv install pypy3.5-5.10.1
+RUN pyenv install pypy2.7-6.0.0
+RUN pyenv install pypy3.5-6.0.0
 
 ENV PATH="/usr/lib/ccache:${PATH}"
index 107abf0..3ebab30 100644 (file)
@@ -1 +1,2 @@
-The Dockerfile is available at https://hub.docker.com/r/lazka/pygobject/
+The images are available at
+https://gitlab.gnome.org/GNOME/pygobject/container_registry
index bdd6e54..4dd1752 100755 (executable)
@@ -2,10 +2,10 @@
 
 set -e
 
-TAG="lazka/pygobject:pyenv-old"
+TAG="registry.gitlab.gnome.org/gnome/pygobject/old:v2"
 
 sudo docker build --build-arg HOST_USER_ID="$UID" --tag "${TAG}" \
     --file "Dockerfile.old" .
-sudo docker run --rm \
+sudo docker run --rm --security-opt label=disable \
     --volume "$(pwd)/..:/home/user/app" --workdir "/home/user/app" \
     --tty --interactive "${TAG}" bash
index 5f8b7ea..aed8831 100755 (executable)
@@ -2,10 +2,10 @@
 
 set -e
 
-TAG="lazka/pygobject:v3"
+TAG="registry.gitlab.gnome.org/gnome/pygobject/main:v6"
 
 sudo docker build --build-arg HOST_USER_ID="$UID" --tag "${TAG}" \
     --file "Dockerfile" .
-sudo docker run -e PYENV_VERSION='3.6.5' --rm \
+sudo docker run -e PYENV_VERSION='3.6.5' --rm --security-opt label=disable \
     --volume "$(pwd)/..:/home/user/app" --workdir "/home/user/app" \
     --tty --interactive "${TAG}" bash
index 1b0af3b..46a195e 100755 (executable)
@@ -4,40 +4,44 @@ set -e
 
 python --version
 
-PYVER=$(python -c "import sys; sys.stdout.write(''.join(map(str, sys.version_info[:3])))")
+PYVER=$(python -c "import sys; sys.stdout.write('.'.join(map(str, sys.version_info[:2])))")
 PYIMPL=$(python -c "import sys, platform; sys.stdout.write(platform.python_implementation())")
 SOURCE_DIR="$(pwd)"
 COV_DIR="${SOURCE_DIR}/coverage"
 export MALLOC_CHECK_=3
 export MALLOC_PERTURB_=$((${RANDOM} % 255 + 1))
 export G_SLICE="debug-blocks"
-export COVERAGE_FILE="${COV_DIR}/.coverage.${PYVER}"
+export COVERAGE_FILE="${COV_DIR}/.coverage.${CI_JOB_NAME}"
 export CCACHE_BASEDIR="$(pwd)"
 export CCACHE_DIR="${CCACHE_BASEDIR}/_ccache"
 
 mkdir -p "${CCACHE_DIR}"
 mkdir -p "${COV_DIR}"
 
-if [[ "${PYIMPL}" == "PyPy" ]]; then
-    # https://bitbucket.org/pypy/pypy/issues/2776
-    export MALLOC_CHECK_=
-fi;
-
 python -m pip install git+https://github.com/pygobject/pycairo.git
 python -m pip install flake8 pytest pytest-faulthandler coverage
 
 export CFLAGS="-coverage -ftest-coverage -fprofile-arcs -Werror"
 
+# MESON
+/usr/bin/python3 -m pip install --user git+https://github.com/mesonbuild/meson.git
+export PATH="${HOME}/.local/bin:${PATH}"
+export PKG_CONFIG_PATH="$(python -c 'import sys; sys.stdout.write(sys.prefix)')/lib/pkgconfig"
+# pycairo install under PyPy doesn't install a .pc file
 if [[ "${PYIMPL}" == "PyPy" ]]; then
-    python setup.py build_tests
-    exit 0;
-fi;
+    meson _build -Dpython="$(which python)" -Dpycairo=false
+else
+    meson _build -Dpython="$(which python)"
+fi
+ninja -C _build
+xvfb-run -a meson test -C _build -v
+rm -Rf _build
 
 # CODE QUALITY
 python -m flake8
 
 # DOCUMENTATION CHECKS
-if [[ "${PYENV_VERSION}" == "2.7.14" ]]; then
+if [[ "${PYVER}" == "2.7" ]] && [[ "${PYIMPL}" == "CPython" ]]; then
     python -m pip install sphinx sphinx_rtd_theme
     python -m sphinx -W -a -E -b html -n docs docs/_build
 fi;
@@ -48,4 +52,4 @@ xvfb-run -a python -m coverage run tests/runtests.py
 
 # COLLECT GCOV COVERAGE
 lcov --rc lcov_branch_coverage=1 --directory . --capture --output-file \
-    "${COV_DIR}/${PYVER}.lcov"
+    "${COV_DIR}/${CI_JOB_NAME}.lcov"
index 10e97b6..25de999 100644 (file)
@@ -1,5 +1,4 @@
 include setup.cfg
-include pyproject.toml
 include COPYING
 include *.in
 include NEWS
@@ -7,8 +6,12 @@ include tools/pygi-convert.sh
 include pygobject.doap
 include README.rst
 include .gitlab-ci.yml
+include subprojects/*.wrap
+include meson.build
+include meson_options.txt
 recursive-include examples *.py *.png *.css *.ui *.gif *.gresource *.jpg *.xml
-recursive-include gi *.h
-recursive-include tests *.py *.c *.h *.xml
+recursive-include gi *.h meson.build
+recursive-include tests *.py *.c *.h *.xml *.supp meson.build
 recursive-include docs *.rst *.svg LICENSE *.ico *.png *.css *.py *.dia Makefile
 recursive-include .gitlab-ci *.sh *.rst *.py Dockerfile*
+recursive-include pygtkcompat meson.build
diff --git a/NEWS b/NEWS
index c989f91..fc87917 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,43 @@
+3.29.2 - 2018-05-16
+-------------------
+
+* Add a meson build system. :issue:`165`
+  (:user:`Mathieu Duponchelle<mathieudu>`)
+* Gtk.Template: Allow marking children as "internal-child". :mr:`58`
+* Gio.ListModel: implement most of the mutable sequence protocol.
+  :issue:`115` :mr:`59`
+* Gio.Settings: implement __iter__.
+* Gio.Settings: support range types in __setitem__. :issue:`134`
+* Add overrides for Gio.ListStore.sort and Gio.ListStore.insert_sorted.
+  :issue:`130`
+* Make Gtk.Widget.freeze_child_notify a context manager. :issue:`45`
+* OptionParser.parse_args: return leftover arguments. :issue:`200`
+* Release the GIL when emitting a signal. :mr:`66`
+  (John Bassett <john.bassett@pexip.com>)
+* Add ActionMap and ActionMap.add_action_entries() to overrides.
+  :issue:`29` :mr:`65` (:user:`yangfl`)
+* importer: raise ImportError in load_module() and not find_module().
+  :issue:`213`
+* Don't wrap GValue in GValue when creating GValueArray. :mr:`66`
+  (Stian Selnes <stian@pexip.com>)
+* ossig: Don't leak the callbacks in case the event loops are not stopped
+  through SIGINT. :issue:`219` :mr:`72`
+* Various fixes (Havard Graff <havard.graff@gmail.com>)
+* Destroy custom GLib.Source instances when they get freed. :issue:`193`
+* Revert "Add PEP518/pyproject.toml file", fixes installation with pip 10,
+  see https://github.com/pypa/pip/issues/5244
+* Various fixes/improvements for PyPy.
+* Don't crash on multiple calls to GObject.Value.__del__. :mr:`66`
+
+Documentation:
+  * Added StackOverflow (with PyGObject tag) as an contact resource.
+    (:user:`buhtz`)
+  * Add introduction to handling GLib.Error. :mr:`68`
+    (:user:`Kai Willadsen <kaiw>`)
+  * Add pycairo requires for development setup. :mr:`70`
+    (:user:`Kai Willadsen <kaiw>`)
+
+
 3.29.1 - 2018-04-15
 -------------------
 
index 8489119..08b8816 100644 (file)
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.2
 Name: PyGObject
-Version: 3.29.1
+Version: 3.29.2
 Summary: Python bindings for GObject Introspection
 Home-page: https://pygobject.readthedocs.io
 Author: James Henstridge
@@ -8,12 +8,7 @@ Author-email: james@daa.com.au
 Maintainer: Simon Feltman
 Maintainer-email: sfeltman@src.gnome.org
 License: GNU LGPL
-Description: .. image:: https://gitlab.gnome.org/GNOME/pygobject/badges/master/coverage.svg
-           :target: https://gnome.pages.gitlab.gnome.org/pygobject
-        
-        ᅟ
-        
-        .. image:: https://pygobject.readthedocs.io/en/latest/_images/pygobject.svg
+Description: .. image:: https://pygobject.readthedocs.io/en/latest/_images/pygobject.svg
            :align: center
            :width: 400px
            :height: 98px
@@ -27,9 +22,10 @@ Description: .. image:: https://gitlab.gnome.org/GNOME/pygobject/badges/master/c
         <https://developer.gnome.org/glib/stable/>`__, `GIO
         <https://developer.gnome.org/gio/stable/>`__ and many more.
         
-        It supports Linux, Windows and macOS and works with **Python 2.7+** as well as
-        **Python 3.4+**. PyGObject, including this documentation, is licensed under
-        the **LGPLv2.1+**.
+        It supports Linux, Windows and macOS and works with **Python 2.7+**, **Python
+        3.5+**, **PyPy** and **PyPy3**. PyGObject, including this documentation, is
+        licensed under the **LGPLv2.1+**.
+        
         
         ----
         
index 8489119..08b8816 100644 (file)
@@ -1,6 +1,6 @@
 Metadata-Version: 1.2
 Name: PyGObject
-Version: 3.29.1
+Version: 3.29.2
 Summary: Python bindings for GObject Introspection
 Home-page: https://pygobject.readthedocs.io
 Author: James Henstridge
@@ -8,12 +8,7 @@ Author-email: james@daa.com.au
 Maintainer: Simon Feltman
 Maintainer-email: sfeltman@src.gnome.org
 License: GNU LGPL
-Description: .. image:: https://gitlab.gnome.org/GNOME/pygobject/badges/master/coverage.svg
-           :target: https://gnome.pages.gitlab.gnome.org/pygobject
-        
-        ᅟ
-        
-        .. image:: https://pygobject.readthedocs.io/en/latest/_images/pygobject.svg
+Description: .. image:: https://pygobject.readthedocs.io/en/latest/_images/pygobject.svg
            :align: center
            :width: 400px
            :height: 98px
@@ -27,9 +22,10 @@ Description: .. image:: https://gitlab.gnome.org/GNOME/pygobject/badges/master/c
         <https://developer.gnome.org/glib/stable/>`__, `GIO
         <https://developer.gnome.org/gio/stable/>`__ and many more.
         
-        It supports Linux, Windows and macOS and works with **Python 2.7+** as well as
-        **Python 3.4+**. PyGObject, including this documentation, is licensed under
-        the **LGPLv2.1+**.
+        It supports Linux, Windows and macOS and works with **Python 2.7+**, **Python
+        3.5+**, **PyPy** and **PyPy3**. PyGObject, including this documentation, is
+        licensed under the **LGPLv2.1+**.
+        
         
         ----
         
index fdfb167..bc687c6 100644 (file)
@@ -4,9 +4,10 @@ MANIFEST.in
 NEWS
 PKG-INFO.in
 README.rst
+meson.build
+meson_options.txt
 pygobject-3.0.pc.in
 pygobject.doap
-pyproject.toml
 setup.cfg
 setup.py
 .gitlab-ci/Dockerfile
@@ -53,6 +54,7 @@ docs/guide/testing.rst
 docs/guide/threading.rst
 docs/guide/api/api.rst
 docs/guide/api/basic_types.rst
+docs/guide/api/error_handling.rst
 docs/guide/api/flags_enums.rst
 docs/guide/api/gobject.rst
 docs/guide/api/index.rst
@@ -149,6 +151,7 @@ gi/docstring.py
 gi/gimodule.c
 gi/gimodule.h
 gi/importer.py
+gi/meson.build
 gi/module.py
 gi/pygboxed.c
 gi/pygboxed.h
@@ -239,15 +242,22 @@ gi/overrides/Gtk.py
 gi/overrides/Pango.py
 gi/overrides/__init__.py
 gi/overrides/keysyms.py
+gi/overrides/meson.build
 gi/repository/__init__.py
+gi/repository/meson.build
 pygtkcompat/__init__.py
 pygtkcompat/generictreemodel.py
+pygtkcompat/meson.build
 pygtkcompat/pygtkcompat.py
+subprojects/glib.wrap
+subprojects/gobject-introspection.wrap
+subprojects/libffi.wrap
 tests/__init__.py
 tests/conftest.py
 tests/gimarshallingtestsextra.c
 tests/gimarshallingtestsextra.h
 tests/helper.py
+tests/meson.build
 tests/org.gnome.test.gschema.xml
 tests/regressextra.c
 tests/regressextra.h
@@ -281,6 +291,7 @@ tests/test_object_marshaling.py
 tests/test_option.py
 tests/test_ossig.py
 tests/test_overrides_gdk.py
+tests/test_overrides_gio.py
 tests/test_overrides_glib.py
 tests/test_overrides_gtk.py
 tests/test_overrides_pango.py
@@ -296,6 +307,7 @@ tests/test_thread.py
 tests/test_typeclass.py
 tests/test_unknown.py
 tests/testhelpermodule.c
+tests/valgrind.supp
 tests/gi/overrides/Regress.py
 tests/gi/overrides/__init__.py
 tools/pygi-convert.sh
\ No newline at end of file
index 26a5305..b08d664 100644 (file)
@@ -1,8 +1,3 @@
-.. image:: https://gitlab.gnome.org/GNOME/pygobject/badges/master/coverage.svg
-   :target: https://gnome.pages.gitlab.gnome.org/pygobject
-
-ᅟ
-
 .. image:: https://pygobject.readthedocs.io/en/latest/_images/pygobject.svg
    :align: center
    :width: 400px
 <https://developer.gnome.org/glib/stable/>`__, `GIO
 <https://developer.gnome.org/gio/stable/>`__ and many more.
 
-It supports Linux, Windows and macOS and works with **Python 2.7+** as well as
-**Python 3.4+**. PyGObject, including this documentation, is licensed under
-the **LGPLv2.1+**.
+It supports Linux, Windows and macOS and works with **Python 2.7+**, **Python
+3.5+**, **PyPy** and **PyPy3**. PyGObject, including this documentation, is
+licensed under the **LGPLv2.1+**.
+
 
 ----
 
index 82b97db..211de81 100644 (file)
@@ -17,5 +17,10 @@ Mailing List
     of the GNOME project use the mailing list at
     https://mail.gnome.org/mailman/listinfo/python-hackers-list.
 
+StackOverflow / StackExchange
+    If you have technical questions about PyGObject you can find answers on
+    `Stack Overflow <https://stackoverflow.com/questions/tagged/pygobject>`__.
+    When asking there please use the tag `PyGObject`.
+
 If you are unsure which communication channel to use **please use the issue
 tracker**.
index 265a551..db31678 100644 (file)
@@ -257,6 +257,7 @@ additional steps:
     pipenv --python 3
     pipenv install pytest
     pipenv install flake8
+    pipenv install pycairo
     pipenv shell
 
 
diff --git a/docs/guide/api/error_handling.rst b/docs/guide/api/error_handling.rst
new file mode 100644 (file)
index 0000000..e392cca
--- /dev/null
@@ -0,0 +1,48 @@
+==============
+Error Handling
+==============
+
+GLib has its own method of handling errors using :obj:`GLib.Error`. These are
+raised as Python exceptions, but with a few small differences.
+
+It's common in Python for exception subclasses to be used (e.g.,
+:obj:`ValueError` versus :obj:`IOError`) to distinguish different types of
+errors. Libraries often define their own :obj:`Exception` subclasses, and
+library users will handle these cases explicitly.
+
+In GLib-using libraries, errors are all :obj:`GLib.Error` instances, with no
+subclassing for different error types. Instead, every :obj:`GLib.Error`
+instance has attributes that distinguish types of error:
+
+* :attr:`GLib.Error.domain` is the error domain, usually a string that you can
+  convert to a ``GLib`` quark with :func:`GLib.quark_from_string`
+* :attr:`GLib.Error.code` identifies a specific error within the domain
+* :attr:`GLib.Error.message` is a human-readable description of the error
+
+Error domains are defined per-module, and you can get an error domain from
+``*_error_quark`` functions on the relevant module. For example, IO errors
+from ``Gio`` are in the domain returned by :func:`Gio.io_error_quark`, and
+possible error code values are enumerated in :obj:`Gio.IOErrorEnum`.
+
+Once you've caught a :obj:`GLib.Error`, you can call
+:meth:`GLib.Error.matches` to see whether it matches the specific error you
+want to handle.
+
+
+Examples
+--------
+
+Catching a specific error:
+
+.. code:: pycon
+
+    >>> from gi.repository import GLib, Gio
+    >>> f = Gio.File.new_for_path('missing-path')
+    >>> try:
+    ...     f.read()
+    ... except GLib.Error as err:
+    ...     if err.matches(Gio.io_error_quark(), Gio.IOErrorEnum.NOT_FOUND):
+    ...         print('File not found')
+    ...     else:
+    ...         raise
+    File not found
index efeade3..41f17ab 100644 (file)
@@ -10,3 +10,4 @@ API Reference
     basic_types
     flags_enums
     gobject
+    error_handling
index b8a3506..00f5fbb 100644 (file)
@@ -29,6 +29,8 @@ if sys.version_info[0] == 2:
     text_type = eval("unicode")
 
     reload = eval("reload")
+    xrange = eval("xrange")
+    cmp = eval("cmp")
 
     exec("def reraise(tp, value, tb):\n raise tp, value, tb")
 else:
@@ -47,6 +49,8 @@ else:
 
     from importlib import reload
     reload
+    xrange = range
+    cmp = lambda a, b: (a > b) - (a < b)
 
     def reraise(tp, value, tb):
         raise tp(value).with_traceback(tb)
index 37707e8..df9afdb 100644 (file)
@@ -83,7 +83,7 @@ def register_template(cls):
                         widget_name, attr_name, old_attr_name))
             else:
                 bound_widgets[widget_name] = attr_name
-                cls.bind_template_child_full(widget_name, False, 0)
+                cls.bind_template_child_full(widget_name, obj._internal, 0)
 
     cls.__gtktemplate_methods__ = bound_methods
     cls.__gtktemplate_widgets__ = bound_widgets
@@ -122,8 +122,11 @@ def init_template(self, cls, base_init_template):
 
 class Child(object):
 
-    def __init__(self, name=None):
+    def __init__(self, name=None, **kwargs):
         self._name = name
+        self._internal = kwargs.pop("internal", False)
+        if kwargs:
+            raise TypeError("Unhandled arguments: %r" % kwargs)
 
 
 class CallThing(object):
index 3791137..9babb2a 100644 (file)
@@ -341,7 +341,6 @@ class OptionParser(optparse.OptionParser):
         rargs[:] = context.parse([sys.argv[0]] + rargs)[1:]
 
     def parse_args(self, args=None, values=None):
-        old_args = args or []
         try:
             options, args = optparse.OptionParser.parse_args(
                 self, args, values)
@@ -362,7 +361,6 @@ class OptionParser(optparse.OptionParser):
             for key, value in group.values.__dict__.items():
                 options.ensure_value(key, value)
 
-        args = args[2:-len(old_args)]
         return options, args
 
 
index 5176e97..213f096 100644 (file)
@@ -224,8 +224,9 @@ def register_sigint_fallback(callback):
             try:
                 yield
             finally:
+                cb = _callback_stack.pop()
                 if _sigint_called:
-                    _callback_stack.pop()()
+                    cb()
         else:
             # There is a signal handler set by the user, just do nothing
             yield
@@ -242,9 +243,11 @@ def register_sigint_fallback(callback):
         _callback_stack.pop()()
 
     _callback_stack.append(callback)
-    with sigint_handler_set_and_restore_default(sigint_handler):
-        try:
+    try:
+        with sigint_handler_set_and_restore_default(sigint_handler):
             yield
-        finally:
-            if _sigint_called:
-                signal.default_int_handler(signal.SIGINT, None)
+    finally:
+        if _sigint_called:
+            signal.default_int_handler(signal.SIGINT, None)
+        else:
+            _callback_stack.pop()
index a4b5e17..6f82ab6 100644 (file)
@@ -17,8 +17,6 @@
 # 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/>.
 
-import traceback
-
 from . import _gi
 from ._compat import string_types, long_
 from ._constants import \
@@ -209,19 +207,7 @@ class Property(object):
             return self
 
         self._exc = None
-
-        # Simply return the result of fget directly, no need to go through GObject.
-        # See: https://bugzilla.gnome.org/show_bug.cgi?id=723872
-        # We catch and print any exception occurring within the fget for compatibility
-        # prior to the fast path addition from bug 723872, this should eventually
-        # be removed and exceptions raised directly to the caller as in:
-        # https://bugzilla.gnome.org/show_bug.cgi?id=575652
-        try:
-            value = self.fget(instance)
-        except Exception:
-            traceback.print_exc()
-            value = None
-
+        value = self.fget(instance)
         if self._exc:
             exc = self._exc
             self._exc = None
index 8e5d722..e46af02 100644 (file)
@@ -1030,7 +1030,7 @@ pygobject__g_instance_init(GTypeInstance   *instance,
                            gpointer         g_class)
 {
     GObject *object = (GObject *) instance;
-    PyObject *wrapper, *args, *kwargs;
+    PyObject *wrapper, *result;
     PyGILState_STATE state;
 
     wrapper = g_object_get_qdata(object, pygobject_wrapper_key);
@@ -1057,18 +1057,16 @@ pygobject__g_instance_init(GTypeInstance   *instance,
          * so we don't destroy the wrapper. The next call to pygobject_new_full
          * will take the ref */
         pygobject_ref_float ((PyGObject *) wrapper);
-        args = PyTuple_New(0);
-        kwargs = PyDict_New();
-        if (Py_TYPE(wrapper)->tp_init(wrapper, args, kwargs))
-            PyErr_Print();
 
-        Py_DECREF(args);
-        Py_DECREF(kwargs);
+        result = PyObject_CallMethod (wrapper, "__init__", NULL);
+        if (result == NULL)
+            PyErr_Print ();
+        else
+            Py_DECREF (result);
     }
 
     /* XXX: used for Gtk.Template */
     if (PyObject_HasAttrString (wrapper, "__dontuse_ginstance_init__")) {
-        PyObject *result;
         result = PyObject_CallMethod (wrapper, "__dontuse_ginstance_init__", NULL);
         if (result == NULL)
             PyErr_Print ();
@@ -1887,12 +1885,6 @@ _wrap_pyg_variant_type_from_string (PyObject *self, PyObject *args)
     return py_variant;
 }
 
-static PyObject *
-_wrap_pyg_source_new (PyObject *self, PyObject *args)
-{
-    return pyg_source_new ();
-}
-
 #define CHUNK_SIZE 8192
 
 static PyObject*
@@ -2246,7 +2238,34 @@ _wrap_pyig_pyos_getsig (PyObject *self, PyObject *args)
     return PyLong_FromVoidPtr ((void *)(PyOS_getsig (sig_num)));
 }
 
+static PyObject *
+_wrap_pygobject_new_full (PyObject *self, PyObject *args)
+{
+    PyObject *ptr_value, *long_value;
+    PyObject *steal;
+    GObject *obj;
+
+    if (!PyArg_ParseTuple (args, "OO", &ptr_value, &steal))
+        return NULL;
+
+    long_value = PyNumber_Long (ptr_value);
+    if (!long_value) {
+        PyErr_SetString (PyExc_TypeError, "first argument must be an integer");
+        return NULL;
+    }
+    obj = PyLong_AsVoidPtr (long_value);
+    Py_DECREF (long_value);
+
+    if (!G_IS_OBJECT (obj)) {
+        PyErr_SetString (PyExc_TypeError, "pointer is not a GObject");
+        return NULL;
+    }
+
+    return pygobject_new_full (obj, PyObject_IsTrue (steal), NULL);
+}
+
 static PyMethodDef _gi_functions[] = {
+    { "pygobject_new_full", (PyCFunction) _wrap_pygobject_new_full, METH_VARARGS },
     { "enum_add", (PyCFunction) _wrap_pyg_enum_add, METH_VARARGS | METH_KEYWORDS },
     { "enum_register_new_gtype_and_add", (PyCFunction) _wrap_pyg_enum_register_new_gtype_and_add, METH_VARARGS | METH_KEYWORDS },
     { "flags_add", (PyCFunction) _wrap_pyg_flags_add, METH_VARARGS | METH_KEYWORDS },
@@ -2255,9 +2274,9 @@ static PyMethodDef _gi_functions[] = {
     { "register_interface_info", (PyCFunction) _wrap_pyg_register_interface_info, METH_VARARGS },
     { "hook_up_vfunc_implementation", (PyCFunction) _wrap_pyg_hook_up_vfunc_implementation, METH_VARARGS },
     { "variant_type_from_string", (PyCFunction) _wrap_pyg_variant_type_from_string, METH_VARARGS },
-    { "source_new", (PyCFunction) _wrap_pyg_source_new, METH_NOARGS },
+    { "source_new", (PyCFunction) pygi_source_new, METH_NOARGS },
     { "pyos_getsig", (PyCFunction) _wrap_pyig_pyos_getsig, METH_VARARGS },
-    { "source_set_callback", (PyCFunction) pyg_source_set_callback, METH_VARARGS },
+    { "source_set_callback", (PyCFunction) pygi_source_set_callback, METH_VARARGS },
     { "io_channel_read", (PyCFunction) pyg_channel_read, METH_VARARGS },
     { "require_foreign", (PyCFunction) pygi_require_foreign, METH_VARARGS | METH_KEYWORDS },
     { "spawn_async",
@@ -2380,7 +2399,7 @@ pygi_register_api(PyObject *d)
 {
     PyObject *api;
 
-    api = PYGLIB_CPointer_WrapPointer(&pygobject_api_functions, "gobject._PyGObject_API");
+    api = PyCapsule_New (&pygobject_api_functions, "gobject._PyGObject_API", NULL);
     if (api == NULL)
         return -1;
     PyDict_SetItemString(d, "_PyGObject_API", api);
@@ -2533,7 +2552,7 @@ PYGLIB_MODULE_START(_gi, "_gi")
     Py_INCREF(PyGIDeprecationWarning);
     PyModule_AddObject(module, "PyGIDeprecationWarning", PyGIDeprecationWarning);
 
-    api = PYGLIB_CPointer_WrapPointer ( (void *) &CAPI, "gi._API");
+    api = PyCapsule_New ( (void *) &CAPI, "gi._API", NULL);
     if (api == NULL) {
         return PYGLIB_MODULE_ERROR_RETURN;
     }
index 4ed6196..e14d47b 100644 (file)
@@ -116,15 +116,7 @@ class DynamicImporter(object):
         if path != self.path:
             return
 
-        # is_registered() is faster than enumerate_versions() and
-        # in the common case of a namespace getting loaded before its
-        # dependencies, is_registered() returns True for all dependencies.
-        if repository.is_registered(namespace) or \
-                repository.enumerate_versions(namespace):
-            return self
-        else:
-            raise ImportError('cannot import name %s, '
-                              'introspection typelib not found' % namespace)
+        return self
 
     def load_module(self, fullname):
         if fullname in sys.modules:
@@ -132,6 +124,14 @@ class DynamicImporter(object):
 
         path, namespace = fullname.rsplit('.', 1)
 
+        # is_registered() is faster than enumerate_versions() and
+        # in the common case of a namespace getting loaded before its
+        # dependencies, is_registered() returns True for all dependencies.
+        if not repository.is_registered(namespace) and not \
+                repository.enumerate_versions(namespace):
+            raise ImportError('cannot import name %s, '
+                              'introspection typelib not found' % namespace)
+
         stacklevel = get_import_stacklevel(import_hook=True)
         with _check_require_version(namespace, stacklevel=stacklevel):
             try:
diff --git a/gi/meson.build b/gi/meson.build
new file mode 100644 (file)
index 0000000..ded10ac
--- /dev/null
@@ -0,0 +1,85 @@
+sources = [
+  'pygboxed.c',
+  'pygenum.c',
+  'pygflags.c',
+  'pyginterface.c',
+  'pygobject-object.c',
+  'pygparamspec.c',
+  'pygpointer.c',
+  'pygoptioncontext.c',
+  'pygoptiongroup.c',
+  'pygspawn.c',
+  'gimodule.c',
+  'pygi-repository.c',
+  'pygi-info.c',
+  'pygi-foreign.c',
+  'pygi-struct.c',
+  'pygi-source.c',
+  'pygi-argument.c',
+  'pygi-resulttuple.c',
+  'pygi-type.c',
+  'pygi-boxed.c',
+  'pygi-closure.c',
+  'pygi-ccallback.c',
+  'pygi-util.c',
+  'pygi-property.c',
+  'pygi-signal-closure.c',
+  'pygi-invoke.c',
+  'pygi-cache.c',
+  'pygi-marshal-cleanup.c',
+  'pygi-basictype.c',
+  'pygi-list.c',
+  'pygi-array.c',
+  'pygi-error.c',
+  'pygi-object.c',
+  'pygi-value.c',
+  'pygi-enum-marshal.c',
+  'pygi-struct-marshal.c',
+  'pygi-hashtable.c']
+
+headers = [
+  'pygobject.h'
+]
+
+install_headers(headers, subdir : 'pygobject-@0@'.format(platform_version))
+
+python_sources = [
+  '_compat.py',
+  '_constants.py',
+  'docstring.py',
+  '_error.py',
+  '_gtktemplate.py',
+  'importer.py',
+  '__init__.py',
+  'module.py',
+  '_option.py',
+  '_ossighelper.py',
+  '_propertyhelper.py',
+  'pygtkcompat.py',
+  '_signalhelper.py',
+  'types.py',
+]
+
+python.install_sources(python_sources,
+  pure : false,
+  subdir : 'gi'
+)
+
+giext = python.extension_module('_gi', sources,
+  dependencies : [python_dep, glib_dep, gi_dep, ffi_dep],
+  include_directories: include_directories('..'),
+  install: true,
+  subdir : 'gi',
+  c_args: pyext_c_args + main_c_args
+)
+
+if with_pycairo
+  gicairoext = python.extension_module('_gi_cairo', ['pygi-foreign-cairo.c'],
+    dependencies : [python_dep, glib_dep, gi_dep, ffi_dep, pycairo_dep, cairo_dep, cairo_gobject_dep],
+    install: true,
+    subdir : 'gi',
+    c_args: pyext_c_args + main_c_args)
+endif
+
+subdir('overrides')
+subdir('repository')
index ed1f573..d717d5f 100644 (file)
@@ -514,7 +514,7 @@ __all__.append('MainContext')
 
 class Source(GLib.Source):
     def __new__(cls, *args, **kwargs):
-        # use our custom pyg_source_new() here as g_source_new() is not
+        # use our custom pygi_source_new() here as g_source_new() is not
         # bindable
         source = source_new()
         source.__class__ = cls
@@ -526,11 +526,12 @@ class Source(GLib.Source):
 
     def __del__(self):
         if hasattr(self, '__pygi_custom_source'):
-            self.unref()
+            self.destroy()
+        super(Source, self).__del__()
 
     def set_callback(self, fn, user_data=None):
         if hasattr(self, '__pygi_custom_source'):
-            # use our custom pyg_source_set_callback() if for a GSource object
+            # use our custom pygi_source_set_callback() if for a GSource object
             # with custom functions
             source_set_callback(self, fn, user_data)
         else:
index f758e6d..312119d 100644 (file)
@@ -218,8 +218,9 @@ class Value(GObjectModule.Value):
                 self.set_value(py_value)
 
     def __del__(self):
-        if self._free_on_dealloc and self.g_type != TYPE_INVALID:
-            self.unset()
+        if self._is_valid:
+            if self._free_on_dealloc and self.g_type != TYPE_INVALID:
+                self.unset()
 
         # We must call base class __del__() after unset.
         super(Value, self).__del__()
index 5ab23fc..b282c2a 100644 (file)
@@ -23,6 +23,8 @@ import warnings
 from .._ossighelper import wakeup_on_signal, register_sigint_fallback
 from ..overrides import override, deprecated_init
 from ..module import get_introspection_module
+from .._compat import xrange
+from gi._gi import pygobject_new_full
 from gi import PyGIWarning
 
 from gi.repository import GLib
@@ -62,6 +64,84 @@ VolumeMonitor = override(VolumeMonitor)
 __all__.append('VolumeMonitor')
 
 
+class ActionMap(Gio.ActionMap):
+    def add_action_entries(self, entries, user_data=None):
+        """
+        The add_action_entries() method is a convenience function for creating
+        multiple Gio.SimpleAction instances and adding them to a Gio.ActionMap.
+        Each action is constructed as per one entry.
+
+        :param list entries:
+            List of entry tuples for add_action() method. The entry tuple can
+            vary in size with the following information:
+
+                * The name of the action. Must be specified.
+                * The callback to connect to the "activate" signal of the
+                  action. Since GLib 2.40, this can be None for stateful
+                  actions, in which case the default handler is used. For
+                  boolean-stated actions with no parameter, this is a toggle.
+                  For other state types (and parameter type equal to the state
+                  type) this will be a function that just calls change_state
+                  (which you should provide).
+                * The type of the parameter that must be passed to the activate
+                  function for this action, given as a single GLib.Variant type
+                  string (or None for no parameter)
+                * The initial state for this action, given in GLib.Variant text
+                  format. The state is parsed with no extra type information, so
+                  type tags must be added to the string if they are necessary.
+                  Stateless actions should give None here.
+                * The callback to connect to the "change-state" signal of the
+                  action. All stateful actions should provide a handler here;
+                  stateless actions should not.
+
+        :param user_data:
+            The user data for signal connections, or None
+        """
+        try:
+            iter(entries)
+        except (TypeError):
+            raise TypeError('entries must be iterable')
+
+        def _process_action(name, activate=None, parameter_type=None,
+                            state=None, change_state=None):
+            if parameter_type:
+                if not GLib.VariantType.string_is_valid(parameter_type):
+                    raise TypeError("The type string '%s' given as the "
+                                    "parameter type for action '%s' is "
+                                    "not a valid GVariant type string. " %
+                                    (parameter_type, name))
+                variant_parameter = GLib.VariantType.new(parameter_type)
+            else:
+                variant_parameter = None
+
+            if state is not None:
+                # stateful action
+                variant_state = GLib.Variant.parse(None, state, None, None)
+                action = Gio.SimpleAction.new_stateful(name, variant_parameter,
+                                                       variant_state)
+                if change_state is not None:
+                    action.connect('change-state', change_state, user_data)
+            else:
+                # stateless action
+                if change_state is not None:
+                    raise ValueError("Stateless action '%s' should give "
+                                     "None for 'change_state', not '%s'." %
+                                     (name, change_state))
+                action = Gio.SimpleAction(name=name, parameter_type=variant_parameter)
+
+            if activate is not None:
+                action.connect('activate', activate, user_data)
+            self.add_action(action)
+
+        for entry in entries:
+            # using inner function above since entries can leave out optional arguments
+            _process_action(*entry)
+
+
+ActionMap = override(ActionMap)
+__all__.append('ActionMap')
+
+
 class FileEnumerator(Gio.FileEnumerator):
     def __iter__(self):
         return self
@@ -104,6 +184,10 @@ class Settings(Gio.Settings):
     def __len__(self):
         return len(self.list_keys())
 
+    def __iter__(self):
+        for key in self.list_keys():
+            yield key
+
     def __bool__(self):
         # for "if mysettings" we don't want a dictionary-like test here, just
         # if the object isn't None
@@ -140,6 +224,13 @@ class Settings(Gio.Settings):
             allowed = v.unpack()
             if value not in allowed:
                 raise ValueError('value %s is not an allowed enum (%s)' % (value, allowed))
+        elif type_ == 'range':
+            tuple_ = v.get_child_value(0)
+            type_str = tuple_.get_child_value(0).get_type_string()
+            min_, max_ = tuple_.unpack()
+            if value < min_ or value > max_:
+                raise ValueError(
+                    'value %s not in range (%s - %s)' % (value, min_, max_))
         else:
             raise NotImplementedError('Cannot handle allowed type range class ' + str(type_))
 
@@ -271,3 +362,127 @@ class DBusProxy(Gio.DBusProxy):
 
 DBusProxy = override(DBusProxy)
 __all__.append('DBusProxy')
+
+
+class ListModel(Gio.ListModel):
+
+    def __getitem__(self, key):
+        if isinstance(key, slice):
+            return [self.get_item(i) for i in xrange(*key.indices(len(self)))]
+        elif isinstance(key, int):
+            if key < 0:
+                key += len(self)
+            if key < 0:
+                raise IndexError
+            ret = self.get_item(key)
+            if ret is None:
+                raise IndexError
+            return ret
+        else:
+            raise TypeError
+
+    def __contains__(self, item):
+        pytype = self.get_item_type().pytype
+        if not isinstance(item, pytype):
+            raise TypeError(
+                "Expected type %s.%s" % (pytype.__module__, pytype.__name__))
+        for i in self:
+            if i == item:
+                return True
+        return False
+
+    def __len__(self):
+        return self.get_n_items()
+
+    def __iter__(self):
+        for i in xrange(len(self)):
+            yield self.get_item(i)
+
+
+ListModel = override(ListModel)
+__all__.append('ListModel')
+
+
+def _wrap_list_store_sort_func(func):
+
+    def wrap(a, b, *user_data):
+        a = pygobject_new_full(a, False)
+        b = pygobject_new_full(b, False)
+        return func(a, b, *user_data)
+
+    return wrap
+
+
+class ListStore(Gio.ListStore):
+
+    def sort(self, compare_func, *user_data):
+        compare_func = _wrap_list_store_sort_func(compare_func)
+        return super(ListStore, self).sort(compare_func, *user_data)
+
+    def insert_sorted(self, item, compare_func, *user_data):
+        compare_func = _wrap_list_store_sort_func(compare_func)
+        return super(ListStore, self).insert_sorted(
+            item, compare_func, *user_data)
+
+    def __delitem__(self, key):
+        if isinstance(key, slice):
+            start, stop, step = key.indices(len(self))
+            if step == 1:
+                self.splice(start, max(stop - start, 0), [])
+            elif step == -1:
+                self.splice(stop + 1, max(start - stop, 0), [])
+            else:
+                for i in sorted(xrange(start, stop, step), reverse=True):
+                    self.remove(i)
+        elif isinstance(key, int):
+            if key < 0:
+                key += len(self)
+            if key < 0 or key >= len(self):
+                raise IndexError
+            self.remove(key)
+        else:
+            raise TypeError
+
+    def __setitem__(self, key, value):
+        if isinstance(key, slice):
+            pytype = self.get_item_type().pytype
+            valuelist = []
+            for v in value:
+                if not isinstance(v, pytype):
+                    raise TypeError(
+                        "Expected type %s.%s" % (
+                            pytype.__module__, pytype.__name__))
+                valuelist.append(v)
+
+            start, stop, step = key.indices(len(self))
+            if step == 1:
+                self.__delitem__(key)
+                for v in reversed(valuelist):
+                    self.insert(start, v)
+            else:
+                indices = list(xrange(start, stop, step))
+                if len(indices) != len(valuelist):
+                    raise ValueError
+                for i, v in zip(indices, valuelist):
+                    self.remove(i)
+                    self.insert(i, v)
+        elif isinstance(key, int):
+            if key < 0:
+                key += len(self)
+            if key < 0 or key >= len(self):
+                raise IndexError
+
+            pytype = self.get_item_type().pytype
+            if not isinstance(value, pytype):
+                raise TypeError(
+                    "Expected type %s.%s" % (
+                        pytype.__module__, pytype.__name__))
+
+            self.remove(key)
+            self.insert(key, value)
+        else:
+            raise TypeError
+
+
+ListStore = override(ListStore)
+__all__.append('ListStore')
index 23d06ad..612c07d 100644 (file)
@@ -118,10 +118,25 @@ def _builder_connect_callback(builder, gobj, signal_name, handler_name, connect_
             gobj.connect(signal_name, handler, *args)
 
 
+class _FreezeNotifyManager(object):
+    def __init__(self, obj):
+        self.obj = obj
+
+    def __enter__(self):
+        pass
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        self.obj.thaw_child_notify()
+
+
 class Widget(Gtk.Widget):
 
     translate_coordinates = strip_boolean_result(Gtk.Widget.translate_coordinates)
 
+    def freeze_child_notify(self):
+        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))
diff --git a/gi/overrides/meson.build b/gi/overrides/meson.build
new file mode 100644 (file)
index 0000000..b8ecae9
--- /dev/null
@@ -0,0 +1,14 @@
+python_sources = [
+  'GLib.py',
+  'Gtk.py',
+  'Gdk.py',
+  'GObject.py',
+  'Gio.py',
+  'GIMarshallingTests.py',
+  'Pango.py',
+  'keysyms.py',
+  '__init__.py']
+
+python.install_sources(python_sources,
+  subdir : join_paths('gi', 'overrides')
+)
index a0634dc..073e143 100644 (file)
@@ -215,7 +215,7 @@ _pygi_marshal_from_py_array (PyGIInvokeState   *state,
 
     if (!PySequence_Check (py_arg)) {
         PyErr_Format (PyExc_TypeError, "Must be sequence, not %s",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
         return FALSE;
     }
 
index dad79c5..3ffd711 100644 (file)
@@ -47,8 +47,8 @@ pygi_gpointer_from_py (PyObject *py_arg, gpointer *result)
     if (py_arg == Py_None) {
         *result = NULL;
         return TRUE;
-    } else if (PYGLIB_CPointer_Check(py_arg)) {
-        temp = PYGLIB_CPointer_GetPointer (py_arg, NULL);
+    } else if (PyCapsule_CheckExact (py_arg)) {
+        temp = PyCapsule_GetPointer (py_arg, NULL);
         if (temp == NULL)
             return FALSE;
         *result = temp;
@@ -102,7 +102,7 @@ base_float_checks (PyObject *object)
 {
     if (!PyNumber_Check (object)) {
         PyErr_Format (PyExc_TypeError, "Must be number, not %s",
-                      object->ob_type->tp_name);
+                      Py_TYPE (object)->tp_name);
         return NULL;
     }
 
@@ -212,7 +212,7 @@ pygi_gunichar_from_py (PyObject *py_arg, gunichar *result)
 #endif
     } else {
        PyErr_Format (PyExc_TypeError, "Must be string, not %s",
-                     py_arg->ob_type->tp_name);
+                     Py_TYPE (py_arg)->tp_name);
        return FALSE;
     }
 
@@ -260,7 +260,7 @@ pygi_gtype_from_py (PyObject *py_arg, GType *type)
 
     if (temp == 0) {
         PyErr_Format (PyExc_TypeError, "Must be gobject.GType, not %s",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
         return FALSE;
     }
 
@@ -294,7 +294,7 @@ pygi_utf8_from_py (PyObject *py_arg, gchar **result)
 #endif
     else {
         PyErr_Format (PyExc_TypeError, "Must be string, not %s",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
         return FALSE;
     }
 
@@ -343,7 +343,7 @@ filename_from_py_unix (PyObject *py_arg, gchar **result)
         Py_DECREF (bytes);
     } else {
         PyErr_Format (PyExc_TypeError, "Must be bytes, not %s",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
         return FALSE;
     }
 
@@ -387,7 +387,7 @@ filename_from_py_win32 (PyObject *py_arg, gchar **result)
         Py_DECREF (bytes);
     } else {
         PyErr_Format (PyExc_TypeError, "Must be unicode, not %s",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
         return FALSE;
     }
 #else
@@ -437,7 +437,7 @@ filename_from_py_win32 (PyObject *py_arg, gchar **result)
         Py_DECREF (bytes);
     } else {
         PyErr_Format (PyExc_TypeError, "Must be str, not %s",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
         return FALSE;
     }
 #endif
@@ -463,7 +463,7 @@ base_number_checks (PyObject *object)
 
     if (!PyNumber_Check (object)) {
         PyErr_Format (PyExc_TypeError, "Must be number, not %s",
-                      object->ob_type->tp_name);
+                      Py_TYPE (object)->tp_name);
         return NULL;
     }
 
@@ -505,6 +505,55 @@ pygi_gboolean_to_py (gboolean value)
     return PyBool_FromLong (value);
 }
 
+/* A super set of pygi_gint8_from_py (also handles unicode) */
+gboolean
+pygi_gschar_from_py (PyObject *object, gint8 *result)
+{
+    if (PyUnicode_Check (object)) {
+        gunichar uni;
+        PyObject *temp;
+        gboolean status;
+
+        if (!pygi_gunichar_from_py (object, &uni))
+            return FALSE;
+
+        temp = pygi_guint32_to_py (uni);
+        status = pygi_gint8_from_py (temp, result);
+        Py_DECREF (temp);
+        return status;
+    } else {
+        /* pygi_gint8_from_py handles numbers and bytes */
+        return pygi_gint8_from_py (object, result);
+    }
+
+    return FALSE;
+}
+
+/* A super set of pygi_guint8_from_py (also handles unicode) */
+gboolean
+pygi_guchar_from_py (PyObject *object, guchar *result)
+{
+    if (PyUnicode_Check (object)) {
+        gunichar uni;
+        PyObject *temp;
+        gboolean status;
+        gint8 codepoint;
+
+        if (!pygi_gunichar_from_py (object, &uni))
+            return FALSE;
+
+        temp = pygi_guint32_to_py (uni);
+        status = pygi_gint8_from_py (temp, &codepoint);
+        Py_DECREF (temp);
+        if (status)
+            *result = (guchar)codepoint;
+        return status;
+    } else {
+        /* pygi_guint8_from_py handles numbers and bytes */
+        return pygi_guint8_from_py (object, result);
+    }
+}
+
 gboolean
 pygi_gint_from_py (PyObject *object, gint *result)
 {
@@ -662,7 +711,7 @@ pygi_gulong_to_py (gulong value)
         return PyLong_FromUnsignedLong (value);
 }
 
-static gboolean
+gboolean
 pygi_gint8_from_py (PyObject *object, gint8 *result)
 {
     long long_value;
@@ -710,7 +759,7 @@ pygi_gint8_to_py (gint8 value)
     return PYGLIB_PyLong_FromLong (value);
 }
 
-static gboolean
+gboolean
 pygi_guint8_from_py (PyObject *object, guint8 *result)
 {
     long long_value;
@@ -904,7 +953,7 @@ overflow:
     return FALSE;
 }
 
-static PyObject *
+PyObject *
 pygi_guint32_to_py (guint32 value)
 {
 #if (G_MAXUINT <= LONG_MAX)
index 396cedc..04d520a 100644 (file)
@@ -66,6 +66,7 @@ PyObject *pygi_gulong_to_py (gulong value);
 PyObject *pygi_filename_to_py (gchar *value);
 PyObject *pygi_gsize_to_py (gsize value);
 PyObject *pygi_gssize_to_py (gssize value);
+PyObject *pygi_guint32_to_py (guint32 value);
 
 gboolean pygi_gboolean_from_py (PyObject *object, gboolean *result);
 gboolean pygi_gint64_from_py (PyObject *object, gint64 *result);
@@ -78,6 +79,10 @@ gboolean pygi_gulong_from_py (PyObject *object, gulong *result);
 gboolean pygi_gint_from_py (PyObject *object, gint *result);
 gboolean pygi_guint_from_py (PyObject *object, guint *result);
 gboolean pygi_gunichar_from_py (PyObject *py_arg, gunichar *result);
+gboolean pygi_gint8_from_py (PyObject *object, gint8 *result);
+gboolean pygi_gschar_from_py (PyObject *object, gint8 *result);
+gboolean pygi_guint8_from_py (PyObject *object, guint8 *result);
+gboolean pygi_guchar_from_py (PyObject *object, guchar *result);
 
 G_END_DECLS
 
index ce3d748..6a48159 100644 (file)
@@ -140,7 +140,7 @@ boxed_init (PyObject *self,
 
     if (!PyArg_ParseTupleAndKeywords (args, kwargs, "", kwlist)) {
         PyErr_Clear ();
-        PyErr_Warn (PyExc_TypeError,
+        PyErr_Warn (PyExc_DeprecationWarning,
                 "Passing arguments to gi.types.Boxed.__init__() is deprecated. "
                 "All arguments passed will be ignored.");
     }
@@ -193,6 +193,12 @@ boxed_get_free_on_dealloc(PyGIBoxed *self, void *closure)
   return pygi_gboolean_to_py( ((PyGBoxed *)self)->free_on_dealloc );
 }
 
+static PyObject *
+boxed_get_is_valid (PyGIBoxed *self, void *closure)
+{
+  return pygi_gboolean_to_py (pyg_boxed_get_ptr (self) != NULL);
+}
+
 /**
  * pygi_boxed_copy_in_place:
  *
@@ -219,6 +225,7 @@ pygi_boxed_copy_in_place (PyGIBoxed *self)
 
 static PyGetSetDef pygi_boxed_getsets[] = {
     { "_free_on_dealloc", (getter)boxed_get_free_on_dealloc, (setter)0 },
+    { "_is_valid", (getter)boxed_get_is_valid, (setter)0 },
     { NULL, 0, 0 }
 };
 
index 0eef41e..d878876 100644 (file)
@@ -741,7 +741,7 @@ _pygi_marshal_from_py_interface_callback (PyGIInvokeState   *state,
     if (!PyCallable_Check (py_arg)) {
         PyErr_Format (PyExc_TypeError,
                       "Callback needs to be a function or method not %s",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
 
         return FALSE;
     }
index e98d023..fc33089 100644 (file)
@@ -181,7 +181,7 @@ err:
     if (interface)
         g_base_info_unref (interface);
     PyErr_Format (PyExc_TypeError, "Expected a %s, but got %s",
-                  iface_cache->type_name, py_arg->ob_type->tp_name);
+                  iface_cache->type_name, Py_TYPE (py_arg)->tp_name);
     return FALSE;
 }
 
@@ -228,7 +228,7 @@ _pygi_marshal_from_py_interface_flags (PyGIInvokeState   *state,
 
 err:
     PyErr_Format (PyExc_TypeError, "Expected a %s, but got %s",
-                  iface_cache->type_name, py_arg->ob_type->tp_name);
+                  iface_cache->type_name, Py_TYPE (py_arg)->tp_name);
     return FALSE;
 
 }
index 85aaf29..8e4a793 100644 (file)
@@ -125,7 +125,7 @@ pygi_error_marshal_from_py (PyObject *pyerr, GError **error)
 
     if (PyObject_IsInstance (pyerr, PyGError) != 1) {
         PyErr_Format (PyExc_TypeError, "Must be GLib.Error, not %s",
-                      pyerr->ob_type->tp_name);
+                      Py_TYPE (pyerr)->tp_name);
         return FALSE;
     }
 
index f0cda78..285eca1 100644 (file)
@@ -70,7 +70,7 @@ _pygi_marshal_from_py_ghash (PyGIInvokeState   *state,
     py_keys = PyMapping_Keys (py_arg);
     if (py_keys == NULL) {
         PyErr_Format (PyExc_TypeError, "Must be mapping, not %s",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
         return FALSE;
     }
 
index d52be76..7010158 100644 (file)
@@ -96,6 +96,7 @@ _get_child_info_by_name (PyGIBaseInfo *self, PyObject *py_name,
         return NULL;
 
     info = get_child_info_by_name ((GIObjectInfo*)self->info, name);
+    g_free (name);
     if (info == NULL) {
         Py_RETURN_NONE;
     }
@@ -326,6 +327,7 @@ _wrap_g_base_info_get_attribute (PyGIBaseInfo *self, PyObject *arg)
         return NULL;
 
     value = g_base_info_get_attribute (self->info, name);
+    g_free (name);
     if (value == NULL) {
         Py_RETURN_NONE;
     }
@@ -598,12 +600,7 @@ _function_info_call (PyGICallableInfo *self, PyObject *args, PyObject *kwargs)
                 py_str_name = tmp;
             }
 
-#if PY_VERSION_HEX < 0x03000000
-            str_name = PyString_AsString (py_str_name);
-#else
-            str_name = PyBytes_AsString (py_str_name);
-#endif
-
+            str_name = PYGLIB_PyBytes_AsString (py_str_name);
             if (strcmp (str_name, _safe_base_info_get_name (container_info))) {
                 PyErr_Format (PyExc_TypeError,
                               "%s constructor cannot be used to create instances of "
@@ -741,10 +738,11 @@ _wrap_g_callable_info_get_return_attribute (PyGIBaseInfo *self, PyObject *py_nam
 
     attr = g_callable_info_get_return_attribute (self->info, name);
     if (attr) {
-        return pygi_utf8_to_py (
-                g_callable_info_get_return_attribute (self->info, name));
+        g_free (name);
+        return pygi_utf8_to_py (attr);
     } else {
         PyErr_Format(PyExc_AttributeError, "return attribute %s not found", name);
+        g_free (name);
         return NULL;
     }
 }
index b298623..712c372 100644 (file)
@@ -50,7 +50,7 @@ _pygi_marshal_from_py_glist (PyGIInvokeState   *state,
 
     if (!PySequence_Check (py_arg)) {
         PyErr_Format (PyExc_TypeError, "Must be sequence, not %s",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
         return FALSE;
     }
 
@@ -127,7 +127,7 @@ _pygi_marshal_from_py_gslist (PyGIInvokeState   *state,
 
     if (!PySequence_Check (py_arg)) {
         PyErr_Format (PyExc_TypeError, "Must be sequence, not %s",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
         return FALSE;
     }
 
index 07fd599..bd5e085 100644 (file)
@@ -96,7 +96,7 @@ pygi_arg_gobject_out_arg_from_py (PyObject *py_arg, /*in*/
      * https://bugzilla.gnome.org/show_bug.cgi?id=693393
      */
     gobj = arg->v_pointer;
-    if (py_arg->ob_refcnt == 1 && gobj->ref_count == 1) {
+    if (Py_REFCNT (py_arg) == 1 && gobj->ref_count == 1) {
         /* If both object ref counts are only 1 at this point (the reference held
          * in a return tuple), we assume the GObject will be free'd before reaching
          * its target and become invalid. So instead of getting invalid object errors
@@ -163,7 +163,7 @@ _pygi_marshal_from_py_interface_object (PyGIInvokeState             *state,
                       ( (PyGIInterfaceCache *)arg_cache)->type_name,
                       module ? PYGLIB_PyUnicode_AsString(module) : "",
                       module ? "." : "",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
         if (module)
             Py_DECREF (module);
         return FALSE;
index b2f36c6..595167b 100644 (file)
@@ -107,16 +107,8 @@ pygi_call_do_get_property (PyObject *instance, GParamSpec *pspec)
 
     py_pspec = pyg_param_spec_new (pspec);
     retval = PyObject_CallMethod (instance, "do_get_property", "O", py_pspec);
-    if (retval == NULL) {
-        PyErr_Print();
-    }
-
     Py_DECREF (py_pspec);
-    if (retval) {
-        return retval;
-    }
-
-    Py_RETURN_NONE;
+    return retval;
 }
 
 PyObject *
index 78c6660..76999db 100644 (file)
 
 #include <Python.h>
 
-# define PYGLIB_CPointer_Check PyCapsule_CheckExact
-# define PYGLIB_CPointer_WrapPointer(ptr, typename) \
-    PyCapsule_New(ptr, typename, NULL)
-# define PYGLIB_CPointer_GetPointer(obj, typename) \
-    PyCapsule_GetPointer(obj, typename)
-# define PYGLIB_CPointer_Import(module, symbol) \
-    PyCapsule_Import(##module##.##symbol##, FALSE)
-
 #define PYGLIB_MODULE_ERROR_RETURN NULL
 
 #ifdef __GNUC__
 /* Compilation on Python 2.x */
 #if PY_VERSION_HEX < 0x03000000
 
-#define RO READONLY
-
-#define PYGLIB_PyBaseString_Check(ob) (PyString_Check(ob) || PyUnicode_Check(ob))
-
 #define PYGLIB_PyUnicode_Check PyString_Check
 #define PYGLIB_PyUnicode_AsString PyString_AsString
 #define PYGLIB_PyUnicode_AsStringAndSize PyString_AsStringAndSize
 #define PYGLIB_PyUnicode_FromString PyString_FromString
 #define PYGLIB_PyUnicode_FromStringAndSize PyString_FromStringAndSize
 #define PYGLIB_PyUnicode_FromFormat PyString_FromFormat
-#define PYGLIB_PyUnicode_AS_STRING PyString_AS_STRING
-#define PYGLIB_PyUnicode_GET_SIZE PyString_GET_SIZE
 #define PYGLIB_PyUnicode_Type PyString_Type
 #define PYGLIB_PyUnicode_InternFromString PyString_InternFromString
 #define PYGLIB_PyUnicode_InternInPlace PyString_InternInPlace
@@ -78,7 +64,6 @@
 #define PYGLIB_PyLongObject PyIntObject
 #define PYGLIB_PyLong_Type PyInt_Type
 #define PYGLIB_PyLong_AS_LONG PyInt_AS_LONG
-#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
 
 #define PYGLIB_Py_hash_t long
 
 
 #define PYGLIB_PyNumber_Long PyNumber_Int
 
-#ifndef PyVarObject_HEAD_INIT
-#define PyVarObject_HEAD_INIT(base, size) \
-  PyObject_HEAD_INIT(base) \
-  size,
-#endif
-
 #define PYGLIB_MODULE_START(symbol, modname)           \
 PyObject * pyglib_##symbol##_module_create(void);       \
 PYGI_MODINIT_FUNC init##symbol(void);                   \
@@ -168,8 +147,6 @@ PyTypeObject symbol = {                                 \
            return -1;                                         \
     PyDict_SetItemString(d, name, (PyObject *)&type);
 
-#define PYGLIB_PyBaseString_Check PyUnicode_Check
-
 #define PYGLIB_PyUnicode_Check PyUnicode_Check
 #define PYGLIB_PyUnicode_AsString _PyUnicode_AsString
 #define PYGLIB_PyUnicode_AsStringAndSize(obj, buf, size) \
@@ -177,7 +154,6 @@ PyTypeObject symbol = {                                 \
 #define PYGLIB_PyUnicode_FromString PyUnicode_FromString
 #define PYGLIB_PyUnicode_FromStringAndSize PyUnicode_FromStringAndSize
 #define PYGLIB_PyUnicode_FromFormat PyUnicode_FromFormat
-#define PYGLIB_PyUnicode_GET_SIZE PyUnicode_GET_SIZE
 #define PYGLIB_PyUnicode_Resize PyUnicode_Resize
 #define PYGLIB_PyUnicode_Type PyUnicode_Type
 #define PYGLIB_PyUnicode_InternFromString PyUnicode_InternFromString
index f3a58e1..0d060d0 100644 (file)
@@ -125,7 +125,7 @@ resulttuple_dir(PyObject *self)
     Py_DECREF (mapping_attr);
     if (mapping == NULL)
         goto error;
-    items = PyObject_Dir ((PyObject*)self->ob_type);
+    items = PyObject_Dir ((PyObject*)Py_TYPE (self));
     if (items == NULL)
         goto error;
     mapping_values = PyDict_Keys (mapping);
index d34044d..d553d76 100644 (file)
@@ -224,7 +224,7 @@ pygi_signal_closure_marshal(GClosure *closure,
     list_item = pass_by_ref_structs;
     while (list_item) {
         PyObject *item = list_item->data;
-        if (item->ob_refcnt > 1) {
+        if (Py_REFCNT (item) > 1) {
             pygi_boxed_copy_in_place ((PyGIBoxed *)item);
         }
         list_item = g_slist_next (list_item);
index b49b7d5..c6cdfda 100644 (file)
@@ -38,7 +38,7 @@ typedef struct
 } PyGRealSource;
 
 static gboolean
-pyg_source_prepare(GSource *source, gint *timeout)
+source_prepare(GSource *source, gint *timeout)
 {
     PyGRealSource *pysource = (PyGRealSource *)source;
     PyObject *t;
@@ -91,7 +91,7 @@ bail:
 }
 
 static gboolean
-pyg_source_check(GSource *source)
+source_check(GSource *source)
 {
     PyGRealSource *pysource = (PyGRealSource *)source;
     PyObject *t;
@@ -116,7 +116,7 @@ pyg_source_check(GSource *source)
 }
 
 static gboolean
-pyg_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
+source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
 {
     PyGRealSource *pysource = (PyGRealSource *)source;
     PyObject *func, *args, *tuple, *t;
@@ -151,7 +151,7 @@ pyg_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
 }
 
 static void
-pyg_source_finalize(GSource *source)
+source_finalize(GSource *source)
 {
     PyGRealSource *pysource = (PyGRealSource *)source;
     PyObject *func, *t;
@@ -178,10 +178,10 @@ pyg_source_finalize(GSource *source)
 
 static GSourceFuncs pyg_source_funcs =
 {
-    pyg_source_prepare,
-    pyg_source_check,
-    pyg_source_dispatch,
-    pyg_source_finalize
+    source_prepare,
+    source_check,
+    source_dispatch,
+    source_finalize
 };
 
 /**
@@ -192,7 +192,7 @@ static GSourceFuncs pyg_source_funcs =
  * call Py_DECREF on the data.
  */
 static void
-_pyglib_destroy_notify(gpointer user_data)
+destroy_notify(gpointer user_data)
 {
     PyObject *obj = (PyObject *)user_data;
     PyGILState_STATE state;
@@ -203,7 +203,7 @@ _pyglib_destroy_notify(gpointer user_data)
 }
 
 static gboolean
-_pyglib_handler_marshal(gpointer user_data)
+handler_marshal(gpointer user_data)
 {
     PyObject *tuple, *ret;
     gboolean res;
@@ -230,7 +230,7 @@ _pyglib_handler_marshal(gpointer user_data)
 }
 
 PyObject *
-pyg_source_set_callback(PyGObject *self_module, PyObject *args)
+pygi_source_set_callback (PyGObject *self_module, PyObject *args)
 {
     PyObject *self, *first, *callback, *cbargs = NULL, *data;
     Py_ssize_t len;
@@ -268,30 +268,42 @@ pyg_source_set_callback(PyGObject *self_module, PyObject *args)
        return NULL;
 
     g_source_set_callback(pyg_boxed_get (self, GSource),
-                         _pyglib_handler_marshal, data,
-                         _pyglib_destroy_notify);
+                         handler_marshal, data,
+                         destroy_notify);
 
     Py_INCREF(Py_None);
     return Py_None;
 }
 
 /**
- * pyg_source_new:
+ * pygi_source_new:
  *
  * Wrap the un-bindable g_source_new() and provide wrapper callbacks in the
  * GSourceFuncs which call back to Python.
+ *
+ * Returns NULL on error and sets an exception.
  */
 PyObject*
-pyg_source_new (void)
+pygi_source_new (PyObject *self, PyObject *args)
 {
-    PyGRealSource *source = NULL;
-    PyObject      *py_type;
+    PyGRealSource *source;
+    PyObject *py_type, *boxed;
 
-    source = (PyGRealSource*) g_source_new (&pyg_source_funcs, sizeof (PyGRealSource));
+    g_assert (args == NULL);
 
     py_type = pygi_type_import_by_name ("GLib", "Source");
+    if (!py_type)
+        return NULL;
+
+    source = (PyGRealSource*) g_source_new (&pyg_source_funcs, sizeof (PyGRealSource));
     /* g_source_new uses malloc, not slices */
-    source->obj = pygi_boxed_new ( (PyTypeObject *) py_type, source, FALSE, 0);
+    boxed = pygi_boxed_new ( (PyTypeObject *) py_type, source, TRUE, 0);
+    Py_DECREF (py_type);
+    if (!boxed) {
+        g_source_unref ((GSource *)source);
+        return NULL;
+    }
+    source->obj = boxed;
 
     return source->obj;
 }
index a602767..5d60c63 100644 (file)
@@ -24,8 +24,8 @@
 #ifndef __PYGI_SOURCE_H__
 #define __PYGI_SOURCE_H__
 
-PyObject *pyg_source_new (void);
-PyObject *pyg_source_set_callback (PyGObject *self, PyObject *args);
+PyObject *pygi_source_new (PyObject *self, PyObject *args);
+PyObject *pygi_source_set_callback (PyGObject *self, PyObject *args);
 
 #endif /* __PYGI_SOURCE_H__ */
 
index a387715..a7705a5 100644 (file)
@@ -106,7 +106,7 @@ pygi_arg_gvalue_from_py_marshal (PyObject *py_arg,
     GValue *value;
     GType object_type;
 
-    object_type = pyg_type_from_object_strict ( (PyObject *) py_arg->ob_type, FALSE);
+    object_type = pyg_type_from_object_strict ( (PyObject *) Py_TYPE (py_arg), FALSE);
     if (object_type == G_TYPE_INVALID) {
         PyErr_SetString (PyExc_RuntimeError, "unable to retrieve object's GType");
         return FALSE;
@@ -145,7 +145,7 @@ pygi_arg_gvalue_from_py_cleanup (PyGIInvokeState *state,
     /* Note py_arg can be NULL for hash table which is a bug. */
     if (was_processed && py_arg != NULL) {
         GType py_object_type =
-            pyg_type_from_object_strict ( (PyObject *) py_arg->ob_type, FALSE);
+            pyg_type_from_object_strict ( (PyObject *) Py_TYPE (py_arg), FALSE);
 
         /* When a GValue was not passed, it means the marshalers created a new
          * one to pass in, clean this up.
@@ -172,7 +172,7 @@ pygi_arg_gclosure_from_py_marshal (PyObject   *py_arg,
     if ( !(PyCallable_Check(py_arg) ||
            g_type_is_a (object_gtype, G_TYPE_CLOSURE))) {
         PyErr_Format (PyExc_TypeError, "Must be callable, not %s",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
         return FALSE;
     }
 
@@ -314,7 +314,7 @@ type_error:
                       type_name,
                       module ? PYGLIB_PyUnicode_AsString(module) : "",
                       module ? "." : "",
-                      py_arg->ob_type->tp_name);
+                      Py_TYPE (py_arg)->tp_name);
         if (module)
             Py_DECREF (module);
         g_free (type_name);
index 0c5a428..3ca0fc8 100644 (file)
@@ -1055,7 +1055,7 @@ pyg_signal_class_closure_marshal(GClosure *closure,
     for (i = 0; i < len; i++) {
        PyObject *item = PyTuple_GetItem(params, i);
        if (item != NULL && PyObject_TypeCheck(item, &PyGBoxed_Type)
-           && item->ob_refcnt != 1) {
+           && Py_REFCNT (item) != 1) {
            PyGBoxed* boxed_item = (PyGBoxed*)item;
            if (!boxed_item->free_on_dealloc) {
                gpointer boxed_ptr = pyg_boxed_get_ptr (boxed_item);
index c4416fc..9366934 100644 (file)
@@ -196,8 +196,6 @@ pyg_value_array_from_pyobject(GValue *value,
     for (i = 0; i < len; ++i) {
         PyObject *item = PySequence_GetItem(obj, i);
         GType type;
-        GValue item_value = { 0, };
-        int status;
 
         if (! item) {
             PyErr_Clear();
@@ -219,20 +217,27 @@ pyg_value_array_from_pyobject(GValue *value,
             }
         }
 
-        g_value_init(&item_value, type);
-        status = (pspec && pspec->element_spec)
-                 ? pyg_param_gvalue_from_pyobject(&item_value, item, pspec->element_spec)
-                 : pyg_value_from_pyobject(&item_value, item);
-        Py_DECREF(item);
+        if (type == G_TYPE_VALUE) {
+            const GValue * item_value = pyg_boxed_get(item, GValue);
+            g_value_array_append(value_array, item_value);
+        } else {
+            GValue item_value = { 0, };
+            int status;
 
-        if (status == -1) {
-            g_value_array_free(value_array);
+            g_value_init(&item_value, type);
+            status = (pspec && pspec->element_spec)
+                ? pyg_param_gvalue_from_pyobject(&item_value, item, pspec->element_spec)
+                : pyg_value_from_pyobject(&item_value, item);
+            Py_DECREF(item);
+
+            if (status == -1) {
+                g_value_array_free(value_array);
+                g_value_unset(&item_value);
+                return -1;
+            }
+            g_value_array_append(value_array, &item_value);
             g_value_unset(&item_value);
-            return -1;
         }
-
-        g_value_array_append(value_array, &item_value);
-        g_value_unset(&item_value);
     }
 
     g_value_take_boxed(value, value_array);
@@ -312,7 +317,6 @@ pyg_array_from_pyobject(GValue *value,
 int
 pyg_value_from_pyobject_with_error(GValue *value, PyObject *obj)
 {
-    PyObject *tmp;
     GType value_type = G_VALUE_TYPE(value);
 
     switch (G_TYPE_FUNDAMENTAL(value_type)) {
@@ -339,50 +343,23 @@ pyg_value_from_pyobject_with_error(GValue *value, PyObject *obj)
         }
         break;
     case G_TYPE_CHAR:
-        if (PYGLIB_PyLong_Check(obj)) {
-            glong val;
-            val = PYGLIB_PyLong_AsLong(obj);
-            if (val >= -128 && val <= 127)
-                g_value_set_schar(value, (gchar) val);
-            else
-                return -1;
-        }
-#if PY_VERSION_HEX < 0x03000000
-        else if (PyString_Check(obj)) {
-            g_value_set_schar(value, PyString_AsString(obj)[0]);
-        }
-#endif
-        else if (PyUnicode_Check(obj)) {
-            tmp = PyUnicode_AsUTF8String(obj);
-            g_value_set_schar(value, PYGLIB_PyBytes_AsString(tmp)[0]);
-            Py_DECREF(tmp);
-        } else {
-            PyErr_SetString(PyExc_TypeError, "Cannot convert to TYPE_CHAR");
+    {
+        gint8 temp;
+        if (pygi_gschar_from_py (obj, &temp)) {
+            g_value_set_schar (value, temp);
+            return 0;
+        } else
             return -1;
-        }
-
-        break;
+    }
     case G_TYPE_UCHAR:
-        if (PYGLIB_PyLong_Check(obj)) {
-            glong val;
-            val = PYGLIB_PyLong_AsLong(obj);
-            if (val >= 0 && val <= 255)
-                g_value_set_uchar(value, (guchar) val);
-            else
-                return -1;
-#if PY_VERSION_HEX < 0x03000000
-        } else if (PyString_Check(obj)) {
-            g_value_set_uchar(value, PyString_AsString(obj)[0]);
-#endif
-        } else if (PyUnicode_Check(obj)) {
-            tmp = PyUnicode_AsUTF8String(obj);
-            g_value_set_uchar(value, PYGLIB_PyBytes_AsString(tmp)[0]);
-            Py_DECREF(tmp);
-        } else {
-            PyErr_Clear();
+    {
+        guchar temp;
+        if (pygi_guchar_from_py (obj, &temp)) {
+            g_value_set_uchar (value, temp);
+            return 0;
+        } else
             return -1;
-        }
-        break;
+    }
     case G_TYPE_BOOLEAN:
     {
         gboolean temp;
@@ -487,7 +464,7 @@ pyg_value_from_pyobject_with_error(GValue *value, PyObject *obj)
     {
         gchar *temp;
         if (pygi_utf8_from_py (obj, &temp)) {
-            g_value_set_string (value, temp);
+            g_value_take_string (value, temp);
             return 0;
         } else {
             /* also allows setting anything implementing __str__ */
@@ -498,7 +475,7 @@ pyg_value_from_pyobject_with_error(GValue *value, PyObject *obj)
                 return -1;
             if (pygi_utf8_from_py (str, &temp)) {
                 Py_DECREF (str);
-                g_value_set_string (value, temp);
+                g_value_take_string (value, temp);
                 return 0;
             }
             Py_DECREF (str);
@@ -511,8 +488,8 @@ pyg_value_from_pyobject_with_error(GValue *value, PyObject *obj)
         else if (PyObject_TypeCheck(obj, &PyGPointer_Type) &&
                 G_VALUE_HOLDS(value, ((PyGPointer *)obj)->gtype))
             g_value_set_pointer(value, pyg_pointer_get(obj, gpointer));
-        else if (PYGLIB_CPointer_Check(obj))
-            g_value_set_pointer(value, PYGLIB_CPointer_GetPointer(obj, NULL));
+        else if (PyCapsule_CheckExact (obj))
+            g_value_set_pointer(value, PyCapsule_GetPointer (obj, NULL));
         else if (G_VALUE_HOLDS_GTYPE (value))
             g_value_set_gtype (value, pyg_type_from_object (obj));
         else {
@@ -568,8 +545,8 @@ pyg_value_from_pyobject_with_error(GValue *value, PyObject *obj)
         }
         else if ((bm = pyg_type_lookup(G_VALUE_TYPE(value))) != NULL)
             return bm->tovalue(value, obj);
-        else if (PYGLIB_CPointer_Check(obj))
-            g_value_set_boxed(value, PYGLIB_CPointer_GetPointer(obj, NULL));
+        else if (PyCapsule_CheckExact (obj))
+            g_value_set_boxed(value, PyCapsule_GetPointer (obj, NULL));
         else {
             PyErr_SetString(PyExc_TypeError, "Expected Boxed");
             return -1;
@@ -582,7 +559,7 @@ pyg_value_from_pyobject_with_error(GValue *value, PyObject *obj)
         if (G_IS_PARAM_SPEC (pygobject_get (obj)))
             g_value_set_param(value, G_PARAM_SPEC (pygobject_get (obj)));
         else if (pyg_param_spec_check (obj))
-            g_value_set_param(value, PYGLIB_CPointer_GetPointer(obj, NULL));
+            g_value_set_param(value, PyCapsule_GetPointer (obj, NULL));
         else {
             PyErr_SetString(PyExc_TypeError, "Expected ParamSpec");
             return -1;
index d52c7dd..02f2478 100644 (file)
@@ -1923,7 +1923,9 @@ pygobject_emit(PyGObject *self, PyObject *args)
     if (query.return_type != G_TYPE_NONE)
        g_value_init(&ret, query.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE);
     
+    Py_BEGIN_ALLOW_THREADS;
     g_signal_emitv(params, signal_id, detail, &ret);
+    Py_END_ALLOW_THREADS;
 
     for (i = 0; i < query.n_params + 1; i++)
        g_value_unset(&params[i]);
@@ -2209,7 +2211,7 @@ pygobject_get_refcount(PyGObject *self, void *closure)
 static PyObject *
 pygobject_get_pointer(PyGObject *self, void *closure)
 {
-    return PYGLIB_CPointer_WrapPointer (self->obj, NULL);
+    return PyCapsule_New (self->obj, NULL, NULL);
 }
 
 static int
index 3762429..d242515 100644 (file)
@@ -343,11 +343,13 @@ pygobject_init(int req_major, int req_minor, int req_micro)
     }
 
     cobject = PyObject_GetAttrString(gobject, "_PyGObject_API");
-    if (cobject && PyCapsule_CheckExact(cobject))
+    if (cobject && PyCapsule_CheckExact(cobject)) {
         _PyGObject_API = (struct _PyGObject_Functions *) PyCapsule_GetPointer(cobject, "gobject._PyGObject_API");
-    else {
+        Py_DECREF (cobject);
+    } else {
         PyErr_SetString(PyExc_ImportError,
                         "could not import gobject (could not find _PyGObject_API object)");
+        Py_XDECREF (cobject);
         Py_DECREF(gobject);
         return NULL;
     }
index d807e0a..61ecb76 100644 (file)
@@ -341,7 +341,7 @@ pyg_option_context_richcompare(PyObject *self, PyObject *other, int op)
 static PyObject *
 pyg_option_get_context(PyGOptionContext *self)
 {
-    return PYGLIB_CPointer_WrapPointer(self->context, "goption.context");
+    return PyCapsule_New (self->context, "goption.context", NULL);
 }
 
 static PyMethodDef pyg_option_context_methods[] = {
diff --git a/gi/repository/meson.build b/gi/repository/meson.build
new file mode 100644 (file)
index 0000000..fdc136b
--- /dev/null
@@ -0,0 +1,5 @@
+python_sources = ['__init__.py']
+
+python.install_sources(python_sources,
+  subdir : join_paths('gi', 'repository')
+)
diff --git a/meson.build b/meson.build
new file mode 100644 (file)
index 0000000..d3cb9b0
--- /dev/null
@@ -0,0 +1,145 @@
+project('pygobject', 'c',
+  version : '3.29.2',
+  meson_version : '>= 0.46.0',
+  default_options : [ 'warning_level=1',
+                      'buildtype=debugoptimized'])
+
+pygobject_version = meson.project_version()
+version_arr = pygobject_version.split('.')
+pygobject_version_major = version_arr[0].to_int()
+pygobject_version_minor = version_arr[1].to_int()
+pygobject_version_micro = version_arr[2].to_int()
+
+platform_version = '@0@.0'.format(pygobject_version_major)
+
+pymod = import('python')
+python = pymod.find_installation(get_option('python'))
+
+python_dep = python.dependency()
+
+glib_version_req = '>= 2.38.0'
+gi_version_req = '>= 1.46.0'
+pycairo_version_req = '>= 1.11.1'
+libffi_version_req = '>= 3.0'
+
+gi_dep = dependency('gobject-introspection-1.0', version : gi_version_req,
+  fallback: ['gobject-introspection', 'girepo_dep'])
+glib_dep = dependency('glib-2.0', version : glib_version_req,
+  fallback: ['glib', 'libglib_dep'])
+gobject_dep = dependency('gobject-2.0', version : glib_version_req,
+  fallback: ['glib', 'libgobject_dep'])
+gio_dep = dependency('gio-2.0', version : glib_version_req,
+  fallback: ['glib', 'libgio_dep'])
+gmodule_dep = dependency('gmodule-2.0', version : glib_version_req,
+  fallback: ['glib', 'libgmodule_dep'])
+ffi_dep = dependency('libffi', version : '>= 3.0',
+  fallback : ['libffi', 'ffi_dep'])
+
+with_pycairo = get_option('pycairo')
+
+if with_pycairo
+  cairo_dep = dependency('cairo')
+  cairo_gobject_dep = dependency('cairo-gobject')
+
+  if python.language_version().version_compare('>= 3.0')
+    pycairo_dep = dependency('py3cairo', version : pycairo_version_req)
+  else
+    pycairo_dep = dependency('pycairo', version : pycairo_version_req)
+  endif
+endif
+
+cc = meson.get_compiler('c')
+
+main_c_args = [
+  '-Wall',
+  '-Warray-bounds',
+  '-Wcast-align',
+  '-Wdeclaration-after-statement',
+  '-Wduplicated-branches',
+  '-Wextra',
+  '-Wformat=2',
+  '-Wformat-nonliteral',
+  '-Wformat-security',
+  '-Wimplicit-function-declaration',
+  '-Winit-self',
+  '-Winline',
+  '-Wjump-misses-init',
+  '-Wlogical-op',
+  '-Wmissing-declarations',
+  '-Wmissing-format-attribute',
+  '-Wmissing-include-dirs',
+  '-Wmissing-noreturn',
+  '-Wmissing-prototypes',
+  '-Wnested-externs',
+  '-Wnull-dereference',
+  '-Wold-style-definition',
+  '-Wpacked',
+  '-Wpointer-arith',
+  '-Wrestrict',
+  '-Wreturn-type',
+  '-Wshadow',
+  '-Wsign-compare',
+  '-Wstrict-aliasing',
+  '-Wstrict-prototypes',
+  '-Wundef',
+  '-Wunused-but-set-variable',
+  '-Wwrite-strings',
+  '-Wconversion',
+]
+
+main_c_args += [
+  '-Wno-incompatible-pointer-types-discards-qualifiers',
+  '-Wno-missing-field-initializers',
+  '-Wno-unused-parameter',
+  '-Wno-discarded-qualifiers',
+  '-Wno-sign-conversion',
+  '-Wno-cast-function-type',
+]
+
+main_c_args += [
+  '-fno-strict-aliasing',
+  '-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)
+
+pyext_c_args = ['-DPY_SSIZE_T_CLEAN']
+
+cdata = configuration_data()
+
+cdata.set('PYGOBJECT_MAJOR_VERSION', pygobject_version_major)
+cdata.set('PYGOBJECT_MINOR_VERSION', pygobject_version_minor)
+cdata.set('PYGOBJECT_MICRO_VERSION', pygobject_version_micro)
+
+configure_file(output : 'config.h', configuration : cdata)
+
+pkgconf = configuration_data()
+
+pkgconf.set('prefix', join_paths(get_option('prefix')))
+pkgconf.set('exec_prefix', '${prefix}')
+pkgconf.set('includedir', join_paths('${prefix}', get_option('includedir')))
+pkgconf.set('datarootdir', join_paths('${prefix}', get_option('datadir')))
+pkgconf.set('datadir', '${datarootdir}')
+pkgconf.set('VERSION', pygobject_version)
+
+pkg_install_dir = '@0@/pkgconfig'.format(get_option('libdir'))
+
+configure_file(input : 'pygobject-@0@.pc.in'.format(platform_version),
+  output : 'pygobject-@0@.pc'.format(platform_version),
+  configuration : pkgconf,
+  install_dir : pkg_install_dir)
+
+configure_file(input : 'PKG-INFO.in',
+  output : 'pygobject-@0@-py@1@.egg-info'.format(pygobject_version, python.language_version()),
+  configuration : pkgconf,
+  install_dir : python.get_install_dir(subdir : 'gi'))
+
+subdir('gi')
+subdir('pygtkcompat')
+subdir('tests')
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644 (file)
index 0000000..31f3aa3
--- /dev/null
@@ -0,0 +1,2 @@
+option('python', type : 'string', value : 'python3')
+option('pycairo', type : 'boolean', value : true, description : 'build with pycairo integration')
diff --git a/pygtkcompat/meson.build b/pygtkcompat/meson.build
new file mode 100644 (file)
index 0000000..9e43c44
--- /dev/null
@@ -0,0 +1,8 @@
+python_sources = [
+  '__init__.py',
+  'generictreemodel.py',
+  'pygtkcompat.py']
+
+python.install_sources(python_sources,
+  subdir : 'pygtkcompat'
+)
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644 (file)
index 830e77a..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-[build-system]
-requires = ["setuptools", "wheel", "pycairo"]
index 17219ea..b38a43e 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -39,7 +39,7 @@ from distutils import dir_util, log
 from distutils.spawn import find_executable
 
 
-PYGOBJECT_VERISON = "3.29.1"
+PYGOBJECT_VERISON = "3.29.2"
 GLIB_VERSION_REQUIRED = "2.38.0"
 GI_VERSION_REQUIRED = "1.46.0"
 PYCAIRO_VERSION_REQUIRED = "1.11.1"
@@ -320,9 +320,9 @@ class distcheck(du_sdist):
         assert process.returncode == 0
 
         tracked_files = out.splitlines()
-        for ignore in [".gitignore"]:
-            if ignore in tracked_files:
-                tracked_files.remove(ignore)
+        tracked_files = [
+            f for f in tracked_files
+            if os.path.basename(f) not in [".gitignore"]]
 
         diff = set(tracked_files) - set(included_files)
         assert not diff, (
@@ -510,6 +510,9 @@ class build_tests(Command):
                 "--library=gimarshallingtests",
                 "--pkg=glib-2.0",
                 "--pkg=gio-2.0",
+                "--cflags-begin",
+                "-I%s" % gi_tests_dir,
+                "--cflags-end",
                 "--output=%s" % gir_path,
             ] + ext.sources + ext.depends)
 
@@ -651,6 +654,8 @@ def get_suppression_files():
     files = []
     for prefix in prefixes:
         files.extend(get_suppression_files_for_prefix(prefix))
+
+    files.append(os.path.join(get_script_dir(), "tests", "valgrind.supp"))
     return sorted(set(files))
 
 
@@ -855,16 +860,21 @@ def add_ext_pkg_config_dep(ext, compiler_type, name):
         "libffi": ["ffi"],
     }
 
+    def add(target, new):
+        for entry in new:
+            if entry not in target:
+                target.append(entry)
+
     fallback_libs = msvc_libraries[name]
     if compiler_type == "msvc":
         # assume that INCLUDE and LIB contains the right paths
-        ext.libraries += fallback_libs
+        add(ext.libraries, fallback_libs)
     else:
         min_version = get_version_requirement(name)
         pkg_config_version_check(name, min_version)
-        ext.include_dirs += pkg_config_parse("--cflags-only-I", name)
-        ext.library_dirs += pkg_config_parse("--libs-only-L", name)
-        ext.libraries += pkg_config_parse("--libs-only-l", name)
+        add(ext.include_dirs, pkg_config_parse("--cflags-only-I", name))
+        add(ext.library_dirs, pkg_config_parse("--libs-only-L", name))
+        add(ext.libraries, pkg_config_parse("--libs-only-l", name))
 
 
 def add_ext_compiler_flags(ext, compiler, _cache={}):
@@ -919,6 +929,7 @@ def add_ext_compiler_flags(ext, compiler, _cache={}):
             "-Wno-unused-parameter",
             "-Wno-discarded-qualifiers",
             "-Wno-sign-conversion",
+            "-Wno-cast-function-type",
         ]
 
         # silence clang for unused gcc CFLAGS added by Debian
diff --git a/subprojects/glib.wrap b/subprojects/glib.wrap
new file mode 100644 (file)
index 0000000..c86fea7
--- /dev/null
@@ -0,0 +1,5 @@
+[wrap-git]
+directory=glib
+url=git://git.gnome.org/glib
+push-url=ssh://git.gnome.org/git/glib
+revision=master
diff --git a/subprojects/gobject-introspection.wrap b/subprojects/gobject-introspection.wrap
new file mode 100644 (file)
index 0000000..561c20a
--- /dev/null
@@ -0,0 +1,5 @@
+[wrap-git]
+directory=gobject-introspection
+url=https://gitlab.gnome.org/GNOME/gobject-introspection.git
+push-url=git@gitlab.gnome.org:GNOME/gobject-introspection.git
+revision=master
diff --git a/subprojects/libffi.wrap b/subprojects/libffi.wrap
new file mode 100644 (file)
index 0000000..3d15e2a
--- /dev/null
@@ -0,0 +1,4 @@
+[wrap-git]
+directory=libffi
+url=https://github.com/centricular/libffi.git
+revision=meson
index 09a20e3..eaa541d 100644 (file)
@@ -7,6 +7,25 @@ import signal
 import subprocess
 import atexit
 import warnings
+import imp
+
+
+class GIImport:
+    def find_module(self, fullname, path=None):
+        if fullname == 'gi._gi':
+            return self
+        return None
+
+    def load_module(self, name):
+        if name in sys.modules:
+            return sys.modules[name]
+        module_info = imp.find_module('_gi')
+        module = imp.load_module(name, *module_info)
+        sys.modules[name] = module
+        return module
+
+
+sys.meta_path.insert(0, GIImport())
 
 
 def init_test_environ():
@@ -46,6 +65,7 @@ def init_test_environ():
     tests_srcdir = os.path.abspath(os.path.dirname(__file__))
     srcdir = os.path.dirname(tests_srcdir)
 
+    sys.path.insert(0, os.path.join(builddir, 'gi'))
     sys.path.insert(0, tests_srcdir)
     sys.path.insert(0, srcdir)
     sys.path.insert(0, tests_builddir)
index 50b3f75..c08a563 100644 (file)
@@ -1,5 +1,7 @@
 # -*- coding: utf-8 -*-
 
+from __future__ import absolute_import
+
 import sys
 
 import pytest
index 0e777b8..bc5f8fe 100644 (file)
@@ -20,6 +20,7 @@
 #define EXTRA_TESTS
 
 #include <glib-object.h>
+#include <gitestmacros.h>
 
 typedef enum
 {
@@ -35,22 +36,34 @@ typedef enum
   GI_MARSHALLING_TESTS_EXTRA_FLAGS_VALUE2 = (gint)(1 << 31),
 } GIMarshallingTestsExtraFlags;
 
+
+_GI_TEST_EXTERN
 GType gi_marshalling_tests_extra_flags_get_type (void) G_GNUC_CONST;
 #define GI_MARSHALLING_TESTS_TYPE_EXTRA_FLAGS (gi_marshalling_tests_extra_flags_get_type ())
 
+_GI_TEST_EXTERN
 void gi_marshalling_tests_compare_two_gerrors_in_gvalue (GValue *v, GValue *v1);
+_GI_TEST_EXTERN
 void gi_marshalling_tests_ghashtable_enum_none_in (GHashTable *hash_table);
+_GI_TEST_EXTERN
 GHashTable * gi_marshalling_tests_ghashtable_enum_none_return (void);
 
+_GI_TEST_EXTERN
 gchar * gi_marshalling_tests_filename_copy (gchar *path_in);
+_GI_TEST_EXTERN
 gboolean gi_marshalling_tests_filename_exists (gchar *path);
+_GI_TEST_EXTERN
 gchar * gi_marshalling_tests_filename_to_glib_repr (gchar *path_in, gsize *len);
 
+_GI_TEST_EXTERN
 GIMarshallingTestsExtraEnum * gi_marshalling_tests_enum_array_return_type (gsize *n_members);
 
+_GI_TEST_EXTERN
 void gi_marshalling_tests_extra_flags_large_in (GIMarshallingTestsExtraFlags value);
 
+_GI_TEST_EXTERN
 gchar *gi_marshalling_tests_extra_utf8_full_return_invalid (void);
+_GI_TEST_EXTERN
 void gi_marshalling_tests_extra_utf8_full_out_invalid (gchar **utf8);
 
 #endif /* EXTRA_TESTS */
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644 (file)
index 0000000..5933ddb
--- /dev/null
@@ -0,0 +1,127 @@
+gnome = import('gnome')
+
+host_system = host_machine.system()
+
+cc = meson.get_compiler('c')
+
+visibility_args = []
+if get_option('default_library') != 'static'
+  if host_system == 'windows'
+    visibility_args += ['-DDLL_EXPORT']
+    if cc.get_id() == 'msvc'
+      visibility_args += ['-D_GI_EXTERN=__declspec(dllexport) extern']
+    elif cc.has_argument('-fvisibility=hidden')
+      visibility_args += ['-D_GI_EXTERN=__attribute__((visibility("default"))) __declspec(dllexport) extern']
+      visibility_args += ['-fvisibility=hidden']
+    endif
+  elif cc.has_argument('-fvisibility=hidden')
+    visibility_args += ['-D_GI_EXTERN=__attribute__((visibility("default"))) extern']
+    visibility_args += ['-fvisibility=hidden']
+  endif
+endif
+
+if gi_dep.type_name() == 'pkgconfig'
+  gi_datadir = gi_dep.get_pkgconfig_variable('gidatadir')
+  regress_sources = [join_paths(gi_datadir, 'tests', 'regress.c')]
+  regress_headers = [join_paths(gi_datadir, 'tests', 'regress.h')]
+  regress_incdir = include_directories(join_paths(gi_datadir, 'tests'))
+  marshalling_sources = [join_paths(gi_datadir, 'tests', 'gimarshallingtests.c')]
+  marshalling_headers = [join_paths(gi_datadir, 'tests', 'gimarshallingtests.h')]
+else
+  gi_subproject = subproject('gobject-introspection')
+  regress_sources = gi_subproject.get_variable('test_regress_sources')
+  regress_headers = gi_subproject.get_variable('test_regress_headers')
+  regress_incdir = gi_subproject.get_variable('test_regress_incdirs')
+  marshalling_sources = gi_subproject.get_variable('test_marshalling_sources')
+  marshalling_headers = gi_subproject.get_variable('test_marshalling_headers')
+  gi_datadir = join_paths(meson.source_root(), 'subprojects', 'gobject-introspection', 'tests')
+endif
+
+marshalling_sources += ['gimarshallingtestsextra.c']
+
+marshalling_headers += ['gimarshallingtestsextra.h']
+
+marshalling_lib = library(
+  'gimarshallingtests',
+  sources : marshalling_sources,
+  dependencies : [glib_dep, gobject_dep, gio_dep, gmodule_dep],
+  include_directories : regress_incdir,
+  c_args: visibility_args,
+)
+
+gnome.generate_gir(
+  marshalling_lib,
+  sources : marshalling_sources + marshalling_headers,
+  nsversion : '1.0',
+  namespace : 'GIMarshallingTests',
+  dependencies : [glib_dep, gobject_dep, gio_dep, gmodule_dep],
+  symbol_prefix : 'gi_marshalling_tests',
+  includes : ['Gio-2.0'],
+  build_by_default : true,
+)
+
+regress_sources += ['regressextra.c']
+
+regress_headers += ['regressextra.h']
+
+regress_deps = [glib_dep, gobject_dep, gio_dep, gmodule_dep]
+regress_c_args = []
+
+if with_pycairo
+  regress_deps += [cairo_dep, cairo_gobject_dep]
+else
+  regress_c_args += ['-D_GI_DISABLE_CAIRO']
+endif
+
+regress_lib = library(
+  'regress',
+  sources : regress_sources,
+  dependencies : regress_deps,
+  include_directories : regress_incdir,
+  c_args: regress_c_args + visibility_args,
+)
+
+gnome.generate_gir(
+  regress_lib,
+  sources : regress_sources + regress_headers,
+  nsversion : '1.0',
+  namespace : 'Regress',
+  includes : ['Gio-2.0', 'cairo-1.0'],
+  build_by_default : true,
+  dependencies : regress_deps,
+  extra_args: regress_c_args,
+)
+
+helper_sources = [
+  'testhelpermodule.c',
+  'test-floating.c',
+  'test-thread.c',
+  'test-unknown.c']
+
+helperext = python.extension_module('testhelper', helper_sources,
+  dependencies : [python_dep, glib_dep, gobject_dep],
+  c_args: pyext_c_args + main_c_args,
+  include_directories: include_directories(join_paths('..', 'gi'))
+)
+
+schemas = gnome.compile_schemas(build_by_default: true)
+
+envdata = environment()
+envdata.append('GI_TYPELIB_PATH', meson.current_build_dir())
+if gi_dep.type_name() == 'internal'
+  envdata.append('GI_TYPELIB_PATH', join_paths(meson.build_root(), 'subprojects', 'gobject-introspection', 'gir'))
+endif
+
+if host_machine.system() == 'linux'
+    envdata.prepend('LD_LIBRARY_PATH', meson.current_build_dir())
+endif
+if host_machine.system() == 'windows'
+    envdata.prepend('PATH', join_paths(get_option('prefix'), get_option('bindir')))
+endif
+envdata.append('PYTHONPATH', join_paths(meson.current_build_dir(), '..'))
+envdata.append('TESTS_BUILDDIR', meson.current_build_dir())
+
+test('pygobject-test-suite', python,
+  args : [join_paths(meson.current_source_dir(), 'runtests.py')],
+  env : envdata,
+  timeout : 90)
index eb9aab8..0c1e5c4 100644 (file)
        <key name="test-enum" enum="org.gnome.test.FruitType">
            <default>'banana'</default>
        </key>
+       <key name="test-range" type="i">
+           <range min="7" max="65535"/>
+           <default>123</default>
+       </key>
     </schema>
 
     <schema id="org.gnome.nopathtest">
index 14c72ac..6f5d2f5 100644 (file)
@@ -23,19 +23,33 @@ GList *regress_test_glist_boxed_full_return (guint count);
 
 #ifndef _GI_DISABLE_CAIRO
 
+_GI_TEST_EXTERN
 cairo_t *regress_test_cairo_context_none_return (void);
+_GI_TEST_EXTERN
 void regress_test_cairo_context_full_in (cairo_t *context);
+_GI_TEST_EXTERN
 cairo_path_t *regress_test_cairo_path_full_return (void);
+_GI_TEST_EXTERN
 void regress_test_cairo_path_none_in (cairo_path_t *path);
+_GI_TEST_EXTERN
 cairo_path_t * regress_test_cairo_path_full_in_full_return (cairo_path_t *path);
+_GI_TEST_EXTERN
 cairo_font_options_t *regress_test_cairo_font_options_full_return (void);
+_GI_TEST_EXTERN
 cairo_font_options_t *regress_test_cairo_font_options_none_return (void);
+_GI_TEST_EXTERN
 void regress_test_cairo_font_options_full_in (cairo_font_options_t *options);
+_GI_TEST_EXTERN
 void regress_test_cairo_font_options_none_in (cairo_font_options_t *options);
+_GI_TEST_EXTERN
 void regress_test_cairo_region_full_in (cairo_region_t *region);
+_GI_TEST_EXTERN
 void regress_test_cairo_surface_full_in (cairo_surface_t *surface);
+_GI_TEST_EXTERN
 void regress_test_cairo_matrix_none_in (const cairo_matrix_t *matrix);
+_GI_TEST_EXTERN
 cairo_matrix_t *regress_test_cairo_matrix_none_return (void);
+_GI_TEST_EXTERN
 void regress_test_cairo_matrix_out_caller_allocates (cairo_matrix_t *matrix);
 
 #endif
index f218928..bccc94b 100644 (file)
@@ -12,6 +12,7 @@ import sys
 import os
 import re
 import platform
+import gc
 
 import pytest
 
@@ -1330,7 +1331,7 @@ class TestBoxed(unittest.TestCase):
         with warnings.catch_warnings(record=True) as warn:
             warnings.simplefilter('always')
             boxed = Everything.TestBoxedB(42, 47)
-            self.assertTrue(issubclass(warn[0].category, TypeError))
+            self.assertTrue(issubclass(warn[0].category, DeprecationWarning))
 
         self.assertEqual(boxed.some_int8, 0)
         self.assertEqual(boxed.some_long, 0)
@@ -1353,6 +1354,8 @@ class TestBoxed(unittest.TestCase):
         # - another owned by @obj
         self.assertEqual(obj.refcount, 2)
         del wrapper
+        gc.collect()
+        gc.collect()
         self.assertEqual(obj.refcount, 1)
 
     def test_boxed_c_wrapper_copy(self):
@@ -1367,10 +1370,16 @@ class TestBoxed(unittest.TestCase):
         # - another owned by @obj
         self.assertEqual(obj.refcount, 3)
         del wrapper
+        gc.collect()
+        gc.collect()
         self.assertEqual(obj.refcount, 2)
         del wrapper_copy
+        gc.collect()
+        gc.collect()
         self.assertEqual(obj.refcount, 1)
         del obj
+        gc.collect()
+        gc.collect()
 
     def test_array_fixed_boxed_none_out(self):
         arr = Everything.test_array_fixed_boxed_none_out()
index 51a9ecd..3d87662 100644 (file)
@@ -2246,13 +2246,13 @@ class TestStructure(unittest.TestCase):
             warnings.simplefilter('always')
             GIMarshallingTests.Union(42)
 
-        self.assertTrue(issubclass(warn[0].category, TypeError))
+        self.assertTrue(issubclass(warn[0].category, DeprecationWarning))
 
         with warnings.catch_warnings(record=True) as warn:
             warnings.simplefilter('always')
             GIMarshallingTests.Union(f=42)
 
-        self.assertTrue(issubclass(warn[0].category, TypeError))
+        self.assertTrue(issubclass(warn[0].category, DeprecationWarning))
 
     def test_union(self):
         union = GIMarshallingTests.Union()
index 92159c1..b5fcdf3 100644 (file)
@@ -7,6 +7,8 @@ import os
 import unittest
 import warnings
 
+import pytest
+
 import gi.overrides
 from gi import PyGIWarning
 from gi.repository import GLib, Gio
@@ -64,6 +66,17 @@ class TestGSettings(unittest.TestCase):
         self.settings.reset('test-boolean')
         self.settings.reset('test-enum')
 
+    def test_iter(self):
+        assert list(self.settings) == [
+            'test-tuple', 'test-array', 'test-boolean', 'test-string',
+            'test-enum', 'test-range']
+
+    def test_get_set(self):
+        for key in self.settings:
+            old_value = self.settings[key]
+            self.settings[key] = old_value
+            assert self.settings[key] == old_value
+
     def test_native(self):
         self.assertTrue('test-array' in self.settings.list_keys())
 
@@ -82,6 +95,9 @@ class TestGSettings(unittest.TestCase):
         v = self.settings.get_value('test-tuple')
         self.assertEqual(v.unpack(), (1, 2))
 
+        v = self.settings.get_value('test-range')
+        assert v.unpack() == 123
+
         # set a value
         self.settings.set_string('test-string', 'World')
         self.assertEqual(self.settings.get_string('test-string'), 'World')
@@ -99,7 +115,7 @@ class TestGSettings(unittest.TestCase):
         self.assertEqual(with_path['np-int'], 42)
 
     def test_dictionary_api(self):
-        self.assertEqual(len(self.settings), 5)
+        self.assertEqual(len(self.settings), 6)
         self.assertTrue('test-array' in self.settings)
         self.assertTrue('test-array' in self.settings.keys())
         self.assertFalse('nonexisting' in self.settings)
@@ -130,6 +146,18 @@ class TestGSettings(unittest.TestCase):
         self.assertRaises(ValueError, self.settings.__setitem__, 'test-enum', 'plum')
         self.assertRaises(KeyError, self.settings.__setitem__, 'unknown', 'moo')
 
+    def test_set_range(self):
+        self.settings['test-range'] = 7
+        assert self.settings['test-range'] == 7
+        self.settings['test-range'] = 65535
+        assert self.settings['test-range'] == 65535
+
+        with pytest.raises(ValueError, match=".*7 - 65535.*"):
+            self.settings['test-range'] = 7 - 1
+
+        with pytest.raises(ValueError, match=".*7 - 65535.*"):
+            self.settings['test-range'] = 65535 + 1
+
     def test_empty(self):
         empty = Gio.Settings.new_with_path('org.gnome.empty', '/tests/')
         self.assertEqual(len(empty), 0)
index 3f7e008..51256f8 100644 (file)
@@ -7,12 +7,14 @@ import gc
 import unittest
 import warnings
 
-from gi.repository import GObject, GLib
+import pytest
+
+from gi.repository import GObject, GLib, Gio
 from gi import PyGIDeprecationWarning
 from gi.module import get_introspection_module
 from gi import _gi
 
-from . import testhelper
+import testhelper
 
 
 class TestGObjectAPI(unittest.TestCase):
@@ -647,6 +649,12 @@ class TestGValue(unittest.TestCase):
         value.set_value(42.0)
         self.assertEqual(value.get_value(), 42)
 
+    def test_multi_del(self):
+        value = GObject.Value(str, 'foo_bar')
+        value.__del__()
+        value.__del__()
+        del value
+
     def test_string(self):
         value = GObject.Value(str, 'foo_bar')
         self.assertEqual(value.g_type, GObject.TYPE_STRING)
@@ -696,6 +704,24 @@ class TestGValue(unittest.TestCase):
         value.set_value([32, 'foo_bar', 0.3])
         self.assertEqual(value.get_value(), [32, 'foo_bar', 0.3])
 
+    def test_value_array_from_gvalue_list(self):
+        value = GObject.Value(GObject.ValueArray, [
+            GObject.Value(GObject.TYPE_UINT, 0xffffffff),
+            GObject.Value(GObject.TYPE_STRING, 'foo_bar')])
+        self.assertEqual(value.g_type, GObject.type_from_name('GValueArray'))
+        self.assertEqual(value.get_value(), [0xffffffff, 'foo_bar'])
+        self.assertEqual(testhelper.value_array_get_nth_type(value, 0), GObject.TYPE_UINT)
+        self.assertEqual(testhelper.value_array_get_nth_type(value, 1), GObject.TYPE_STRING)
+
+    def test_value_array_append_gvalue(self):
+        arr = GObject.ValueArray.new(0)
+        arr.append(GObject.Value(GObject.TYPE_UINT, 0xffffffff))
+        arr.append(GObject.Value(GObject.TYPE_STRING, 'foo_bar'))
+        self.assertEqual(arr.get_nth(0), 0xffffffff)
+        self.assertEqual(arr.get_nth(1), 'foo_bar')
+        self.assertEqual(testhelper.value_array_get_nth_type(arr, 0), GObject.TYPE_UINT)
+        self.assertEqual(testhelper.value_array_get_nth_type(arr, 1), GObject.TYPE_STRING)
+
     def test_gerror_boxing(self):
         error = GLib.Error('test message', domain='mydomain', code=42)
         value = GObject.Value(GLib.Error, error)
@@ -711,3 +737,43 @@ class TestGValue(unittest.TestCase):
         value = GObject.Value(GLib.Error)
         self.assertEqual(value.g_type, GObject.type_from_name('GError'))
         self.assertEqual(value.get_value(), None)
+
+
+def test_list_properties():
+
+    def find_param(l, name):
+        for param in l:
+            if param.name == name:
+                return param
+        return
+
+    list_props = GObject.list_properties
+
+    props = list_props(Gio.Action)
+    param = find_param(props, "enabled")
+    assert param
+    assert param.value_type == GObject.TYPE_BOOLEAN
+    assert list_props("GAction") == list_props(Gio.Action)
+    assert list_props(Gio.Action.__gtype__) == list_props(Gio.Action)
+
+    props = list_props(Gio.SimpleAction)
+    assert find_param(props, "enabled")
+
+    def names(l):
+        return [p.name for p in l]
+
+    assert (set(names(list_props(Gio.Action))) <=
+            set(names(list_props(Gio.SimpleAction))))
+
+    props = list_props(Gio.FileIcon)
+    param = find_param(props, "file")
+    assert param
+    assert param.value_type == Gio.File.__gtype__
+
+    assert list_props("GFileIcon") == list_props(Gio.FileIcon)
+    assert list_props(Gio.FileIcon.__gtype__) == list_props(Gio.FileIcon)
+    assert list_props(Gio.FileIcon()) == list_props(Gio.FileIcon)
+
+    for obj in [Gio.ActionEntry, Gio.DBusError, 0, object()]:
+        with pytest.raises(TypeError):
+            list_props(obj)
index e55f298..6f112dd 100644 (file)
@@ -480,3 +480,76 @@ def test_constructors():
     Gtk.Template.from_string("bla")
     Gtk.Template.from_resource("foo")
     Gtk.Template.from_file("foo")
+
+
+def test_child_construct():
+    Gtk.Template.Child()
+    Gtk.Template.Child("name")
+    with pytest.raises(TypeError):
+        Gtk.Template.Child("name", True)
+    Gtk.Template.Child("name", internal=True)
+    with pytest.raises(TypeError):
+        Gtk.Template.Child("name", internal=True, something=False)
+
+
+def test_internal_child():
+
+    main_type_name = new_gtype_name()
+
+    xml = """\
+    <interface>
+      <template class="{0}" parent="GtkBox">
+        <child>
+          <object class="GtkBox" id="somechild">
+            <property name="margin">42</property>
+          </object>
+        </child>
+      </template>
+    </interface>
+    """.format(main_type_name)
+
+    @Gtk.Template.from_string(xml)
+    class MainThing(Gtk.Box):
+        __gtype_name__ = main_type_name
+
+        somechild = Gtk.Template.Child(internal=True)
+
+    thing = MainThing()
+    assert thing.somechild.props.margin == 42
+
+    other_type_name = new_gtype_name()
+
+    xml = """\
+    <interface>
+      <template class="{0}" parent="GtkBox">
+        <child>
+          <object class="{1}">
+            <child internal-child="somechild">
+              <object class="GtkBox">
+                <property name="margin">24</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label">foo</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </template>
+    </interface>
+    """.format(other_type_name, main_type_name)
+
+    @Gtk.Template.from_string(xml)
+    class OtherThing(Gtk.Box):
+        __gtype_name__ = other_type_name
+
+    other = OtherThing()
+    child = other.get_children()[0]
+    assert isinstance(child, MainThing)
+    child = child.get_children()[0]
+    assert isinstance(child, Gtk.Box)
+    assert child.props.margin == 24
+    child = child.get_children()[0]
+    assert isinstance(child, Gtk.Label)
+    assert child.props.label == "foo"
index bed37f3..ca1e0f4 100644 (file)
@@ -5,7 +5,7 @@ from __future__ import absolute_import
 import unittest
 
 from gi.repository import GObject
-from . import testhelper
+import testhelper
 
 
 GUnknown = GObject.type_from_name("TestUnknown")
index 7b2b8ea..1ed822c 100644 (file)
@@ -9,7 +9,7 @@ import pytest
 from gi.repository import GLib, GObject
 from gi._compat import PY3
 
-from . import testhelper
+import testhelper
 
 
 class PyGObject(GObject.GObject):
@@ -116,3 +116,16 @@ def test_to_unichar_conv():
 
     with pytest.raises(TypeError):
         testhelper.test_to_unichar_conv(u"AA")
+
+
+def test_constant_strip_prefix():
+    assert testhelper.constant_strip_prefix("foo", "bar") == "foo"
+    assert testhelper.constant_strip_prefix("foo", "f") == "oo"
+    assert testhelper.constant_strip_prefix("foo", "f") == "oo"
+    assert testhelper.constant_strip_prefix("ha2foo", "ha") == "a2foo"
+    assert testhelper.constant_strip_prefix("2foo", "ha") == "2foo"
+    assert testhelper.constant_strip_prefix("bla_foo", "bla") == "_foo"
+
+
+def test_state_ensure_release():
+    testhelper.test_state_ensure_release()
index e50cbb6..3d8ebb8 100644 (file)
@@ -128,11 +128,13 @@ class TestVFuncsWithObjectArg(unittest.TestCase):
         with warnings.catch_warnings(record=True) as warn:
             warnings.simplefilter('always')
             ref_count, is_floating = vfuncs.get_ref_info_for_vfunc_out_object_transfer_none()
-            self.assertTrue(issubclass(warn[0].category, RuntimeWarning))
+            if hasattr(sys, "getrefcount"):
+                self.assertTrue(issubclass(warn[0].category, RuntimeWarning))
 
         # The ref count of the GObject returned to the caller (get_ref_info_for_vfunc_return_object_transfer_none)
         # should be a single floating ref
-        self.assertEqual(ref_count, 1)
+        if hasattr(sys, "getrefcount"):
+            self.assertEqual(ref_count, 1)
         self.assertFalse(is_floating)
 
         gc.collect()
@@ -144,9 +146,11 @@ class TestVFuncsWithObjectArg(unittest.TestCase):
         with warnings.catch_warnings(record=True) as warn:
             warnings.simplefilter('always')
             ref_count, is_floating = vfuncs.get_ref_info_for_vfunc_out_object_transfer_none()
-            self.assertTrue(issubclass(warn[0].category, RuntimeWarning))
+            if hasattr(sys, "getrefcount"):
+                self.assertTrue(issubclass(warn[0].category, RuntimeWarning))
 
-        self.assertEqual(ref_count, 1)
+        if hasattr(sys, "getrefcount"):
+            self.assertEqual(ref_count, 1)
         self.assertFalse(is_floating)
 
         gc.collect()
@@ -158,7 +162,8 @@ class TestVFuncsWithObjectArg(unittest.TestCase):
 
         # The vfunc caller receives full ownership of a single ref which should not
         # be floating.
-        self.assertEqual(ref_count, 1)
+        if hasattr(sys, "getrefcount"):
+            self.assertEqual(ref_count, 1)
         self.assertFalse(is_floating)
 
         gc.collect()
@@ -169,7 +174,8 @@ class TestVFuncsWithObjectArg(unittest.TestCase):
         vfuncs = self.VFuncs()
         ref_count, is_floating = vfuncs.get_ref_info_for_vfunc_out_object_transfer_full()
 
-        self.assertEqual(ref_count, 1)
+        if hasattr(sys, "getrefcount"):
+            self.assertEqual(ref_count, 1)
         self.assertFalse(is_floating)
 
         gc.collect()
@@ -183,9 +189,12 @@ class TestVFuncsWithObjectArg(unittest.TestCase):
         self.assertEqual(vfuncs.in_object_grefcount, 2)  # initial + python wrapper
         self.assertFalse(vfuncs.in_object_is_floating)
 
-        self.assertEqual(ref_count, 1)  # ensure python wrapper released
+        if hasattr(sys, "getrefcount"):
+            self.assertEqual(ref_count, 1)  # ensure python wrapper released
         self.assertFalse(is_floating)
 
+        gc.collect()
+        gc.collect()
         self.assertTrue(vfuncs.object_ref() is None)
 
     def test_vfunc_in_object_transfer_full(self):
@@ -199,9 +208,12 @@ class TestVFuncsWithObjectArg(unittest.TestCase):
         self.assertFalse(vfuncs.in_object_is_floating)
 
         # ensure python wrapper took ownership and released, after vfunc was complete
-        self.assertEqual(ref_count, 0)
+        if hasattr(sys, "getrefcount"):
+            self.assertEqual(ref_count, 0)
         self.assertFalse(is_floating)
 
+        gc.collect()
+        gc.collect()
         self.assertTrue(vfuncs.object_ref() is None)
 
 
@@ -213,6 +225,7 @@ class TestVFuncsWithFloatingArg(unittest.TestCase):
         Object = GObject.InitiallyUnowned
         ObjectRef = weakref.ref
 
+    @unittest.skipUnless(hasattr(sys, "getrefcount"), "refcount specific")
     def test_vfunc_return_object_transfer_none_with_floating(self):
         # Python is expected to return a single floating reference without warning.
         vfuncs = self.VFuncs()
@@ -226,6 +239,7 @@ class TestVFuncsWithFloatingArg(unittest.TestCase):
         gc.collect()
         self.assertTrue(vfuncs.object_ref() is None)
 
+    @unittest.skipUnless(hasattr(sys, "getrefcount"), "refcount specific")
     def test_vfunc_out_object_transfer_none_with_floating(self):
         # Same as above except uses out arg instead of return
         vfuncs = self.VFuncs()
@@ -242,7 +256,8 @@ class TestVFuncsWithFloatingArg(unittest.TestCase):
         ref_count, is_floating = vfuncs.get_ref_info_for_vfunc_return_object_transfer_full()
 
         # The vfunc caller receives full ownership of a single ref.
-        self.assertEqual(ref_count, 1)
+        if hasattr(sys, "getrefcount"):
+            self.assertEqual(ref_count, 1)
         self.assertFalse(is_floating)
 
         gc.collect()
@@ -253,7 +268,8 @@ class TestVFuncsWithFloatingArg(unittest.TestCase):
         vfuncs = self.VFuncs()
         ref_count, is_floating = vfuncs.get_ref_info_for_vfunc_out_object_transfer_full()
 
-        self.assertEqual(ref_count, 1)
+        if hasattr(sys, "getrefcount"):
+            self.assertEqual(ref_count, 1)
         self.assertFalse(is_floating)
 
         gc.collect()
@@ -270,9 +286,12 @@ class TestVFuncsWithFloatingArg(unittest.TestCase):
         self.assertTrue(vfuncs.in_object_is_floating)
 
         # vfunc caller should only have a single floating ref after the vfunc finishes
-        self.assertEqual(ref_count, 1)
+        if hasattr(sys, "getrefcount"):
+            self.assertEqual(ref_count, 1)
         self.assertTrue(is_floating)
 
+        gc.collect()
+        gc.collect()
         self.assertTrue(vfuncs.object_ref() is None)
 
     def test_vfunc_in_object_transfer_full_with_floating(self):
@@ -286,9 +305,12 @@ class TestVFuncsWithFloatingArg(unittest.TestCase):
         self.assertFalse(vfuncs.in_object_is_floating)
 
         # ensure python wrapper took ownership and released
-        self.assertEqual(ref_count, 0)
+        if hasattr(sys, "getrefcount"):
+            self.assertEqual(ref_count, 0)
         self.assertFalse(is_floating)
 
+        gc.collect()
+        gc.collect()
         self.assertTrue(vfuncs.object_ref() is None)
 
 
index 2854508..251eb3a 100644 (file)
@@ -57,29 +57,53 @@ class TestOption(unittest.TestCase):
         self.parser.add_option_group(group)
         return group
 
-    def test_parse_args(self):
+    def test_integer(self):
+        self._create_group()
         options, args = self.parser.parse_args(
-            ["test_option.py"])
-        self.assertFalse(args)
+            ["--test-integer", "42", "bla"])
+        assert options.test_integer == 42
+        assert args == ["bla"]
+
+    def test_file(self):
+        self._create_group()
 
         options, args = self.parser.parse_args(
-            ["test_option.py", "foo"])
-        self.assertEqual(args, [])
+            ["--file", "fn", "bla"])
+        assert options.unit_file == "fn"
+        assert args == ["bla"]
+
+    def test_mixed(self):
+        self._create_group()
 
         options, args = self.parser.parse_args(
-            ["test_option.py", "foo", "bar"])
-        self.assertEqual(args, [])
+            ["--file", "fn", "--test-integer", "12", "--test",
+             "--g-fatal-warnings", "nope"])
+
+        assert options.unit_file == "fn"
+        assert options.test_integer == 12
+        assert options.test is False
+        assert options.fatal_warnings is True
+        assert args == ["nope"]
+
+    def test_parse_args(self):
+        options, args = self.parser.parse_args([])
+        self.assertFalse(args)
+
+        options, args = self.parser.parse_args(["foo"])
+        self.assertEqual(args, ["foo"])
+
+        options, args = self.parser.parse_args(["foo", "bar"])
+        self.assertEqual(args, ["foo", "bar"])
 
     def test_parse_args_double_dash(self):
-        options, args = self.parser.parse_args(
-            ["test_option.py", "--", "-xxx"])
-        # self.assertEqual(args, ["-xxx"])
+        options, args = self.parser.parse_args(["--", "-xxx"])
+        self.assertEqual(args, ["--", "-xxx"])
 
     def test_parse_args_group(self):
         group = self._create_group()
 
         options, args = self.parser.parse_args(
-            ["test_option.py", "--test", "-f", "test"])
+            ["--test", "-f", "test"])
 
         self.assertFalse(options.test)
         self.assertEqual(options.unit_file, "test")
@@ -92,12 +116,12 @@ class TestOption(unittest.TestCase):
     def test_option_value_error(self):
         self._create_group()
         self.assertRaises(GLib.option.OptionValueError, self.parser.parse_args,
-                          ["test_option.py", "--test-integer=text"])
+                          ["--test-integer=text"])
 
     def test_bad_option_error(self):
         self.assertRaises(GLib.option.BadOptionError,
                           self.parser.parse_args,
-                          ["test_option.py", "--unknwon-option"])
+                          ["--unknwon-option"])
 
     def test_option_group_constructor(self):
         self.assertRaises(TypeError, GLib.option.OptionGroup)
@@ -106,8 +130,7 @@ class TestOption(unittest.TestCase):
         self._create_group()
 
         with capture_exceptions() as exc:
-            self.parser.parse_args(
-                ["test_option.py", "--callback-failure-test"])
+            self.parser.parse_args(["--callback-failure-test"])
 
         assert len(exc) == 1
         assert exc[0].value.args[0] == "foo"
diff --git a/tests/test_overrides_gio.py b/tests/test_overrides_gio.py
new file mode 100644 (file)
index 0000000..b6516f9
--- /dev/null
@@ -0,0 +1,346 @@
+from __future__ import absolute_import
+
+import random
+import platform
+
+import pytest
+
+from gi.repository import Gio, GObject
+from gi._compat import cmp
+
+
+class Item(GObject.Object):
+    _id = 0
+
+    def __init__(self, **kwargs):
+        super(Item, self).__init__(**kwargs)
+        Item._id += 1
+        self._id = self._id
+
+    def __repr__(self):
+        return str(self._id)
+
+
+class NamedItem(Item):
+
+    name = GObject.Property(type=str, default='')
+
+    def __repr__(self):
+        return self.props.name
+
+
+def test_list_store_sort():
+    store = Gio.ListStore()
+    items = [NamedItem(name=n) for n in "cabx"]
+    sorted_items = sorted(items, key=lambda i: i.props.name)
+
+    user_data = [object(), object()]
+
+    def sort_func(a, b, *args):
+        assert list(args) == user_data
+        assert isinstance(a, NamedItem)
+        assert isinstance(b, NamedItem)
+        return cmp(a.props.name, b.props.name)
+
+    store[:] = items
+    assert store[:] != sorted_items
+    store.sort(sort_func, *user_data)
+    assert store[:] == sorted_items
+
+
+def test_list_store_insert_sorted():
+    store = Gio.ListStore()
+    items = [NamedItem(name=n) for n in "cabx"]
+    sorted_items = sorted(items, key=lambda i: i.props.name)
+
+    user_data = [object(), object()]
+
+    def sort_func(a, b, *args):
+        assert list(args) == user_data
+        assert isinstance(a, NamedItem)
+        assert isinstance(b, NamedItem)
+        return cmp(a.props.name, b.props.name)
+
+    for item in items:
+        index = store.insert_sorted(item, sort_func, *user_data)
+        assert isinstance(index, int)
+    assert store[:] == sorted_items
+
+
+def test_list_model_len():
+    model = Gio.ListStore.new(Item)
+    assert len(model) == 0
+    assert not model
+    for i in range(1, 10):
+        model.append(Item())
+        assert len(model) == i
+    assert model
+    model.remove_all()
+    assert not model
+    assert len(model) == 0
+
+
+def test_list_model_get_item_simple():
+    model = Gio.ListStore.new(Item)
+    with pytest.raises(IndexError):
+        model[0]
+    first_item = Item()
+    model.append(first_item)
+    assert model[0] is first_item
+    assert model[-1] is first_item
+    second_item = Item()
+    model.append(second_item)
+    assert model[1] is second_item
+    assert model[-1] is second_item
+    assert model[-2] is first_item
+    with pytest.raises(IndexError):
+        model[-3]
+
+    with pytest.raises(TypeError):
+        model[object()]
+
+
+def test_list_model_get_item_slice():
+    model = Gio.ListStore.new(Item)
+    source = [Item() for i in range(30)]
+    for i in source:
+        model.append(i)
+    assert model[1:10] == source[1:10]
+    assert model[1:-2] == source[1:-2]
+    assert model[-4:-1] == source[-4:-1]
+    assert model[-100:-1] == source[-100:-1]
+    assert model[::-1] == source[::-1]
+    assert model[:] == source[:]
+
+
+def test_list_model_contains():
+    model = Gio.ListStore.new(Item)
+    item = Item()
+    model.append(item)
+    assert item in model
+    assert Item() not in model
+    with pytest.raises(TypeError):
+        object() in model
+    with pytest.raises(TypeError):
+        None in model
+
+
+def test_list_model_iter():
+    model = Gio.ListStore.new(Item)
+    item = Item()
+    model.append(item)
+
+    it = iter(model)
+    assert next(it) is item
+    repr(item)
+
+
+def test_list_store_delitem_simple():
+    store = Gio.ListStore.new(Item)
+    store.append(Item())
+    del store[0]
+    assert not store
+    with pytest.raises(IndexError):
+        del store[0]
+    with pytest.raises(IndexError):
+        del store[-1]
+
+    store.append(Item())
+    with pytest.raises(IndexError):
+        del store[-2]
+    del store[-1]
+    assert not store
+
+    source = [Item(), Item()]
+    store.append(source[0])
+    store.append(source[1])
+    del store[-1]
+    assert store[:] == [source[0]]
+
+    with pytest.raises(TypeError):
+        del store[object()]
+
+
+def test_list_store_delitem_slice():
+
+    def do_del(count, key):
+
+        events = []
+
+        def on_changed(m, *args):
+            events.append(args)
+
+        store = Gio.ListStore.new(Item)
+        source = [Item() for i in range(count)]
+        for item in source:
+            store.append(item)
+        store.connect("items-changed", on_changed)
+        source.__delitem__(key)
+        store.__delitem__(key)
+        assert source == store[:]
+        return events
+
+    values = [None, 1, -15, 3, -2, 0, -3, 5, 7]
+    variants = set()
+    for i in range(500):
+        start = random.choice(values)
+        stop = random.choice(values)
+        step = random.choice(values)
+        length = abs(random.choice(values) or 0)
+        if step == 0:
+            step += 1
+        variants.add((length, start, stop, step))
+
+    for length, start, stop, step in variants:
+        do_del(length, slice(start, stop, step))
+
+    # basics
+    do_del(10, slice(None, None, None))
+    do_del(10, slice(None, None, None))
+    do_del(10, slice(None, None, -1))
+    do_del(10, slice(0, 5, None))
+    do_del(10, slice(0, 10, 1))
+    do_del(10, slice(0, 10, 2))
+    do_del(10, slice(14, 2, -1))
+
+    # test some fast paths
+    assert do_del(100, slice(None, None, None)) == [(0, 100, 0)]
+    assert do_del(100, slice(None, None, -1)) == [(0, 100, 0)]
+    assert do_del(100, slice(0, 50, 1)) == [(0, 50, 0)]
+
+
+def test_list_store_setitem_simple():
+
+    store = Gio.ListStore.new(Item)
+    first = Item()
+    store.append(first)
+
+    class Wrong(GObject.Object):
+        pass
+
+    with pytest.raises(TypeError):
+        store[0] = object()
+    with pytest.raises(TypeError):
+        store[0] = None
+    with pytest.raises(TypeError):
+        store[0] = Wrong()
+
+    assert store[:] == [first]
+
+    new = Item()
+    store[0] = new
+    assert len(store) == 1
+    store[-1] = Item()
+    assert len(store) == 1
+
+    with pytest.raises(IndexError):
+        store[1] = Item()
+    with pytest.raises(IndexError):
+        store[-2] = Item()
+
+    store = Gio.ListStore.new(Item)
+    source = [Item(), Item(), Item()]
+    for item in source:
+        store.append(item)
+    new = Item()
+    store[1] = new
+    assert store[:] == [source[0], new, source[2]]
+
+    with pytest.raises(TypeError):
+        store[object()] = Item()
+
+
+def test_list_store_setitem_slice():
+
+    def do_set(count, key, new_count):
+        if count == 0 and key.step is not None \
+                and platform.python_implementation() == "PyPy":
+            # https://bitbucket.org/pypy/pypy/issues/2804
+            return
+        store = Gio.ListStore.new(Item)
+        source = [Item() for i in range(count)]
+        new = [Item() for i in range(new_count)]
+        for item in source:
+            store.append(item)
+        source_error = None
+        try:
+            source.__setitem__(key, new)
+        except ValueError as e:
+            source_error = type(e)
+
+        store_error = None
+        try:
+            store.__setitem__(key, new)
+        except Exception as e:
+            store_error = type(e)
+
+        assert source_error == store_error
+        assert source == store[:]
+
+    values = [None, 1, -15, 3, -2, 0, 3, 4, 100]
+    variants = set()
+    for i in range(500):
+        start = random.choice(values)
+        stop = random.choice(values)
+        step = random.choice(values)
+        length = abs(random.choice(values) or 0)
+        new = random.choice(values) or 0
+        if step == 0:
+            step += 1
+        variants.add((length, start, stop, step, new))
+
+    for length, start, stop, step, new in variants:
+        do_set(length, slice(start, stop, step), new)
+
+    # basics
+    do_set(10, slice(None, None, None), 20)
+    do_set(10, slice(None, None, None), 0)
+    do_set(10, slice(None, None, -1), 20)
+    do_set(10, slice(None, None, -1), 10)
+    do_set(10, slice(0, 5, None), 20)
+    do_set(10, slice(0, 10, 1), 0)
+
+    # test iterators
+    store = Gio.ListStore.new(Item)
+    store[:] = iter([Item() for i in range(10)])
+    assert len(store) == 10
+
+    # make sure we do all or nothing
+    store = Gio.ListStore.new(Item)
+    with pytest.raises(TypeError):
+        store[:] = [Item(), object()]
+    assert len(store) == 0
+
+
+def test_action_map_add_action_entries():
+    actionmap = Gio.SimpleActionGroup()
+
+    test_data = []
+
+    def f(action, parameter, data):
+        test_data.append('test back')
+
+    actionmap.add_action_entries((
+        ("simple", f),
+        ("with_type", f, "i"),
+        ("with_state", f, "s", "'left'", f),
+    ))
+    assert actionmap.has_action("simple")
+    assert actionmap.has_action("with_type")
+    assert actionmap.has_action("with_state")
+    actionmap.add_action_entries((
+        ("with_user_data", f),
+    ), "user_data")
+    assert actionmap.has_action("with_user_data")
+
+    with pytest.raises(TypeError):
+        actionmap.add_action_entries((
+            ("invaild_type_string", f, 'asdf'),
+        ))
+    with pytest.raises(ValueError):
+        actionmap.add_action_entries((
+            ("stateless_with_change_state", f, None, None, f),
+        ))
+
+    actionmap.activate_action("simple")
+    assert test_data[0] == 'test back'
index 7ffb506..1e36552 100644 (file)
@@ -70,6 +70,32 @@ def realized(widget):
 
 
 @unittest.skipUnless(Gtk, 'Gtk not available')
+def test_freeze_child_notif():
+
+    events = []
+
+    def on_notify(widget, spec):
+        events.append(spec.name)
+
+    b = Gtk.Box()
+    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)
+    c.thaw_child_notify()
+    assert events.count("expand") == 1
+    del events[:]
+
+    with c.freeze_child_notify():
+        b.child_set_property(c, "expand", True)
+        b.child_set_property(c, "expand", False)
+
+    assert events.count("expand") == 1
+
+
+@unittest.skipUnless(Gtk, 'Gtk not available')
 def test_wrapper_toggle_refs():
     class MyButton(Gtk.Button):
         def __init__(self, height):
index ca90270..35b03b7 100644 (file)
@@ -32,7 +32,7 @@ from gi.repository import Regress
 from gi import _propertyhelper as propertyhelper
 
 from gi._compat import long_, PY3, PY2
-from .helper import capture_glib_warnings, capture_output
+from .helper import capture_glib_warnings
 
 
 class PropertyObject(GObject.GObject):
@@ -548,7 +548,6 @@ class TestProperty(unittest.TestCase):
         self.assertEqual(o.prop, 'value')
         self.assertRaises(TypeError, setattr, o, 'prop', 'xxx')
 
-    @unittest.expectedFailure  # https://bugzilla.gnome.org/show_bug.cgi?id=575652
     def test_getter_exception(self):
         class C(GObject.Object):
             @GObject.Property(type=int)
@@ -557,16 +556,14 @@ class TestProperty(unittest.TestCase):
 
         o = C()
 
-        # silence exception printed to stderr
-        with capture_output():
-            with self.assertRaisesRegex(ValueError, 'something bad happend'):
-                o.prop
+        with self.assertRaisesRegex(ValueError, 'something bad happend'):
+            o.prop
 
-            with self.assertRaisesRegex(ValueError, 'something bad happend'):
-                o.get_property('prop')
+        with self.assertRaisesRegex(ValueError, 'something bad happend'):
+            o.get_property('prop')
 
-            with self.assertRaisesRegex(ValueError, 'something bad happend'):
-                o.props.prop
+        with self.assertRaisesRegex(ValueError, 'something bad happend'):
+            o.props.prop
 
     def test_custom_setter(self):
         class C(GObject.GObject):
@@ -1024,6 +1021,26 @@ class CPropertiesTestBase(object):
         obj = GIMarshallingTests.PropertiesObject(some_char=-42)
         self.assertEqual(self.get_prop(obj, 'some-char'), -42)
 
+        with pytest.raises(OverflowError):
+            self.set_prop(obj, 'some-char', GLib.MAXINT8 + 1)
+        with pytest.raises(OverflowError):
+            self.set_prop(obj, 'some-char', GLib.MININT8 - 1)
+
+        self.set_prop(obj, 'some-char', b"\x44")
+        assert self.get_prop(obj, 'some-char') == 0x44
+
+        self.set_prop(obj, 'some-char', b"\xff")
+        assert self.get_prop(obj, 'some-char') == -1
+
+        obj = GIMarshallingTests.PropertiesObject(some_char=u"\x7f")
+        assert self.get_prop(obj, 'some-char') == 0x7f
+
+        with pytest.raises(TypeError):
+            GIMarshallingTests.PropertiesObject(some_char=u"€")
+
+        with pytest.raises(TypeError):
+            GIMarshallingTests.PropertiesObject(some_char=u"\ud83d")
+
     def test_uchar(self):
         self.assertEqual(self.get_prop(self.obj, 'some-uchar'), 0)
         self.set_prop(self.obj, 'some-uchar', GLib.MAXUINT8)
@@ -1032,6 +1049,26 @@ class CPropertiesTestBase(object):
         obj = GIMarshallingTests.PropertiesObject(some_uchar=42)
         self.assertEqual(self.get_prop(obj, 'some-uchar'), 42)
 
+        with pytest.raises(OverflowError):
+            self.set_prop(obj, 'some-uchar', GLib.MAXUINT8 + 1)
+        with pytest.raises(OverflowError):
+            self.set_prop(obj, 'some-uchar', -1)
+
+        self.set_prop(obj, 'some-uchar', b"\x57")
+        assert self.get_prop(obj, 'some-uchar') == 0x57
+
+        self.set_prop(obj, 'some-uchar', b"\xff")
+        assert self.get_prop(obj, 'some-uchar') == 255
+
+        obj = GIMarshallingTests.PropertiesObject(some_uchar=u"\x7f")
+        assert self.get_prop(obj, 'some-uchar') == 127
+
+        with pytest.raises(TypeError):
+            GIMarshallingTests.PropertiesObject(some_uchar=u"\x80")
+
+        with pytest.raises(TypeError):
+            GIMarshallingTests.PropertiesObject(some_uchar=u"\ud83d")
+
     def test_int(self):
         self.assertEqual(self.get_prop(self.obj, 'some_int'), 0)
         self.set_prop(self.obj, 'some-int', GLib.MAXINT)
index f4641a8..16d95c2 100644 (file)
@@ -14,7 +14,7 @@ from gi import _signalhelper as signalhelper
 from gi.module import repository as repo
 from gi._compat import PY3, long_
 
-from . import testhelper
+import testhelper
 from .helper import capture_glib_warnings, capture_gi_deprecation_warnings
 
 
index 8cd0085..e4e6399 100644 (file)
@@ -229,10 +229,9 @@ class TestSource(unittest.TestCase):
         gc.collect()
         self.assertTrue(self.finalized)
 
-    @unittest.skip('https://bugzilla.gnome.org/show_bug.cgi?id=722387')
     def test_python_unref_with_active_source(self):
         # Tests a Python derived Source which is free'd in the context of
-        # Python, but remains active in the MainContext (via source.attach())
+        # Python, but which was attached to a MainContext (via source.attach())
         self.dispatched = False
         self.finalized = False
 
@@ -250,29 +249,23 @@ class TestSource(unittest.TestCase):
             def finalize(s):
                 self.finalized = True
 
+        context = GLib.MainContext.new()
         source = S()
-        id = source.attach()
+        id_ = source.attach(context)
         self.assertFalse(self.finalized)
         self.assertFalse(source.is_destroyed())
 
-        # Delete the source from Python but should still remain
-        # active in the main context.
+        # Delete the source from Python, it should detach
         del source
+        gc.collect()
+        gc.collect()
 
-        context = GLib.MainContext.default()
         while context.iteration(may_block=False):
             pass
 
-        self.assertTrue(self.dispatched)
-        self.assertFalse(self.finalized)
-
-        source = context.find_source_by_id(id)
-        source.destroy()  # Remove from main context.
-        self.assertTrue(source.is_destroyed())
-
-        # Source should be finalized called after del
-        del source
-        self.assertTrue(self.finalized)
+        assert self.finalized
+        assert not self.dispatched
+        assert context.find_source_by_id(id_) is None
 
     def test_extra_init_args(self):
         class SourceWithInitArgs(GLib.Source):
index e2bbda0..45ebd0b 100644 (file)
@@ -6,7 +6,7 @@ import unittest
 
 from gi.repository import GLib
 
-from . import testhelper
+import testhelper
 
 
 class TestThread(unittest.TestCase):
index e483003..1ed1fb8 100644 (file)
@@ -6,7 +6,7 @@ import unittest
 
 from gi.repository import GObject
 
-from . import testhelper
+import testhelper
 
 
 TestInterface = GObject.GType.from_name('TestInterface')
index 9fc4fc6..e26a004 100644 (file)
@@ -505,6 +505,15 @@ _wrap_test_value(PyObject *self, PyObject *args)
 }
 
 static PyObject *
+_wrap_test_state_ensure_release(PyObject *self, PyObject *args)
+{
+    int state = pyg_gil_state_ensure ();
+    pyg_gil_state_release (state);
+
+    Py_RETURN_NONE;
+}
+
+static PyObject *
 _wrap_test_value_array(PyObject *self, PyObject *args)
 {
   GValue tvalue = {0,}, *value = &tvalue;
@@ -525,6 +534,56 @@ _wrap_test_value_array(PyObject *self, PyObject *args)
   return pyg_value_as_pyobject(value, FALSE);
 }
 
+
+static PyObject *
+_wrap_value_array_get_nth_type(PyObject *self, PyObject *args)
+{
+  guint n;
+  GType type;
+  GValue *nth;
+  GValueArray *arr;
+  PyObject *obj;
+
+  if (!PyArg_ParseTuple(args, "OI", &obj, &n))
+    return NULL;
+
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+
+  if (pyg_boxed_check(obj, G_TYPE_VALUE) &&
+      G_VALUE_HOLDS(pyg_boxed_get(obj, GValue), G_TYPE_VALUE_ARRAY)) {
+    arr = g_value_get_boxed(pyg_boxed_get(obj, GValue));
+  } else if (pyg_boxed_check(obj, G_TYPE_VALUE_ARRAY)) {
+    arr = pyg_boxed_get(obj, GValueArray);
+  } else {
+    PyErr_SetString(PyExc_TypeError, "First argument is not GValueArray");
+    return NULL;
+  }
+
+  if (n >= arr->n_values) {
+    PyErr_SetString(PyExc_TypeError, "Index is out of bounds");
+    return NULL;
+  }
+  nth = g_value_array_get_nth(arr, n);
+  type = G_VALUE_TYPE(nth);
+
+  G_GNUC_END_IGNORE_DEPRECATIONS
+
+  return pyg_type_wrapper_new(type);
+}
+
+static PyObject *
+_wrap_constant_strip_prefix(PyObject *self, PyObject *args)
+{
+    const char *name, *strip_prefix;
+    const gchar *result;
+
+    if (!PyArg_ParseTuple (args, "ss", &name, &strip_prefix))
+        return NULL;
+
+    result = pyg_constant_strip_prefix (name, strip_prefix);
+    return PYGLIB_PyUnicode_FromString (result);
+}
+
 static PyObject *
 _wrap_test_gerror_exception(PyObject *self, PyObject *args)
 {
@@ -638,10 +697,13 @@ static PyMethodDef testhelper_functions[] = {
     { "test_to_unichar_conv", (PyCFunction)_wrap_test_to_unichar_conv, METH_VARARGS },
     { "get_unknown", (PyCFunction)_wrap_get_unknown, METH_NOARGS },
     { "create_test_type", (PyCFunction)_wrap_create_test_type, METH_NOARGS },
+    { "test_state_ensure_release", (PyCFunction)_wrap_test_state_ensure_release, METH_NOARGS },
     { "test_g_object_new", (PyCFunction)_wrap_test_g_object_new, METH_NOARGS },
     { "connectcallbacks", (PyCFunction)_wrap_connectcallbacks, METH_VARARGS },
     { "test_value", (PyCFunction)_wrap_test_value, METH_VARARGS },      
     { "test_value_array", (PyCFunction)_wrap_test_value_array, METH_VARARGS },
+    { "value_array_get_nth_type", (PyCFunction)_wrap_value_array_get_nth_type, METH_VARARGS },
+    { "constant_strip_prefix", (PyCFunction)_wrap_constant_strip_prefix, METH_VARARGS },
     { "test_gerror_exception", (PyCFunction)_wrap_test_gerror_exception, METH_VARARGS },
     { "owned_by_library_get_instance_list", (PyCFunction)_wrap_test_owned_by_library_get_instance_list, METH_NOARGS },
     { "floating_and_sunk_get_instance_list", (PyCFunction)_wrap_test_floating_and_sunk_get_instance_list, METH_NOARGS },
diff --git a/tests/valgrind.supp b/tests/valgrind.supp
new file mode 100644 (file)
index 0000000..5792a7c
--- /dev/null
@@ -0,0 +1,30 @@
+# https://bugzilla.redhat.com/show_bug.cgi?id=1538073
+
+{
+   <py36-start1>
+   Memcheck:Cond
+   fun:__wcsnlen_sse4_1
+   fun:wcsrtombs
+   fun:wcstombs
+   fun:wcstombs
+   fun:encode_current_locale*
+}
+
+{
+   <fontconfig>
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   fun:FcPatternObjectInsertElt
+   fun:FcPatternObjectAddWithBinding
+}
+
+{
+   <fontconfig-2>
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   fun:FcPatternCreate
+   fun:FcParsePattern
+   fun:FcEndElement
+}