diff options
Diffstat (limited to 'plugins/pychrysalide/helpers.c')
-rw-r--r-- | plugins/pychrysalide/helpers.c | 428 |
1 files changed, 293 insertions, 135 deletions
diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c index 92a5db9..c2b1868 100644 --- a/plugins/pychrysalide/helpers.c +++ b/plugins/pychrysalide/helpers.c @@ -32,11 +32,14 @@ #include <stdlib.h> #include <string.h> #include <strings.h> -#include <gtk/gtk.h> +#ifdef INCLUDE_GTK_SUPPORT +# include <gtk/gtk.h> +#endif #include <i18n.h> #include <common/extstr.h> +#include <plugins/dt.h> #include "access.h" @@ -233,6 +236,8 @@ PyObject *run_python_method(PyObject *module, const char *method, PyObject *args PyObject *traceback; /* Pile d'appels de l'exception*/ PyObject *refmsg; /* Message de référence */ + assert(PyGILState_Check() == 1); + /* Exécution */ result = NULL; @@ -505,6 +510,131 @@ bool register_python_module_object(PyObject *module, PyTypeObject *type) /****************************************************************************** * * +* Paramètres : type = type du nouvel objet à mettre en place. * +* gbase = type de base natif. * +* args = éventuelle liste d'arguments. * +* kwds = éventuel dictionnaire de valeurs mises à disposition.* +* * +* Description : Accompagne la création d'une instance dérivée en Python. * +* * +* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *type, GType gbase, PyObject *args, PyObject *kwds) +{ + PyObject *result; /* Objet à retourner */ + PyTypeObject *base; /* Type parent version Python */ + bool first_time; /* Evite les multiples passages*/ + GType gtype; /* Nouveau type de processeur */ + bool status; /* Bilan d'un enregistrement */ + + /* Validations diverses */ + + base = pygobject_lookup_class(gbase); + + if (type == base) + goto simple_way; + + /* Mise en place d'un type dédié */ + + first_time = (g_type_from_name(type->tp_name) == 0); + + gtype = build_dynamic_type(gbase, type->tp_name, NULL, NULL, NULL); + + if (first_time) + { + status = register_class_for_dynamic_pygobject(gtype, type); + + if (!status) + { + result = NULL; + goto exit; + } + + } + + /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ + + simple_way: + + result = PyType_GenericNew(type, args, kwds); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : type = type du nouvel objet à mettre en place. * +* gbase = type de base natif. * +* cinit = procédure d'initialisation de la classe associée. * +* args = éventuelle liste d'arguments. * +* kwds = éventuel dictionnaire de valeurs mises à disposition.* +* * +* Description : Accompagne la création d'une instance dérivée en Python. * +* * +* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *type, GType gbase, GClassInitFunc cinit, PyObject *args, PyObject *kwds) +{ + PyObject *result; /* Objet à retourner */ + PyTypeObject *base; /* Type parent version Python */ + bool first_time; /* Evite les multiples passages*/ + GType gtype; /* Nouveau type de processeur */ + bool status; /* Bilan d'un enregistrement */ + + /* Validations diverses */ + + base = pygobject_lookup_class(gbase); + + if (type == base) + { + result = NULL; + PyErr_Format(PyExc_RuntimeError, _("%s is an abstract class"), type->tp_name); + goto exit; + } + + /* Mise en place d'un type dédié */ + + first_time = (g_type_from_name(type->tp_name) == 0); + + gtype = build_dynamic_type(gbase, type->tp_name, cinit, NULL, NULL); + + if (first_time) + { + status = register_class_for_dynamic_pygobject(gtype, type); + + if (!status) + { + result = NULL; + goto exit; + } + + } + + /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ + + result = PyType_GenericNew(type, args, kwds); + + exit: + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : type = type du nouvel objet à mettre en place. * * args = éventuelle liste d'arguments. * * kwds = éventuel dictionnaire de valeurs mises à disposition. * @@ -659,7 +789,7 @@ PyObject *not_yet_implemented_getter(PyObject *self, void *closure) /****************************************************************************** * * -* Paramètres : spec = définition à mettre en place dynamiquement. * +* Paramètres : otype = définition à mettre en place dynamiquement. * * * * Description : Définit dans le tas de Python un nouveau type. * * * @@ -669,102 +799,63 @@ PyObject *not_yet_implemented_getter(PyObject *self, void *closure) * * ******************************************************************************/ -PyTypeObject *define_python_dynamic_type(const PyTypeObject *spec) +PyTypeObject *define_python_dynamic_type(const PyTypeObject *otype) { PyTypeObject *result; /* Définition créée à renvoyer */ - PyTypeObject *type; /* Type de tous les types */ - size_t size; /* Taille de la définition */ - char *s; /* Marqueur de début de chaîne */ - PyHeapTypeObject *hobj; /* Version réelle du type créé */ + PyType_Slot slots[10]; /* Emplacements pour infos */ + PyType_Spec spec; /* Définition du type */ + PyType_Slot *iter; /* Boucle de parcours */ + PyObject *bases; /* Bases de construction */ - /** - * Le cahier des charges est ici d'éviter les erreurs suivantes : - * - * TypeError: type 'XXX' is not dynamically allocated but its base type 'YYY' is dynamically allocated - * Fatal Python error: unexpected exception during garbage collection - * - * L'allocation dynamique est marquée par le fanion Py_TPFLAGS_HEAPTYPE. - * - * Une des rares fonctions qui appliquent ce fanion est PyType_FromSpecWithBases(), - * mais elle appelle ensuite PyType_Ready(), ce qui est incompatible avec des modifications - * utltérieures avant un appel à pygobject_register_class(). - * - * Le code suivant s'inspire fortement des méthodes originales de Python, - * dont les mécanismes employés par PyType_GenericAlloc(). - */ - - type = &PyType_Type; - size = _PyObject_SIZE(type); - - if (PyType_IS_GC(type)) - result = (PyTypeObject *)_PyObject_GC_Malloc(size); - else - result = (PyTypeObject *)PyObject_MALLOC(size); - - if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) - Py_INCREF(type); - - /* Définitions sommaires */ - - memset(result, 0, sizeof(PyHeapTypeObject)); + bases = PyTuple_Pack(1, &PyType_Type); - memcpy(result, spec, sizeof(PyTypeObject)); - - result->tp_flags |= Py_TPFLAGS_HEAPTYPE; - - /* Définitions des noms */ - - /** - * Pour un type dynamique, les désignations ne s'appuient pas sur la partie réservée - * au type, mais sur les données suivantes (cf. type_name() et type_qualname()). - * - * Les deux fonctions désignées sont par ailleurs facilement accessibles : - * - * #0 0x0000555555689bf0 in type_qualname (...) at Objects/typeobject.c:393 - * #1 0x000055555568b3b4 in type_repr (...) at Objects/typeobject.c:855 - * #2 0x0000555555693574 in object_str (...) at Objects/typeobject.c:3511 - * #3 0x0000555555670d02 in PyObject_Str (...) at Objects/object.c:535 - * ... - * - * On s'inspire donc du contenu de PyType_FromSpecWithBases() pour éviter tout - * plantage du fait de données non initialisées. - */ - - hobj = (PyHeapTypeObject *)result; - - s = strrchr(spec->tp_name, '.'); - - if (s == NULL) - s = (char *)spec->tp_name; - else - s++; + spec.name = otype->tp_name; + spec.basicsize = otype->tp_basicsize; + spec.flags = otype->tp_flags; + spec.slots = slots; - hobj->ht_name = PyUnicode_FromString(s); - assert(hobj->ht_name != NULL); + iter = &slots[0]; - hobj->ht_qualname = hobj->ht_name; - Py_INCREF(hobj->ht_qualname); + if (otype->tp_doc != NULL) + { + iter->slot = Py_tp_doc; + iter->pfunc = (void *)otype->tp_doc; + iter++; + } - result->tp_as_async = &hobj->as_async; - result->tp_as_number = &hobj->as_number; - result->tp_as_sequence = &hobj->as_sequence; - result->tp_as_mapping = &hobj->as_mapping; - result->tp_as_buffer = &hobj->as_buffer; + if (otype->tp_methods != NULL) + { + iter->slot = Py_tp_methods; + iter->pfunc = otype->tp_methods; + iter++; + } - hobj->ht_cached_keys = _PyDict_NewKeysForClass(); + if (otype->tp_getset != NULL) + { + iter->slot = Py_tp_getset; + iter->pfunc = otype->tp_getset; + iter++; + } + if (otype->tp_init != NULL) + { + iter->slot = Py_tp_init; + iter->pfunc = otype->tp_init; + iter++; + } + if (otype->tp_new != NULL) + { + iter->slot = Py_tp_new; + iter->pfunc = otype->tp_new; + iter++; + } + iter->slot = 0; -#if 0 - if (type->tp_itemsize == 0) - (void)PyObject_INIT(result, type); - else - (void) PyObject_INIT_VAR((PyVarObject *)result, type, 0); + result = (PyTypeObject *)PyType_FromSpecWithBases(&spec, bases); - if (PyType_IS_GC(type)) - _PyObject_GC_TRACK(result); -#endif + Py_DECREF(bases); return result; @@ -800,7 +891,6 @@ static void define_auto_documentation(PyTypeObject *type) * Paramètres : dict = dictionnaire où conserver une référence au type créé.* * gtype = type dans sa version GLib. * * type = type dans sa version Python. * -* base = type de base de l'objet. * * * * Description : Enregistre correctement une surcouche de conversion GObject. * * * @@ -810,13 +900,11 @@ static void define_auto_documentation(PyTypeObject *type) * * ******************************************************************************/ -bool _register_class_for_pygobject(PyObject *dict, GType gtype, PyTypeObject *type, PyTypeObject *base, ...) +bool register_class_for_pygobject(PyObject *dict, GType gtype, PyTypeObject *type) { bool result; /* Bilan à retourner */ - Py_ssize_t size; /* Taille de liste actuelle */ - PyObject *static_bases; /* Base(s) de l'objet */ - va_list ap; /* Parcours des arguments */ - PyTypeObject *static_base; /* Base à rajouter à la liste */ + GType parent_type; /* Type parent version GObject */ + PyTypeObject *base; /* Type parent version Python */ assert(gtype != G_TYPE_INVALID); @@ -841,45 +929,17 @@ bool _register_class_for_pygobject(PyObject *dict, GType gtype, PyTypeObject *ty * Et quelqu'un doit se coller à la tâche. PyGObject ne fait rien, donc... */ + parent_type = g_type_parent(gtype); + + base = pygobject_lookup_class(parent_type); + if (type->tp_basicsize < base->tp_basicsize) { assert(type->tp_basicsize == 0); type->tp_basicsize = base->tp_basicsize; } - size = 1; - static_bases = PyTuple_New(size); - - Py_INCREF(base); - PyTuple_SetItem(static_bases, 0, (PyObject *)base); - - va_start(ap, base); - - while (1) - { - static_base = va_arg(ap, PyTypeObject *); - - if (static_base == NULL) break; - - _PyTuple_Resize(&static_bases, ++size); - - Py_INCREF(static_base); - PyTuple_SetItem(static_bases, size - 1, (PyObject *)static_base); - - } - - va_end(ap); - - /** - * les renseignements suivants ne semblent pas nécessaires... - */ - - /* - type->tp_weaklistoffset = offsetof(PyGObject, weakreflist); - type->tp_dictoffset = offsetof(PyGObject, inst_dict); - */ - - pygobject_register_class(dict, NULL, gtype, type, static_bases); + pygobject_register_class(dict, NULL, gtype, type, NULL); if (PyErr_Occurred() == NULL) result = true; @@ -956,10 +1016,8 @@ bool register_interface_for_pygobject(PyObject *dict, GType gtype, PyTypeObject /****************************************************************************** * * -* Paramètres : dict = dictionnaire où conserver une référence au type créé.* -* gtype = type dans sa version GLib. * +* Paramètres : gtype = type dans sa version GLib. * * type = type dans sa version Python. * -* base = type de base de l'objet. * * * * Description : Enregistre un type Python dérivant d'un type GLib dynamique. * * * @@ -969,7 +1027,7 @@ bool register_interface_for_pygobject(PyObject *dict, GType gtype, PyTypeObject * * ******************************************************************************/ -bool register_class_for_dynamic_pygobject(GType gtype, PyTypeObject *type, PyTypeObject *base) +bool register_class_for_dynamic_pygobject(GType gtype, PyTypeObject *type) { bool result; /* Bilan à retourner */ PyTypeObject *legacy_parent; /* Type parent d'origine */ @@ -1024,9 +1082,9 @@ bool register_class_for_dynamic_pygobject(GType gtype, PyTypeObject *type, PyTyp dict = PyModule_GetDict(module); - result = _register_class_for_pygobject(dict, gtype, type, &PyGObject_Type, base, NULL); + result = register_class_for_pygobject(dict, gtype, type); - Py_TYPE(type) = legacy_parent; + Py_SET_TYPE(type, legacy_parent); /** * Comme la mise en place dynamique de nouveau GType court-circuite les @@ -1189,6 +1247,9 @@ int convert_to_gobject(PyObject *arg, void *dst) } +#ifdef INCLUDE_GTK_SUPPORT + + /****************************************************************************** * * * Paramètres : arg = argument quelconque à tenter de convertir. * @@ -1299,6 +1360,9 @@ int convert_to_gtk_container(PyObject *arg, void *dst) } +#endif + + /****************************************************************************** * * * Paramètres : color = couleur dans sa définition native à copier. * @@ -1440,15 +1504,15 @@ int convert_to_gdk_rgba(PyObject *arg, void *dst) * * * Description : Officialise un groupe de constantes avec sémentique. * * * -* Retour : true en cas de succès de l'opération, false sinon. * +* Retour : Groupe de constantes mis en place ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ -bool _attach_constants_group(const char *owner, PyObject *dict, bool flags, const char *name, PyObject *values, const char *doc) +PyObject *_attach_constants_group(const char *owner, PyObject *dict, bool flags, const char *name, PyObject *values, const char *doc) { - bool result; /* Bilan à retourner */ + PyObject *result; /* Instance à retourner */ PyObject *enum_mod; /* Module Python enum */ PyObject *class; /* Classe "Enum*" */ PyObject *str_obj; /* Conversion en Python */ @@ -1462,7 +1526,7 @@ bool _attach_constants_group(const char *owner, PyObject *dict, bool flags, cons PyObject *features; /* Module à recompléter */ PyObject *features_dict; /* Dictionnaire à compléter */ - result = false; + result = NULL; /* Recherche de la classe Python */ @@ -1539,7 +1603,8 @@ bool _attach_constants_group(const char *owner, PyObject *dict, bool flags, cons ret = PyDict_SetItemString(features_dict, name, new); if (ret != 0) goto register_1_error; - result = true; + result = new; + Py_INCREF(result); /* Sortie propre */ @@ -1570,6 +1635,99 @@ bool _attach_constants_group(const char *owner, PyObject *dict, bool flags, cons } +/****************************************************************************** +* * +* Paramètres : owner = désignation du propriétaire du dictionnaire visé. * +* dict = dictionnaire dont le contenu est à compléter. * +* flags = indique le type d'énumération ciblée. * +* name = désignation humaine du groupe à constituer. * +* values = noms et valeurs associées. * +* doc = documentation à associer au groupe. * +* gtype = énumération GLib à lier. * +* * +* Description : Officialise un groupe de constantes avec lien GLib. * +* * +* Retour : Groupe de constantes mis en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *_attach_constants_group_with_pyg_enum(const char *owner, PyObject *dict, bool flags, const char *name, PyObject *values, const char *doc, GType gtype) +{ + PyObject *result; /* Instance à retourner */ + PyObject *values_set; /* Zone pour nouvelles valeurs */ + int ret; /* Bilan d'une insertion */ + PyObject *new; /* Nouvelle instance en place */ + PyObject *values_src; /* Source de nouvelles valeurs */ + PyObject *values_dest; /* Destination des valeurs */ + + static GQuark pygenum_class_key = 0; /* Clef d'accès au marquage */ + + result = NULL; + + /** + * Le seul intérêt d'un tel enregistrement en bonne et due forme est de + * permettre une impression, via str() ou repr(), de l'énumération + * transcrite en GLib via g_enum_register_static() et potentiellement + * convertie de façon brusque par _pygi_argument_to_object(), lors d'une + * émission de signal par exemple. + * + * La satisfaction de la fonction pyg_enum_from_gtype() est ainsi recherchée. + * Tous les éléments sont normalement mis en place à partir de la fonction + * pyg_enum_add(). + */ + + /* Préparation du réceptacle */ + + values_set = PyDict_New(); + + ret = PyDict_SetItemString(values, "__enum_values__", values_set); + + Py_DECREF(values_set); + + if (ret != 0) goto exit; + + /* Création */ + + new = _attach_constants_group(owner, dict, flags, name, values, doc); + if (new == NULL) goto exit; + + /* Actualisation des valeurs */ + + values_src = PyDict_GetItemString(((PyTypeObject *)new)->tp_dict, "_value2member_map_"); + if (values_src == NULL) goto exit_without_src; + + values_dest = PyDict_GetItemString(((PyTypeObject *)new)->tp_dict, "__enum_values__"); + if (values_dest == NULL) goto exit_without_dest; + + assert(values_dest == values_set); + + ret = PyDict_Merge(values_dest, values_src, true); + + if (ret == 0) + { + result = new; + Py_INCREF(result); + + if (pygenum_class_key == 0) + pygenum_class_key = g_quark_from_static_string("PyGEnum::class"); + + g_type_set_qdata(gtype, pygenum_class_key, result); + + } + + exit_without_dest: + exit_without_src: + + Py_DECREF(new); + + exit: + + return result; + +} + /****************************************************************************** * * |