Commit 204f5a18 authored by Simon Feltman's avatar Simon Feltman
Browse files

marshal refactoring: Move GValue marshaling from pytype into pygi-value

Move marshaling of GValues to and from PyObjects into pygi-value.c. Make
PyGTypeMarshal struct and related functions accessible via pygtype.h.

https://bugzilla.gnome.org/show_bug.cgi?id=709700
parent b8120d84
......@@ -38,6 +38,8 @@
#include "pygtype.h"
#include "pygoptiongroup.h"
#include "pygi-value.h"
static GHashTable *log_handlers = NULL;
static gboolean log_handlers_disabled = FALSE;
......
......@@ -24,6 +24,7 @@
#include "pygi-struct-marshal.h"
#include "pygi-private.h"
#include "pygi-value.h"
/*
* _is_union_member - check to see if the py_arg is actually a member of the
......
......@@ -17,7 +17,10 @@
*/
#include "pygi-value.h"
#include "pyglib-python-compat.h"
#include "pygobject-private.h"
#include "pygtype.h"
#include "pygparamspec.h"
GIArgument
_pygi_argument_from_g_value(const GValue *value,
......@@ -142,3 +145,718 @@ _pygi_argument_from_g_value(const GValue *value,
return arg;
}
static int
pyg_value_array_from_pyobject(GValue *value,
PyObject *obj,
const GParamSpecValueArray *pspec)
{
int len;
GValueArray *value_array;
int i;
len = PySequence_Length(obj);
if (len == -1) {
PyErr_Clear();
return -1;
}
if (pspec && pspec->fixed_n_elements > 0 && len != pspec->fixed_n_elements)
return -1;
value_array = g_value_array_new(len);
for (i = 0; i < len; ++i) {
PyObject *item = PySequence_GetItem(obj, i);
GType type;
GValue item_value = { 0, };
int status;
if (! item) {
PyErr_Clear();
g_value_array_free(value_array);
return -1;
}
if (pspec && pspec->element_spec)
type = G_PARAM_SPEC_VALUE_TYPE(pspec->element_spec);
else if (item == Py_None)
type = G_TYPE_POINTER; /* store None as NULL */
else {
type = pyg_type_from_object((PyObject*)Py_TYPE(item));
if (! type) {
PyErr_Clear();
g_value_array_free(value_array);
Py_DECREF(item);
return -1;
}
}
g_value_init(&item_value, type);
status = (pspec && pspec->element_spec)
? pyg_param_gvalue_from_pyobject(&item_value, item, pspec->element_spec)
: pyg_value_from_pyobject(&item_value, item);
Py_DECREF(item);
if (status == -1) {
g_value_array_free(value_array);
g_value_unset(&item_value);
return -1;
}
g_value_array_append(value_array, &item_value);
g_value_unset(&item_value);
}
g_value_take_boxed(value, value_array);
return 0;
}
static int
pyg_array_from_pyobject(GValue *value,
PyObject *obj)
{
int len;
GArray *array;
int i;
len = PySequence_Length(obj);
if (len == -1) {
PyErr_Clear();
return -1;
}
array = g_array_new(FALSE, TRUE, sizeof(GValue));
for (i = 0; i < len; ++i) {
PyObject *item = PySequence_GetItem(obj, i);
GType type;
GValue item_value = { 0, };
int status;
if (! item) {
PyErr_Clear();
g_array_free(array, FALSE);
return -1;
}
if (item == Py_None)
type = G_TYPE_POINTER; /* store None as NULL */
else {
type = pyg_type_from_object((PyObject*)Py_TYPE(item));
if (! type) {
PyErr_Clear();
g_array_free(array, FALSE);
Py_DECREF(item);
return -1;
}
}
g_value_init(&item_value, type);
status = pyg_value_from_pyobject(&item_value, item);
Py_DECREF(item);
if (status == -1) {
g_array_free(array, FALSE);
g_value_unset(&item_value);
return -1;
}
g_array_append_val(array, item_value);
}
g_value_take_boxed(value, array);
return 0;
}
/**
* pyg_value_from_pyobject_with_error:
* @value: the GValue object to store the converted value in.
* @obj: the Python object to convert.
*
* This function converts a Python object and stores the result in a
* GValue. The GValue must be initialised in advance with
* g_value_init(). If the Python object can't be converted to the
* type of the GValue, then an error is returned.
*
* Returns: 0 on success, -1 on error.
*/
int
pyg_value_from_pyobject_with_error(GValue *value, PyObject *obj)
{
PyObject *tmp;
GType value_type = G_VALUE_TYPE(value);
switch (G_TYPE_FUNDAMENTAL(value_type)) {
case G_TYPE_INTERFACE:
/* we only handle interface types that have a GObject prereq */
if (g_type_is_a(value_type, G_TYPE_OBJECT)) {
if (obj == Py_None)
g_value_set_object(value, NULL);
else {
if (!PyObject_TypeCheck(obj, &PyGObject_Type)) {
PyErr_SetString(PyExc_TypeError, "GObject is required");
return -1;
}
if (!G_TYPE_CHECK_INSTANCE_TYPE(pygobject_get(obj),
value_type)) {
PyErr_SetString(PyExc_TypeError, "Invalid GObject type for assignment");
return -1;
}
g_value_set_object(value, pygobject_get(obj));
}
} else {
PyErr_SetString(PyExc_TypeError, "Unsupported conversion");
return -1;
}
break;
case G_TYPE_CHAR:
if (PYGLIB_PyLong_Check(obj)) {
glong val;
val = PYGLIB_PyLong_AsLong(obj);
if (val >= -128 && val <= 127)
g_value_set_schar(value, (gchar) val);
else
return -1;
}
#if PY_VERSION_HEX < 0x03000000
else if (PyString_Check(obj)) {
g_value_set_schar(value, PyString_AsString(obj)[0]);
}
#endif
else if (PyUnicode_Check(obj)) {
tmp = PyUnicode_AsUTF8String(obj);
g_value_set_schar(value, PYGLIB_PyBytes_AsString(tmp)[0]);
Py_DECREF(tmp);
} else {
PyErr_SetString(PyExc_TypeError, "Cannot convert to TYPE_CHAR");
return -1;
}
break;
case G_TYPE_UCHAR:
if (PYGLIB_PyLong_Check(obj)) {
glong val;
val = PYGLIB_PyLong_AsLong(obj);
if (val >= 0 && val <= 255)
g_value_set_uchar(value, (guchar) val);
else
return -1;
#if PY_VERSION_HEX < 0x03000000
} else if (PyString_Check(obj)) {
g_value_set_uchar(value, PyString_AsString(obj)[0]);
#endif
} else if (PyUnicode_Check(obj)) {
tmp = PyUnicode_AsUTF8String(obj);
g_value_set_uchar(value, PYGLIB_PyBytes_AsString(tmp)[0]);
Py_DECREF(tmp);
} else {
PyErr_Clear();
return -1;
}
break;
case G_TYPE_BOOLEAN:
g_value_set_boolean(value, PyObject_IsTrue(obj));
break;
case G_TYPE_INT:
g_value_set_int(value, PYGLIB_PyLong_AsLong(obj));
break;
case G_TYPE_UINT:
{
if (PYGLIB_PyLong_Check(obj)) {
guint val;
/* check that number is not negative */
if (PyLong_AsLongLong(obj) < 0)
return -1;
val = PyLong_AsUnsignedLong(obj);
if (val <= G_MAXUINT)
g_value_set_uint(value, val);
else
return -1;
} else {
g_value_set_uint(value, PyLong_AsUnsignedLong(obj));
}
}
break;
case G_TYPE_LONG:
g_value_set_long(value, PYGLIB_PyLong_AsLong(obj));
break;
case G_TYPE_ULONG:
#if PY_VERSION_HEX < 0x03000000
if (PyInt_Check(obj)) {
long val;
val = PYGLIB_PyLong_AsLong(obj);
if (val < 0) {
PyErr_SetString(PyExc_OverflowError, "negative value not allowed for uint64 property");
return -1;
}
g_value_set_ulong(value, (gulong)val);
} else
#endif
if (PyLong_Check(obj))
g_value_set_ulong(value, PyLong_AsUnsignedLong(obj));
else
return -1;
break;
case G_TYPE_INT64:
g_value_set_int64(value, PyLong_AsLongLong(obj));
break;
case G_TYPE_UINT64:
#if PY_VERSION_HEX < 0x03000000
if (PyInt_Check(obj)) {
long v = PyInt_AsLong(obj);
if (v < 0) {
PyErr_SetString(PyExc_OverflowError, "negative value not allowed for uint64 property");
return -1;
}
g_value_set_uint64(value, v);
} else
#endif
if (PyLong_Check(obj))
g_value_set_uint64(value, PyLong_AsUnsignedLongLong(obj));
else
return -1;
break;
case G_TYPE_ENUM:
{
gint val = 0;
if (pyg_enum_get_value(G_VALUE_TYPE(value), obj, &val) < 0) {
return -1;
}
g_value_set_enum(value, val);
}
break;
case G_TYPE_FLAGS:
{
guint val = 0;
if (pyg_flags_get_value(G_VALUE_TYPE(value), obj, &val) < 0) {
return -1;
}
g_value_set_flags(value, val);
}
break;
case G_TYPE_FLOAT:
g_value_set_float(value, PyFloat_AsDouble(obj));
break;
case G_TYPE_DOUBLE:
g_value_set_double(value, PyFloat_AsDouble(obj));
break;
case G_TYPE_STRING:
if (obj == Py_None) {
g_value_set_string(value, NULL);
} else {
PyObject* tmp_str = PyObject_Str(obj);
if (tmp_str == NULL) {
PyErr_Clear();
if (PyUnicode_Check(obj)) {
tmp = PyUnicode_AsUTF8String(obj);
g_value_set_string(value, PYGLIB_PyBytes_AsString(tmp));
Py_DECREF(tmp);
} else {
PyErr_SetString(PyExc_TypeError, "Expected string");
return -1;
}
} else {
#if PY_VERSION_HEX < 0x03000000
g_value_set_string(value, PyString_AsString(tmp_str));
#else
tmp = PyUnicode_AsUTF8String(tmp_str);
g_value_set_string(value, PyBytes_AsString(tmp));
Py_DECREF(tmp);
#endif
}
Py_XDECREF(tmp_str);
}
break;
case G_TYPE_POINTER:
if (obj == Py_None)
g_value_set_pointer(value, NULL);
else if (PyObject_TypeCheck(obj, &PyGPointer_Type) &&
G_VALUE_HOLDS(value, ((PyGPointer *)obj)->gtype))
g_value_set_pointer(value, pyg_pointer_get(obj, gpointer));
else if (PYGLIB_CPointer_Check(obj))
g_value_set_pointer(value, PYGLIB_CPointer_GetPointer(obj, NULL));
else if (G_VALUE_HOLDS_GTYPE (value))
g_value_set_gtype (value, pyg_type_from_object (obj));
else {
PyErr_SetString(PyExc_TypeError, "Expected pointer");
return -1;
}
break;
case G_TYPE_BOXED: {
PyGTypeMarshal *bm;
if (obj == Py_None)
g_value_set_boxed(value, NULL);
else if (G_VALUE_HOLDS(value, PY_TYPE_OBJECT))
g_value_set_boxed(value, obj);
else if (PyObject_TypeCheck(obj, &PyGBoxed_Type) &&
G_VALUE_HOLDS(value, ((PyGBoxed *)obj)->gtype))
g_value_set_boxed(value, pyg_boxed_get(obj, gpointer));
else if (G_VALUE_HOLDS(value, G_TYPE_VALUE)) {
GType type;
GValue *n_value;
type = pyg_type_from_object((PyObject*)Py_TYPE(obj));
if (G_UNLIKELY (! type)) {
return -1;
}
n_value = g_new0 (GValue, 1);
g_value_init (n_value, type);
g_value_take_boxed (value, n_value);
return pyg_value_from_pyobject_with_error (n_value, obj);
}
else if (PySequence_Check(obj) &&
G_VALUE_HOLDS(value, G_TYPE_VALUE_ARRAY))
return pyg_value_array_from_pyobject(value, obj, NULL);
else if (PySequence_Check(obj) &&
G_VALUE_HOLDS(value, G_TYPE_ARRAY))
return pyg_array_from_pyobject(value, obj);
else if (PYGLIB_PyUnicode_Check(obj) &&
G_VALUE_HOLDS(value, G_TYPE_GSTRING)) {
GString *string;
char *buffer;
Py_ssize_t len;
if (PYGLIB_PyUnicode_AsStringAndSize(obj, &buffer, &len))
return -1;
string = g_string_new_len(buffer, len);
g_value_set_boxed(value, string);
g_string_free (string, TRUE);
break;
}
else if ((bm = pyg_type_lookup(G_VALUE_TYPE(value))) != NULL)
return bm->tovalue(value, obj);
else if (PYGLIB_CPointer_Check(obj))
g_value_set_boxed(value, PYGLIB_CPointer_GetPointer(obj, NULL));
else {
PyErr_SetString(PyExc_TypeError, "Expected Boxed");
return -1;
}
break;
}
case G_TYPE_PARAM:
/* we need to support both the wrapped _gobject.GParamSpec and the GI
* GObject.ParamSpec */
if (G_IS_PARAM_SPEC (pygobject_get (obj)))
g_value_set_param(value, G_PARAM_SPEC (pygobject_get (obj)));
else if (PyGParamSpec_Check(obj))
g_value_set_param(value, PYGLIB_CPointer_GetPointer(obj, NULL));
else {
PyErr_SetString(PyExc_TypeError, "Expected ParamSpec");
return -1;
}
break;
case G_TYPE_OBJECT:
if (obj == Py_None) {
g_value_set_object(value, NULL);
} else if (PyObject_TypeCheck(obj, &PyGObject_Type) &&
G_TYPE_CHECK_INSTANCE_TYPE(pygobject_get(obj),
G_VALUE_TYPE(value))) {
g_value_set_object(value, pygobject_get(obj));
} else {
PyErr_SetString(PyExc_TypeError, "Expected GObject");
return -1;
}
break;
case G_TYPE_VARIANT:
{
if (obj == Py_None)
g_value_set_variant(value, NULL);
else if (pyg_type_from_object_strict(obj, FALSE) == G_TYPE_VARIANT)
g_value_set_variant(value, pyg_boxed_get(obj, GVariant));
else {
PyErr_SetString(PyExc_TypeError, "Expected Variant");
return -1;
}
break;
}
default:
{
PyGTypeMarshal *bm;
if ((bm = pyg_type_lookup(G_VALUE_TYPE(value))) != NULL) {
return bm->tovalue(value, obj);
} else {
PyErr_SetString(PyExc_TypeError, "Unknown value type");
return -1;
}
break;
}
}
/* If an error occurred, unset the GValue but don't clear the Python error. */
if (PyErr_Occurred()) {
g_value_unset(value);
return -1;
}
return 0;
}
/**
* pyg_value_from_pyobject:
* @value: the GValue object to store the converted value in.
* @obj: the Python object to convert.
*
* Same basic function as pyg_value_from_pyobject_with_error but clears
* any Python errors before returning.
*
* Returns: 0 on success, -1 on error.
*/
int
pyg_value_from_pyobject(GValue *value, PyObject *obj)
{
int res = pyg_value_from_pyobject_with_error (value, obj);
if (PyErr_Occurred()) {
PyErr_Clear();
return -1;
}
return res;
}
/**
* pyg_value_as_pyobject:
* @value: the GValue object.
* @copy_boxed: true if boxed values should be copied.
*
* This function creates/returns a Python wrapper object that
* represents the GValue passed as an argument.
*
* Returns: a PyObject representing the value.
*/
PyObject *
pyg_value_as_pyobject(const GValue *value, gboolean copy_boxed)
{
gchar buf[128];
switch (G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value))) {
case G_TYPE_INTERFACE:
if (g_type_is_a(G_VALUE_TYPE(value), G_TYPE_OBJECT))
return pygobject_new(g_value_get_object(value));
else
break;
case G_TYPE_CHAR: {
gint8 val = g_value_get_schar(value);
return PYGLIB_PyUnicode_FromStringAndSize((char *)&val, 1);
}
case G_TYPE_UCHAR: {
guint8 val = g_value_get_uchar(value);
return PYGLIB_PyBytes_FromStringAndSize((char *)&val, 1);
}
case G_TYPE_BOOLEAN: {
return PyBool_FromLong(g_value_get_boolean(value));
}
case G_TYPE_INT:
return PYGLIB_PyLong_FromLong(g_value_get_int(value));
case G_TYPE_UINT:
{
/* in Python, the Int object is backed by a long. If a
long can hold the whole value of an unsigned int, use
an Int. Otherwise, use a Long object to avoid overflow.
This matches the ULongArg behavior in codegen/argtypes.h */
#if (G_MAXUINT <= G_MAXLONG)
return PYGLIB_PyLong_FromLong((glong) g_value_get_uint(value));
#else
return PyLong_FromUnsignedLong((gulong) g_value_get_uint(value));
#endif
}
case G_TYPE_LONG:
return PYGLIB_PyLong_FromLong(g_value_get_long(value));
case G_TYPE_ULONG:
{
gulong val = g_value_get_ulong(value);
if (val <= G_MAXLONG)
return PYGLIB_PyLong_FromLong((glong) val);
else
return PyLong_FromUnsignedLong(val);
}
case G_TYPE_INT64:
{
gint64 val = g_value_get_int64(value);
if (G_MINLONG <= val && val <= G_MAXLONG)
return PYGLIB_PyLong_FromLong((glong) val);
else
return PyLong_FromLongLong(val);
}
case G_TYPE_UINT64:
{
guint64 val = g_value_get_uint64(value);
if (val <= G_MAXLONG)
return PYGLIB_PyLong_FromLong((glong) val);
else
return PyLong_FromUnsignedLongLong(val);
}
case G_TYPE_ENUM:
return pyg_enum_from_gtype(G_VALUE_TYPE(value), g_value_get_enum(value));
case G_TYPE_FLAGS:
return pyg_flags_from_gtype(G_VALUE_TYPE(value), g_value_get_flags(value));
case G_TYPE_FLOAT:
return PyFloat_FromDouble(g_value_get_float(value));
case G_TYPE_DOUBLE:
return PyFloat_FromDouble(g_value_get_double(value));
case G_TYPE_STRING:
{
const gchar *str = g_value_get_string(value);
if (str)
return PYGLIB_PyUnicode_FromString(str);
Py_INCREF(Py_None);
return Py_None;
}
case G_TYPE_POINTER:
if (G_VALUE_HOLDS_GTYPE (value))
return pyg_type_wrapper_new (g_value_get_gtype (value));
else
return pyg_pointer_new(G_VALUE_TYPE(value),
g_value_get_pointer(value));
case G_TYPE_BOXED: {
PyGTypeMarshal *bm;
if (G_VALUE_HOLDS(value, PY_TYPE_OBJECT)) {
PyObject *ret = (PyObject *)g_value_dup_boxed(value);
if (ret == NULL) {
Py_INCREF(Py_None);
return Py_None;
}
return ret;
} else if (G_VALUE_HOLDS(value, G_TYPE_VALUE)) {
GValue *n_value = g_value_get_boxed (value);
return pyg_value_as_pyobject(n_value, copy_boxed);
} else if (G_VALUE_HOLDS(value, G_TYPE_VALUE_ARRAY)) {
GValueArray *array = (GValueArray *) g_value_get_boxed(value);
PyObject *ret = PyList_New(array->n_values);
int i;
for (i = 0; i < array->n_values; ++i)
PyList_SET_ITEM(ret, i, pyg_value_as_pyobject