+commit 1a63a04eaf2a77c1752b90e80ab571677f27ac3d
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date: Mon Mar 3 06:49:09 2014 -0800
+
+ build: Update release-news to use srcdir
+
+ Use $(top_srcdir)/NEWS for pulling in news items to ensure
+ "make release-news" works in a vpath build environment.
+
+ Makefile.am | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit 5798f94b6a727b930b07fe840b0aef264f98a80e
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date: Fri Feb 7 20:16:21 2014 -0800
+
+ Use ffi_call directly instead of g_callable_info_invoke
+
+ Cleanup internal callable cache and state tracking by removing
+ multiple
+ counting schemes for differently sized "in" and "out" argument arrays.
+ Use a single count based on the total number of arguments passed to C
+ (inclusive of instance argument and GError exception where
+ applicable).
+ Size all state tracking arrays to the same size and ensure argument
+ cache
+ indices always line up with these arrays. This cleans up logic
+ which was
+ required by g_callable_info_invoke for splitting "in" and "out"
+ arguments
+ up.
+
+ Cleanup array marshaling which can now rely on the new scheme
+ which ensures
+ the "arg_values" array always points to the correct location for
+ length
+ argument values.
+
+ Cache the ffi_cif struct in PyGICallableCache via GIFunctionInvoker
+ and
+ related GI methods. Overall, these changes can give a performance
+ boost of
+ almost 2x for simple function calls (see ticket for micro benchmarks).
+
+ https://bugzilla.gnome.org/show_bug.cgi?id=723642
+
+ gi/pygi-array.c | 26 ++---
+ gi/pygi-cache.c | 61 +++++++++--
+ gi/pygi-cache.h | 10 +-
+ gi/pygi-ccallback.c | 3 +-
+ gi/pygi-closure.c | 6 +-
+ gi/pygi-invoke-state-struct.h | 42 +++++---
+ gi/pygi-invoke.c | 241
+ +++++++++++++++++++++++-------------------
+ gi/pygi-invoke.h | 2 +-
+ gi/pygi-marshal-cleanup.c | 4 +-
+ 9 files changed, 230 insertions(+), 165 deletions(-)
+
+commit ad680ae9c37a0091628a7d66010fbf70aa1a2e43
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date: Mon Mar 3 04:51:09 2014 -0800
+
+ tests: Move class definition depending on GTK+ within function
+ evaluation
+
+ Move the definition of WindowWithSizeAllocOverride inside of the test
+ function call to so it is lazily defined. This avoids problems
+ running tests
+ on systems without GTK+ installed.
+
+ tests/test_overrides_gtk.py | 32 ++++++++++++++++----------------
+ 1 file changed, 16 insertions(+), 16 deletions(-)
+
+commit 45d45e7c2704d68a3008f739e501fa332d326b8b
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date: Mon Mar 3 04:45:59 2014 -0800
+
+ tests: Conditionalize usage of GTK+ in tests_generictreemodel
+
+ This allows running make check without GTK+ installed.
+
+ tests/test_generictreemodel.py | 16 +++++++++++++---
+ 1 file changed, 13 insertions(+), 3 deletions(-)
+
+commit 038563ed620e0d966e385a1779455d9b0e148c41
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date: Mon Mar 3 04:39:35 2014 -0800
+
+ tests: Conditionalize usage of regress typelib in test_properties
+
+ Unconditional usage of regress breaks tests when PyGObject is
+ built without
+ cairo.
+
+ tests/test_properties.py | 27 +++++++++++++++++----------
+ 1 file changed, 17 insertions(+), 10 deletions(-)
+
+commit 1fa93ddc51b2d223d772aee7930fc96c0ced0e00
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date: Mon Mar 3 02:44:12 2014 -0800
+
+ configure.ac: Use -std=c90 and error on declaration-after-statement
+
+ Replace gcc option of -std=c9x with c90 and add
+ -Werror=declaration-after-statement
+ This ensures we keep compatibility with msvc builds.
+
+ configure.ac | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+commit cee414ab5725c51d79a2c6aa1e8760e9fd754545
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date: Mon Mar 3 02:38:30 2014 -0800
+
+ Use g_snprintf instead of snprintf
+
+ Use g_snprintf for consistency with the rest of gobjectmodule.c
+
+ gi/gobjectmodule.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit b016ae6793839b2a6a00a69d00de30937bc611be
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date: Thu Feb 27 04:27:41 2014 -0800
+
+ Use C style comments
+
+ Update various locations which use C99 single line comments to
+ conform to
+ C90 style comments. Found with: make CFLAGS="-std=C90"
+
+ gi/gimodule.c | 2 +-
+ gi/pygi-array.c | 3 ++-
+ gi/pygi-property.c | 2 +-
+ gi/pygtype.c | 2 +-
+ 4 files changed, 5 insertions(+), 4 deletions(-)
+
+commit df7cba1495c167f1019dec7f4398dc5de62a5937
+Author: Chun-wei Fan <fanchunwei@src.gnome.org>
+Date: Tue Feb 25 14:38:41 2014 +0800
+
+ Fix Build on Visual Studio
+
+ Some items from pygi-enum-marshal.c were moved to pygi-basictype.c,
+ which
+ included the use of the NAN and INFINITY macros/constants, so the
+ definitions for those need to be moved to pygi-basictype.c as well.
+ Also
+ avoid defining a variable in the middle of the block.
+
+ https://bugzilla.gnome.org/show_bug.cgi?id=725122
+
+ gi/pygi-basictype.c | 16 ++++++++++++++++
+ gi/pygi-cache.c | 3 ++-
+ gi/pygi-enum-marshal.c | 16 ----------------
+ 3 files changed, 18 insertions(+), 17 deletions(-)
+
+commit bb5550bc85ac0ff60ea39912416e347f27853fb4
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date: Mon Feb 17 17:22:40 2014 -0800
+
+ Update release steps to be more explicit in regards to NEWS
+
+ Add an explicit step to commit the NEWS changes and push prior
+ tagging.
+
+ HACKING | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+commit c6ac95286bce858f1925a9d6173a91866d7e9f88
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date: Mon Feb 17 17:18:10 2014 -0800
+
+ configure.ac: post release version bump to 3.11.91
+
+ configure.ac | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+commit f87e341c5528d066371d4ec493956db28dd0bafa
+Author: Simon Feltman <sfeltman@src.gnome.org>
+Date: Mon Feb 17 17:08:13 2014 -0800
+
+ release 3.11.90
+
+ NEWS | 4 ++++
+ 1 file changed, 4 insertions(+)
+
commit 9b345b153e86ca6c9b7290cf2ad3b38f6ad9d0e5
Author: Simon Feltman <sfeltman@src.gnome.org>
Date: Wed Feb 12 10:28:35 2014 -0800
@echo -e "\n\n\nFor blogging, you can copy&paste this HTML formatted news:"
@echo "-------------- 8< -------------"
- @last=`head -n1 NEWS | cut -f1 -d' '`; \
+ @last=`head -n1 $(top_srcdir)/NEWS | cut -f1 -d' '`; \
echo "<ul>"; \
for commit in `git rev-list $$last..`; do \
data=`git log --format="format:%s%n%an%n%b" $$commit^..$$commit`; \
@echo -e "\n\n\nFor blogging, you can copy&paste this HTML formatted news:"
@echo "-------------- 8< -------------"
- @last=`head -n1 NEWS | cut -f1 -d' '`; \
+ @last=`head -n1 $(top_srcdir)/NEWS | cut -f1 -d' '`; \
echo "<ul>"; \
for commit in `git rev-list $$last..`; do \
data=`git log --format="format:%s%n%an%n%b" $$commit^..$$commit`; \
+3.11.91 03-Mar-2014
+ - Use ffi_call directly instead of g_callable_info_invoke
+ (Simon Feltman) (#723642)
+ - configure.ac: Use -std=c90 and error on declaration-after-statement
+ (Simon Feltman)
+ - Fix Build on Visual Studio (Chun-wei Fan) (#725122)
+
3.11.90 17-Feb-2014
- Use GObject type checking for instance arguments (Simon Feltman) (#724009)
- configure.ac: post release version bump to 3.11.90 (Simon Feltman)
Metadata-Version: 1.0
Name: PyGObject
-Version: 3.11.90
+Version: 3.11.91
Summary: Python bindings for GObject
Home-page: http://www.pygtk.org/
Author: James Henstridge
Maintainer: Johan Dahlin
Maintainer-email: johan@gnome.org
License: GNU LGPL
-Download-url: ftp://ftp.gnome.org/pub/GNOME/sources/pygobject/3.11/pygobject-3.11.90.tar.gz
+Download-url: ftp://ftp.gnome.org/pub/GNOME/sources/pygobject/3.11/pygobject-3.11.91.tar.gz
Description: Python bindings for GLib and GObject
Platform: POSIX, Windows
Classifier: Development Status :: 5 - Production/Stable
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for pygobject 3.11.90.
+# Generated by GNU Autoconf 2.69 for pygobject 3.11.91.
#
# Report bugs to <http://bugzilla.gnome.org/enter_bug.cgi?product=pygobject>.
#
# Identity of this package.
PACKAGE_NAME='pygobject'
PACKAGE_TARNAME='pygobject'
-PACKAGE_VERSION='3.11.90'
-PACKAGE_STRING='pygobject 3.11.90'
+PACKAGE_VERSION='3.11.91'
+PACKAGE_STRING='pygobject 3.11.91'
PACKAGE_BUGREPORT='http://bugzilla.gnome.org/enter_bug.cgi?product=pygobject'
PACKAGE_URL='https://live.gnome.org/PyGObject/'
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures pygobject 3.11.90 to adapt to many kinds of systems.
+\`configure' configures pygobject 3.11.91 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of pygobject 3.11.90:";;
+ short | recursive ) echo "Configuration of pygobject 3.11.91:";;
esac
cat <<\_ACEOF
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-pygobject configure 3.11.90
+pygobject configure 3.11.91
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by pygobject $as_me 3.11.90, which was
+It was created by pygobject $as_me 3.11.91, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
PYGOBJECT_MINOR_VERSION=11
-$as_echo "#define PYGOBJECT_MICRO_VERSION 90" >>confdefs.h
+$as_echo "#define PYGOBJECT_MICRO_VERSION 91" >>confdefs.h
-PYGOBJECT_MICRO_VERSION=90
+PYGOBJECT_MICRO_VERSION=91
ac_config_headers="$ac_config_headers config.h"
# Define the identity of the package.
PACKAGE='pygobject'
- VERSION='3.11.90'
+ VERSION='3.11.91'
cat >>confdefs.h <<_ACEOF
;;
esac
+case " $CFLAGS " in
+*[\ \ ]-Werror=declaration-after-statement[\ \ ]*)
+ ;;
+*)
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -Werror=declaration-after-statement"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -Werror=declaration-after-statement" >&5
+$as_echo_n "checking whether $CC understands -Werror=declaration-after-statement... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ jh_has_option=yes
+else
+ jh_has_option=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $jh_has_option" >&5
+$as_echo "$jh_has_option" >&6; }
+ if test $jh_has_option = no; then
+ CFLAGS="$save_CFLAGS"
+ fi
+ ;;
+esac
+
case $host_os in
solaris*)
;;
*)
case " $CFLAGS " in
-*[\ \ ]-std=c9x[\ \ ]*)
+*[\ \ ]-std=c90[\ \ ]*)
;;
*)
save_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS -std=c9x"
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -std=c9x" >&5
-$as_echo_n "checking whether $CC understands -std=c9x... " >&6; }
+ CFLAGS="$CFLAGS -std=c90"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -std=c90" >&5
+$as_echo_n "checking whether $CC understands -std=c90... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by pygobject $as_me 3.11.90, which was
+This file was extended by pygobject $as_me 3.11.91, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-pygobject config.status 3.11.90
+pygobject config.status 3.11.91
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
dnl the pygobject version number
m4_define(pygobject_major_version, 3)
m4_define(pygobject_minor_version, 11)
-m4_define(pygobject_micro_version, 90)
+m4_define(pygobject_micro_version, 91)
m4_define(pygobject_version, pygobject_major_version.pygobject_minor_version.pygobject_micro_version)
dnl versions of packages we require ...
JH_ADD_CFLAG([-Wall])
JH_ADD_CFLAG([-Werror=unused-variable])
JH_ADD_CFLAG([-fno-strict-aliasing])
+ JH_ADD_CFLAG([-Werror=declaration-after-statement])
case $host_os in
solaris*)
;;
*)
- JH_ADD_CFLAG([-std=c9x])
+ JH_ADD_CFLAG([-std=c90])
;;
esac
static void
initialize_interface (GTypeInterface *iface, PyTypeObject *pytype)
{
- // pygobject prints a warning if interface_init is NULL
+ /* pygobject prints a warning if interface_init is NULL */
}
static PyObject *
while (name_serial < 1000)
{
g_free(type_name);
- snprintf(name_serial_str, 16, "-v%i", name_serial);
+ g_snprintf(name_serial_str, 16, "-v%i", name_serial);
module = PyObject_GetAttrString((PyObject *)class, "__module__");
if (module && PYGLIB_PyUnicode_Check(module)) {
type_name = g_strconcat(PYGLIB_PyUnicode_AsString(module), ".",
PyGIArgCache *child_cache =
_pygi_callable_cache_get_arg (callable_cache, array_cache->len_arg_index);
- if (child_cache->direction == PYGI_DIRECTION_BIDIRECTIONAL) {
- gint *len_arg = (gint *)state->in_args[child_cache->c_arg_index].v_pointer;
- /* if we are not setup yet just set the in arg */
- if (len_arg == NULL) {
- if (!gi_argument_from_py_ssize_t (&state->in_args[child_cache->c_arg_index],
- length,
- child_cache->type_tag)) {
- goto err;
- }
- } else {
- *len_arg = length;
- }
- } else {
- if (!gi_argument_from_py_ssize_t (&state->in_args[child_cache->c_arg_index],
- length,
- child_cache->type_tag)) {
- goto err;
- }
+ if (!gi_argument_from_py_ssize_t (&state->arg_values[child_cache->c_arg_index],
+ length,
+ child_cache->type_tag)) {
+ goto err;
}
}
len = g_strv_length ((gchar **)arg->v_pointer);
}
} else {
- GIArgument *len_arg = state->args[array_cache->len_arg_index];
+ GIArgument *len_arg = &state->arg_values[array_cache->len_arg_index];
PyGIArgCache *arg_cache = _pygi_callable_cache_get_arg (callable_cache,
array_cache->len_arg_index);
} else if (item_arg_cache->type_tag == GI_TYPE_TAG_INTERFACE) {
PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *) item_arg_cache;
- // FIXME: This probably doesn't work with boxed types or gvalues. See fx. _pygi_marshal_from_py_array()
+ /* FIXME: This probably doesn't work with boxed types or gvalues.
+ * See fx. _pygi_marshal_from_py_array() */
switch (g_base_info_get_type (iface_cache->interface_info)) {
case GI_INFO_TYPE_STRUCT:
if (arg_cache->transfer == GI_TRANSFER_EVERYTHING &&
} else if (array_cache->is_zero_terminated) {
len = g_strv_length ((gchar **)data);
} else if (array_cache->len_arg_index >= 0) {
- GIArgument *len_arg = state->args[array_cache->len_arg_index];
+ GIArgument *len_arg = &state->arg_values[array_cache->len_arg_index];
len = len_arg->v_long;
}
#include "pygi-argument.h"
#include "pygi-private.h"
+#ifdef G_OS_WIN32
+#ifdef _MSC_VER
+#include <math.h>
+
+#ifndef NAN
+static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff};
+#define NAN (*(const float *) __nan)
+#endif
+
+#ifndef INFINITY
+#define INFINITY HUGE_VAL
+#endif
+
+#endif
+#endif
+
/*
* From Python Marshaling
#include <girepository.h>
+#include "pyglib.h"
#include "pygi-info.h"
#include "pygi-cache.h"
#include "pygi-marshal-cleanup.h"
if (cache->return_cache != NULL)
pygi_arg_cache_free (cache->return_cache);
+ g_function_invoker_destroy (&cache->invoker);
g_slice_free (PyGICallableCache, cache);
}
GITransfer return_transfer;
PyGIArgCache *return_cache;
PyGIDirection return_direction;
+ gssize last_explicit_arg_index;
/* Return arguments are always considered out */
return_direction = _pygi_get_direction (callable_cache, GI_DIRECTION_OUT);
_pygi_callable_cache_set_arg (callable_cache, arg_index, instance_cache);
arg_index++;
- callable_cache->n_from_py_args++;
callable_cache->n_py_args++;
}
arg_cache->meta_type = PYGI_META_ARG_TYPE_CLOSURE;
arg_cache->c_arg_index = i;
- callable_cache->n_from_py_args++;
-
} else {
GITypeInfo *type_info;
*/
arg_cache = _pygi_callable_cache_get_arg (callable_cache, arg_index);
if (arg_cache != NULL) {
+ /* ensure c_arg_index always aligns with callable_cache->args_cache
+ * and all of the various PyGIInvokeState arrays. */
+ arg_cache->c_arg_index = arg_index;
+
if (arg_cache->meta_type == PYGI_META_ARG_TYPE_CHILD_WITH_PYARG) {
arg_cache->py_arg_index = callable_cache->n_py_args;
callable_cache->n_py_args++;
}
- if (direction & PYGI_DIRECTION_FROM_PYTHON) {
- arg_cache->c_arg_index = callable_cache->n_from_py_args;
- callable_cache->n_from_py_args++;
- }
-
if (direction & PYGI_DIRECTION_TO_PYTHON) {
callable_cache->n_to_py_args++;
}
if (direction & PYGI_DIRECTION_FROM_PYTHON) {
py_arg_index = callable_cache->n_py_args;
- callable_cache->n_from_py_args++;
callable_cache->n_py_args++;
}
callable_cache->n_py_required_args = 0;
callable_cache->user_data_varargs_index = -1;
- gssize last_explicit_arg_index = -1;
+ last_explicit_arg_index = -1;
/* Reverse loop through all the arguments to setup arg_name_list/hash
* and find the number of required arguments */
return TRUE;
}
+static gboolean
+_setup_invoker (GICallableInfo *callable_info,
+ GIInfoType info_type,
+ GIFunctionInvoker *invoker,
+ GCallback function_ptr)
+{
+ GError *error = NULL;
+
+ if (info_type == GI_INFO_TYPE_FUNCTION) {
+ if (g_function_info_prep_invoker ((GIFunctionInfo *)callable_info,
+ invoker,
+ &error)) {
+ return TRUE;
+ }
+ if (!pyglib_error_check (&error)) {
+ PyErr_Format (PyExc_RuntimeError,
+ "unknown error creating invoker for %s",
+ g_base_info_get_name ((GIBaseInfo *)callable_info));
+ }
+ return FALSE;
+
+ } else {
+ if (!g_function_invoker_new_for_address (function_ptr,
+ (GIFunctionInfo *)callable_info,
+ invoker,
+ &error)) {
+ if (!pyglib_error_check (&error)) {
+ PyErr_Format (PyExc_RuntimeError,
+ "unknown error creating invoker for %s",
+ g_base_info_get_name ((GIBaseInfo *)callable_info));
+ }
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
PyGICallableCache *
-pygi_callable_cache_new (GICallableInfo *callable_info, gboolean is_ccallback)
+pygi_callable_cache_new (GICallableInfo *callable_info,
+ GCallback function_ptr,
+ gboolean is_ccallback)
{
gint n_args;
PyGICallableCache *cache;
return NULL;
cache->name = g_base_info_get_name ((GIBaseInfo *)callable_info);
+ cache->throws = g_callable_info_can_throw_gerror ((GIBaseInfo *)callable_info);
if (g_base_info_is_deprecated (callable_info)) {
const gchar *deprecated = g_base_info_get_attribute (callable_info, "deprecated");
if (!_args_cache_generate (callable_info, cache))
goto err;
+ if (!_setup_invoker (callable_info, type, &cache->invoker, function_ptr)) {
+ goto err;
+ }
+
return cache;
err:
pygi_callable_cache_free (cache);
#include <Python.h>
#include <girepository.h>
+#include <girffi.h>
#include "pygi-invoke-state-struct.h"
GSList *to_py_args;
GSList *arg_name_list; /* for keyword arg matching */
GHashTable *arg_name_hash;
+ gboolean throws;
/* Index of user_data arg that can eat variable args passed to a callable. */
gssize user_data_varargs_index;
- /* Number of in args passed to g_function_info_invoke.
- * This is used for the length of PyGIInvokeState.in_args */
- gssize n_from_py_args;
-
/* Number of out args passed to g_function_info_invoke.
* This is used for the length of PyGIInvokeState.out_values */
gssize n_to_py_args;
/* Minimum number of args required to call the callable from Python.
* This count does not include args with defaults. */
gssize n_py_required_args;
+
+ /* An invoker with ffi_cif already setup */
+ GIFunctionInvoker invoker;
};
gboolean
PyGICallableCache *
pygi_callable_cache_new (GICallableInfo *callable_info,
+ GCallback function_ptr,
gboolean is_ccallback);
#define _pygi_callable_cache_args_len(cache) ((cache)->args_cache)->len
PyObject *result;
if (self->cache == NULL) {
- self->cache = pygi_callable_cache_new (self->info, TRUE);
+ self->cache = pygi_callable_cache_new (self->info, self->callback, TRUE);
if (self->cache == NULL)
return NULL;
}
args,
kwargs,
self->cache,
- self->callback,
self->user_data);
return result;
}
* The return trip to python will marshal this back and pull the python user data out.
*/
if (user_data_cache != NULL) {
- state->in_args[user_data_cache->c_arg_index].v_pointer = closure;
+ state->arg_values[user_data_cache->c_arg_index].v_pointer = closure;
}
/* Setup a GDestroyNotify callback if this method supports it along with
if (destroy_cache) {
if (user_data_cache != NULL) {
PyGICClosure *destroy_notify = _pygi_destroy_notify_create ();
- state->in_args[destroy_cache->c_arg_index].v_pointer = destroy_notify->closure;
+ state->arg_values[destroy_cache->c_arg_index].v_pointer = destroy_notify->closure;
} else {
gchar *msg = g_strdup_printf("Callables passed to %s will leak references because "
"the method does not support a user_data argument. "
return FALSE;
}
g_free(msg);
- state->in_args[destroy_cache->c_arg_index].v_pointer = _pygi_destroy_notify_dummy;
+ state->arg_values[destroy_cache->c_arg_index].v_pointer = _pygi_destroy_notify_dummy;
}
}
#include "pygi-enum-marshal.h"
#include "pygi-private.h"
-#ifdef _WIN32
-#ifdef _MSC_VER
-#include <math.h>
-
-#ifndef NAN
-static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff};
-#define NAN (*(const float *) __nan)
-#endif
-
-#ifndef INFINITY
-#define INFINITY HUGE_VAL
-#endif
-
-#endif
-#endif
-
static gboolean
gi_argument_from_c_long (GIArgument *arg_out,
long c_long_in,
{
PyObject *py_in_args;
gssize n_py_in_args;
- gssize current_arg;
GType implementor_gtype;
+ /* Number of arguments the ffi wrapped C function takes. Used as the exact
+ * count for argument related arrays held in this struct.
+ */
+ gssize n_args;
+
+ /* List of arguments passed to ffi. Elements can point directly to values held in
+ * arg_values for "in/from Python" or indirectly via arg_pointers for
+ * "out/inout/to Python". In the latter case, the arg_pointers[x]->v_pointer
+ * member points to memory for the value storage.
+ */
GIArgument **args;
- GIArgument *in_args;
+
+ /* Holds memory for the C value of arguments marshaled "to" or "from" Python. */
+ GIArgument *arg_values;
+
+ /* Holds pointers to values in arg_values or a caller allocated chunk of
+ * memory via arg_pointers[x].v_pointer.
+ */
+ GIArgument *arg_pointers;
/* Array of pointers allocated to the same length as args which holds from_py
* marshaler cleanup data.
*/
gpointer *args_cleanup_data;
- /* Out args and out values
- * In order to pass a parameter and get something back out in C
- * we need to pass a pointer to the value, e.g.
- * int *out_integer;
- *
- * so while out_args == out_integer, out_value == *out_integer
- * or in other words out_args = &out_values
- *
- * We do all of our processing on out_values but we pass out_args to
- * the actual function.
- */
- GIArgument *out_args;
- GIArgument *out_values;
-
+ /* Memory to receive the result of the C ffi function call. */
GIArgument return_arg;
+ /* A GError exception which is indirectly bound into the last position of
+ * the "args" array if the callable caches "throws" member is set.
+ */
GError *error;
gboolean failed;
gpointer user_data;
+
+ /* Function pointer to call with ffi. */
+ gpointer function_ptr;
+
} PyGIInvokeState;
G_END_DECLS
static inline gboolean
_invoke_callable (PyGIInvokeState *state,
PyGICallableCache *cache,
- GICallableInfo *callable_info,
- GCallback function_ptr)
+ GICallableInfo *callable_info)
{
- GError *error;
- gint retval;
-
- error = NULL;
+ GIFFIReturnValue ffi_return_value = {0};
Py_BEGIN_ALLOW_THREADS;
- /* FIXME: use this for now but we can streamline the calls */
- if (cache->function_type == PYGI_FUNCTION_TYPE_VFUNC)
- retval = g_vfunc_info_invoke ( callable_info,
- state->implementor_gtype,
- state->in_args,
- cache->n_from_py_args,
- state->out_args,
- cache->n_to_py_args,
- &state->return_arg,
- &error);
- else if (g_base_info_get_type (callable_info) == GI_INFO_TYPE_CALLBACK)
- retval = g_callable_info_invoke (callable_info,
- function_ptr,
- state->in_args,
- cache->n_from_py_args,
- state->out_args,
- cache->n_to_py_args,
- &state->return_arg,
- FALSE,
- FALSE,
- &error);
- else
- retval = g_function_info_invoke ( callable_info,
- state->in_args,
- cache->n_from_py_args,
- state->out_args,
- cache->n_to_py_args,
- &state->return_arg,
- &error);
- Py_END_ALLOW_THREADS;
-
- if (!retval) {
- g_assert (error != NULL);
- pyglib_error_check (&error);
-
- /* It is unclear if the error occured before or after the C
- * function was invoked so for now assume success
- * We eventually should marshal directly to FFI so we no longer
- * have to use the reference implementation
- */
- pygi_marshal_cleanup_args_from_py_marshal_success (state, cache);
+ ffi_call (&cache->invoker.cif,
+ state->function_ptr,
+ (void *)&ffi_return_value,
+ (void **)state->args);
- return FALSE;
- }
+ Py_END_ALLOW_THREADS;
+ /* If the callable throws, the address of state->error will be bound into
+ * the state->args as the last value. When the callee sets an error using
+ * the state->args passed, it will have the side effect of setting
+ * state->error allowing for easy checking here.
+ */
if (state->error != NULL) {
if (pyglib_error_check (&(state->error))) {
/* even though we errored out, the call itself was successful,
}
}
+ if (cache->return_cache) {
+ gi_type_info_extract_ffi_return_value (cache->return_cache->type_info,
+ &ffi_return_value,
+ &state->return_arg);
+ }
+
return TRUE;
}
}
static inline gboolean
-_invoke_state_init_from_callable_cache (PyGIInvokeState *state,
+_invoke_state_init_from_callable_cache (GIBaseInfo *info,
+ PyGIInvokeState *state,
PyGICallableCache *cache,
PyObject *py_args,
PyObject *kwargs)
{
PyObject *combined_args = NULL;
state->implementor_gtype = 0;
+ state->n_args = _pygi_callable_cache_args_len (cache);
+
+ if (cache->throws) {
+ state->n_args++;
+ }
+
+ /* Copy the function pointer to the state for the normal case. For vfuncs,
+ * this will be filled out based on the implementor_gtype calculated below.
+ */
+ state->function_ptr = cache->invoker.native_address;
/* TODO: We don't use the class parameter sent in by the structure
* so we remove it from the py_args tuple but we can keep it
}
} else if (cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) {
PyObject *py_gtype;
+ GError *error = NULL;
+
py_gtype = PyTuple_GetItem (py_args, 0);
if (py_gtype == NULL) {
PyErr_SetString (PyExc_TypeError,
if (state->implementor_gtype == 0)
return FALSE;
+
+ /* vfunc addresses are pulled into the state at call time and cannot be
+ * cached because the call site can specify a different portion of the
+ * class hierarchy. e.g. Object.do_func vs. SubObject.do_func might
+ * retrieve a different vfunc address but GI gives us the same vfunc info.
+ */
+ state->function_ptr = g_vfunc_info_get_address ((GIVFuncInfo *)info,
+ state->implementor_gtype,
+ &error);
+ if (pyglib_error_check (&error)) {
+ return FALSE;
+ }
}
if (cache->function_type == PYGI_FUNCTION_TYPE_CONSTRUCTOR ||
}
state->n_py_in_args = PyTuple_Size (state->py_in_args);
- state->args = g_slice_alloc0 (_pygi_callable_cache_args_len (cache) * sizeof (GIArgument *));
- if (state->args == NULL && _pygi_callable_cache_args_len (cache) != 0) {
+ state->args = g_slice_alloc0 (state->n_args * sizeof (GIArgument *));
+ if (state->args == NULL && state->n_args != 0) {
PyErr_NoMemory();
return FALSE;
}
- state->args_cleanup_data = g_slice_alloc0 (_pygi_callable_cache_args_len (cache) * sizeof (gpointer));
- if (state->args_cleanup_data == NULL && _pygi_callable_cache_args_len (cache) != 0) {
+ state->args_cleanup_data = g_slice_alloc0 (state->n_args * sizeof (gpointer));
+ if (state->args_cleanup_data == NULL && state->n_args != 0) {
PyErr_NoMemory();
return FALSE;
}
- state->in_args = g_slice_alloc0 (cache->n_from_py_args * sizeof(GIArgument));
- if (state->in_args == NULL && cache->n_from_py_args != 0) {
+ state->arg_values = g_slice_alloc0 (state->n_args * sizeof(GIArgument));
+ if (state->arg_values == NULL && state->n_args != 0) {
PyErr_NoMemory ();
return FALSE;
}
- state->out_values = g_slice_alloc0 (cache->n_to_py_args * sizeof(GIArgument));
- if (state->out_values == NULL && cache->n_to_py_args != 0) {
- PyErr_NoMemory ();
- return FALSE;
- }
-
- state->out_args = g_slice_alloc0 (cache->n_to_py_args * sizeof(GIArgument));
- if (state->out_args == NULL && cache->n_to_py_args != 0) {
+ state->arg_pointers = g_slice_alloc0 (state->n_args * sizeof(GIArgument));
+ if (state->arg_pointers == NULL && state->n_args != 0) {
PyErr_NoMemory ();
return FALSE;
}
state->error = NULL;
+ if (cache->throws) {
+ gssize error_index = state->n_args - 1;
+ /* The ffi argument for GError needs to be a triple pointer. */
+ state->arg_pointers[error_index].v_pointer = &state->error;
+ state->args[error_index] = &(state->arg_pointers[error_index]);
+ }
+
return TRUE;
}
static inline void
_invoke_state_clear (PyGIInvokeState *state, PyGICallableCache *cache)
{
- g_slice_free1 (_pygi_callable_cache_args_len (cache) * sizeof(GIArgument *), state->args);
- g_slice_free1 (_pygi_callable_cache_args_len (cache) * sizeof(gpointer), state->args_cleanup_data);
- g_slice_free1 (cache->n_from_py_args * sizeof(GIArgument), state->in_args);
- g_slice_free1 (cache->n_to_py_args * sizeof(GIArgument), state->out_args);
- g_slice_free1 (cache->n_to_py_args * sizeof(GIArgument), state->out_values);
+ g_slice_free1 (state->n_args * sizeof(GIArgument *), state->args);
+ g_slice_free1 (state->n_args * sizeof(gpointer), state->args_cleanup_data);
+ g_slice_free1 (state->n_args * sizeof(GIArgument), state->arg_values);
+ g_slice_free1 (state->n_args * sizeof(GIArgument), state->arg_pointers);
Py_XDECREF (state->py_in_args);
}
-static gboolean _caller_alloc (PyGIInvokeState *state,
- PyGIArgCache *arg_cache,
- gssize arg_count,
- gssize out_count)
+static gboolean
+_caller_alloc (PyGIArgCache *arg_cache, GIArgument *arg)
{
if (arg_cache->type_tag == GI_TYPE_TAG_INTERFACE) {
PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache;
- state->out_args[out_count].v_pointer = NULL;
- state->args[arg_count] = &state->out_args[out_count];
+ arg->v_pointer = NULL;
if (g_type_is_a (iface_cache->g_type, G_TYPE_BOXED)) {
- state->args[arg_count]->v_pointer =
+ arg->v_pointer =
_pygi_boxed_alloc (iface_cache->interface_info, NULL);
} else if (iface_cache->g_type == G_TYPE_VALUE) {
- state->args[arg_count]->v_pointer = g_slice_new0 (GValue);
+ arg->v_pointer = g_slice_new0 (GValue);
} else if (iface_cache->is_foreign) {
PyObject *foreign_struct =
pygi_struct_foreign_convert_from_g_argument (
pygi_struct_foreign_convert_to_g_argument (foreign_struct,
iface_cache->interface_info,
GI_TRANSFER_EVERYTHING,
- state->args[arg_count]);
+ arg);
} else {
gssize size = g_struct_info_get_size(
(GIStructInfo *)iface_cache->interface_info);
- state->args[arg_count]->v_pointer = g_malloc0 (size);
+ arg->v_pointer = g_malloc0 (size);
}
} else if (arg_cache->type_tag == GI_TYPE_TAG_ARRAY) {
PyGIArgGArray *array_cache = (PyGIArgGArray *)arg_cache;
- state->out_args[out_count].v_pointer = g_array_new (TRUE, TRUE, array_cache->item_size);
- state->args[arg_count] = &state->out_args[out_count];
+ arg->v_pointer = g_array_new (TRUE, TRUE, array_cache->item_size);
} else {
return FALSE;
}
- if (state->args[arg_count]->v_pointer == NULL)
+ if (arg->v_pointer == NULL)
return FALSE;
return TRUE;
}
+/* _invoke_marshal_in_args:
+ *
+ * Fills out the state struct argument lists. arg_values will always hold
+ * actual values marshaled either to or from Python and C. arg_pointers will
+ * hold pointers (via v_pointer) to auxilary value storage. This will normally
+ * point to values stored in arg_values. In the case of caller allocated
+ * out args, arg_pointers[x].v_pointer will point to newly allocated memory.
+ * arg_pointers inserts a level of pointer indirection between arg_values
+ * and the argument list ffi receives when dealing with non-caller allocated
+ * out arguments.
+ *
+ * For example:
+ * [[
+ * void callee (int *i, int j) { *i = 50 - j; }
+ * void caller () {
+ * int i = 0;
+ * callee (&i, 8);
+ * }
+ *
+ * args[0] == &arg_pointers[0];
+ * arg_pointers[0].v_pointer == &arg_values[0];
+ * arg_values[0].v_int == 42;
+ *
+ * args[1] == &arg_values[1];
+ * arg_values[1].v_int == 8;
+ * ]]
+ *
+ */
static inline gboolean
_invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache)
{
- gssize i, in_count, out_count;
- in_count = 0;
- out_count = 0;
+ gssize i;
if (state->n_py_in_args > cache->n_py_args) {
PyErr_Format (PyExc_TypeError,
}
for (i = 0; i < _pygi_callable_cache_args_len (cache); i++) {
- GIArgument *c_arg;
+ GIArgument *c_arg = &state->arg_values[i];
PyGIArgCache *arg_cache = g_ptr_array_index (cache->args_cache, i);
PyObject *py_arg = NULL;
switch (arg_cache->direction) {
case PYGI_DIRECTION_FROM_PYTHON:
- state->args[i] = &(state->in_args[in_count]);
- in_count++;
+ /* The ffi argument points directly at memory in arg_values. */
+ state->args[i] = c_arg;
if (arg_cache->meta_type == PYGI_META_ARG_TYPE_CLOSURE) {
state->args[i]->v_pointer = state->user_data;
break;
case PYGI_DIRECTION_BIDIRECTIONAL:
- /* this will be filled in if it is an child value */
- if (state->in_args[in_count].v_pointer != NULL)
- state->out_values[out_count] = state->in_args[in_count];
-
- state->in_args[in_count].v_pointer = &state->out_values[out_count];
- in_count++;
-
if (arg_cache->meta_type != PYGI_META_ARG_TYPE_CHILD) {
if (arg_cache->py_arg_index >= state->n_py_in_args) {
PyErr_Format (PyExc_TypeError,
PyTuple_GET_ITEM (state->py_in_args,
arg_cache->py_arg_index);
}
+ /* Fall through */
+
case PYGI_DIRECTION_TO_PYTHON:
+ /* arg_pointers always stores a pointer to the data to be marshaled "to python"
+ * even in cases where arg_pointers is not being used as indirection between
+ * ffi and arg_values. This gives a guarantee that out argument marshaling
+ * (_invoke_marshal_out_args) can always rely on arg_pointers pointing to
+ * the correct chunk of memory to marshal.
+ */
+ state->arg_pointers[i].v_pointer = c_arg;
+
if (arg_cache->is_caller_allocates) {
- if (!_caller_alloc (state, arg_cache, i, out_count)) {
+ /* In the case of caller allocated out args, we don't use
+ * an extra level of indirection and state->args will point
+ * directly at the data to be marshaled. However, as noted
+ * above, arg_pointers will also point to this caller allocated
+ * chunk of memory used by out argument marshaling.
+ */
+ state->args[i] = c_arg;
+
+ if (!_caller_alloc (arg_cache, c_arg)) {
PyErr_Format (PyExc_TypeError,
"Could not caller allocate argument %zd of callable %s",
i, cache->name);
return FALSE;
}
} else {
- state->out_args[out_count].v_pointer = &state->out_values[out_count];
- state->args[i] = &state->out_values[out_count];
+ /* Non-caller allocated out args will use arg_pointers as an
+ * extra level of indirection */
+ state->args[i] = &state->arg_pointers[i];
}
- out_count++;
+
break;
}
- c_arg = state->args[i];
if (py_arg == _PyGIDefaultArgPlaceholder) {
*c_arg = arg_cache->default_value;
} else if (arg_cache->from_py_marshaller != NULL) {
py_out = arg_cache->to_py_marshaller (state,
cache,
arg_cache,
- state->args[arg_cache->c_arg_index]);
+ state->arg_pointers[arg_cache->c_arg_index].v_pointer);
if (py_out == NULL) {
pygi_marshal_cleanup_args_to_py_parameter_fail (state,
cache,
PyObject *py_obj = arg_cache->to_py_marshaller (state,
cache,
arg_cache,
- state->args[arg_cache->c_arg_index]);
+ state->arg_pointers[arg_cache->c_arg_index].v_pointer);
if (py_obj == NULL) {
if (has_return)
PyObject *
pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args,
PyObject *kwargs, PyGICallableCache *cache,
- GCallback function_ptr, gpointer user_data)
+ gpointer user_data)
{
PyGIInvokeState state = { 0, };
PyObject *ret = NULL;
- if (!_invoke_state_init_from_callable_cache (&state, cache, py_args, kwargs))
+ if (!_invoke_state_init_from_callable_cache (info, &state, cache, py_args, kwargs))
goto err;
if (cache->function_type == PYGI_FUNCTION_TYPE_CCALLBACK)
if (!_invoke_marshal_in_args (&state, cache))
goto err;
- if (!_invoke_callable (&state, cache, info, function_ptr))
+ if (!_invoke_callable (&state, cache, info))
goto err;
ret = _invoke_marshal_out_args (&state, cache);
PyObject *kwargs)
{
if (self->cache == NULL) {
- self->cache = pygi_callable_cache_new (self->info, FALSE);
+ self->cache = pygi_callable_cache_new (self->info, NULL, FALSE);
if (self->cache == NULL)
return NULL;
}
- return pygi_callable_info_invoke (self->info, py_args, kwargs, self->cache, NULL, NULL);
+ return pygi_callable_info_invoke (self->info, py_args, kwargs, self->cache, NULL);
}
PyObject *pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args,
PyObject *kwargs, PyGICallableCache *cache,
- GCallback function_ptr, gpointer user_data);
+ gpointer user_data);
PyObject *_wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args,
PyObject *kwargs);
while (cache_item) {
PyGIArgCache *arg_cache = (PyGIArgCache *) cache_item->data;
PyGIMarshalCleanupFunc cleanup_func = arg_cache->to_py_cleanup;
- gpointer data = state->args[arg_cache->c_arg_index]->v_pointer;
+ gpointer data = state->arg_values[arg_cache->c_arg_index].v_pointer;
if (cleanup_func != NULL && data != NULL)
cleanup_func (state,
for (i = 0; i < _pygi_callable_cache_args_len (cache) && i <= failed_arg_index; i++) {
PyGIArgCache *arg_cache = _pygi_callable_cache_get_arg (cache, i);
PyGIMarshalCleanupFunc cleanup_func = arg_cache->from_py_cleanup;
- gpointer data = state->args[i]->v_pointer;
+ gpointer data = state->arg_values[i].v_pointer;
PyObject *py_arg = PyTuple_GET_ITEM (state->py_in_args,
arg_cache->py_arg_index);
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
- // FIXME: Lots of types still unhandled
+ /* FIXME: Lots of types still unhandled */
type_tag = g_type_info_get_tag (type_info);
switch (type_tag) {
case GI_TYPE_TAG_INTERFACE:
PyGClosure *pyclosure = l->data;
int res = PyObject_RichCompareBool(pyclosure->callback, func, Py_EQ);
if (res == -1) {
- PyErr_Clear(); // Is there anything else to do?
+ PyErr_Clear(); /* Is there anything else to do? */
} else if (res) {
return (GClosure*)pyclosure;
}
# pygobject
from gi.repository import GObject
-from gi.repository import Gtk
-from pygtkcompat.generictreemodel import GenericTreeModel
-from pygtkcompat.generictreemodel import _get_user_data_as_pyobject
+
+try:
+ from gi.repository import Gtk
+ from pygtkcompat.generictreemodel import GenericTreeModel
+ from pygtkcompat.generictreemodel import _get_user_data_as_pyobject
+ has_gtk = True
+except ImportError:
+ GenericTreeModel = object
+ has_gtk = False
class Node(object):
return child.parent()
+@unittest.skipUnless(has_gtk, 'Gtk not available')
class TestReferences(unittest.TestCase):
def setUp(self):
pass
self.assertEqual(ref(), None)
+@unittest.skipUnless(has_gtk, 'Gtk not available')
class TestIteration(unittest.TestCase):
def test_iter_next_root(self):
model = TesterModel()
pass
+@unittest.skipUnless(has_gtk, 'Gtk not available')
class ExceptHook(object):
"""
Temporarily installs an exception hook in a context which
assert issubclass(got, expected), error_message
+@unittest.skipUnless(has_gtk, 'Gtk not available')
class TestReturnsAfterError(unittest.TestCase):
def setUp(self):
self.model = ErrorModel()
@unittest.skipUnless(Gtk, 'Gtk not available')
class TestSignals(unittest.TestCase):
- class WindowWithSizeAllocOverride(Gtk.ScrolledWindow):
- __gsignals__ = {'size-allocate': 'override'}
+ def test_class_closure_override_with_aliased_type(self):
+ class WindowWithSizeAllocOverride(Gtk.ScrolledWindow):
+ __gsignals__ = {'size-allocate': 'override'}
- def __init__(self):
- Gtk.ScrolledWindow.__init__(self)
- self._alloc_called = False
- self._alloc_value = None
- self._alloc_error = None
+ def __init__(self):
+ Gtk.ScrolledWindow.__init__(self)
+ self._alloc_called = False
+ self._alloc_value = None
+ self._alloc_error = None
- def do_size_allocate(self, alloc):
- self._alloc_called = True
- self._alloc_value = alloc
+ def do_size_allocate(self, alloc):
+ self._alloc_called = True
+ self._alloc_value = alloc
- try:
- Gtk.ScrolledWindow.do_size_allocate(self, alloc)
- except Exception as e:
- self._alloc_error = e
+ try:
+ Gtk.ScrolledWindow.do_size_allocate(self, alloc)
+ except Exception as e:
+ self._alloc_error = e
- def test_class_closure_override_with_aliased_type(self):
- win = self.WindowWithSizeAllocOverride()
+ win = WindowWithSizeAllocOverride()
rect = Gdk.Rectangle()
rect.width = 100
rect.height = 100
from gi.repository import Gio
from gi.repository import GLib
-from gi.repository import Regress
from gi.repository import GIMarshallingTests
from gi import _propertyhelper as propertyhelper
+try:
+ from gi.repository import Regress
+ has_regress = True
+except ImportError:
+ has_regress = False
+
if sys.version_info < (3, 0):
TEST_UTF8 = "\xe2\x99\xa5"
UNICODE_UTF8 = unicode(TEST_UTF8, 'UTF-8')
type=Gio.File, flags=PARAM_READWRITE | PARAM_CONSTRUCT)
-class PropertyInheritanceObject(Regress.TestObj):
- # override property from the base class, with a different type
- string = GObject.Property(type=int)
-
- # a property entirely defined at the Python level
- python_prop = GObject.Property(type=str)
+if has_regress:
+ class PropertyInheritanceObject(Regress.TestObj):
+ # override property from the base class, with a different type
+ string = GObject.Property(type=int)
+ # a property entirely defined at the Python level
+ python_prop = GObject.Property(type=str)
-class PropertySubClassObject(PropertyInheritanceObject):
- # override property from the base class, with a different type
- python_prop = GObject.Property(type=int)
+ class PropertySubClassObject(PropertyInheritanceObject):
+ # override property from the base class, with a different type
+ python_prop = GObject.Property(type=int)
+@unittest.skipUnless(has_regress, 'Missing Regress typelib')
class TestPropertyInheritanceObject(unittest.TestCase):
def test_override_gi_property(self):
self.assertNotEqual(Regress.TestObj.props.string.value_type,
b.prop1 = 20
self.assertEqual(b.prop1, 20)
+ @unittest.skipUnless(has_regress, 'Missing regress typelib')
def test_property_subclass_c(self):
class A(Regress.TestSubObj):
prop1 = GObject.Property(type=int)