From 59ab0336eaab192ca2f02b67d143a1ba4d1aac2b Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Mon, 13 Jan 2025 08:34:49 +0100
Subject: Handle load errors due to buggy Python plugins.

---
 plugins/pychrysalide/bindings.c | 120 +++++++++++++++++++++++++++++++-
 plugins/pychrysalide/bindings.h |   5 +-
 plugins/pychrysalide/core.c     | 148 +---------------------------------------
 3 files changed, 123 insertions(+), 150 deletions(-)

diff --git a/plugins/pychrysalide/bindings.c b/plugins/pychrysalide/bindings.c
index 295667f..f715a8e 100644
--- a/plugins/pychrysalide/bindings.c
+++ b/plugins/pychrysalide/bindings.c
@@ -34,6 +34,7 @@
 
 #include <config.h>
 #include <common/cpp.h>
+#include <common/extstr.h>
 #include <plugins/pglist.h> // REMME ?
 #include <plugins/self.h> // REMME ?
 
@@ -929,13 +930,130 @@ PyObject *init_python_pychrysalide_module(const pyinit_details_t *details)
  exit:
 
     if (result == NULL && !details->standalone)
-        /*log_pychrysalide_exception("Loading failed")*/;
+        log_pychrysalide_exception("Python bindings loading failed");
 
     return result;
 
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : prefix = message d'introduction à faire apparaître à l'écran.*
+*                                                                             *
+*  Description : Présente dans le journal une exception survenue.             *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void log_pychrysalide_exception(const char *prefix, ...)
+{
+    va_list ap;                             /* Compléments argumentaires   */
+    char *msg;                              /* Message complet à imprimer  */
+    PyObject *err_type;                     /* Type d'erreur Python        */
+    PyObject *err_value;                    /* Instance Python d'erreur    */
+    PyObject *err_traceback;                /* Trace Python associée       */
+    PyObject *err_string;                   /* Description Python d'erreur */
+    const char *err_msg;                    /* Représentation humaine      */
+
+    assert(PyGILState_Check() == 1);
+
+    if (PyErr_Occurred())
+    {
+        /* Base de la communication */
+
+        va_start(ap, prefix);
+
+        vasprintf(&msg, prefix, ap);
+
+        va_end(ap);
+
+        /* Détails complémentaires */
+
+        PyErr_Fetch(&err_type, &err_value, &err_traceback);
+
+        PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
+
+        if (err_traceback == NULL)
+        {
+            err_traceback = Py_None;
+            Py_INCREF(err_traceback);
+        }
+
+        PyException_SetTraceback(err_value, err_traceback);
+
+        if (err_value == NULL)
+            msg = stradd(msg, _(": no extra information is provided..."));
+
+        else
+        {
+            err_string = PyObject_Str(err_value);
+            err_msg = PyUnicode_AsUTF8(err_string);
+
+            msg = stradd(msg, ": ");
+            msg = stradd(msg, err_msg);
+
+            Py_DECREF(err_string);
+
+        }
+
+        /**
+         * Bien que la documentation précise que la fonction PyErr_Fetch()
+         * transfère la propritété des éléments retournés, la pratique
+         * montre que le programme plante à la terminaison en cas d'exception.
+         *
+         * C'est par exemple le cas quand un greffon Python ne peut se lancer
+         * correctement ; l'exception est alors levée à partir de la fonction
+         * create_python_plugin() et le plantage intervient en sortie d'exécution,
+         * au moment de la libération de l'extension Python :
+         *
+         *    ==14939== Jump to the invalid address stated on the next line
+         *    ==14939==    at 0x1A8FCBC9: ???
+         *    ==14939==    by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3)
+         *    ==14939==    by 0x610F834: on_plugin_ref_toggle (pglist.c:370)
+         *    ==14939==    by 0x610F31A: exit_all_plugins (pglist.c:153)
+         *    ==14939==    by 0x10AD19: main (main.c:440)
+         *    ==14939==  Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd
+         *
+         * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un
+         * appel à PyErr_PrintEx(0) ne change rien.
+         *
+         * La seule différence de l'instruction set_sys_last_vars réside en quelques
+         * lignes dans le code de l'interpréteur Python :
+         *
+         *    if (set_sys_last_vars) {
+         *        _PySys_SetObjectId(&PyId_last_type, exception);
+         *        _PySys_SetObjectId(&PyId_last_value, v);
+         *        _PySys_SetObjectId(&PyId_last_traceback, tb);
+         *    }
+         *
+         * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ?
+         * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence
+         * à ces éléments.
+         *
+         * On reproduit ici le comportement du code correcteur avec PySys_SetObject().
+         */
+
+        PySys_SetObject("last_type", err_type);
+        PySys_SetObject("last_value", err_value);
+        PySys_SetObject("last_traceback", err_traceback);
+
+        Py_XDECREF(err_traceback);
+        Py_XDECREF(err_value);
+        Py_XDECREF(err_type);
+
+        log_plugin_simple_message(LMT_EXT_ERROR, msg);
+
+        free(msg);
+
+    }
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                          FONCTIONS GLOBALES DE CHRYSALIDE                          */
diff --git a/plugins/pychrysalide/bindings.h b/plugins/pychrysalide/bindings.h
index 1c63956..e9ee421 100644
--- a/plugins/pychrysalide/bindings.h
+++ b/plugins/pychrysalide/bindings.h
@@ -48,16 +48,15 @@ typedef struct _pyinit_details_t
 {
     bool standalone;                        /* Chargement depuis Python ?  */
 
-
     bool (* populate_extra) (void);         /* Ajout de types ?            */
 
-
 } pyinit_details_t;
 
 /* Implémente le point d'entrée pour l'initialisation de Python. */
 PyObject *init_python_pychrysalide_module(const pyinit_details_t *);
 
-
+/* Présente dans le journal une exception survenue. */
+void log_pychrysalide_exception(const char *, ...);
 
 
 
diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c
index e815e25..3c551c7 100644
--- a/plugins/pychrysalide/core.c
+++ b/plugins/pychrysalide/core.c
@@ -368,146 +368,6 @@ G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *pl
 
 
 
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : prefix = message d'introduction à faire apparaître à l'écran.*
-*                                                                             *
-*  Description : Présente dans le journal une exception survenue.             *
-*                                                                             *
-*  Retour      : -                                                            *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-void log_pychrysalide_exception(const char *prefix, ...)
-{
-    va_list ap;                             /* Compléments argumentaires   */
-    char *msg;                              /* Message complet à imprimer  */
-    PyObject *err_type;                     /* Type d'erreur Python        */
-    PyObject *err_value;                    /* Instance Python d'erreur    */
-    PyObject *err_traceback;                /* Trace Python associée       */
-    PyObject *err_string;                   /* Description Python d'erreur */
-    const char *err_msg;                    /* Représentation humaine      */
-
-    assert(PyGILState_Check() == 1);
-
-    if (PyErr_Occurred())
-    {
-        /* Base de la communication */
-
-        va_start(ap, prefix);
-
-        vasprintf(&msg, prefix, ap);
-
-        va_end(ap);
-
-        /* Détails complémentaires */
-
-        PyErr_Fetch(&err_type, &err_value, &err_traceback);
-
-        PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
-
-        if (err_traceback == NULL)
-        {
-            err_traceback = Py_None;
-            Py_INCREF(err_traceback);
-        }
-
-        PyException_SetTraceback(err_value, err_traceback);
-
-        if (err_value == NULL)
-            msg = stradd(msg, _(": no extra information is provided..."));
-
-        else
-        {
-            err_string = PyObject_Str(err_value);
-            err_msg = PyUnicode_AsUTF8(err_string);
-
-            msg = stradd(msg, ": ");
-            msg = stradd(msg, err_msg);
-
-            Py_DECREF(err_string);
-
-        }
-
-        /**
-         * Bien que la documentation précise que la fonction PyErr_Fetch()
-         * transfère la propritété des éléments retournés, la pratique
-         * montre que le programme plante à la terminaison en cas d'exception.
-         *
-         * C'est par exemple le cas quand un greffon Python ne peut se lancer
-         * correctement ; l'exception est alors levée à partir de la fonction
-         * create_python_plugin() et le plantage intervient en sortie d'exécution,
-         * au moment de la libération de l'extension Python :
-         *
-         *    ==14939== Jump to the invalid address stated on the next line
-         *    ==14939==    at 0x1A8FCBC9: ???
-         *    ==14939==    by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3)
-         *    ==14939==    by 0x610F834: on_plugin_ref_toggle (pglist.c:370)
-         *    ==14939==    by 0x610F31A: exit_all_plugins (pglist.c:153)
-         *    ==14939==    by 0x10AD19: main (main.c:440)
-         *    ==14939==  Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd
-         *
-         * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un
-         * appel à PyErr_PrintEx(0) ne change rien.
-         *
-         * La seule différence de l'instruction set_sys_last_vars réside en quelques
-         * lignes dans le code de l'interpréteur Python :
-         *
-         *    if (set_sys_last_vars) {
-         *        _PySys_SetObjectId(&PyId_last_type, exception);
-         *        _PySys_SetObjectId(&PyId_last_value, v);
-         *        _PySys_SetObjectId(&PyId_last_traceback, tb);
-         *    }
-         *
-         * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ?
-         * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence
-         * à ces éléments.
-         *
-         * On reproduit ici le comportement du code correcteur avec PySys_SetObject().
-         */
-
-        PySys_SetObject("last_type", err_type);
-        PySys_SetObject("last_value", err_value);
-        PySys_SetObject("last_traceback", err_traceback);
-
-        Py_XDECREF(err_traceback);
-        Py_XDECREF(err_value);
-        Py_XDECREF(err_type);
-
-        log_plugin_simple_message(LMT_ERROR, msg);
-
-        free(msg);
-
-    }
-
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 /* ---------------------------------------------------------------------------------- */
 /*                       IMPLEMENTATION DES FONCTIONS DE CLASSE                       */
 /* ---------------------------------------------------------------------------------- */
@@ -685,8 +545,6 @@ static GPluginModule *create_python_plugin(const char *modname, const char *file
 
     result = G_PLUGIN_MODULE(pygobject_get(instance));
 
-    ///result->filename = strdup(filename);
-
     /**
      * L'instance Python et l'objet GLib résultant sont un même PyGObject.
      *
@@ -696,13 +554,11 @@ static GPluginModule *create_python_plugin(const char *modname, const char *file
 
     Py_DECREF(module);
 
-    printf(" -> REF: %p %u\n", result, G_OBJECT(result)->ref_count);
-
     return result;
 
  no_instance:
 
-    //log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance"));
+    log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance"));
 
  no_class:
 
@@ -714,7 +570,7 @@ static GPluginModule *create_python_plugin(const char *modname, const char *file
 
     Py_XDECREF(module);
 
-    //log_pychrysalide_exception(_("An error occured when importing '%s'"), modname);
+    log_pychrysalide_exception(_("An error occured when importing '%s'"), modname);
 
  bad_exit:
 
-- 
cgit v0.11.2-87-g4458