diff options
Diffstat (limited to 'source/blender/python')
-rw-r--r-- | source/blender/python/generic/IDProp.c | 327 | ||||
-rw-r--r-- | source/blender/python/generic/IDProp.h | 5 | ||||
-rw-r--r-- | source/blender/python/generic/mathutils_Vector.c | 2 | ||||
-rw-r--r-- | source/blender/python/generic/py_capi_utils.c | 70 | ||||
-rw-r--r-- | source/blender/python/generic/py_capi_utils.h | 2 | ||||
-rw-r--r-- | source/blender/python/intern/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_interface.c | 11 | ||||
-rwxr-xr-x | source/blender/python/intern/bpy_intern_string.c | 57 | ||||
-rwxr-xr-x | source/blender/python/intern/bpy_intern_string.h | 37 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_props.c | 6 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_rna.c | 55 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_rna_array.c | 159 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_util.c | 60 |
13 files changed, 583 insertions, 210 deletions
diff --git a/source/blender/python/generic/IDProp.c b/source/blender/python/generic/IDProp.c index a807624187a..2543d34f58c 100644 --- a/source/blender/python/generic/IDProp.c +++ b/source/blender/python/generic/IDProp.c @@ -45,26 +45,31 @@ #include "py_capi_utils.h" #endif -extern PyTypeObject IDArray_Type; -extern PyTypeObject IDGroup_Iter_Type; +extern PyTypeObject BPy_IDArray_Type; +extern PyTypeObject BPy_IDGroup_Iter_Type; +extern PyTypeObject BPy_IDGroup_Type; /*********************** ID Property Main Wrapper Stuff ***************/ -static PyObject *IDGroup_repr( BPy_IDProperty *self ) +/* use for both array and group */ +static long BPy_IDGroup_hash(BPy_IDProperty *self) { - return PyUnicode_FromFormat( "<bpy ID property from \"%s\">", self->id->name); + return _Py_HashPointer(self->prop); } -extern PyTypeObject IDGroup_Type; +static PyObject *BPy_IDGroup_repr(BPy_IDProperty *self) +{ + return PyUnicode_FromFormat( "<bpy id property from \"%s\">", self->id->name); +} PyObject *BPy_IDGroup_WrapData( ID *id, IDProperty *prop ) { switch ( prop->type ) { case IDP_STRING: #ifdef USE_STRING_COERCE - return PyC_UnicodeFromByte(prop->data.pointer); + return PyC_UnicodeFromByte(IDP_Array(prop)); #else - return PyUnicode_FromString(prop->data.pointer); + return PyUnicode_FromString(IDP_Array(prop)); #endif case IDP_INT: return PyLong_FromLong( (long)prop->data.val ); @@ -75,14 +80,14 @@ PyObject *BPy_IDGroup_WrapData( ID *id, IDProperty *prop ) case IDP_GROUP: /*blegh*/ { - BPy_IDProperty *group = PyObject_New(BPy_IDProperty, &IDGroup_Type); + BPy_IDProperty *group = PyObject_New(BPy_IDProperty, &BPy_IDGroup_Type); group->id = id; group->prop = prop; return (PyObject*) group; } case IDP_ARRAY: { - BPy_IDProperty *array = PyObject_New(BPy_IDProperty, &IDArray_Type); + BPy_IDProperty *array = PyObject_New(BPy_IDProperty, &BPy_IDArray_Type); array->id = id; array->prop = prop; return (PyObject*) array; @@ -135,13 +140,13 @@ static int BPy_IDGroup_SetData(BPy_IDProperty *self, IDProperty *prop, PyObject st = _PyUnicode_AsString(value); IDP_ResizeArray(prop, alloc_len); - memcpy(prop->data.pointer, st, alloc_len); + memcpy(IDP_Array(prop), st, alloc_len); Py_XDECREF(value_coerce); } #else st = _PyUnicode_AsString(value); IDP_ResizeArray(prop, strlen(st)+1); - strcpy(prop->data.pointer, st); + strcpy(IDP_Array(prop), st); #endif return 0; @@ -344,7 +349,7 @@ const char *BPy_IDProperty_Map_ValidateAndCreate(const char *name, IDProperty *g prop = IDP_New(IDP_ARRAY, val, name); for (i=0; i<val.array.len; i++) { item = PySequence_GetItem(ob, i); - ((double*)prop->data.pointer)[i] = (float)PyFloat_AsDouble(item); + ((double*)IDP_Array(prop))[i] = (float)PyFloat_AsDouble(item); Py_DECREF(item); } break; @@ -352,7 +357,7 @@ const char *BPy_IDProperty_Map_ValidateAndCreate(const char *name, IDProperty *g prop = IDP_New(IDP_ARRAY, val, name); for (i=0; i<val.array.len; i++) { item = PySequence_GetItem(ob, i); - ((int*)prop->data.pointer)[i] = (int)PyLong_AsSsize_t(item); + ((int*)IDP_Array(prop))[i] = (int)PyLong_AsSsize_t(item); Py_DECREF(item); } break; @@ -465,9 +470,9 @@ static int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject return BPy_Wrap_SetMapItem(self->prop, key, val); } -static PyObject *BPy_IDGroup_SpawnIterator(BPy_IDProperty *self) +static PyObject *BPy_IDGroup_iter(BPy_IDProperty *self) { - BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &IDGroup_Iter_Type); + BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type); iter->group = self; iter->mode = IDPROP_ITER_KEYS; iter->cur = self->prop->data.group.first; @@ -480,9 +485,9 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) switch (prop->type) { case IDP_STRING: #ifdef USE_STRING_COERCE - return PyC_UnicodeFromByte(prop->data.pointer); + return PyC_UnicodeFromByte(IDP_Array(prop)); #else - return PyUnicode_FromString(prop->data.pointer); + return PyUnicode_FromString(IDP_Array(prop)); #endif break; case IDP_FLOAT: @@ -504,20 +509,37 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) return NULL; } - for (i=0; i<prop->len; i++) { - if (prop->subtype == IDP_FLOAT) { - PyList_SET_ITEM(seq, i, - PyFloat_FromDouble(((float*)prop->data.pointer)[i])); + switch(prop->subtype) { + case IDP_FLOAT: + { + float *array= (float*)IDP_Array(prop); + for (i=0; i<prop->len; i++) { + PyList_SET_ITEM(seq, i, PyFloat_FromDouble(array[i])); + } + break; } - else if (prop->subtype == IDP_DOUBLE) { - PyList_SET_ITEM(seq, i, - PyFloat_FromDouble(((double*)prop->data.pointer)[i])); + case IDP_DOUBLE: + { + double *array= (double*)IDP_Array(prop); + for (i=0; i<prop->len; i++) { + PyList_SET_ITEM(seq, i, PyFloat_FromDouble(array[i])); + } + break; } - else { - PyList_SET_ITEM(seq, i, - PyLong_FromLong(((int*)prop->data.pointer)[i])); + case IDP_INT: + { + int *array= (int*)IDP_Array(prop); + for (i=0; i<prop->len; i++) { + PyList_SET_ITEM(seq, i, PyLong_FromLong(array[i])); + } + break; } + default: + PyErr_SetString(PyExc_RuntimeError, "invalid/corrupt array type!"); + Py_DECREF(seq); + return NULL; } + return seq; } case IDP_IDPARRAY: @@ -553,6 +575,7 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) return NULL; PyDict_SetItemString(dict, loop->name, wrap); + Py_DECREF(wrap); } return dict; } @@ -595,7 +618,7 @@ static PyObject *BPy_IDGroup_Pop(BPy_IDProperty *self, PyObject *value) static PyObject *BPy_IDGroup_IterItems(BPy_IDProperty *self) { - BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &IDGroup_Iter_Type); + BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type); iter->group = self; iter->mode = IDPROP_ITER_ITEMS; iter->cur = self->prop->data.group.first; @@ -731,7 +754,7 @@ static PyObject *BPy_IDGroup_Update(BPy_IDProperty *self, PyObject *value) Py_RETURN_NONE; } -static PyObject *BPy_IDGroup_ConvertToPy(BPy_IDProperty *self) +static PyObject *BPy_IDGroup_to_dict(BPy_IDProperty *self) { return BPy_IDGroup_MapDataToPy(self->prop); } @@ -773,7 +796,7 @@ static struct PyMethodDef BPy_IDGroup_methods[] = { "updates the values in the group with the values of another or a dict"}, {"get", (PyCFunction)BPy_IDGroup_Get, METH_VARARGS, "idprop.get(k[,d]) -> idprop[k] if k in idprop, else d. d defaults to None"}, - {"convert_to_pyobject", (PyCFunction)BPy_IDGroup_ConvertToPy, METH_NOARGS, + {"to_dict", (PyCFunction)BPy_IDGroup_to_dict, METH_NOARGS, "return a purely python version of the group"}, {NULL, NULL, 0, NULL} }; @@ -792,16 +815,16 @@ static PySequenceMethods BPy_IDGroup_Seq = { }; static PyMappingMethods BPy_IDGroup_Mapping = { - (lenfunc)BPy_IDGroup_Map_Len, /*inquiry mp_length */ - (binaryfunc)BPy_IDGroup_Map_GetItem, /*binaryfunc mp_subscript */ + (lenfunc)BPy_IDGroup_Map_Len, /*inquiry mp_length */ + (binaryfunc)BPy_IDGroup_Map_GetItem,/*binaryfunc mp_subscript */ (objobjargproc)BPy_IDGroup_Map_SetItem, /*objobjargproc mp_ass_subscript */ }; -PyTypeObject IDGroup_Type = { +PyTypeObject BPy_IDGroup_Type = { PyVarObject_HEAD_INIT(NULL, 0) /* For printing, in format "<module>.<name>" */ - "Blender IDProperty", /* char *tp_name; */ - sizeof( BPy_IDProperty ), /* int tp_basicsize; */ + "Blender IDProperty", /* char *tp_name; */ + sizeof(BPy_IDProperty), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ @@ -811,7 +834,7 @@ PyTypeObject IDGroup_Type = { NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ NULL, /* cmpfunc tp_compare; */ - ( reprfunc ) IDGroup_repr, /* reprfunc tp_repr; */ + (reprfunc)BPy_IDGroup_repr, /* reprfunc tp_repr; */ /* Method suites for standard classes */ @@ -821,7 +844,7 @@ PyTypeObject IDGroup_Type = { /* More standard operations (here for binary compatibility) */ - NULL, /* hashfunc tp_hash; */ + (hashfunc)BPy_IDGroup_hash, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ @@ -850,7 +873,7 @@ PyTypeObject IDGroup_Type = { /*** Added in release 2.2 ***/ /* Iterators */ - (getiterfunc)BPy_IDGroup_SpawnIterator, /* getiterfunc tp_iter; */ + (getiterfunc)BPy_IDGroup_iter, /* getiterfunc tp_iter; */ NULL, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ BPy_IDGroup_methods, /* struct PyMethodDef *tp_methods; */ @@ -861,7 +884,7 @@ PyTypeObject IDGroup_Type = { /*********** Main external wrapping function *******/ PyObject *BPy_Wrap_IDProperty(ID *id, IDProperty *prop, IDProperty *parent) { - BPy_IDProperty *wrap = PyObject_New(BPy_IDProperty, &IDGroup_Type); + BPy_IDProperty *wrap = PyObject_New(BPy_IDProperty, &BPy_IDGroup_Type); wrap->prop = prop; wrap->parent = parent; wrap->id = id; @@ -872,36 +895,58 @@ PyObject *BPy_Wrap_IDProperty(ID *id, IDProperty *prop, IDProperty *parent) /********Array Wrapper********/ -static PyObject *IDArray_repr(BPy_IDArray *self) +static PyTypeObject *idp_array_py_type(BPy_IDArray *self, short *is_double) { - return PyUnicode_FromFormat("(ID Array [%d])", self->prop->len); -} + switch (self->prop->subtype) { + case IDP_FLOAT: + *is_double= 0; + return &PyFloat_Type; + case IDP_DOUBLE: + *is_double= 1; + return &PyFloat_Type; + case IDP_INT: + *is_double= 0; + return &PyLong_Type; + } + *is_double= 0; + return NULL; +} -static PyObject *BPy_IDArray_GetType(BPy_IDArray *self) +static PyObject *BPy_IDArray_repr(BPy_IDArray *self) { - return PyLong_FromSsize_t( self->prop->subtype ); + return PyUnicode_FromFormat("<bpy id property array [%d]>", self->prop->len); } -static PyObject *BPy_IDArray_GetLen(BPy_IDArray *self) +static PyObject *BPy_IDArray_GetType(BPy_IDArray *self) { - return PyLong_FromSsize_t( self->prop->len ); + switch(self->prop->subtype) { + case IDP_FLOAT: + return PyUnicode_FromString("f"); + case IDP_DOUBLE: + return PyUnicode_FromString("d"); + case IDP_INT: + return PyUnicode_FromString("i"); + } + + PyErr_SetString(PyExc_RuntimeError, "invalid/corrupt array type!"); + return NULL; } static PyGetSetDef BPy_IDArray_getseters[] = { - {(char *)"len", (getter)BPy_IDArray_GetLen, (setter)NULL, (char *)"The length of the array, can also be gotten with len(array).", NULL}, - {(char *)"type", (getter)BPy_IDArray_GetType, (setter)NULL, (char *)"The type of the data in the array, is an ant.", NULL}, + /* matches pythons array.typecode */ + {(char *)"typecode", (getter)BPy_IDArray_GetType, (setter)NULL, (char *)"The type of the data in the array, is an int.", NULL}, {NULL, NULL, NULL, NULL, NULL}, }; -static PyObject *BPy_IDArray_ConvertToPy(BPy_IDArray *self) +static PyObject *BPy_IDArray_to_list(BPy_IDArray *self) { return BPy_IDGroup_MapDataToPy(self->prop); } static PyMethodDef BPy_IDArray_methods[] = { - {"convert_to_pyobject", (PyCFunction)BPy_IDArray_ConvertToPy, METH_NOARGS, - "return a purely python version of the group"}, + {"to_list", (PyCFunction)BPy_IDArray_to_list, METH_NOARGS, + "return the array as a list"}, {NULL, NULL, 0, NULL} }; @@ -919,14 +964,11 @@ static PyObject *BPy_IDArray_GetItem(BPy_IDArray *self, int index) switch (self->prop->subtype) { case IDP_FLOAT: - return PyFloat_FromDouble( (double)(((float*)self->prop->data.pointer)[index])); - break; + return PyFloat_FromDouble(((float*)IDP_Array(self->prop))[index]); case IDP_DOUBLE: - return PyFloat_FromDouble( (((double*)self->prop->data.pointer)[index])); - break; + return PyFloat_FromDouble(((double*)IDP_Array(self->prop))[index]); case IDP_INT: - return PyLong_FromLong( (long)((int*)self->prop->data.pointer)[index] ); - break; + return PyLong_FromLong((long)((int*)IDP_Array(self->prop))[index]); } PyErr_SetString(PyExc_RuntimeError, "invalid/corrupt array type!"); @@ -951,7 +993,7 @@ static int BPy_IDArray_SetItem(BPy_IDArray *self, int index, PyObject *value) PyErr_SetString(PyExc_TypeError, "expected a float"); return -1; } - ((float*)self->prop->data.pointer)[index] = f; + ((float*)IDP_Array(self->prop))[index] = f; break; case IDP_DOUBLE: d= PyFloat_AsDouble(value); @@ -959,7 +1001,7 @@ static int BPy_IDArray_SetItem(BPy_IDArray *self, int index, PyObject *value) PyErr_SetString(PyExc_TypeError, "expected a float"); return -1; } - ((double*)self->prop->data.pointer)[index] = d; + ((double*)IDP_Array(self->prop))[index] = d; break; case IDP_INT: i= PyLong_AsSsize_t(value); @@ -968,7 +1010,7 @@ static int BPy_IDArray_SetItem(BPy_IDArray *self, int index, PyObject *value) return -1; } - ((int*)self->prop->data.pointer)[index] = i; + ((int*)IDP_Array(self->prop))[index] = i; break; } return 0; @@ -988,11 +1030,156 @@ static PySequenceMethods BPy_IDArray_Seq = { NULL, /* intargfunc sq_inplace_repeat */ }; -PyTypeObject IDArray_Type = { + + +/* sequence slice (get): idparr[a:b] */ +static PyObject *BPy_IDArray_slice(BPy_IDArray *self, int begin, int end) +{ + IDProperty *prop= self->prop; + PyObject *tuple; + int count; + + CLAMP(begin, 0, prop->len); + if (end<0) end= prop->len+end+1; + CLAMP(end, 0, prop->len); + begin= MIN2(begin, end); + + tuple= PyTuple_New(end - begin); + + switch (prop->subtype) { + case IDP_FLOAT: + { + float *array= (float*)IDP_Array(prop); + for(count = begin; count < end; count++) { + PyTuple_SET_ITEM(tuple, count - begin, PyFloat_FromDouble(array[count])); + } + break; + } + case IDP_DOUBLE: + { + double *array= (double*)IDP_Array(prop); + for(count = begin; count < end; count++) { + PyTuple_SET_ITEM(tuple, count - begin, PyFloat_FromDouble(array[count])); + } + break; + } + case IDP_INT: + { + int *array= (int*)IDP_Array(prop); + for(count = begin; count < end; count++) { + PyTuple_SET_ITEM(tuple, count - begin, PyLong_FromLong(array[count])); + } + break; + } + } + + return tuple; +} +/* sequence slice (set): idparr[a:b] = value */ +static int BPy_IDArray_ass_slice(BPy_IDArray *self, int begin, int end, PyObject *seq) +{ + IDProperty *prop= self->prop; + short is_double= 0; + const PyTypeObject *py_type= idp_array_py_type(self, &is_double); + const size_t elem_size= is_double ? sizeof(double) : sizeof(float); + size_t alloc_len; + size_t size; + void *vec; + + CLAMP(begin, 0, prop->len); + CLAMP(end, 0, prop->len); + begin = MIN2(begin, end); + + size = (end - begin); + alloc_len= size * elem_size; + + vec= MEM_mallocN(alloc_len, "array assignment"); /* NOTE: we count on int/float being the same size here */ + if(PyC_AsArray(vec, seq, size, py_type, is_double, "slice assignment: ") == -1) { + MEM_freeN(vec); + return -1; + } + + memcpy((void *)(((char *)IDP_Array(prop)) + (begin * elem_size)), vec, alloc_len); + + MEM_freeN(vec); + return 0; +} + +static PyObject *BPy_IDArray_subscript(BPy_IDArray* self, PyObject* item) +{ + if (PyIndex_Check(item)) { + Py_ssize_t i; + i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return NULL; + if (i < 0) + i += self->prop->len; + return BPy_IDArray_GetItem(self, i); + } + else if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + + if (PySlice_GetIndicesEx((void *)item, self->prop->len, &start, &stop, &step, &slicelength) < 0) + return NULL; + + if (slicelength <= 0) { + return PyTuple_New(0); + } + else if (step == 1) { + return BPy_IDArray_slice(self, start, stop); + } + else { + PyErr_SetString(PyExc_TypeError, "slice steps not supported with vectors"); + return NULL; + } + } + else { + PyErr_Format(PyExc_TypeError, "vector indices must be integers, not %.200s", Py_TYPE(item)->tp_name); + return NULL; + } +} + +static int BPy_IDArray_ass_subscript(BPy_IDArray* self, PyObject* item, PyObject* value) +{ + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return -1; + if (i < 0) + i += self->prop->len; + return BPy_IDArray_SetItem(self, i, value); + } + else if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + + if (PySlice_GetIndicesEx((void *)item, self->prop->len, &start, &stop, &step, &slicelength) < 0) + return -1; + + if (step == 1) + return BPy_IDArray_ass_slice(self, start, stop, value); + else { + PyErr_SetString(PyExc_TypeError, "slice steps not supported with vectors"); + return -1; + } + } + else { + PyErr_Format(PyExc_TypeError, "vector indices must be integers, not %.200s", Py_TYPE(item)->tp_name); + return -1; + } +} + +static PyMappingMethods BPy_IDArray_AsMapping = { + (lenfunc)BPy_IDArray_Len, + (binaryfunc)BPy_IDArray_subscript, + (objobjargproc)BPy_IDArray_ass_subscript +}; + + +PyTypeObject BPy_IDArray_Type = { PyVarObject_HEAD_INIT(NULL, 0) /* For printing, in format "<module>.<name>" */ "Blender IDArray", /* char *tp_name; */ - sizeof( BPy_IDArray ), /* int tp_basicsize; */ + sizeof(BPy_IDArray), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ @@ -1002,17 +1189,17 @@ PyTypeObject IDArray_Type = { NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ NULL, /* cmpfunc tp_compare; */ - ( reprfunc ) IDArray_repr, /* reprfunc tp_repr; */ + (reprfunc)BPy_IDArray_repr, /* reprfunc tp_repr; */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ - &BPy_IDArray_Seq, /* PySequenceMethods *tp_as_sequence; */ - NULL, /* PyMappingMethods *tp_as_mapping; */ + &BPy_IDArray_Seq, /* PySequenceMethods *tp_as_sequence; */ + &BPy_IDArray_AsMapping, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ - NULL, /* hashfunc tp_hash; */ + NULL, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ @@ -1106,7 +1293,7 @@ static PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self) } } -PyTypeObject IDGroup_Iter_Type = { +PyTypeObject BPy_IDGroup_Iter_Type = { PyVarObject_HEAD_INIT(NULL, 0) /* For printing, in format "<module>.<name>" */ "Blender IDGroup_Iter", /* char *tp_name; */ @@ -1165,7 +1352,7 @@ PyTypeObject IDGroup_Iter_Type = { void IDProp_Init_Types(void) { - PyType_Ready( &IDGroup_Type ); - PyType_Ready( &IDGroup_Iter_Type ); - PyType_Ready( &IDArray_Type ); + PyType_Ready(&BPy_IDGroup_Type); + PyType_Ready(&BPy_IDGroup_Iter_Type); + PyType_Ready(&BPy_IDArray_Type); } diff --git a/source/blender/python/generic/IDProp.h b/source/blender/python/generic/IDProp.h index 0ca8af81f7c..aca5c0195cc 100644 --- a/source/blender/python/generic/IDProp.h +++ b/source/blender/python/generic/IDProp.h @@ -37,14 +37,15 @@ struct BPy_IDGroup_Iter; typedef struct BPy_IDProperty { PyObject_VAR_HEAD struct ID *id; - struct IDProperty *prop, *parent; + struct IDProperty *prop; /* must be second member */ + struct IDProperty *parent; PyObject *data_wrap; } BPy_IDProperty; typedef struct BPy_IDArray { PyObject_VAR_HEAD struct ID *id; - struct IDProperty *prop; + struct IDProperty *prop; /* must be second member */ } BPy_IDArray; typedef struct BPy_IDGroup_Iter { diff --git a/source/blender/python/generic/mathutils_Vector.c b/source/blender/python/generic/mathutils_Vector.c index d0ba94474d4..e07b51c9e4b 100644 --- a/source/blender/python/generic/mathutils_Vector.c +++ b/source/blender/python/generic/mathutils_Vector.c @@ -1634,7 +1634,7 @@ static int Vector_setSwizzle(VectorObject *self, PyObject *value, void *closure) size_from= axis_from; } - else if((size_from=mathutils_array_parse(vec_assign, 2, 4, value, "mathutils.Vector.**** = swizzle assignment")) == -1) { + else if(PyErr_Clear(), (size_from=mathutils_array_parse(vec_assign, 2, 4, value, "mathutils.Vector.**** = swizzle assignment")) == -1) { return -1; } diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index ec774f44075..801e44bd008 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -38,6 +38,76 @@ #define PYC_INTERPRETER_ACTIVE (((PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current)) != NULL) +/* array utility function */ +int PyC_AsArray(void *array, PyObject *value, const int length, const PyTypeObject *type, const short is_double, const char *error_prefix) +{ + PyObject *value_fast; + int value_len; + int i; + + if(!(value_fast=PySequence_Fast(value, error_prefix))) { + return -1; + } + + value_len= PySequence_Fast_GET_SIZE(value_fast); + + if(value_len != length) { + Py_DECREF(value); + PyErr_Format(PyExc_TypeError, + "%.200s: invalid sequence length. expected %d, got %d", + error_prefix, length, value_len); + return -1; + } + + /* for each type */ + if(type == &PyFloat_Type) { + if(is_double) { + double *array_double= array; + for(i=0; i<length; i++) { + array_double[i]= PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value_fast, i)); + } + } + else { + float *array_float= array; + for(i=0; i<length; i++) { + array_float[i]= PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value_fast, i)); + } + } + } + else if(type == &PyLong_Type) { + /* could use is_double for 'long int' but no use now */ + int *array_int= array; + for(i=0; i<length; i++) { + array_int[i]= PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value_fast, i)); + } + } + else if(type == &PyBool_Type) { + int *array_bool= array; + for(i=0; i<length; i++) { + array_bool[i]= (PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value_fast, i)) != 0); + } + } + else { + Py_DECREF(value_fast); + PyErr_Format(PyExc_TypeError, + "%s: internal error %s is invalid", + error_prefix, type->tp_name); + return -1; + } + + Py_DECREF(value_fast); + + if(PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, + "%s: one or more items could not be used as a %s", + error_prefix, type->tp_name); + return -1; + } + + return 0; +} + + /* for debugging */ void PyC_ObSpit(const char *name, PyObject *var) { fprintf(stderr, "<%s> : ", name); diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h index 1730ad71721..bf2eb175882 100644 --- a/source/blender/python/generic/py_capi_utils.h +++ b/source/blender/python/generic/py_capi_utils.h @@ -35,7 +35,7 @@ void PyC_LineSpit(void); PyObject * PyC_ExceptionBuffer(void); PyObject * PyC_Object_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...); void PyC_FileAndNum(const char **filename, int *lineno); -int PyC_AsArray(void *array, PyObject *value, int length, PyTypeObject *type, const char *error_prefix); +int PyC_AsArray(void *array, PyObject *value, const int length, const PyTypeObject *type, const short is_double, const char *error_prefix); /* follow http://www.python.org/dev/peps/pep-0383/ */ PyObject * PyC_UnicodeFromByte(const char *str); diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 454a706a16b..03df9f9cb6c 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -45,6 +45,7 @@ set(SRC bpy_app.c bpy_driver.c bpy_interface.c + bpy_intern_string.c bpy_library.c bpy_operator.c bpy_operator_wrap.c @@ -60,6 +61,7 @@ set(SRC bpy.h bpy_app.h bpy_driver.h + bpy_intern_string.h bpy_operator.h bpy_operator_wrap.h bpy_props.h diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index e6f4c5713a1..0001c1c74b9 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -43,6 +43,7 @@ #include "bpy_rna.h" #include "bpy_util.h" #include "bpy_traceback.h" +#include "bpy_intern_string.h" #include "DNA_space_types.h" #include "DNA_text_types.h" @@ -199,13 +200,15 @@ void BPY_python_start(int argc, const char **argv) /* allow to use our own included python */ PyC_SetHomePath(BLI_get_folder(BLENDER_SYSTEM_PYTHON, NULL)); - /* Python 3.2 now looks for '2.57/python/include/python3.2d/pyconfig.h' to parse + /* Python 3.2 now looks for '2.58/python/include/python3.2d/pyconfig.h' to parse * from the 'sysconfig' module which is used by 'site', so for now disable site. * alternatively we could copy the file. */ Py_NoSiteFlag= 1; Py_Initialize(); - + + bpy_intern_string_init(); + // PySys_SetArgv(argc, argv); // broken in py3, not a huge deal /* sigh, why do python guys not have a char** version anymore? :( */ { @@ -251,7 +254,9 @@ void BPY_python_end(void) pyrna_free_types(); /* clear all python data from structs */ - + + bpy_intern_string_exit(); + Py_Finalize(); #ifdef TIME_PY_RUN diff --git a/source/blender/python/intern/bpy_intern_string.c b/source/blender/python/intern/bpy_intern_string.c new file mode 100755 index 00000000000..963c3469847 --- /dev/null +++ b/source/blender/python/intern/bpy_intern_string.c @@ -0,0 +1,57 @@ +/* + * $Id: bpy_intern_string.c 37626 2011-06-18 23:22:55Z gsrb3d $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/intern/bpy_intern_string.c + * \ingroup pythonintern + */ + +#include <Python.h> + +PyObject *bpy_intern_str_register; +PyObject *bpy_intern_str_unregister; +PyObject *bpy_intern_str_bl_rna; +PyObject *bpy_intern_str_order; +PyObject *bpy_intern_str_attr; +PyObject *bpy_intern_str___slots__; +PyObject *bpy_intern_str___bases__; + +void bpy_intern_string_init(void) +{ + bpy_intern_str_register= PyUnicode_FromString("register"); + bpy_intern_str_unregister= PyUnicode_FromString("unregister");; + bpy_intern_str_bl_rna= PyUnicode_FromString("bl_rna"); + bpy_intern_str_order= PyUnicode_FromString("order"); + bpy_intern_str_attr= PyUnicode_FromString("attr"); + bpy_intern_str___slots__= PyUnicode_FromString("__slots__"); +} + +void bpy_intern_string_exit(void) +{ + Py_DECREF(bpy_intern_str_register); + Py_DECREF(bpy_intern_str_unregister); + Py_DECREF(bpy_intern_str_bl_rna); + Py_DECREF(bpy_intern_str_order); + Py_DECREF(bpy_intern_str_attr); + Py_DECREF(bpy_intern_str___slots__); +} diff --git a/source/blender/python/intern/bpy_intern_string.h b/source/blender/python/intern/bpy_intern_string.h new file mode 100755 index 00000000000..2a4be2b6341 --- /dev/null +++ b/source/blender/python/intern/bpy_intern_string.h @@ -0,0 +1,37 @@ +/* + * $Id: bpy_intern_string.h 37626 2011-06-18 23:22:55Z gsrb3d $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/intern/bpy_intern_string.h + * \ingroup pythonintern + */ + +void bpy_intern_string_init(void); +void bpy_intern_string_exit(void); + +extern PyObject *bpy_intern_str_register; +extern PyObject *bpy_intern_str_unregister; +extern PyObject *bpy_intern_str_bl_rna; +extern PyObject *bpy_intern_str_order; +extern PyObject *bpy_intern_str_attr; +extern PyObject *bpy_intern_str___slots__; diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index 8ed4e41de3e..0ba80bf0850 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -459,7 +459,7 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject return NULL; } - if(pydef && PyC_AsArray(def, pydef, size, &PyBool_Type, "BoolVectorProperty(default=sequence)") < 0) + if(pydef && PyC_AsArray(def, pydef, size, &PyBool_Type, FALSE, "BoolVectorProperty(default=sequence)") < 0) return NULL; if (bpy_prop_callback_check(update_cb, 2) == -1) { @@ -603,7 +603,7 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject return NULL; } - if(pydef && PyC_AsArray(def, pydef, size, &PyLong_Type, "IntVectorProperty(default=sequence)") < 0) + if(pydef && PyC_AsArray(def, pydef, size, &PyLong_Type, FALSE, "IntVectorProperty(default=sequence)") < 0) return NULL; if (bpy_prop_callback_check(update_cb, 2) == -1) { @@ -759,7 +759,7 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec return NULL; } - if(pydef && PyC_AsArray(def, pydef, size, &PyFloat_Type, "FloatVectorProperty(default=sequence)") < 0) + if(pydef && PyC_AsArray(def, pydef, size, &PyFloat_Type, FALSE, "FloatVectorProperty(default=sequence)") < 0) return NULL; if (bpy_prop_callback_check(update_cb, 2) == -1) { diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 38cc3edd8f6..c1c7e0ea740 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -39,6 +39,7 @@ #include "bpy_props.h" #include "bpy_util.h" #include "bpy_rna_callback.h" +#include "bpy_intern_string.h" #ifdef USE_PYRNA_INVALIDATE_WEAKREF #include "MEM_guardedalloc.h" @@ -1380,12 +1381,8 @@ static int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, void *data, PyOb if (RNA_property_array_check(ptr, prop)) { - int ok= 1; - /* done getting the length */ - ok= pyrna_py_to_array(ptr, prop, data, value, error_prefix); - - if (!ok) { + if(pyrna_py_to_array(ptr, prop, data, value, error_prefix) == -1) { return -1; } } @@ -1476,7 +1473,7 @@ static int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, void *data, PyOb param= _PyUnicode_AsString(value); } #else // USE_STRING_COERCE - param= _PyUnicode_AsStringSize(value); + param= _PyUnicode_AsString(value); #endif // USE_STRING_COERCE if (param==NULL) { @@ -1767,8 +1764,8 @@ static int pyrna_py_to_prop_array_index(BPy_PropertyArrayRNA *self, int index, P if (totdim > 1) { /* char error_str[512]; */ - if (!pyrna_py_to_array_index(&self->ptr, self->prop, self->arraydim, self->arrayoffset, index, value, "")) { - /* PyErr_SetString(PyExc_AttributeError, error_str); */ + if (pyrna_py_to_array_index(&self->ptr, self->prop, self->arraydim, self->arrayoffset, index, value, "") == -1) { + /* error is set */ ret= -1; } } @@ -5221,7 +5218,7 @@ static void pyrna_subtype_set_rna(PyObject *newclass, StructRNA *srna) item= pyrna_struct_CreatePyObject(&ptr); /* note, must set the class not the __dict__ else the internal slots are not updated correctly */ - PyObject_SetAttrString(newclass, "bl_rna", item); + PyObject_SetAttr(newclass, bpy_intern_str_bl_rna, item); Py_DECREF(item); /* done with rna instance */ @@ -5283,7 +5280,7 @@ static PyObject* pyrna_srna_ExternalType(StructRNA *srna) //PyObject *slots= PyObject_GetAttrString(newclass, "__slots__"); // cant do this because it gets superclasses values! //PyObject *bases= PyObject_GetAttrString(newclass, "__bases__"); // can do this but faster not to. PyObject *bases= ((PyTypeObject *)newclass)->tp_bases; - PyObject *slots= PyDict_GetItemString(((PyTypeObject *)newclass)->tp_dict, "__slots__"); + PyObject *slots= PyDict_GetItem(((PyTypeObject *)newclass)->tp_dict, bpy_intern_str___slots__); if(slots==NULL) { fprintf(stderr, "pyrna_srna_ExternalType: expected class '%s' to have __slots__ defined\n\nSee bpy_types.py\n", idname); @@ -5653,7 +5650,7 @@ StructRNA *pyrna_struct_as_srna(PyObject *self, int parent, const char *error_pr /* ack, PyObject_GetAttrString wont look up this types tp_dict first :/ */ if(PyType_Check(self)) { - py_srna= (BPy_StructRNA *)PyDict_GetItemString(((PyTypeObject *)self)->tp_dict, "bl_rna"); + py_srna= (BPy_StructRNA *)PyDict_GetItem(((PyTypeObject *)self)->tp_dict, bpy_intern_str_bl_rna); Py_XINCREF(py_srna); } @@ -5661,7 +5658,7 @@ StructRNA *pyrna_struct_as_srna(PyObject *self, int parent, const char *error_pr /* be very careful with this since it will return a parent classes srna. * modifying this will do confusing stuff! */ if(py_srna==NULL) - py_srna= (BPy_StructRNA*)PyObject_GetAttrString(self, "bl_rna"); + py_srna= (BPy_StructRNA*)PyObject_GetAttr(self, bpy_intern_str_bl_rna); } if(py_srna==NULL) { @@ -5751,7 +5748,7 @@ static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item py_srna_cobject= PyCapsule_New(srna, NULL, NULL); /* not 100% nice :/, modifies the dict passed, should be ok */ - PyDict_SetItemString(py_kw, "attr", key); + PyDict_SetItem(py_kw, bpy_intern_str_attr, key); args_fake= PyTuple_New(1); PyTuple_SET_ITEM(args_fake, 0, py_srna_cobject); @@ -5798,7 +5795,7 @@ static int pyrna_deferred_register_props(StructRNA *srna, PyObject *class_dict) /* in both cases PyDict_CheckExact(class_dict) will be true even * though Operators have a metaclass dict namespace */ - if((order= PyDict_GetItemString(class_dict, "order")) && PyList_CheckExact(order)) { + if((order= PyDict_GetItem(class_dict, bpy_intern_str_order)) && PyList_CheckExact(order)) { for(pos= 0; pos<PyList_GET_SIZE(order); pos++) { key= PyList_GET_ITEM(order, pos); item= PyDict_GetItem(class_dict, key); @@ -6044,9 +6041,10 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param PyGILState_STATE gilstate; #ifdef USE_PEDANTIC_WRITE + const int is_operator= RNA_struct_is_a(ptr->type, &RNA_Operator); const char *func_id= RNA_function_identifier(func); /* testing, for correctness, not operator and not draw function */ - const short is_readonly= strstr("draw", func_id) || /*strstr("render", func_id) ||*/ !RNA_struct_is_a(ptr->type, &RNA_Operator); + const short is_readonly= strstr("draw", func_id) || /*strstr("render", func_id) ||*/ !is_operator; #endif py_class= RNA_struct_py_type_get(ptr->type); @@ -6102,6 +6100,11 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param * Although this is annoying to have to impliment a part of pythons typeobject.c:type_call(). */ if(py_class->tp_init) { +#ifdef USE_PEDANTIC_WRITE + const int prev_write= rna_disallow_writes; + rna_disallow_writes= is_operator ? FALSE : TRUE; /* only operators can write on __init__ */ +#endif + /* true in most cases even when the class its self doesn't define an __init__ function. */ args= PyTuple_New(0); if (py_class->tp_init(py_srna, args, NULL) < 0) { @@ -6110,11 +6113,16 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param /* err set below */ } Py_DECREF(args); +#ifdef USE_PEDANTIC_WRITE + rna_disallow_writes= prev_write; +#endif } - py_class_instance= py_srna; #else + const int prev_write= rna_disallow_writes; + rna_disallow_writes= TRUE; + /* 'almost' all the time calling the class isn't needed. * We could just do... py_class_instance= py_srna; @@ -6128,7 +6136,10 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param py_class_instance= PyObject_Call(py_class, args, NULL); Py_DECREF(args); + rna_disallow_writes= prev_write; + #endif + if(py_class_instance == NULL) { err= -1; /* so the error is not overridden below */ } @@ -6303,7 +6314,7 @@ static void bpy_class_free(void *pyob_ptr) // PyDict_Clear(((PyTypeObject*)self)->tp_dict); // // remove the rna attribute instead. - PyDict_DelItemString(((PyTypeObject *)self)->tp_dict, "bl_rna"); + PyDict_DelItem(((PyTypeObject *)self)->tp_dict, bpy_intern_str_bl_rna); if(PyErr_Occurred()) PyErr_Clear(); @@ -6409,7 +6420,7 @@ static PyObject *pyrna_register_class(PyObject *UNUSED(self), PyObject *py_class const char *identifier; PyObject *py_cls_meth; - if(PyDict_GetItemString(((PyTypeObject*)py_class)->tp_dict, "bl_rna")) { + if(PyDict_GetItem(((PyTypeObject*)py_class)->tp_dict, bpy_intern_str_bl_rna)) { PyErr_SetString(PyExc_AttributeError, "register_class(...): already registered as a subclass"); return NULL; } @@ -6474,7 +6485,7 @@ static PyObject *pyrna_register_class(PyObject *UNUSED(self), PyObject *py_class return NULL; /* call classed register method () */ - py_cls_meth= PyObject_GetAttrString(py_class, "register"); + py_cls_meth= PyObject_GetAttr(py_class, bpy_intern_str_register); if(py_cls_meth == NULL) { PyErr_Clear(); } @@ -6532,7 +6543,7 @@ static PyObject *pyrna_unregister_class(PyObject *UNUSED(self), PyObject *py_cla StructRNA *srna; PyObject *py_cls_meth; - /*if(PyDict_GetItemString(((PyTypeObject*)py_class)->tp_dict, "bl_rna")==NULL) { + /*if(PyDict_GetItem(((PyTypeObject*)py_class)->tp_dict, bpy_intern_str_bl_rna)==NULL) { PWM_cursor_wait(0); PyErr_SetString(PyExc_ValueError, "unregister_class(): not a registered as a subclass"); return NULL; @@ -6551,7 +6562,7 @@ static PyObject *pyrna_unregister_class(PyObject *UNUSED(self), PyObject *py_cla } /* call classed unregister method */ - py_cls_meth= PyObject_GetAttrString(py_class, "unregister"); + py_cls_meth= PyObject_GetAttr(py_class, bpy_intern_str_unregister); if(py_cls_meth == NULL) { PyErr_Clear(); } @@ -6601,7 +6612,7 @@ static PyObject *pyrna_unregister_class(PyObject *UNUSED(self), PyObject *py_cla /* call unregister */ unreg(CTX_data_main(C), srna); /* calls bpy_class_free, this decref's py_class */ - PyDict_DelItemString(((PyTypeObject *)py_class)->tp_dict, "bl_rna"); + PyDict_DelItem(((PyTypeObject *)py_class)->tp_dict, bpy_intern_str_bl_rna); if(PyErr_Occurred()) PyErr_Clear(); //return NULL; diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c index df295e812be..9843a57e0f2 100644 --- a/source/blender/python/intern/bpy_rna_array.c +++ b/source/blender/python/intern/bpy_rna_array.c @@ -68,17 +68,24 @@ static int validate_array_type(PyObject *seq, int dim, int totdim, int dimsize[] /* check that a sequence contains dimsize[dim] items */ const int seq_size= PySequence_Size(seq); if(seq_size == -1) { - PyErr_Format(PyExc_ValueError, "%s sequence expected at dimension %d, not %s", error_prefix, (int)dim + 1, Py_TYPE(seq)->tp_name); - return 0; + PyErr_Format(PyExc_ValueError, "%s sequence expected at dimension %d, not '%s'", + error_prefix, (int)dim + 1, Py_TYPE(seq)->tp_name); + return -1; } for (i= 0; i < seq_size; i++) { PyObject *item; int ok= 1; item= PySequence_GetItem(seq, i); - if (!PySequence_Check(item)) { + if(item == NULL) { + PyErr_Format(PyExc_TypeError, "%s sequence type '%s' failed to retrieve index %d", + error_prefix, Py_TYPE(seq)->tp_name, i); + ok= 0; + } + else if (!PySequence_Check(item)) { /* BLI_snprintf(error_str, error_str_size, "expected a sequence of %s", item_type_str); */ - PyErr_Format(PyExc_TypeError, "%s expected a sequence of %s, not %s", error_prefix, item_type_str, Py_TYPE(item)->tp_name); + PyErr_Format(PyExc_TypeError, "%s expected a sequence of %s, not %s", + error_prefix, item_type_str, Py_TYPE(item)->tp_name); ok= 0; } /* arr[3][4][5] @@ -88,42 +95,50 @@ static int validate_array_type(PyObject *seq, int dim, int totdim, int dimsize[] dim=0 */ else if (PySequence_Size(item) != dimsize[dim + 1]) { /* BLI_snprintf(error_str, error_str_size, "sequences of dimension %d should contain %d items", (int)dim + 1, (int)dimsize[dim + 1]); */ - PyErr_Format(PyExc_ValueError, "%s sequences of dimension %d should contain %d items", error_prefix, (int)dim + 1, (int)dimsize[dim + 1]); + PyErr_Format(PyExc_ValueError, "%s sequences of dimension %d should contain %d items", + error_prefix, (int)dim + 1, (int)dimsize[dim + 1]); ok= 0; } - else if (!validate_array_type(item, dim + 1, totdim, dimsize, check_item_type, item_type_str, error_prefix)) { + else if (validate_array_type(item, dim + 1, totdim, dimsize, check_item_type, item_type_str, error_prefix) == -1) { ok= 0; } - Py_DECREF(item); + Py_XDECREF(item); if (!ok) - return 0; + return -1; } } else { /* check that items are of correct type */ const int seq_size= PySequence_Size(seq); if(seq_size == -1) { - PyErr_Format(PyExc_ValueError, "%s sequence expected at dimension %d, not %s", error_prefix, (int)dim + 1, Py_TYPE(seq)->tp_name); - return 0; + PyErr_Format(PyExc_ValueError, "%s sequence expected at dimension %d, not '%s'", + error_prefix, (int)dim + 1, Py_TYPE(seq)->tp_name); + return -1; } for (i= 0; i < seq_size; i++) { PyObject *item= PySequence_GetItem(seq, i); - if (!check_item_type(item)) { + if(item == NULL) { + PyErr_Format(PyExc_TypeError, "%s sequence type '%s' failed to retrieve index %d", + error_prefix, Py_TYPE(seq)->tp_name, i); + return -1; + } + else if (!check_item_type(item)) { Py_DECREF(item); /* BLI_snprintf(error_str, error_str_size, "sequence items should be of type %s", item_type_str); */ - PyErr_Format(PyExc_TypeError, "%s expected sequence items of type %s, not %s", error_prefix, item_type_str, Py_TYPE(item)->tp_name); - return 0; + PyErr_Format(PyExc_TypeError, "%s expected sequence items of type %s, not %s", + error_prefix, item_type_str, Py_TYPE(item)->tp_name); + return -1; } Py_DECREF(item); } } - return 1; + return 0; /* ok */ } /* Returns the number of items in a single- or multi-dimensional sequence. */ @@ -136,8 +151,21 @@ static int count_items(PyObject *seq, int dim) int i; for (i= 0; i < seq_size; i++) { PyObject *item= PySequence_GetItem(seq, i); - totitem += count_items(item, dim - 1); - Py_DECREF(item); + if(item) { + const int tot= count_items(item, dim - 1); + Py_DECREF(item); + if(tot != -1) { + totitem += tot; + } + else { + totitem= -1; + break; + } + } + else { + totitem= -1; + break; + } } } else { @@ -156,18 +184,24 @@ static int validate_array_length(PyObject *rvalue, PointerRNA *ptr, PropertyRNA totdim= RNA_property_array_dimension(ptr, prop, dimsize); tot= count_items(rvalue, totdim - lvalue_dim); - if ((RNA_property_flag(prop) & PROP_DYNAMIC) && lvalue_dim == 0) { + if(tot == -1) { + PyErr_Format(PyExc_ValueError, "%s %.200s.%.200s, error validating the sequence length", + error_prefix, RNA_struct_identifier(ptr->type), RNA_property_identifier(prop)); + return -1; + } + else if ((RNA_property_flag(prop) & PROP_DYNAMIC) && lvalue_dim == 0) { if (RNA_property_array_length(ptr, prop) != tot) { #if 0 /* length is flexible */ if (!RNA_property_dynamic_array_set_length(ptr, prop, tot)) { /* BLI_snprintf(error_str, error_str_size, "%s.%s: array length cannot be changed to %d", RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), tot); */ - PyErr_Format(PyExc_ValueError, "%s %s.%s: array length cannot be changed to %d", error_prefix, RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), tot); - return 0; + PyErr_Format(PyExc_ValueError, "%s %s.%s: array length cannot be changed to %d", + error_prefix, RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), tot); + return -1; } #else *totitem= tot; - return 1; + return 0; #endif } @@ -204,14 +238,15 @@ static int validate_array_length(PyObject *rvalue, PointerRNA *ptr, PropertyRNA if (tot != len) { /* BLI_snprintf(error_str, error_str_size, "sequence must have length of %d", len); */ - PyErr_Format(PyExc_ValueError, "%s %.200s.%.200s, sequence must have %d items total, not %d", error_prefix, RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), len, tot); - return 0; + PyErr_Format(PyExc_ValueError, "%s %.200s.%.200s, sequence must have %d items total, not %d", + error_prefix, RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), len, tot); + return -1; } } *totitem= len; - return 1; + return 0; } static int validate_array(PyObject *rvalue, PointerRNA *ptr, PropertyRNA *prop, int lvalue_dim, ItemTypeCheckFunc check_item_type, const char *item_type_str, int *totitem, const char *error_prefix) @@ -221,8 +256,8 @@ static int validate_array(PyObject *rvalue, PointerRNA *ptr, PropertyRNA *prop, /* validate type first because length validation may modify property array length */ - if (!validate_array_type(rvalue, lvalue_dim, totdim, dimsize, check_item_type, item_type_str, error_prefix)) - return 0; + if (validate_array_type(rvalue, lvalue_dim, totdim, dimsize, check_item_type, item_type_str, error_prefix) == -1) + return -1; return validate_array_length(rvalue, ptr, prop, lvalue_dim, totitem, error_prefix); } @@ -250,25 +285,39 @@ static char *copy_values(PyObject *seq, PointerRNA *ptr, PropertyRNA *prop, int int totdim= RNA_property_array_dimension(ptr, prop, NULL); const int seq_size= PySequence_Size(seq); - assert(seq_size != -1); + /* General note for 'data' being NULL or PySequence_GetItem() failing. + * + * This should never be NULL since we validated it, _but_ some triky python + * developer could write their own sequence type which succeeds on + * validating but fails later somehow, so include checks for safety. */ + + if(seq_size == -1) { + return NULL; + } - for (i= 0; i < seq_size; i++) { + for (i= 0; (i < seq_size) && data; i++) { PyObject *item= PySequence_GetItem(seq, i); + if(item) { + if (dim + 1 < totdim) { + data= copy_values(item, ptr, prop, dim + 1, data, item_size, index, convert_item, rna_set_index); + } + else { + data= copy_value_single(item, ptr, prop, data, item_size, index, convert_item, rna_set_index); + } + + Py_DECREF(item); - if (dim + 1 < totdim) { - data= copy_values(item, ptr, prop, dim + 1, data, item_size, index, convert_item, rna_set_index); + /* data may be NULL, but the for loop checks */ } else { - data= copy_value_single(item, ptr, prop, data, item_size, index, convert_item, rna_set_index); + return NULL; } - - Py_DECREF(item); } return data; } -static int py_to_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *param_data, ItemTypeCheckFunc check_item_type, const char *item_type_str, int item_size, ItemConvertFunc convert_item, RNA_SetArrayFunc rna_set_array, const char *error_prefix) +static int py_to_array(PyObject *seq, PointerRNA *ptr, PropertyRNA *prop, char *param_data, ItemTypeCheckFunc check_item_type, const char *item_type_str, int item_size, ItemConvertFunc convert_item, RNA_SetArrayFunc rna_set_array, const char *error_prefix) { /*int totdim, dim_size[MAX_ARRAY_DIMENSION];*/ int totitem; @@ -276,8 +325,8 @@ static int py_to_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *p /*totdim= RNA_property_array_dimension(ptr, prop, dim_size);*/ /*UNUSED*/ - if (!validate_array(py, ptr, prop, 0, check_item_type, item_type_str, &totitem, error_prefix)) { - return 0; + if (validate_array(seq, ptr, prop, 0, check_item_type, item_type_str, &totitem, error_prefix) == -1) { + return -1; } if (totitem) { @@ -297,16 +346,27 @@ static int py_to_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *p data= PyMem_MALLOC(item_size * totitem); } - copy_values(py, ptr, prop, 0, data, item_size, NULL, convert_item, NULL); + /* will only fail in very rare cases since we already validated the + * python data, the check here is mainly for completeness. */ + if(copy_values(seq, ptr, prop, 0, data, item_size, NULL, convert_item, NULL) != NULL) { + if (param_data==NULL) { + /* NULL can only pass through in case RNA property arraylength is 0 (impossible?) */ + rna_set_array(ptr, prop, data); + PyMem_FREE(data); + } + } + else { + if (param_data==NULL) { + PyMem_FREE(data); + } - if (param_data==NULL) { - /* NULL can only pass through in case RNA property arraylength is 0 (impossible?) */ - rna_set_array(ptr, prop, data); - PyMem_FREE(data); + PyErr_Format(PyExc_TypeError, "%s internal error parsing sequence of type '%s' after successful validation", + error_prefix, Py_TYPE(seq)->tp_name); + return -1; } } - return 1; + return 0; } static int py_to_array_index(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, int lvalue_dim, int arrayoffset, int index, ItemTypeCheckFunc check_item_type, const char *item_type_str, ItemConvertFunc convert_item, RNA_SetIndexFunc rna_set_index, const char *error_prefix) @@ -335,21 +395,24 @@ static int py_to_array_index(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, i if(lvalue_dim == totdim) { /* single item, assign directly */ if(!check_item_type(py)) { - PyErr_Format(PyExc_TypeError, "%s %.200s.%.200s, expected a %s type, not %s", error_prefix, RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), item_type_str, Py_TYPE(py)->tp_name); - return 0; + PyErr_Format(PyExc_TypeError, "%s %.200s.%.200s, expected a %s type, not %s", + error_prefix, RNA_struct_identifier(ptr->type), + RNA_property_identifier(prop), item_type_str, + Py_TYPE(py)->tp_name); + return -1; } copy_value_single(py, ptr, prop, NULL, 0, &index, convert_item, rna_set_index); } else { - if (!validate_array(py, ptr, prop, lvalue_dim, check_item_type, item_type_str, &totitem, error_prefix)) { - return 0; + if (validate_array(py, ptr, prop, lvalue_dim, check_item_type, item_type_str, &totitem, error_prefix) == -1) { + return -1; } if (totitem) { copy_values(py, ptr, prop, lvalue_dim, NULL, 0, &index, convert_item, rna_set_index); } } - return 1; + return 0; } static void py_to_float(PyObject *py, char *data) @@ -414,7 +477,7 @@ int pyrna_py_to_array(PointerRNA *ptr, PropertyRNA *prop, char *param_data, PyOb break; default: PyErr_SetString(PyExc_TypeError, "not an array type"); - ret= 0; + ret= -1; } return ret; @@ -435,7 +498,7 @@ int pyrna_py_to_array_index(PointerRNA *ptr, PropertyRNA *prop, int arraydim, in break; default: PyErr_SetString(PyExc_TypeError, "not an array type"); - ret= 0; + ret= -1; } return ret; diff --git a/source/blender/python/intern/bpy_util.c b/source/blender/python/intern/bpy_util.c index 6e321015bc6..1450621d59e 100644 --- a/source/blender/python/intern/bpy_util.c +++ b/source/blender/python/intern/bpy_util.c @@ -122,63 +122,3 @@ short BPy_errors_to_report(ReportList *reports) Py_DECREF(pystring_format); // workaround return 1; } - -/* array utility function */ -int PyC_AsArray(void *array, PyObject *value, int length, PyTypeObject *type, const char *error_prefix) -{ - PyObject *value_fast; - int value_len; - int i; - - if(!(value_fast=PySequence_Fast(value, error_prefix))) { - return -1; - } - - value_len= PySequence_Fast_GET_SIZE(value_fast); - - if(value_len != length) { - Py_DECREF(value); - PyErr_Format(PyExc_TypeError, - "%.200s: invalid sequence length. expected %d, got %d", - error_prefix, length, value_len); - return -1; - } - - /* for each type */ - if(type == &PyFloat_Type) { - float *array_float= array; - for(i=0; i<length; i++) { - array_float[i]= PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value_fast, i)); - } - } - else if(type == &PyLong_Type) { - int *array_int= array; - for(i=0; i<length; i++) { - array_int[i]= PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value_fast, i)); - } - } - else if(type == &PyBool_Type) { - int *array_bool= array; - for(i=0; i<length; i++) { - array_bool[i]= (PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value_fast, i)) != 0); - } - } - else { - Py_DECREF(value_fast); - PyErr_Format(PyExc_TypeError, - "%s: internal error %s is invalid", - error_prefix, type->tp_name); - return -1; - } - - Py_DECREF(value_fast); - - if(PyErr_Occurred()) { - PyErr_Format(PyExc_TypeError, - "%s: one or more items could not be used as a %s", - error_prefix, type->tp_name); - return -1; - } - - return 0; -} |