[Zope-Checkins] CVS: Zope3/lib/python/Persistence/BTrees - BTreeItemsTemplate.c:1.6 BTreeModuleTemplate.c:1.8 BTreeTemplate.c:1.35 BucketTemplate.c:1.13 SetTemplate.c:1.5 TreeSetTemplate.c:1.5

Tim Peters tim.one@comcast.net
Sat, 22 Jun 2002 03:18:12 -0400


Update of /cvs-repository/Zope3/lib/python/Persistence/BTrees
In directory cvs.zope.org:/tmp/cvs-serv11837

Modified Files:
	BTreeItemsTemplate.c BTreeModuleTemplate.c BTreeTemplate.c 
	BucketTemplate.c SetTemplate.c TreeSetTemplate.c 
Log Message:
Sets, TreeSets, Buckets, and BTrees in Zope3 now participate in the
new-in-Python-2.2 iteration protocol.  The short course is that
anywhere an "iterable object" can be used, you can now use an object
of one of the Bucket/BTree-based data types.

For example, where x is a Set, TreeSet, Bucket, or BTree:

    for key in x:

is the same as

    for key in x.keys():

However, the former is more efficient for TreeSets and BTrees, while for
Sets and Buckets it doesn't need to materialize the whole set of keys in
one gulp.  Note that results are undefined if you mutate x *during* the
iteration.  If the iterator implementation detects (not likely), that
you've done so, it will raise a RuntimeError saying so.

Lots of other contexts in 2.2 accept iterable objects too, including
contexts that in pre-2.2 accepted only sequences.  For example,

    list(x)

is the same as list(x.keys()).  Perhaps subtler (note that this isn't
a special case -- it just falls out of playing along with the iteration
protocol):

    IIBucket(some_IIBTree)

sucks the keys out of some_IIBTree and puts them in a bucket.

On the ridiculous end of the scale, unpacking notation also accepts
iterable objects, so, for example,

>>> s = IISet([7, 4, 2])
>>> x, y, z = s
>>> print x
2
>>> print y
4
>>> print z
7
>>>

Even more ridiculous, min(x) is now a very expensive way to spell
x.minKey().  Stick to things that really need iteration <wink>.


=== Zope3/lib/python/Persistence/BTrees/BTreeItemsTemplate.c 1.5 => 1.6 ===
   return 0;
 }
+
+/* Support for the iteration protocol new in Python 2.2. */
+
+staticforward PyTypeObject BTreeIter_Type;
+
+/* The type of iterator objects, returned by e.g. iter(IIBTree()). */
+typedef struct {
+    PyObject_HEAD
+    /* We use a BTreeItems object because it's convenient and flexible.
+     * We abuse it two ways:
+     *     1. We set currentbucket to NULL when the iteration is finished.
+     *     2. We don't bother keeping pseudoindex in synch.
+     */
+    BTreeItems *pitems;
+} BTreeIter;
+
+/* Return a new iterator object, to traverse the keys and/or values
+ * represented by pitems.  pitems must not be NULL.  Returns NULL if error.
+ */
+static BTreeIter *
+BTreeIter_new(BTreeItems *pitems)
+{
+    BTreeIter *result;
+
+    assert(pitems != NULL);
+    result = PyObject_New(BTreeIter, &BTreeIter_Type);
+    if (result) {
+        Py_INCREF(pitems);
+        result->pitems = pitems;
+    }
+    return result;
+}
+
+/* The iterator's tp_dealloc slot. */
+static void
+BTreeIter_dealloc(BTreeIter *bi)
+{
+	Py_DECREF(bi->pitems);
+	PyObject_Del(bi);
+}
+
+/* The implementation of the iterator's .next() method.  Returns "the next"
+ * item; returns NULL if error; returns NULL and sets StopIteration if the
+ * iteration is exhausted (that's the way to terminate the iteration protocol).
+ */
+static PyObject *
+BTreeIter_next(BTreeIter *bi, PyObject *args)
+{
+	PyObject *result = NULL;        /* until proven innocent */
+        BTreeItems *items = bi->pitems;
+        int i = items->currentoffset;
+	Bucket *bucket = items->currentbucket;
+
+        if (bucket == NULL) {
+            PyErr_SetObject(PyExc_StopIteration, Py_None);
+	    return NULL;
+        }
+
+        PER_USE_OR_RETURN(bucket, NULL);
+        if (i >= bucket->len) {
+            /* We never leave this routine with i >= len:  somebody else
+             * mutated the current bucket.
+             */
+	    PyErr_SetString(PyExc_RuntimeError,
+		            "the bucket being iterated changed size");
+	    goto Done;
+	}
+
+        /* Build the result object, from bucket at offset i. */
+        switch (items->kind) {
+
+        case 'k':
+            COPY_KEY_TO_OBJECT(result, bucket->keys[i]);
+            break;
+
+        case 'v':
+            COPY_VALUE_TO_OBJECT(result, bucket->values[i]);
+            break;
+
+        case 'i': {
+            PyObject *key;
+            PyObject *value;
+
+            COPY_KEY_TO_OBJECT(key, bucket->keys[i]);
+            COPY_VALUE_TO_OBJECT(value, bucket->values[i]);
+
+            result = PyTuple_New(2);
+            if (result == NULL) goto Done;
+            PyTuple_SET_ITEM(result, 0, key);
+            PyTuple_SET_ITEM(result, 1, value);
+            break;
+        }
+
+        default:
+            assert(!"unknown items->kind value");
+            goto Done;
+        }
+
+        /* Advance position for next call. */
+        if (bucket == items->lastbucket && i >= items->last) {
+            /* Next call should terminate the iteration. */
+            Py_DECREF(items->currentbucket);
+            items->currentbucket = NULL;
+        }
+        else {
+            ++i;
+            if (i >= bucket->len) {
+                Py_XINCREF(bucket->next);
+                items->currentbucket = bucket->next;
+                Py_DECREF(bucket);
+                i = 0;
+            }
+            items->currentoffset = i;
+        }
+
+Done:
+    PER_UNUSE(bucket);
+    return result;
+}
+
+static PyObject *
+BTreeIter_getiter(PyObject *it)
+{
+    Py_INCREF(it);
+    return it;
+}
+
+static PyMethodDef btreeiter_methods[] = {
+	{"next", (PyCFunction)BTreeIter_next, METH_VARARGS,
+	 "it.next() -- get the next value, or raise StopIteration"},
+	{NULL,		NULL}		/* sentinel */
+};
+
+static PyTypeObject BTreeIter_Type = {
+        PyObject_HEAD_INIT(NULL)
+	0,					/* ob_size */
+	MOD_NAME_PREFIX "-iterator",		/* tp_name */
+	sizeof(BTreeIter),			/* tp_basicsize */
+	0,					/* tp_itemsize */
+	/* methods */
+	(destructor)BTreeIter_dealloc,          /* tp_dealloc */
+	0,					/* tp_print */
+	0,					/* tp_getattr */
+	0,					/* tp_setattr */
+	0,					/* tp_compare */
+	0,					/* tp_repr */
+	0,					/* tp_as_number */
+	0,					/* tp_as_sequence */
+	0,					/* tp_as_mapping */
+	0,					/* tp_hash */
+	0,					/* tp_call */
+	0,					/* tp_str */
+	PyObject_GenericGetAttr,		/* tp_getattro */
+	0,					/* tp_setattro */
+	0,					/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT,			/* tp_flags */
+ 	0,					/* tp_doc */
+ 	0,					/* tp_traverse */
+ 	0,					/* tp_clear */
+	0,					/* tp_richcompare */
+	0,					/* tp_weaklistoffset */
+	(getiterfunc)BTreeIter_getiter,		/* tp_iter */
+	(iternextfunc)BTreeIter_next,	        /* tp_iternext */
+	btreeiter_methods,			/* tp_methods */
+	0,					/* tp_members */
+	0,					/* tp_getset */
+	0,					/* tp_base */
+	0,					/* tp_dict */
+	0,					/* tp_descr_get */
+	0,					/* tp_descr_set */
+};


=== Zope3/lib/python/Persistence/BTrees/BTreeModuleTemplate.c 1.7 => 1.8 ===
 
     BTreeItemsType.ob_type = &PyType_Type;
+    BTreeIter_Type.ob_type = &PyType_Type;
     if (!init_persist_type(&BucketType))
 	return;
     if (!init_persist_type(&BTreeType))


=== Zope3/lib/python/Persistence/BTrees/BTreeTemplate.c 1.34 => 1.35 ===
 
 static int
-BTree_nonzero( BTree *self)
+BTree_nonzero(BTree *self)
 {
   return BTree_length_or_nonzero(self, 1);
 }
 
+/* The implementation of iter(BTree_or_TreeSet). */
+static PyObject *
+BTree_getiter(BTree *self)
+{
+    BTreeIter *result = NULL;
+    BTreeItems *items = (BTreeItems *)BTree_rangeSearch(self, NULL, 'k');
+
+    if (items) {
+        result = BTreeIter_new(items);
+        Py_DECREF(items);
+    }
+    return (PyObject *)result;
+}
+
 static PyNumberMethods BTree_as_number_for_nonzero = {
   0,0,0,0,0,0,0,0,0,0,
   (inquiry)BTree_nonzero};
@@ -1913,7 +1927,7 @@
     (inquiry)BTree_tp_clear,		/* tp_clear */
     0,					/* tp_richcompare */
     offsetof(BTree, po_weaklist),	/* tp_weaklistoffset */
-    0,					/* tp_iter */
+    (getiterfunc)BTree_getiter,		/* tp_iter */
     0,					/* tp_iternext */
     BTree_methods,			/* tp_methods */
     BTree_members,			/* tp_members */


=== Zope3/lib/python/Persistence/BTrees/BucketTemplate.c 1.12 => 1.13 ===
 }
 
+/* The implementation of iter(Bucket_or_Set). */
+static PyObject *
+Bucket_getiter(Bucket *self)
+{
+    BTreeItems *items;
+    int lowoffset, highoffset;
+    BTreeIter *result = NULL;
+
+    PER_USE_OR_RETURN(self, NULL);
+    if (Bucket_rangeSearch(self, NULL, &lowoffset, &highoffset) < 0) goto Done;
+
+    items = (BTreeItems *)newBTreeItems('k', self, lowoffset, self, highoffset);
+    if (items == NULL) goto Done;
+
+    result = BTreeIter_new(items);      /* win or lose, we're done */
+    Py_DECREF(items);
+
+Done:
+    PER_UNUSE(self);
+    return (PyObject *)result;
+}
+
 static PyTypeObject BucketType = {
     PyObject_HEAD_INIT(NULL) /* PyPersist_Type */
     0,					/* ob_size */
@@ -1452,7 +1474,7 @@
     (inquiry)bucket_tp_clear,		/* tp_clear */
     0,					/* tp_richcompare */
     offsetof(Bucket, po_weaklist),	/* tp_weaklistoffset */
-    0,					/* tp_iter */
+    (getiterfunc)Bucket_getiter,	/* tp_iter */
     0,					/* tp_iternext */
     Bucket_methods,			/* tp_methods */
     Bucket_members,			/* tp_members */


=== Zope3/lib/python/Persistence/BTrees/SetTemplate.c 1.4 => 1.5 ===
     0,					/* tp_richcompare */
     offsetof(Bucket, po_weaklist),	/* tp_weaklistoffset */
-    0,					/* tp_iter */
+    (getiterfunc)Bucket_getiter,	/* tp_iter */
     0,					/* tp_iternext */
     Set_methods,			/* tp_methods */
     0,					/* tp_members */


=== Zope3/lib/python/Persistence/BTrees/TreeSetTemplate.c 1.4 => 1.5 ===
     0,					/* tp_richcompare */
     offsetof(BTree, po_weaklist),	/* tp_weaklistoffset */
-    0,					/* tp_iter */
+    (getiterfunc)BTree_getiter,		/* tp_iter */
     0,					/* tp_iternext */
     TreeSet_methods,			/* tp_methods */
     0,					/* tp_members */