[pygobject] [New API] Add gi.require_foreign



commit 22a952ec532cc83c8227861a7d5bfa2957608c3f
Author: Simon Feltman <sfeltman src gnome org>
Date:   Mon May 5 19:37:18 2014 -0700

    [New API] Add gi.require_foreign
    
    Add gi.require_foreign(namespace, symbol=None) API for determining
    if a foreign marshaling module is available. This can be used in an
    applications import statement block to verify the existence of a
    specific foreign marshaling module (cairo).
    Additionally it forces loading of the foreign marshaling module as
    well as the GI repository module. This allows non-introspected signal
    closures to correctly marshal their arguments (bug 694604).
    
    https://bugzilla.gnome.org/show_bug.cgi?id=707735

 gi/__init__.py      |   28 +++++++++++++++++++++++
 gi/gimodule.c       |    2 +
 gi/pygi-foreign.c   |   60 ++++++++++++++++++++++++++++++++++++++++++++------
 gi/pygi-foreign.h   |    4 +++
 tests/test_cairo.py |    9 +++++++
 5 files changed, 95 insertions(+), 8 deletions(-)
---
diff --git a/gi/__init__.py b/gi/__init__.py
index 7c1a279..df6843c 100644
--- a/gi/__init__.py
+++ b/gi/__init__.py
@@ -26,6 +26,7 @@ __path__ = extend_path(__path__, __name__)
 
 import sys
 import os
+import importlib
 
 # we can't have pygobject 2 loaded at the same time we load the internal _gobject
 if 'gobject' in sys.modules:
@@ -33,6 +34,7 @@ if 'gobject' in sys.modules:
                       'modules like "gobject". Please change all occurrences '
                       'of "import gobject" to "from gi.repository import GObject".')
 
+from . import _gi
 from ._gi import _gobject
 from ._gi import _API
 from ._gi import Repository
@@ -87,3 +89,29 @@ def require_version(namespace, version):
 
 def get_required_version(namespace):
     return _versions.get(namespace, None)
+
+
+def require_foreign(namespace, symbol=None):
+    """Ensure the given foreign marshaling module is available and loaded.
+
+    :param str namespace:
+        Introspection namespace of the foreign module (e.g. "cairo")
+    :param symbol:
+        Optional symbol typename to ensure a converter exists.
+    :type symbol: str or None
+    :raises: ImportError
+
+    :Example:
+
+    .. code-block:: python
+
+        import gi
+        import cairo
+        gi.require_foreign('cairo')
+
+    """
+    try:
+        _gi.require_foreign(namespace, symbol)
+    except Exception as e:
+        raise ImportError(str(e))
+    importlib.import_module('gi.repository', namespace)
diff --git a/gi/gimodule.c b/gi/gimodule.c
index 47cd33c..a1779d3 100644
--- a/gi/gimodule.c
+++ b/gi/gimodule.c
@@ -28,6 +28,7 @@
 #include "pygi.h"
 #include "pyglib.h"
 #include "pygi-error.h"
+#include "pygi-foreign.h"
 
 #include <pyglib-python-compat.h>
 
@@ -616,6 +617,7 @@ static PyMethodDef _gi_functions[] = {
     { "source_new", (PyCFunction) _wrap_pyg_source_new, METH_NOARGS },
     { "source_set_callback", (PyCFunction) pyg_source_set_callback, METH_VARARGS },
     { "io_channel_read", (PyCFunction) pyg_channel_read, METH_VARARGS },
+    { "require_foreign", (PyCFunction) pygi_require_foreign, METH_VARARGS | METH_KEYWORDS },
     { NULL, NULL, 0 }
 };
 
diff --git a/gi/pygi-foreign.c b/gi/pygi-foreign.c
index 8a83c35..82392be 100644
--- a/gi/pygi-foreign.c
+++ b/gi/pygi-foreign.c
@@ -63,20 +63,24 @@ do_lookup (const gchar *namespace, const gchar *name)
     return NULL;
 }
 
+static PyObject *
+pygi_struct_foreign_load_module (const char *namespace)
+{
+    gchar *module_name = g_strconcat ("gi._gi_", namespace, NULL);
+    PyObject *module = PyImport_ImportModule (module_name);
+    g_free (module_name);
+    return module;
+}
+
 static PyGIForeignStruct *
-pygi_struct_foreign_lookup (GIBaseInfo *base_info)
+pygi_struct_foreign_lookup_by_name (const char *namespace, const char *name)
 {
     PyGIForeignStruct *result;
-    const gchar *namespace = g_base_info_get_namespace (base_info);
-    const gchar *name = g_base_info_get_name (base_info);
 
     result = do_lookup (namespace, name);
 
     if (result == NULL) {
-        gchar *module_name = g_strconcat ("gi._gi_", namespace, NULL);
-        PyObject *module = PyImport_ImportModule (module_name);
-
-        g_free (module_name);
+        PyObject *module = pygi_struct_foreign_load_module (namespace);
 
         if (module == NULL)
             PyErr_Clear ();
@@ -88,7 +92,7 @@ pygi_struct_foreign_lookup (GIBaseInfo *base_info)
 
     if (result == NULL) {
         PyErr_Format (PyExc_TypeError,
-                      "Couldn't find conversion for foreign struct '%s.%s'",
+                      "Couldn't find foreign struct converter for '%s.%s'",
                       namespace,
                       name);
     }
@@ -96,6 +100,15 @@ pygi_struct_foreign_lookup (GIBaseInfo *base_info)
     return result;
 }
 
+static PyGIForeignStruct *
+pygi_struct_foreign_lookup (GIBaseInfo *base_info)
+{
+    const gchar *namespace = g_base_info_get_namespace (base_info);
+    const gchar *name = g_base_info_get_name (base_info);
+
+    return pygi_struct_foreign_lookup_by_name (namespace, name);
+}
+
 PyObject *
 pygi_struct_foreign_convert_to_g_argument (PyObject        *value,
                                            GIInterfaceInfo *interface_info,
@@ -163,6 +176,37 @@ pygi_register_foreign_struct (const char* namespace_,
     g_ptr_array_add (foreign_structs, new_struct);
 }
 
+PyObject *
+pygi_require_foreign (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "namespace", "symbol", NULL };
+    const char *namespace = NULL;
+    const char *symbol = NULL;
+
+    if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+                                      "s|z:require_foreign",
+                                      kwlist, &namespace, &symbol)) {
+        return NULL;
+    }
+
+    if (symbol) {
+        PyGIForeignStruct *foreign;
+        foreign = pygi_struct_foreign_lookup_by_name (namespace, symbol);
+        if (foreign == NULL) {
+            return NULL;
+        }
+    } else {
+        PyObject *module = pygi_struct_foreign_load_module (namespace);
+        if (module) {
+            Py_DECREF (module);
+        } else {
+            return NULL;
+        }
+    }
+
+    Py_RETURN_NONE;
+}
+
 void
 pygi_foreign_init (void)
 {
diff --git a/gi/pygi-foreign.h b/gi/pygi-foreign.h
index ca4f75b..afa4768 100644
--- a/gi/pygi-foreign.h
+++ b/gi/pygi-foreign.h
@@ -44,6 +44,10 @@ void pygi_register_foreign_struct (const char* namespace_,
                                    PyGIArgOverrideFromGIArgumentFunc from_func,
                                    PyGIArgOverrideReleaseFunc release_func);
 
+PyObject *pygi_require_foreign    (PyObject *self,
+                                   PyObject *args,
+                                   PyObject *kwargs);
+
 void pygi_foreign_init (void);
 
 #endif /* __PYGI_FOREIGN_H__ */
diff --git a/tests/test_cairo.py b/tests/test_cairo.py
index 4674068..e4ef65a 100644
--- a/tests/test_cairo.py
+++ b/tests/test_cairo.py
@@ -4,6 +4,8 @@
 
 import unittest
 
+import gi
+
 try:
     import cairo
     from gi.repository import Regress
@@ -53,6 +55,13 @@ class Test(unittest.TestCase):
         self.assertEqual(surface.get_width(), 10)
         self.assertEqual(surface.get_height(), 10)
 
+    def test_require_foreign(self):
+        self.assertEqual(gi.require_foreign('cairo'), None)
+        self.assertEqual(gi.require_foreign('cairo', 'Context'), None)
+        self.assertRaises(ImportError, gi.require_foreign, 'invalid_module')
+        self.assertRaises(ImportError, gi.require_foreign, 'invalid_module', 'invalid_symbol')
+        self.assertRaises(ImportError, gi.require_foreign, 'cairo', 'invalid_symbol')
+
 
 @unittest.skipUnless(has_cairo, 'built without cairo support')
 @unittest.skipUnless(Gtk, 'Gtk not available')


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