summaryrefslogtreecommitdiff
path: root/plugins/pychrysalide/helpers.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/pychrysalide/helpers.c')
-rw-r--r--plugins/pychrysalide/helpers.c428
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;
+
+}
+
/******************************************************************************
* *