From 29a47836eb9dd9c21c81da904b7ad5372a538144 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sun, 8 Dec 2024 18:16:57 +0100
Subject: Improve support for the Python GObject Introspection (GI).

---
 plugins/pychrysalide/core.c | 96 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 90 insertions(+), 6 deletions(-)

diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c
index 8d69933..f63fd9e 100644
--- a/plugins/pychrysalide/core.c
+++ b/plugins/pychrysalide/core.c
@@ -394,12 +394,18 @@ static bool install_metaclass_for_python_gobjects(void)
      *
      *    from _gi import types
      *
-     * On simule ici une déclaration similaire si nécessaire
+     * On simule ici une déclaration similaire si nécessaire, selon la valeur
+     * portée par PyGObject_Type.ob_base.ob_base.ob_type.tp_name :
+     *    - "type" (PyType_Type) : état initial ;
+     *    - "_GObjectMetaBase" : état revu.
      */
 
-    result = false;
+    /**
+     * PyGObject_Type.ob_base.ob_base.ob_type != &PyType_Type ?
+     */
+    result = (PyType_CheckExact(&PyGObject_Type) == 0);
 
-    if (PyType_CheckExact(&PyGObject_Type))
+    if (!result)
     {
         gi_types_mod = PyImport_ImportModule("gi.types");
 
@@ -412,6 +418,11 @@ static bool install_metaclass_for_python_gobjects(void)
 
     }
 
+#ifndef NDEBUG
+    if (result)
+        assert(strcmp(PyGObject_Type.ob_base.ob_base.ob_type->tp_name, "_GObjectMetaBase") == 0);
+#endif
+
     return result;
 
 }
@@ -533,6 +544,8 @@ static void PyExit_pychrysalide(void)
 PyMODINIT_FUNC PyInit_pychrysalide(void)
 {
     PyObject *result;                       /* Module Python à retourner   */
+    PyTypeObject *py_gobj_def;              /* Définition GObject courante */
+    GQuark pygobject_class_key;             /* Copie d'un accès GI interne */
     bool status;                            /* Bilan des inclusions        */
     int ret;                                /* Bilan de préparatifs        */
 #ifdef PYTHON_PACKAGE
@@ -583,7 +596,13 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
     }
 
     if (!is_current_abi_suitable())
+    {
+        /**
+         * Un message d'erreur est défini par is_current_abi_suitable() en cas
+         * d'interpréteur pas adapté.
+         */
         goto exit;
+    }
 
     if (pygobject_init(-1, -1, -1) == NULL)
     {
@@ -592,7 +611,10 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
     }
 
     if (!install_metaclass_for_python_gobjects())
+    {
+        PyErr_SetString(PyExc_SystemError, "unable to install metaclass for Python GObjects.");
         goto exit;
+    }
 
     /**
      * Le chargement forcé de l'espace GLib pour Python permet d'éviter un écueil,
@@ -615,8 +637,10 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
      *    from gi.repository import GLib
      *
      */
+#if 0
     if (!import_namespace_from_gi_repository("GLib", "2.0"))
         goto exit;
+#endif
 
 #if 0
 #ifdef INCLUDE_GTK_SUPPORT
@@ -637,6 +661,56 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
 
     register_access_to_python_module(py_chrysalide_module.m_name, result);
 
+    /**
+     * Les appels suivants procèdent à l'enregistrement de différents éléments
+     * dans l'espace de noms Python pour Chrysalide.
+     *
+     * Une majeure partie de ces éléments est constituée d'objets dérivés de
+     * GObject. Ce type d'objet (G_TYPE_OBJECT) est représenté par deux types
+     * en Python :
+     *
+     *    - gi._gi.GObject, mis en place lors de l'importation du module gi.
+     *
+     *      Ce dernier lance automatiquement l'importation du module natif gi._gi,
+     *      lequel, via son initialisation dans la fonction PyInit__gi()
+     *      (cf. ./gi/gimodule.c) lance un appel à pyi_object_register_types()
+     *      qui procède à l'enregistrement du type "GObject" porté par la structure
+     *      PyGObject_Type en correspondance au type GLib G_TYPE_OBJECT (cf. appel
+     *      à pygobject_register_class() dans gi/pygobject-object.c).
+     *
+     *    - gi.repository.GObject.Object, qui vient surclasser le type précédent
+     *      lors d'un appel d'initialisation : from gi.repository import GObject.
+     *
+     *      Cette seconde définition est destinée à apporter une représentation
+     *      de l'introspection GObject de plus haut niveau pour l'utilisateur par
+     *      rapport à celle de bas niveau gi._gi.
+     *
+     * Il demeure que la seconde définition est entièrement implémentée en Python
+     * et porte ainsi le fanion Py_TPFLAGS_HEAPTYPE, imposant cette même dernière
+     * propriétée à tous les objets qui en dérivent.
+     *
+     * Les définitions de Chrysalide sont cependant toutes statiques et donc
+     * incompatibles avec une définition gi.repository.GObject.Object, comme le
+     * pointent les validations opérées par PyType_Ready().
+     *
+     * Une solution consiste ici à restaurer au besoin la définition gi._gi.GObject
+     * brute, effectuer les enregistrements de Chrysalide sur cette base, et
+     * remettre en place la définition éventuellement remplacée ensuite.
+     */
+
+    py_gobj_def = pygobject_lookup_class(G_TYPE_OBJECT);
+
+    if (py_gobj_def != &PyGObject_Type)
+    {
+        Py_INCREF((PyObject *)py_gobj_def);
+
+        /* Définition récupérée de pyi_object_register_types() */
+        pygobject_class_key = g_quark_from_static_string("PyGObject::class");
+
+        g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, &PyGObject_Type);
+
+    }
+
     status = true;
 
     if (status) status = add_features_module(result);
@@ -689,7 +763,7 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
         PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components.");
         Py_DECREF(result);
         result = NULL;
-        goto exit;
+        goto exit_and_restore;
     }
 
     if (_standalone)
@@ -701,7 +775,7 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
             PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function.");
             Py_DECREF(result);
             result = NULL;
-            goto exit;
+            goto exit_and_restore;
         }
 
         /**
@@ -733,7 +807,7 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
             LOG_ERROR_DL_N("dladdr");
             Py_DECREF(result);
             result = NULL;
-            goto exit;
+            goto exit_and_restore;
         }
 
         self = g_plugin_module_new(info.dli_fname);
@@ -769,6 +843,16 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
 
     }
 
+ exit_and_restore:
+
+    if (py_gobj_def != &PyGObject_Type)
+    {
+        g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, py_gobj_def);
+
+        Py_DECREF((PyObject *)py_gobj_def);
+
+    }
+
  exit:
 
     if (result == NULL && !_standalone)
-- 
cgit v0.11.2-87-g4458