diff options
-rw-r--r-- | gi/gimodule.c | 2 | ||||
-rw-r--r-- | gi/overrides/GObject.py | 111 | ||||
-rw-r--r-- | gi/pygi-basictype.c | 2 | ||||
-rw-r--r-- | gi/pygi-value.c | 16 | ||||
-rw-r--r-- | gi/pygi-value.h | 1 | ||||
-rw-r--r-- | tests/test_gi.py | 5 | ||||
-rw-r--r-- | tests/test_overrides_gobject.py | 197 |
7 files changed, 246 insertions, 88 deletions
diff --git a/gi/gimodule.c b/gi/gimodule.c index 7ff9b0eb..2be5e478 100644 --- a/gi/gimodule.c +++ b/gi/gimodule.c @@ -2291,6 +2291,8 @@ static PyMethodDef _gi_functions[] = { (PyCFunction)pyg__install_metaclass, METH_O }, { "_gvalue_get", (PyCFunction)pyg__gvalue_get, METH_O }, + { "_gvalue_get_type", + (PyCFunction)pyg__gvalue_get_type, METH_O }, { "_gvalue_set", (PyCFunction)pyg__gvalue_set, METH_VARARGS }, { NULL, NULL, 0 } diff --git a/gi/overrides/GObject.py b/gi/overrides/GObject.py index 938a19a8..81f214f0 100644 --- a/gi/overrides/GObject.py +++ b/gi/overrides/GObject.py @@ -209,69 +209,52 @@ class Value(GObjectModule.Value): if py_value is not None: self.set_value(py_value) + @property + def __g_type(self): + # XXX: This is the same as self.g_type, but the field marshalling + # code is currently very slow. + return _gi._gvalue_get_type(self) + def set_boxed(self, boxed): + if not self.__g_type.is_a(TYPE_BOXED): + warnings.warn('Calling set_boxed() on a non-boxed type deprecated', + PyGIDeprecationWarning, stacklevel=2) # Workaround the introspection marshalers inability to know # these methods should be marshaling boxed types. This is because # the type information is stored on the GValue. _gi._gvalue_set(self, boxed) def get_boxed(self): + if not self.__g_type.is_a(TYPE_BOXED): + warnings.warn('Calling get_boxed() on a non-boxed type deprecated', + PyGIDeprecationWarning, stacklevel=2) return _gi._gvalue_get(self) def set_value(self, py_value): - gtype = self.g_type + gtype = self.__g_type - if gtype == _gi.TYPE_INVALID: - raise TypeError("GObject.Value needs to be initialized first") - elif gtype == TYPE_BOOLEAN: - self.set_boolean(py_value) - elif gtype == TYPE_CHAR: + if gtype == TYPE_CHAR: self.set_char(py_value) elif gtype == TYPE_UCHAR: self.set_uchar(py_value) - elif gtype == TYPE_INT: - self.set_int(py_value) - elif gtype == TYPE_UINT: - self.set_uint(py_value) - elif gtype == TYPE_LONG: - self.set_long(py_value) - elif gtype == TYPE_ULONG: - self.set_ulong(py_value) - elif gtype == TYPE_INT64: - self.set_int64(py_value) - elif gtype == TYPE_UINT64: - self.set_uint64(py_value) - elif gtype == TYPE_FLOAT: - self.set_float(py_value) - elif gtype == TYPE_DOUBLE: - self.set_double(py_value) elif gtype == TYPE_STRING: - if isinstance(py_value, str): - py_value = str(py_value) - elif PY2: - if isinstance(py_value, text_type): - py_value = py_value.encode('UTF-8') + if not isinstance(py_value, str) and py_value is not None: + if PY2: + if isinstance(py_value, text_type): + py_value = py_value.encode('UTF-8') + else: + raise TypeError("Expected string or unicode but got %s%s" % + (py_value, type(py_value))) else: - raise TypeError("Expected string or unicode but got %s%s" % + raise TypeError("Expected string but got %s%s" % (py_value, type(py_value))) - else: - raise TypeError("Expected string but got %s%s" % - (py_value, type(py_value))) - self.set_string(py_value) + _gi._gvalue_set(self, py_value) elif gtype == TYPE_PARAM: self.set_param(py_value) - elif gtype == TYPE_PYOBJECT: - self.set_boxed(py_value) - elif gtype.is_a(TYPE_ENUM): - self.set_enum(py_value) elif gtype.is_a(TYPE_FLAGS): self.set_flags(py_value) - elif gtype.is_a(TYPE_BOXED): - self.set_boxed(py_value) elif gtype == TYPE_POINTER: self.set_pointer(py_value) - elif gtype.is_a(TYPE_OBJECT): - self.set_object(py_value) elif gtype == TYPE_GTYPE: self.set_gtype(py_value) elif gtype == TYPE_VARIANT: @@ -279,62 +262,44 @@ class Value(GObjectModule.Value): else: # Fall back to _gvalue_set which handles some more cases # like fundamentals for which a converter is registered - _gi._gvalue_set(self, py_value) + try: + _gi._gvalue_set(self, py_value) + except TypeError: + if gtype == TYPE_INVALID: + raise TypeError("GObject.Value needs to be initialized first") + raise def get_value(self): - gtype = self.g_type + gtype = self.__g_type - if gtype == TYPE_BOOLEAN: - return self.get_boolean() - elif gtype == TYPE_CHAR: + if gtype == TYPE_CHAR: return self.get_char() elif gtype == TYPE_UCHAR: return self.get_uchar() - elif gtype == TYPE_INT: - return self.get_int() - elif gtype == TYPE_UINT: - return self.get_uint() - elif gtype == TYPE_LONG: - return self.get_long() - elif gtype == TYPE_ULONG: - return self.get_ulong() - elif gtype == TYPE_INT64: - return self.get_int64() - elif gtype == TYPE_UINT64: - return self.get_uint64() - elif gtype == TYPE_FLOAT: - return self.get_float() - elif gtype == TYPE_DOUBLE: - return self.get_double() - elif gtype == TYPE_STRING: - return self.get_string() - elif gtype == TYPE_PYOBJECT: - return self.get_boxed() elif gtype == TYPE_PARAM: return self.get_param() elif gtype.is_a(TYPE_ENUM): return self.get_enum() elif gtype.is_a(TYPE_FLAGS): return self.get_flags() - elif gtype.is_a(TYPE_BOXED): - return self.get_boxed() elif gtype == TYPE_POINTER: return self.get_pointer() - elif gtype.is_a(TYPE_OBJECT): - return self.get_object() elif gtype == TYPE_GTYPE: return self.get_gtype() elif gtype == TYPE_VARIANT: # get_variant was missing annotations # https://gitlab.gnome.org/GNOME/glib/merge_requests/492 return self.dup_variant() - elif gtype == _gi.TYPE_INVALID: - return None else: - return _gi._gvalue_get(self) + try: + return _gi._gvalue_get(self) + except TypeError: + if gtype == TYPE_INVALID: + return None + raise def __repr__(self): - return '<Value (%s) %s>' % (self.g_type.name, self.get_value()) + return '<Value (%s) %s>' % (self.__g_type.name, self.get_value()) Value = override(Value) diff --git a/gi/pygi-basictype.c b/gi/pygi-basictype.c index dafddf27..c0e5d1d0 100644 --- a/gi/pygi-basictype.c +++ b/gi/pygi-basictype.c @@ -1342,6 +1342,8 @@ arg_basic_type_setup_from_info (PyGIArgCache *arg_cache, break; case GI_TYPE_TAG_BOOLEAN: + arg_cache->allow_none = TRUE; + /* fall through */ case GI_TYPE_TAG_INT8: case GI_TYPE_TAG_UINT8: case GI_TYPE_TAG_INT16: diff --git a/gi/pygi-value.c b/gi/pygi-value.c index 93669345..8376231e 100644 --- a/gi/pygi-value.c +++ b/gi/pygi-value.c @@ -887,6 +887,22 @@ pyg__gvalue_get(PyObject *module, PyObject *pygvalue) } PyObject * +pyg__gvalue_get_type(PyObject *module, PyObject *pygvalue) +{ + GValue *value; + GType type; + + if (!pyg_boxed_check (pygvalue, G_TYPE_VALUE)) { + PyErr_SetString (PyExc_TypeError, "Expected GValue argument."); + return NULL; + } + + value = pyg_boxed_get (pygvalue, GValue); + type = G_VALUE_TYPE (value); + return pyg_type_wrapper_new (type); +} + +PyObject * pyg__gvalue_set(PyObject *module, PyObject *args) { PyObject *pygvalue; diff --git a/gi/pygi-value.h b/gi/pygi-value.h index 6450112d..5778a22c 100644 --- a/gi/pygi-value.h +++ b/gi/pygi-value.h @@ -45,6 +45,7 @@ PyObject *pygi_value_to_py_basic_type (const GValue *value, PyObject *pyg__gvalue_get(PyObject *module, PyObject *pygvalue); PyObject *pyg__gvalue_set(PyObject *module, PyObject *args); +PyObject *pyg__gvalue_get_type(PyObject *module, PyObject *pygvalue); G_END_DECLS diff --git a/tests/test_gi.py b/tests/test_gi.py index 2b6f3c0a..61ad4f18 100644 --- a/tests/test_gi.py +++ b/tests/test_gi.py @@ -87,6 +87,11 @@ class TestBoolean(unittest.TestCase): GIMarshallingTests.boolean_in_true(1) GIMarshallingTests.boolean_in_false(0) + def test_boolean_in_other_types(self): + GIMarshallingTests.boolean_in_true([""]) + GIMarshallingTests.boolean_in_false([]) + GIMarshallingTests.boolean_in_false(None) + def test_boolean_out(self): self.assertEqual(True, GIMarshallingTests.boolean_out_true()) self.assertEqual(False, GIMarshallingTests.boolean_out_false()) diff --git a/tests/test_overrides_gobject.py b/tests/test_overrides_gobject.py index 37292dbb..7f20e81a 100644 --- a/tests/test_overrides_gobject.py +++ b/tests/test_overrides_gobject.py @@ -5,7 +5,7 @@ from __future__ import absolute_import import pytest from gi import PyGIDeprecationWarning -from gi.repository import GObject, GLib +from gi.repository import GObject, GLib, GIMarshallingTests from gi._compat import PY2 from .helper import ignore_gi_deprecation_warnings @@ -72,6 +72,12 @@ def test_value_invalid_type(): with pytest.raises(ValueError, match="Invalid GType"): v.init(GObject.TYPE_INVALID) + with pytest.raises( + TypeError, match="GObject.Value needs to be initialized first"): + v.set_value(None) + + assert v.get_value() is None + def test_value_long(): v = GObject.Value(GObject.TYPE_LONG) @@ -107,6 +113,74 @@ def test_value_ulong(): with pytest.raises(OverflowError): v.set_value(-1) + with pytest.raises(TypeError): + v.set_value(object()) + + with pytest.raises(TypeError): + v.set_value(None) + + +def test_value_float(): + v = GObject.Value(GObject.TYPE_FLOAT) + + for getter, setter in [(v.get_value, v.set_value), + (v.get_float, v.set_float)]: + + assert getter() == 0.0 + setter(0) + assert getter() == 0 + + setter(GLib.MAXFLOAT) + assert getter() == GLib.MAXFLOAT + + setter(GLib.MINFLOAT) + assert getter() == GLib.MINFLOAT + + setter(-GLib.MAXFLOAT) + assert getter() == -GLib.MAXFLOAT + + with pytest.raises(OverflowError): + setter(GLib.MAXFLOAT * 2) + + with pytest.raises(OverflowError): + setter(-GLib.MAXFLOAT * 2) + + with pytest.raises(TypeError): + setter(object()) + + with pytest.raises(TypeError): + setter(None) + + with pytest.raises(TypeError): + setter(1j) + + v.reset() + + +def test_value_double(): + v = GObject.Value(GObject.TYPE_DOUBLE) + assert v.get_value() == 0.0 + v.set_value(0) + assert v.get_value() == 0 + + v.set_value(GLib.MAXDOUBLE) + assert v.get_value() == GLib.MAXDOUBLE + + v.set_value(GLib.MINDOUBLE) + assert v.get_value() == GLib.MINDOUBLE + + v.set_value(-GLib.MAXDOUBLE) + assert v.get_value() == -GLib.MAXDOUBLE + + with pytest.raises(TypeError): + v.set_value(object()) + + with pytest.raises(TypeError): + v.set_value(None) + + with pytest.raises(TypeError): + v.set_value(1j) + def test_value_uint64(): v = GObject.Value(GObject.TYPE_UINT64) @@ -141,6 +215,12 @@ def test_value_int64(): with pytest.raises(OverflowError): v.set_value(GLib.MININT64 - 1) + with pytest.raises(TypeError): + v.set_value(object()) + + with pytest.raises(TypeError): + v.set_value(None) + def test_value_pointer(): v = GObject.Value(GObject.TYPE_POINTER) @@ -200,24 +280,29 @@ def test_value_param(): def test_value_string(): v = GObject.Value(GObject.TYPE_STRING) - assert v.get_value() is None + for getter, setter in [(v.get_value, v.set_value), + (v.get_string, v.set_string)]: - if PY2: - v.set_value(b"bar") - assert v.get_value() == b"bar" + assert getter() is None - v.set_value(u"öäü") - assert v.get_value().decode("utf-8") == u"öäü" - else: - with pytest.raises(TypeError): - v.set_value(b"bar") + if PY2: + setter(b"bar") + assert getter() == b"bar" - v.set_value(u"quux") - assert v.get_value() == u"quux" - assert isinstance(v.get_value(), str) + setter(u"öäü") + assert getter().decode("utf-8") == u"öäü" + else: + with pytest.raises(TypeError): + setter(b"bar") - with pytest.raises(TypeError): - v.set_value(None) + setter(u"quux") + assert getter() == u"quux" + assert isinstance(getter(), str) + + setter(None) + assert getter() is None + + v.reset() def test_value_pyobject(): @@ -266,3 +351,85 @@ def test_value_uchar(): with pytest.raises(OverflowError): v.set_value(256) + + +def test_value_set_boxed_deprecate_non_boxed(): + v = GObject.Value(GObject.TYPE_POINTER) + with pytest.warns(PyGIDeprecationWarning): + v.get_boxed() + with pytest.warns(PyGIDeprecationWarning): + v.set_boxed(None) + + +def test_value_boolean(): + v = GObject.Value(GObject.TYPE_BOOLEAN) + for getter, setter in [(v.get_value, v.set_value), + (v.get_boolean, v.set_boolean)]: + assert getter() is False + assert isinstance(getter(), bool) + + setter(42) + assert getter() is True + setter(-1) + assert getter() is True + setter(0) + assert getter() is False + + setter([]) + assert getter() is False + setter(["foo"]) + assert getter() is True + + setter(None) + assert getter() is False + v.reset() + + +def test_value_enum(): + t = GIMarshallingTests.GEnum + v = GObject.Value(t) + + for getter, setter in [(v.get_value, v.set_value), + (v.get_enum, v.set_enum)]: + assert v.g_type == t.__gtype__ + assert getter() == 0 + + setter(t.VALUE1) + assert getter() == t.VALUE1 + # FIXME: we should try to return an enum type + assert type(getter()) is int + + setter(2424242) + assert getter() == 2424242 + + setter(-1) + assert getter() == -1 + + with pytest.raises(TypeError): + setter(object()) + + with pytest.raises(TypeError): + setter(None) + + v.reset() + + +def test_value_object(): + v = GObject.Value(GIMarshallingTests.Object) + assert v.g_type.is_a(GObject.TYPE_OBJECT) + + for getter, setter in [(v.get_value, v.set_value), + (v.get_object, v.set_object)]: + assert getter() is None + + setter(None) + assert getter() is None + + obj = GIMarshallingTests.Object() + setter(obj) + assert getter() is obj + + with pytest.raises(TypeError): + setter(object()) + + v.reset() |