diff options
Diffstat (limited to 'plugins/pychrysalide/core.c')
-rw-r--r-- | plugins/pychrysalide/core.c | 232 |
1 files changed, 168 insertions, 64 deletions
diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c index ef119fb..08f570f 100644 --- a/plugins/pychrysalide/core.c +++ b/plugins/pychrysalide/core.c @@ -40,7 +40,6 @@ #include <unistd.h> -#include <config.h> #include <i18n.h> #include <gleak.h> #include <common/cpp.h> @@ -65,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" @@ -85,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 *); @@ -101,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); @@ -295,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. * @@ -304,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 */ @@ -340,6 +463,7 @@ static bool set_version_for_gtk_namespace(const char *version) return result; } +#endif /****************************************************************************** @@ -462,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)) { @@ -488,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); @@ -503,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(); @@ -656,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 */ @@ -675,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); } @@ -696,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); @@ -736,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) { @@ -784,7 +907,7 @@ static void load_python_plugins(GPluginModule *plugin) } - closedir(dir); + closedir(dir); } @@ -808,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; @@ -825,7 +949,7 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin) Py_Initialize(); - PySys_SetArgv(0, (wchar_t *[]) { NULL }); + gstate = PyGILState_Ensure(); _chrysalide_module = PyImport_ImportModule("pychrysalide"); @@ -842,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: @@ -867,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); + } @@ -911,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 */ @@ -922,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); @@ -962,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); @@ -982,6 +1110,8 @@ G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin, } + PyGILState_Release(gstate); + } @@ -1002,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); @@ -1025,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; @@ -1078,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 */ @@ -1124,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 |