[pygobject] Fix error messages on interface/class type mismatches



commit 121b1402860407fe46f7501e42447bf3607872ec
Author: Martin Pitt <martinpitt gnome org>
Date:   Tue Jul 31 00:37:55 2012 +0200

    Fix error messages on interface/class type mismatches
    
    Previously, when you called a function with an argument which was not
    compatible with the expected class/interface type, you got an error message
    like
    
      TypeError: Expected Gtk.TreeViewColumn, but got GObjectMeta
    
    which had the wrong (and useless) class name for the actual type, and did not
    tell you which argument caused the problem. With this it says e. g.
    
      TypeError: argument column: Expected Gtk.TreeViewColumn, but got Gtk.Button
    
    instead.

 gi/pygi-marshal-from-py.c |   41 +++++++++++++++++++++++++-------
 tests/test_gi.py          |   57 ++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 88 insertions(+), 10 deletions(-)
---
diff --git a/gi/pygi-marshal-from-py.c b/gi/pygi-marshal-from-py.c
index abdc49e..fba8154 100644
--- a/gi/pygi-marshal-from-py.c
+++ b/gi/pygi-marshal-from-py.c
@@ -1438,10 +1438,17 @@ _pygi_marshal_from_py_interface_struct (PyGIInvokeState   *state,
     } else if (!PyObject_IsInstance (py_arg, iface_cache->py_type)) {
         /* first check to see if this is a member of the expected union */
         if (!_is_union_member (iface_cache, py_arg)) {
-            if (!PyErr_Occurred())
-                PyErr_Format (PyExc_TypeError, "Expected %s, but got %s",
+            if (!PyErr_Occurred()) {
+                PyObject *module = PyObject_GetAttrString(py_arg, "__module__");
+
+                PyErr_Format (PyExc_TypeError, "argument %s: Expected %s, but got %s.%s",
+                              arg_cache->arg_name ? arg_cache->arg_name : "self",
                               iface_cache->type_name,
-                              iface_cache->py_type->ob_type->tp_name);
+                              module ? _PyUnicode_AsString(module) : "<unknown module>",
+                              py_arg->ob_type->tp_name);
+                if (module)
+                    Py_DECREF (module);
+            }
 
             return FALSE;
         }
@@ -1490,9 +1497,15 @@ _pygi_marshal_from_py_interface_object (PyGIInvokeState   *state,
     }
 
     if (!PyObject_IsInstance (py_arg, ( (PyGIInterfaceCache *)arg_cache)->py_type)) {
-        PyErr_Format (PyExc_TypeError, "Expected %s, but got %s",
+        PyObject *module = PyObject_GetAttrString(py_arg, "__module__");
+
+        PyErr_Format (PyExc_TypeError, "argument %s: Expected %s, but got %s.%s",
+                      arg_cache->arg_name ? arg_cache->arg_name : "self",
                       ( (PyGIInterfaceCache *)arg_cache)->type_name,
-                      ( (PyGIInterfaceCache *)arg_cache)->py_type->ob_type->tp_name);
+                      module ? _PyUnicode_AsString(module) : "<unknown module>",
+                      py_arg->ob_type->tp_name);
+        if (module)
+            Py_DECREF (module);
         return FALSE;
     }
 
@@ -1534,12 +1547,17 @@ gboolean _pygi_marshal_from_py_interface_instance (PyGIInvokeState   *state,
             if (!PyObject_IsInstance (py_arg, iface_cache->py_type)) {
                 /* wait, we might be a member of a union so manually check */
                 if (!_is_union_member (iface_cache, py_arg)) {
-                    if (!PyErr_Occurred())
+                    if (!PyErr_Occurred()) {
+                        PyObject *module = PyObject_GetAttrString(py_arg, "__module__");
                         PyErr_Format (PyExc_TypeError,
-                                      "Expected a %s, but got %s",
+                                      "argument %s: Expected a %s, but got %s.%s",
+                                      arg_cache->arg_name ? arg_cache->arg_name : "self",
                                       iface_cache->type_name,
+                                      module ? _PyUnicode_AsString(module) : "<unknown module>",
                                       py_arg->ob_type->tp_name);
-
+                        if (module)
+                            Py_DECREF (module);
+                    }
                     return FALSE;
                 }
             }
@@ -1565,9 +1583,14 @@ gboolean _pygi_marshal_from_py_interface_instance (PyGIInvokeState   *state,
                 GType expected_type = iface_cache->g_type;
 
                 if (!g_type_is_a (obj_type, expected_type)) {
-                    PyErr_Format (PyExc_TypeError, "Expected a %s, but got %s",
+                    PyObject *module = PyObject_GetAttrString(py_arg, "__module__");
+                    PyErr_Format (PyExc_TypeError, "argument %s: Expected %s, but got %s.%s",
+                                  arg_cache->arg_name ? arg_cache->arg_name : "self",
                                   iface_cache->type_name,
+                                  module ? _PyUnicode_AsString(module) : "<unknown module>",
                                   py_arg->ob_type->tp_name);
+                    if (module)
+                        Py_DECREF (module);
                     return FALSE;
                 }
             }
diff --git a/tests/test_gi.py b/tests/test_gi.py
index f8ccfc8..41fb5e7 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -13,7 +13,7 @@ import subprocess
 from io import StringIO, BytesIO
 
 import gi
-from gi.repository import GObject, GLib
+from gi.repository import GObject, GLib, Gio
 
 from gi.repository import GIMarshallingTests
 
@@ -1874,6 +1874,61 @@ class TestInterfaces(unittest.TestCase):
                                  GIMarshallingTests.Interface2):
             pass
 
+    def test_type_mismatch(self):
+        obj = GIMarshallingTests.Object()
+
+        # wrong type for first argument: interface
+        enum = Gio.File.new_for_path('.').enumerate_children(
+            '', Gio.FileQueryInfoFlags.NONE, None)
+        try:
+            enum.next_file(obj)
+            self.fail('call with wrong type argument unexpectedly succeeded')
+        except TypeError as e:
+            # should have argument name
+            self.assertTrue('cancellable' in str(e), e)
+            # should have expected type
+            self.assertTrue('xpected Gio.Cancellable' in str(e), e)
+            # should have actual type
+            self.assertTrue('GIMarshallingTests.Object' in str(e), e)
+
+        # wrong type for self argument: interface
+        try:
+            Gio.FileEnumerator.next_file(obj, None)
+            self.fail('call with wrong type argument unexpectedly succeeded')
+        except TypeError as e:
+            # should have argument name
+            self.assertTrue('self' in str(e), e)
+            # should have expected type
+            self.assertTrue('xpected Gio.FileEnumerator' in str(e), e)
+            # should have actual type
+            self.assertTrue('GIMarshallingTests.Object' in str(e), e)
+
+        # wrong type for first argument: GObject
+        var = GLib.Variant('s', 'mystring')
+        action = Gio.SimpleAction.new('foo', var.get_type())
+        try:
+            action.activate(obj)
+            self.fail('call with wrong type argument unexpectedly succeeded')
+        except TypeError as e:
+            # should have argument name
+            self.assertTrue('parameter' in str(e), e)
+            # should have expected type
+            self.assertTrue('xpected GLib.Variant' in str(e), e)
+            # should have actual type
+            self.assertTrue('GIMarshallingTests.Object' in str(e), e)
+
+        # wrong type for self argument: GObject
+        try:
+            Gio.SimpleAction.activate(obj, obj)
+            self.fail('call with wrong type argument unexpectedly succeeded')
+        except TypeError as e:
+            # should have argument name
+            self.assertTrue('self' in str(e), e)
+            # should have expected type
+            self.assertTrue('xpected Gio.Action' in str(e), e)
+            # should have actual type
+            self.assertTrue('GIMarshallingTests.Object' in str(e), e)
+
 
 class TestInterfaceClash(unittest.TestCase):
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]