slice support working in py3 for Vector and Matrix types.
authorCampbell Barton <ideasman42@gmail.com>
Wed, 1 Jul 2009 13:31:36 +0000 (13:31 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Wed, 1 Jul 2009 13:31:36 +0000 (13:31 +0000)
Added slice to PyRNA collections and arrays (py3 only).

eg.
some_verts = mesh.verts[0:10]
some_rna_array[4:-1] = [0,1,2,3]

Collections dont support assignment, when assigning slices, resizing the array isnt support like with python lists.

source/blender/python/generic/matrix.c
source/blender/python/generic/vector.c
source/blender/python/intern/bpy_rna.c

index 41411559fe031f89740b810483cee34e9e23aa21..34d29dacdd17af85bc513b7317552cb1873793a2 100644 (file)
@@ -760,8 +760,7 @@ static PyObject *Matrix_slice(MatrixObject * self, int begin, int end)
 }
 /*----------------------------object[z:y]------------------------
   sequence slice (set)*/
-static int Matrix_ass_slice(MatrixObject * self, int begin, int end,
-                            PyObject * seq)
+static int Matrix_ass_slice(MatrixObject * self, int begin, int end, PyObject * seq)
 {
        int i, x, y, size, sub_size = 0;
        float mat[16], f;
@@ -999,6 +998,83 @@ static PySequenceMethods Matrix_SeqMethods = {
 };
 
 
+
+#if (PY_VERSION_HEX >= 0x03000000)
+static PyObject *Matrix_subscript(MatrixObject* 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->rowSize;
+               return Matrix_item(self, i);
+       } else if (PySlice_Check(item)) {
+               Py_ssize_t start, stop, step, slicelength;
+
+               if (PySlice_GetIndicesEx((PySliceObject*)item, self->rowSize, &start, &stop, &step, &slicelength) < 0)
+                       return NULL;
+
+               if (slicelength <= 0) {
+                       return PyList_New(0);
+               }
+               else if (step == 1) {
+                       return Matrix_slice(self, start, stop);
+               }
+               else {
+                       PyErr_SetString(PyExc_TypeError, "slice steps not supported with matricies");
+                       return NULL;
+               }
+       }
+       else {
+               PyErr_Format(PyExc_TypeError,
+                            "vector indices must be integers, not %.200s",
+                            item->ob_type->tp_name);
+               return NULL;
+       }
+}
+
+static int Matrix_ass_subscript(MatrixObject* 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->rowSize;
+               return Matrix_ass_item(self, i, value);
+       }
+       else if (PySlice_Check(item)) {
+               Py_ssize_t start, stop, step, slicelength;
+
+               if (PySlice_GetIndicesEx((PySliceObject*)item, self->rowSize, &start, &stop, &step, &slicelength) < 0)
+                       return -1;
+
+               if (step == 1)
+                       return Matrix_ass_slice(self, start, stop, value);
+               else {
+                       PyErr_SetString(PyExc_TypeError, "slice steps not supported with matricies");
+                       return -1;
+               }
+       }
+       else {
+               PyErr_Format(PyExc_TypeError,
+                            "matrix indices must be integers, not %.200s",
+                            item->ob_type->tp_name);
+               return -1;
+       }
+}
+
+static PyMappingMethods Matrix_AsMapping = {
+       (lenfunc)Matrix_len,
+       (binaryfunc)Matrix_subscript,
+       (objobjargproc)Matrix_ass_subscript
+};
+#endif /*  (PY_VERSION_HEX >= 0x03000000) */
+
+
+
 #if (PY_VERSION_HEX >= 0x03000000)
 static PyNumberMethods Matrix_NumMethods = {
                (binaryfunc)    Matrix_add,     /*nb_add*/
@@ -1106,7 +1182,7 @@ PyTypeObject matrix_Type = {
        (reprfunc) Matrix_repr,                 /*tp_repr*/
        &Matrix_NumMethods,                             /*tp_as_number*/
        &Matrix_SeqMethods,                             /*tp_as_sequence*/
-       0,                                                              /*tp_as_mapping*/
+       &Matrix_AsMapping,                              /*tp_as_mapping*/
        0,                                                              /*tp_hash*/
        0,                                                              /*tp_call*/
        0,                                                              /*tp_str*/
index 0af646bd82f7e44b6eb18bdaebb60ee8f0bb60da..5abc68c63ca8cc66f2bc1c4d898e5569fe633273 100644 (file)
@@ -1051,16 +1051,100 @@ static PyObject* Vector_richcmpr(PyObject *objectA, PyObject *objectB, int compa
                Py_RETURN_FALSE;
        }
 }
+
 /*-----------------PROTCOL DECLARATIONS--------------------------*/
 static PySequenceMethods Vector_SeqMethods = {
        (inquiry) Vector_len,                                           /* sq_length */
        (binaryfunc) 0,                                                         /* sq_concat */
-       (ssizeargfunc) 0,                                                               /* sq_repeat */
+       (ssizeargfunc) 0,                                                       /* sq_repeat */
        (ssizeargfunc) Vector_item,                                     /* sq_item */
-       (ssizessizeargfunc) Vector_slice,                               /* sq_slice */
+#if (PY_VERSION_HEX < 0x03000000)
+       (ssizessizeargfunc) Vector_slice,                       /* sq_slice */ /* PY2 ONLY */
+#else
+       NULL,
+#endif
        (ssizeobjargproc) Vector_ass_item,                      /* sq_ass_item */
-       (ssizessizeobjargproc) Vector_ass_slice,                /* sq_ass_slice */
+#if (PY_VERSION_HEX < 0x03000000)
+       (ssizessizeobjargproc) Vector_ass_slice,        /* sq_ass_slice */ /* PY2 ONLY */
+#else
+       NULL,
+#endif
+};
+
+
+#if (PY_VERSION_HEX >= 0x03000000)
+static PyObject *Vector_subscript(VectorObject* 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->size;
+               return Vector_item(self, i);
+       } else if (PySlice_Check(item)) {
+               Py_ssize_t start, stop, step, slicelength;
+
+               if (PySlice_GetIndicesEx((PySliceObject*)item, self->size, &start, &stop, &step, &slicelength) < 0)
+                       return NULL;
+
+               if (slicelength <= 0) {
+                       return PyList_New(0);
+               }
+               else if (step == 1) {
+                       return Vector_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",
+                            item->ob_type->tp_name);
+               return NULL;
+       }
+}
+
+static int Vector_ass_subscript(VectorObject* 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->size;
+               return Vector_ass_item(self, i, value);
+       }
+       else if (PySlice_Check(item)) {
+               Py_ssize_t start, stop, step, slicelength;
+
+               if (PySlice_GetIndicesEx((PySliceObject*)item, self->size, &start, &stop, &step, &slicelength) < 0)
+                       return -1;
+
+               if (step == 1)
+                       return Vector_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",
+                            item->ob_type->tp_name);
+               return -1;
+       }
+}
+
+static PyMappingMethods Vector_AsMapping = {
+       (lenfunc)Vector_len,
+       (binaryfunc)Vector_subscript,
+       (objobjargproc)Vector_ass_subscript
 };
+#endif /*  (PY_VERSION_HEX >= 0x03000000) */
  
 #if (PY_VERSION_HEX >= 0x03000000)
 static PyNumberMethods Vector_NumMethods = {
@@ -1813,7 +1897,7 @@ PyTypeObject vector_Type = {
 
        &Vector_NumMethods,                       /* PyNumberMethods *tp_as_number; */
        &Vector_SeqMethods,                       /* PySequenceMethods *tp_as_sequence; */
-       NULL,                       /* PyMappingMethods *tp_as_mapping; */
+       &Vector_AsMapping,                       /* PyMappingMethods *tp_as_mapping; */
 
        /* More standard operations (here for binary compatibility) */
 
index e13f02c5529ffe9f245b331a153b9d91d924944b..4729620bb8afc40c2e684f56478294c3961e2fa9 100644 (file)
@@ -35,6 +35,7 @@
 #include "RNA_define.h" /* for defining our own rna */
 
 #include "MEM_guardedalloc.h"
+#include "BKE_utildefines.h"
 #include "BKE_context.h"
 #include "BKE_global.h" /* evil G.* */
 #include "BKE_report.h"
@@ -820,108 +821,246 @@ static Py_ssize_t pyrna_prop_len( BPy_PropertyRNA * self )
        return len;
 }
 
-static PyObject *pyrna_prop_subscript( BPy_PropertyRNA * self, PyObject *key )
+/* internal use only */
+static PyObject *prop_subscript_collection_int(BPy_PropertyRNA * self, int keynum)
 {
-       PyObject *ret;
        PointerRNA newptr;
-       int keynum = 0;
-       char *keyname = NULL;
-       
+
+       if(keynum < 0) keynum += RNA_property_collection_length(&self->ptr, self->prop);
+
+       if(RNA_property_collection_lookup_int(&self->ptr, self->prop, keynum, &newptr))
+               return pyrna_struct_CreatePyObject(&newptr);
+
+       PyErr_SetString(PyExc_IndexError, "out of range");
+       return NULL;
+}
+static PyObject *prop_subscript_array_int(BPy_PropertyRNA * self, int keynum)
+{
+       int len= RNA_property_array_length(self->prop);
+
+       if(keynum < 0) keynum += len;
+
+       if(keynum >= 0 && keynum < len)
+               return pyrna_prop_to_py_index(&self->ptr, self->prop, keynum);
+
+       PyErr_SetString(PyExc_IndexError, "out of range");
+       return NULL;
+}
+
+static PyObject *prop_subscript_collection_str(BPy_PropertyRNA * self, char *keyname)
+{
+       PointerRNA newptr;
+       if(RNA_property_collection_lookup_string(&self->ptr, self->prop, keyname, &newptr))
+               return pyrna_struct_CreatePyObject(&newptr);
+
+       PyErr_SetString(PyExc_KeyError, "key not found");
+       return NULL;
+}
+/* static PyObject *prop_subscript_array_str(BPy_PropertyRNA * self, char *keyname) */
+
+
+
+
+#if PY_VERSION_HEX >= 0x03000000
+static PyObject *prop_subscript_collection_slice(BPy_PropertyRNA * self, int start, int stop)
+{
+       PointerRNA newptr;
+       PyObject *list = PyList_New(stop - start);
+       int count;
+
+       start = MIN2(start,stop); /* values are clamped from  */
+
+       for(count = start; count < stop; count++) {
+               if(RNA_property_collection_lookup_int(&self->ptr, self->prop, count - start, &newptr)) {
+                       PyList_SetItem(list, count - start, pyrna_struct_CreatePyObject(&newptr));
+               }
+               else {
+                       Py_DECREF(list);
+
+                       PyErr_SetString(PyExc_RuntimeError, "error getting an rna struct from a collection");
+                       return NULL;
+               }
+       }
+
+       return list;
+}
+static PyObject *prop_subscript_array_slice(BPy_PropertyRNA * self, int start, int stop)
+{
+       PyObject *list = PyList_New(stop - start);
+       int count;
+
+       start = MIN2(start,stop); /* values are clamped from PySlice_GetIndicesEx */
+
+       for(count = start; count < stop; count++)
+               PyList_SetItem(list, count - start, pyrna_prop_to_py_index(&self->ptr, self->prop, count));
+
+       return list;
+}
+#endif
+
+static PyObject *prop_subscript_collection(BPy_PropertyRNA * self, PyObject *key)
+{
        if (PyUnicode_Check(key)) {
-               keyname = _PyUnicode_AsString(key);
-       } else if (PyLong_Check(key)) {
-               keynum = PyLong_AsSsize_t(key);
-       } else {
+               return prop_subscript_collection_str(self, _PyUnicode_AsString(key));
+       }
+       else if (PyLong_Check(key)) {
+               return prop_subscript_collection_int(self, PyLong_AsSsize_t(key));
+       }
+#if PY_VERSION_HEX >= 0x03000000
+       else if (PySlice_Check(key)) {
+               int len= RNA_property_collection_length(&self->ptr, self->prop);
+               Py_ssize_t start, stop, step, slicelength;
+
+               if (PySlice_GetIndicesEx((PySliceObject*)key, len, &start, &stop, &step, &slicelength) < 0)
+                       return NULL;
+
+               if (slicelength <= 0) {
+                       return PyList_New(0);
+               }
+               else if (step == 1) {
+                       return prop_subscript_collection_slice(self, start, stop);
+               }
+               else {
+                       PyErr_SetString(PyExc_TypeError, "slice steps not supported with rna");
+                       return NULL;
+               }
+       }
+#endif
+       else {
                PyErr_SetString(PyExc_AttributeError, "invalid key, key must be a string or an int");
                return NULL;
        }
-       
-       if (RNA_property_type(self->prop) == PROP_COLLECTION) {
-               int ok;
-               if (keyname)    ok = RNA_property_collection_lookup_string(&self->ptr, self->prop, keyname, &newptr);
-               else                    ok = RNA_property_collection_lookup_int(&self->ptr, self->prop, keynum, &newptr);
-               
-               if (ok) {
-                       ret = pyrna_struct_CreatePyObject(&newptr);
-               } else {
-                       PyErr_SetString(PyExc_AttributeError, "out of range");
-                       ret = NULL;
+}
+
+static PyObject *prop_subscript_array(BPy_PropertyRNA * self, PyObject *key)
+{
+       /*if (PyUnicode_Check(key)) {
+               return prop_subscript_array_str(self, _PyUnicode_AsString(key));
+       } else*/
+       if (PyLong_Check(key)) {
+               return prop_subscript_array_int(self, PyLong_AsSsize_t(key));
+       }
+#if PY_VERSION_HEX >= 0x03000000
+       else if (PySlice_Check(key)) {
+               int len= RNA_property_array_length(self->prop);
+               Py_ssize_t start, stop, step, slicelength;
+
+               if (PySlice_GetIndicesEx((PySliceObject*)key, len, &start, &stop, &step, &slicelength) < 0)
+                       return NULL;
+
+               if (slicelength <= 0) {
+                       return PyList_New(0);
                }
-               
-       } else if (keyname) {
-               PyErr_SetString(PyExc_AttributeError, "string keys are only supported for collections");
-               ret = NULL;
-       } else {
-               int len = RNA_property_array_length(self->prop);
-               
-               if (len==0) { /* not an array*/
-                       PyErr_Format(PyExc_AttributeError, "not an array or collection %d", keynum);
-                       ret = NULL;
+               else if (step == 1) {
+                       return prop_subscript_array_slice(self, start, stop);
                }
-               
-               if (keynum >= len){
-                       PyErr_SetString(PyExc_AttributeError, "index out of range");
-                       ret = NULL;
-               } else { /* not an array*/
-                       ret = pyrna_prop_to_py_index(&self->ptr, self->prop, keynum);
+               else {
+                       PyErr_SetString(PyExc_TypeError, "slice steps not supported with rna");
+                       return NULL;
                }
        }
-       
-       return ret;
+#endif
+       else {
+               PyErr_SetString(PyExc_AttributeError, "invalid key, key must be an int");
+               return NULL;
+       }
 }
 
+static PyObject *pyrna_prop_subscript( BPy_PropertyRNA * self, PyObject *key )
+{
+       if (RNA_property_type(self->prop) == PROP_COLLECTION) {
+               return prop_subscript_collection(self, key);
+       } else if (RNA_property_array_length(self->prop)) { /* arrays are currently fixed length, zero length means its not an array */
+               return prop_subscript_array(self, key);
+       } else {
+               PyErr_SetString(PyExc_TypeError, "rna type is not an array or a collection");
+               return NULL;
+       }
+
+}
 
-static int pyrna_prop_assign_subscript( BPy_PropertyRNA * self, PyObject *key, PyObject *value )
+#if PY_VERSION_HEX >= 0x03000000
+static int prop_subscript_ass_array_slice(BPy_PropertyRNA * self, int begin, int end, PyObject *value)
 {
-       int ret = 0;
-       int keynum = 0;
-       char *keyname = NULL;
+       int count;
+
+       /* values are clamped from */
+       begin = MIN2(begin,end);
+
+       for(count = begin; count < end; count++) {
+               if(pyrna_py_to_prop_index(&self->ptr, self->prop, count - begin, value) == -1) {
+                       /* TODO - this is wrong since some values have been assigned... will need to fix that */
+                       return -1; /* pyrna_struct_CreatePyObject should set the error */
+               }
+       }
+
+       return 0;
+}
+#endif
+
+static int prop_subscript_ass_array_int(BPy_PropertyRNA * self, int keynum, PyObject *value)
+{
+
+       int len= RNA_property_array_length(self->prop);
+
+       if(keynum < 0) keynum += len;
+
+       if(keynum >= 0 && keynum < len)
+               return pyrna_py_to_prop_index(&self->ptr, self->prop, keynum, value);
+
+       PyErr_SetString(PyExc_IndexError, "out of range");
+       return -1;
+}
+
+static int pyrna_prop_ass_subscript( BPy_PropertyRNA * self, PyObject *key, PyObject *value )
+{
+       /* char *keyname = NULL; */ /* not supported yet */
        
        if (!RNA_property_editable(&self->ptr, self->prop)) {
                PyErr_Format( PyExc_AttributeError, "PropertyRNA - attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(self->prop), RNA_struct_identifier(self->ptr.type) );
                return -1;
        }
        
-       if (PyUnicode_Check(key)) {
-               keyname = _PyUnicode_AsString(key);
-       } else if (PyLong_Check(key)) {
-               keynum = PyLong_AsSsize_t(key);
-       } else {
-               PyErr_SetString(PyExc_AttributeError, "PropertyRNA - invalid key, key must be a string or an int");
+       /* maybe one day we can support this... */
+       if (RNA_property_type(self->prop) == PROP_COLLECTION) {
+               PyErr_Format( PyExc_AttributeError, "PropertyRNA - attribute \"%s\" from \"%s\" is a collection, assignment not supported", RNA_property_identifier(self->prop), RNA_struct_identifier(self->ptr.type) );
                return -1;
        }
-       
-       if (RNA_property_type(self->prop) == PROP_COLLECTION) {
-               PyErr_SetString(PyExc_AttributeError, "PropertyRNA - assignment is not supported for collections (yet)");
-               ret = -1;
-       } else if (keyname) {
-               PyErr_SetString(PyExc_AttributeError, "PropertyRNA - string keys are only supported for collections");
-               ret = -1;
-       } else {
-               int len = RNA_property_array_length(self->prop);
-               
-               if (len==0) { /* not an array*/
-                       PyErr_Format(PyExc_AttributeError, "PropertyRNA - not an array or collection %d", keynum);
-                       ret = -1;
+
+       if (PyLong_Check(key)) {
+               return prop_subscript_ass_array_int(self, PyLong_AsSsize_t(key), value);
+       }
+#if PY_VERSION_HEX >= 0x03000000
+       else if (PySlice_Check(key)) {
+               int len= RNA_property_array_length(self->prop);
+               Py_ssize_t start, stop, step, slicelength;
+
+               if (PySlice_GetIndicesEx((PySliceObject*)key, len, &start, &stop, &step, &slicelength) < 0)
+                       return -1;
+
+               if (slicelength <= 0) {
+                       return 0;
                }
-               
-               if (keynum >= len){
-                       PyErr_SetString(PyExc_AttributeError, "PropertyRNA - index out of range");
-                       ret = -1;
-               } else {
-                       ret = pyrna_py_to_prop_index(&self->ptr, self->prop, keynum, value);
+               else if (step == 1) {
+                       return prop_subscript_ass_array_slice(self, start, stop, value);
+               }
+               else {
+                       PyErr_SetString(PyExc_TypeError, "slice steps not supported with rna");
+                       return -1;
                }
        }
-       
-       return ret;
+#endif
+       else {
+               PyErr_SetString(PyExc_AttributeError, "invalid key, key must be an int");
+               return -1;
+       }
 }
 
 
-
 static PyMappingMethods pyrna_prop_as_mapping = {
        ( lenfunc ) pyrna_prop_len,     /* mp_length */
        ( binaryfunc ) pyrna_prop_subscript,    /* mp_subscript */
-       ( objobjargproc ) pyrna_prop_assign_subscript,  /* mp_ass_subscript */
+       ( objobjargproc ) pyrna_prop_ass_subscript,     /* mp_ass_subscript */
 };
 
 static int pyrna_prop_contains(BPy_PropertyRNA * self, PyObject *value)
@@ -1252,8 +1391,10 @@ static int foreach_parse_args(
                /*values to assign */
                char **attr, PyObject **seq, int *tot, int *size, RawPropertyType *raw_type, int *attr_tot, int *attr_signed)
 {
+#if 0
        int array_tot;
        int target_tot;
+#endif
 
        *size= *raw_type= *attr_tot= *attr_signed= 0;
 
@@ -1340,7 +1481,7 @@ static PyObject *foreach_getset(BPy_PropertyRNA *self, PyObject *args, int set)
                        Py_buffer buf;
                        PyObject_GetBuffer(seq, &buf, PyBUF_SIMPLE | PyBUF_FORMAT);
 
-                       /* check if the buffer matches, TODO - signed/unsigned types */
+                       /* check if the buffer matches */
 
                        buffer_is_compat = foreach_compat_buffer(raw_type, attr_signed, buf.format);