From 4293dfb9d679799fc46240436636fdcd431bccad Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Fri, 19 May 2023 16:03:55 +0200 Subject: Install metaclass for Python GObjects, if needed. --- plugins/pychrysalide/core.c | 126 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c index 66dbdf0..d4f2292 100644 --- a/plugins/pychrysalide/core.c +++ b/plugins/pychrysalide/core.c @@ -98,6 +98,9 @@ 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. */ static bool set_version_for_gtk_namespace(const char *); @@ -292,6 +295,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 à g_python_plugin_new(). 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 /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 /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 /Objects/typeobject.c : + * + * int _PyType_CheckConsistency(PyTypeObject *type) + * { + * [...] + * CHECK(!_PyObject_IsFreed((PyObject *)type)); + * [...] + * } + * + * - cf. _PyObject_IsFreed() dans /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 /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. * @@ -459,6 +582,9 @@ PyMODINIT_FUNC PyInit_pychrysalide(void) goto exit; } + if (!install_metaclass_for_python_gobjects()) + goto exit; + if (!set_version_for_gtk_namespace("3.0")) goto exit; -- cgit v0.11.2-87-g4458