summaryrefslogtreecommitdiff
path: root/plugins/pychrysalide/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/pychrysalide/core.c')
-rw-r--r--plugins/pychrysalide/core.c242
1 files changed, 175 insertions, 67 deletions
diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c
index 98f94b6..08f570f 100644
--- a/plugins/pychrysalide/core.c
+++ b/plugins/pychrysalide/core.c
@@ -21,6 +21,11 @@
*/
+#undef NO_IMPORT_PYGOBJECT
+#include <pygobject.h>
+#define NO_IMPORT_PYGOBJECT
+
+
#include "core.h"
@@ -35,7 +40,6 @@
#include <unistd.h>
-#include <config.h>
#include <i18n.h>
#include <gleak.h>
#include <common/cpp.h>
@@ -60,8 +64,10 @@
#include "debug/module.h"
#include "format/module.h"
#include "glibext/module.h"
-#include "gtkext/module.h"
-#include "gui/module.h"
+#ifdef INCLUDE_GTK_SUPPORT
+# include "gtkext/module.h"
+# include "gui/module.h"
+#endif
#include "mangling/module.h"
#include "plugins/module.h"
#include "plugins/plugin.h"
@@ -80,9 +86,6 @@ static bool _standalone = true;
/* Réceptacle pour le chargement forcé */
static PyObject *_chrysalide_module = NULL;
-/* Conservation des informations du thread principal */
-static PyThreadState *_main_tstate = NULL;
-
/* Fournit la révision du programme global. */
static PyObject *py_chrysalide_revision(PyObject *, PyObject *);
@@ -96,8 +99,13 @@ static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *);
/* Détermine si l'interpréteur lancé est celui pris en compte. */
static bool is_current_abi_suitable(void);
+/* Assure une pleine initialisation des objets de Python-GI. */
+static bool install_metaclass_for_python_gobjects(void);
+
/* Définit la version attendue de GTK à charger dans Python. */
+#ifdef INCLUDE_GTK_SUPPORT
static bool set_version_for_gtk_namespace(const char *);
+#endif
/* Point de sortie pour l'initialisation de Python. */
static void PyExit_pychrysalide(void);
@@ -249,7 +257,8 @@ static bool is_current_abi_suitable(void)
#define GRAB_ABI_FLAGS_IN_PYTHON \
"import sys" "\n" \
"import os" "\n" \
- "os.write(%d, bytes(sys.abiflags, 'UTF-8'))" "\n"
+ "data = bytes(sys.abiflags, 'UTF-8') + b'\\0'" "\n" \
+ "os.write(%d, data)" "\n"
result = false;
@@ -289,6 +298,126 @@ static bool is_current_abi_suitable(void)
/******************************************************************************
* *
+* Paramètres : - *
+* *
+* Description : Assure une pleine initialisation des objets de Python-GI. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool install_metaclass_for_python_gobjects(void)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *gi_types_mod; /* Module Python-GObject */
+
+ /**
+ * Les extensions Python sont chargées à partir de la fonction load_python_plugins(),
+ * qui fait appel à create_python_plugin(). Une instance y est construite via un
+ * appel à PyObject_CallFunction() avec la classe spécifiée par l'alias AutoLoad
+ * dans le fichier __init__.py présent dans chaque module d'extension.
+ *
+ * Le constructeur py_plugin_module_new() renvoie in fine à la fonction générique
+ * python_abstract_constructor_with_dynamic_gtype(), laquelle conduit à la fonction
+ * pygobject_register_class() définie dans <python3-gi>/gi/pygobject-object.c.
+ * Le code de cette dernière comprend notamment la portion suivante :
+ *
+ * [...]
+ * Py_SET_TYPE(type, PyGObject_MetaType);
+ * [...]
+ * if (PyType_Ready(type) < 0) {
+ * g_warning ("couldn't make the type `%s' ready", type->tp_name);
+ * return;
+ * }
+ * [...]
+ *
+ * La fonction PyType_Ready() est définie dans <python3>/Objects/typeobject.c
+ * et commence par :
+ *
+ * int PyType_Ready(PyTypeObject *type)
+ * {
+ * if (type->tp_flags & Py_TPFLAGS_READY) {
+ * assert(_PyType_CheckConsistency(type));
+ * return 0;
+ * }
+ * [...]
+ * }
+ *
+ * La vérification de cohérencce commence par analyser le type et son propre
+ * type :
+ *
+ * - cf. _PyType_CheckConsistency() dans <python3>/Objects/typeobject.c :
+ *
+ * int _PyType_CheckConsistency(PyTypeObject *type)
+ * {
+ * [...]
+ * CHECK(!_PyObject_IsFreed((PyObject *)type));
+ * [...]
+ * }
+ *
+ * - cf. _PyObject_IsFreed() dans <python3>/Objects/object.c :
+ *
+ * int _PyObject_IsFreed(PyObject *op)
+ * {
+ * if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) {
+ * return 1;
+ * }
+ *
+ * La fonction _PyMem_IsPtrFreed() recherche entre autres la valeur NULL.
+ *
+ * Or le type du type est écrasé dans la fonction pygobject_register_class()
+ * avec la valeur de la variable PyGObject_MetaType. Cette variable n'est
+ * définie qu'à un seul endroit, dans <python3-gi>/gi/gimodule.c :
+ *
+ * static PyObject *
+ * pyg__install_metaclass(PyObject *dummy, PyTypeObject *metaclass)
+ * {
+ * Py_INCREF(metaclass);
+ * PyGObject_MetaType = metaclass;
+ * Py_INCREF(metaclass);
+ *
+ * Py_SET_TYPE(&PyGObject_Type, metaclass);
+ *
+ * Py_INCREF(Py_None);
+ * return Py_None;
+ * }
+ *
+ * Afin de valider la vérification de _PyType_CheckConsistency() pour les
+ * modules externes qui entraînent un enregistrement tout en portant le drapeau
+ * Py_TPFLAGS_READY (typiquement ceux du répertoire "plugins/python/", il faut
+ * initialiser au besoin la variable PyGObject_MetaType.
+ *
+ * Une ligne suffit donc à enregistrer le type intermédiaire :
+ *
+ * from _gi import types
+ *
+ * On simule ici une déclaration similaire si nécessaire
+ */
+
+ result = false;
+
+ if (PyType_CheckExact(&PyGObject_Type))
+ {
+ gi_types_mod = PyImport_ImportModule("gi.types");
+
+ result = (PyErr_Occurred() == NULL);
+
+ if (result)
+ result = (PyType_CheckExact(&PyGObject_Type) == 0);
+
+ Py_XDECREF(gi_types_mod);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
* Paramètres : version = idenfiant de la version de GTK à stipuler. *
* *
* Description : Définit la version attendue de GTK à charger dans Python. *
@@ -298,7 +427,7 @@ static bool is_current_abi_suitable(void)
* Remarques : - *
* *
******************************************************************************/
-
+#ifdef INCLUDE_GTK_SUPPORT
static bool set_version_for_gtk_namespace(const char *version)
{
bool result; /* Bilan à retourner */
@@ -334,6 +463,7 @@ static bool set_version_for_gtk_namespace(const char *version)
return result;
}
+#endif
/******************************************************************************
@@ -456,8 +586,13 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
goto exit;
}
+ if (!install_metaclass_for_python_gobjects())
+ goto exit;
+
+#ifdef INCLUDE_GTK_SUPPORT
if (!set_version_for_gtk_namespace("3.0"))
goto exit;
+#endif
if (!load_all_core_components(true))
{
@@ -482,8 +617,10 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
if (status) status = add_debug_module(result);
if (status) status = add_format_module(result);
if (status) status = add_glibext_module(result);
+#ifdef INCLUDE_GTK_SUPPORT
if (status) status = add_gtkext_module(result);
if (status) status = add_gui_module(result);
+#endif
if (status) status = add_mangling_module(result);
if (status) status = add_plugins_module(result);
@@ -497,8 +634,10 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
if (status) status = populate_debug_module();
if (status) status = populate_format_module();
if (status) status = populate_glibext_module();
+#ifdef INCLUDE_GTK_SUPPORT
if (status) status = populate_gtkext_module();
if (status) status = populate_gui_module();
+#endif
if (status) status = populate_mangling_module();
if (status) status = populate_plugins_module();
@@ -650,7 +789,6 @@ static void load_python_plugins(GPluginModule *plugin)
struct dirent *entry; /* Elément trouvé */
char *modname; /* Nom du module pour Python */
char *filename; /* Chemin d'accès reconstruit */
- PyThreadState *tstate; /* Contexte d'environnement */
GPluginModule *pyplugin; /* Lien vers un grffon Python */
bool status; /* Bilan d'une opération */
GGenConfig *config; /* Configuration à charger */
@@ -669,11 +807,11 @@ static void load_python_plugins(GPluginModule *plugin)
if (dir != NULL)
{
- closedir(dir);
+ closedir(dir);
- edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python");
- extend_python_path(edir);
- free(edir);
+ edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python");
+ extend_python_path(edir);
+ free(edir);
}
@@ -690,7 +828,7 @@ static void load_python_plugins(GPluginModule *plugin)
save = NULL; /* gcc... */
for (path = strtok_r(paths, ":", &save);
- path != NULL;
+ path != NULL;
path = strtok_r(NULL, ":", &save))
{
dir = opendir(path);
@@ -730,16 +868,7 @@ static void load_python_plugins(GPluginModule *plugin)
filename = stradd(filename, G_DIR_SEPARATOR_S);
filename = stradd(filename, entry->d_name);
- if (!_standalone)
- {
- tstate = get_pychrysalide_main_tstate();
- PyEval_RestoreThread(tstate);
- }
-
- pyplugin = g_python_plugin_new(modname, filename);
-
- if (!_standalone)
- PyEval_SaveThread();
+ pyplugin = create_python_plugin(modname, filename);
if (pyplugin == NULL)
{
@@ -778,7 +907,7 @@ static void load_python_plugins(GPluginModule *plugin)
}
- closedir(dir);
+ closedir(dir);
}
@@ -802,6 +931,7 @@ static void load_python_plugins(GPluginModule *plugin)
G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
{
bool result; /* Bilan à retourner */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
int ret; /* Bilan de préparatifs */
_standalone = false;
@@ -819,9 +949,7 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
Py_Initialize();
- PyEval_InitThreads();
-
- PySys_SetArgv(0, (wchar_t *[]) { NULL });
+ gstate = PyGILState_Ensure();
_chrysalide_module = PyImport_ImportModule("pychrysalide");
@@ -838,9 +966,7 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
result = (_chrysalide_module != NULL);
- _main_tstate = PyThreadState_Get();
-
- PyEval_ReleaseLock();
+ PyGILState_Release(gstate);
cpi_done:
@@ -863,10 +989,16 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *plugin)
{
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+
+ gstate = PyGILState_Ensure();
+
clear_all_accesses_to_python_modules();
Py_XDECREF(_chrysalide_module);
+ PyGILState_Release(gstate);
+
}
@@ -907,8 +1039,8 @@ static void free_native_plugin_type(PyTypeObject *type)
G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin, PluginAction action)
{
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
size_t count; /* Quantité de greffons chargés*/
- PyTypeObject *parent; /* Type Python pour greffon */
PyObject *module; /* Module à recompléter */
PyObject *dict; /* Dictionnaire du module */
GPluginModule **list; /* Ensemble de ces greffons */
@@ -918,14 +1050,14 @@ G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin,
int ret; /* Bilan d'un appel */
PyTypeObject *type; /* Nouveau type dynamique */
+ gstate = PyGILState_Ensure();
+
if (action == PGA_NATIVE_PLUGINS_LOADED)
{
/* Intégration des greffons natifs en Python */
if (ensure_python_plugin_module_is_registered())
{
- parent = get_python_plugin_module_type();
-
module = get_access_to_python_module("pychrysalide.plugins");
assert(module != NULL);
@@ -958,7 +1090,7 @@ G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin,
type->tp_flags = Py_TPFLAGS_DEFAULT;
type->tp_new = no_python_constructor_allowed;
- if (register_class_for_pygobject(dict, G_OBJECT_TYPE(list[i]), type, parent))
+ if (register_class_for_pygobject(dict, G_OBJECT_TYPE(list[i]), type))
g_object_set_data_full(G_OBJECT(list[i]), "python_type", type,
(GDestroyNotify)free_native_plugin_type);
@@ -978,6 +1110,8 @@ G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin,
}
+ PyGILState_Release(gstate);
+
}
@@ -998,17 +1132,13 @@ G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin,
G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *plugin, PluginAction action, GType type)
{
gpointer result; /* Instance à retourner */
- PyThreadState *tstate; /* Contexte d'environnement */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyTypeObject *pytype; /* Classe Python concernée */
PyObject *instance; /* Initialisation forcée */
result = NULL;
- if (!_standalone)
- {
- tstate = get_pychrysalide_main_tstate();
- PyEval_RestoreThread(tstate);
- }
+ gstate = PyGILState_Ensure();
pytype = pygobject_lookup_class(type);
@@ -1021,31 +1151,7 @@ G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *pl
}
- if (!_standalone)
- PyEval_SaveThread();
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : - *
-* *
-* Description : Fournit les informations du thread principal. *
-* *
-* Retour : Indications utiles à Python. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-PyThreadState *get_pychrysalide_main_tstate(void)
-{
- PyThreadState *result; /* Indications à retourner */
-
- result = _main_tstate;
+ PyGILState_Release(gstate);
return result;
@@ -1074,6 +1180,8 @@ void log_pychrysalide_exception(const char *prefix, ...)
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 */
@@ -1120,7 +1228,7 @@ void log_pychrysalide_exception(const char *prefix, ...)
*
* 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
- * g_python_plugin_new() et le plantage intervient en sortie d'exécution,
+ * 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