[Zodb-checkins] SVN: ZODB/trunk/ Merge the alienoid-btrees_pop branch.

Tim Peters tim.one at comcast.net
Fri Sep 2 18:28:17 EDT 2005


Log message for revision 38270:
  Merge the alienoid-btrees_pop branch.
  
  This adds .pop() methods to BTrees and Buckets.
  
  Alas, zpkg changed since the branch was made, and it
  wasn't (obviously) possible to build the branch anymore,
  so much of this code didn't actually appear on the
  branch.  The C code was changed to simplify and purge
  non-ANSI gcc extensions, and the new test cases beefed
  up a little.
  

Changed:
  U   ZODB/trunk/NEWS.txt
  U   ZODB/trunk/src/BTrees/BTreeTemplate.c
  U   ZODB/trunk/src/BTrees/BucketTemplate.c
  U   ZODB/trunk/src/BTrees/Interfaces.py
  U   ZODB/trunk/src/BTrees/tests/testBTrees.py

-=-
Modified: ZODB/trunk/NEWS.txt
===================================================================
--- ZODB/trunk/NEWS.txt	2005-09-02 22:12:23 UTC (rev 38269)
+++ ZODB/trunk/NEWS.txt	2005-09-02 22:28:17 UTC (rev 38270)
@@ -31,12 +31,12 @@
 BTrees
 ------
 
-- (3.6a1) BTrees and Buckets now implement the ``setdefault()`` method.
-  This is exactly like Python's ``setdefault()`` method for dictionaries,
-  except that both arguments are required (and Python is likely to change
-  to require both arguments too -- defaulting the ``default`` argument to
-  ``None`` has no viable use cases).  Thanks to Ruslan Spivak for
-  contributing code, tests, and documentation.
+- (3.6a1) BTrees and Buckets now implement the ``setdefault()`` and ``pop()``
+  methods. These are exactly like Python's dictionary methods of the same
+  names, except that ``setdefault()`` requires both arguments (and Python is
+  likely to change to require both arguments too -- defaulting the
+  ``default`` argument to ``None`` has no viable use cases).  Thanks to
+  Ruslan Spivak for contributing code, tests, and documentation.
 
 - (3.6a1) Collector 1873.  It wasn't possible to construct a BTree or Bucket
   from, or apply their update() methods to, a PersistentMapping or

Modified: ZODB/trunk/src/BTrees/BTreeTemplate.c
===================================================================
--- ZODB/trunk/src/BTrees/BTreeTemplate.c	2005-09-02 22:12:23 UTC (rev 38269)
+++ ZODB/trunk/src/BTrees/BTreeTemplate.c	2005-09-02 22:28:17 UTC (rev 38270)
@@ -1729,7 +1729,51 @@
     return value;
 }
 
+/* forward declaration */
+static int
+BTree_length_or_nonzero(BTree *self, int nonzero);
 
+static PyObject *
+BTree_pop(BTree *self, PyObject *args)
+{
+    PyObject *key;
+    PyObject *failobj = NULL; /* default */
+    PyObject *value;          /* return value */
+
+    if (! PyArg_UnpackTuple(args, "pop", 1, 2, &key, &failobj))
+    	return NULL;
+
+    value = _BTree_get(self, key, 0);
+    if (value != NULL) {
+        /* Delete key and associated value. */
+        if (_BTree_set(self, key, NULL, 0, 0) < 0) {
+            Py_DECREF(value);
+            return NULL;;
+        }
+        return value;
+    }
+
+    /* The key isn't in the tree.  If that's not due to a KeyError exception,
+     * pass back the unexpected exception.
+     */
+    if (! PyErr_ExceptionMatches(PyExc_KeyError))
+        return NULL;
+
+    if (failobj != NULL) {
+    	/* Clear the KeyError and return the explicit default. */
+    	PyErr_Clear();
+    	Py_INCREF(failobj);
+    	return failobj;
+    }
+
+    /* No default given.  The only difference in this case is the error
+     * message, which depends on whether the tree is empty.
+     */
+    if (BTree_length_or_nonzero(self, 1) == 0) /* tree is empty */
+        PyErr_SetString(PyExc_KeyError, "pop(): BTree is empty");
+    return NULL;
+}
+
 /* Search BTree self for key.  This is the sq_contains slot of the
  * PySequenceMethods.
  *
@@ -1873,6 +1917,11 @@
      "Return the value like get() except that if key is missing, d is both\n"
      "returned and inserted into the BTree as the value of k."},
 
+    {"pop", (PyCFunction) BTree_pop, METH_VARARGS,
+     "D.pop(k[, d]) -> v, remove key and return the corresponding value.\n\n"
+     "If key is not found, d is returned if given, otherwise KeyError\n"
+     "is raised."},
+
     {"maxKey", (PyCFunction) BTree_maxKey,	METH_VARARGS,
      "maxKey([max]) -> key\n\n"
      "Return the largest key in the BTree.  If max is specified, return\n"

Modified: ZODB/trunk/src/BTrees/BucketTemplate.c
===================================================================
--- ZODB/trunk/src/BTrees/BucketTemplate.c	2005-09-02 22:12:23 UTC (rev 38269)
+++ ZODB/trunk/src/BTrees/BucketTemplate.c	2005-09-02 22:28:17 UTC (rev 38270)
@@ -1287,6 +1287,53 @@
     return value;
 }
 
+
+/* forward declaration */
+static int
+Bucket_length(Bucket *self);
+
+static PyObject *
+bucket_pop(Bucket *self, PyObject *args)
+{
+    PyObject *key;
+    PyObject *failobj = NULL; /* default */
+    PyObject *value;          /* return value */
+    int dummy_changed;        /* in order to call _bucket_set */
+
+    if (! PyArg_UnpackTuple(args, "pop", 1, 2, &key, &failobj))
+    	return NULL;
+
+    value = _bucket_get(self, key, 0);
+    if (value != NULL) {
+        /* Delete key and associated value. */
+        if (_bucket_set(self, key, NULL, 0, 0, &dummy_changed) < 0) {
+            Py_DECREF(value);
+            return NULL;;
+        }
+        return value;
+    }
+
+    /* The key isn't in the bucket.  If that's not due to a KeyError exception,
+     * pass back the unexpected exception.
+     */
+    if (! PyErr_ExceptionMatches(PyExc_KeyError))
+        return NULL;
+
+    if (failobj != NULL) {
+    	/* Clear the KeyError and return the explicit default. */
+    	PyErr_Clear();
+    	Py_INCREF(failobj);
+    	return failobj;
+    }
+
+    /* No default given.  The only difference in this case is the error
+     * message, which depends on whether the bucket is empty.
+     */
+    if (Bucket_length(self) == 0)
+        PyErr_SetString(PyExc_KeyError, "pop(): Bucket is empty");
+    return NULL;
+ }
+
 /* Search bucket self for key.  This is the sq_contains slot of the
  * PySequenceMethods.
  *
@@ -1515,6 +1562,11 @@
      "Return the value like get() except that if key is missing, d is both\n"
      "returned and inserted into the bucket as the value of k."},
 
+    {"pop", (PyCFunction) bucket_pop, METH_VARARGS,
+     "D.pop(k[, d]) -> v, remove key and return the corresponding value.\n\n"
+     "If key is not found, d is returned if given, otherwise KeyError\n"
+     "is raised."},
+
     {"iterkeys", (PyCFunction) Bucket_iterkeys,  METH_KEYWORDS,
      "B.iterkeys([min[,max]]) -> an iterator over the keys of B"},
 

Modified: ZODB/trunk/src/BTrees/Interfaces.py
===================================================================
--- ZODB/trunk/src/BTrees/Interfaces.py	2005-09-02 22:12:23 UTC (rev 38269)
+++ ZODB/trunk/src/BTrees/Interfaces.py	2005-09-02 22:28:17 UTC (rev 38270)
@@ -226,6 +226,13 @@
         IIBTree can have only integers as values).
         """
 
+    def pop(key, d):
+        """D.pop(k[, d]) -> v, remove key and return the corresponding value.
+
+        If key is not found, d is returned if given, otherwise KeyError is
+        raised.
+        """
+
 class IBTree(IDictionaryIsh):
 
     def insert(key, value):

Modified: ZODB/trunk/src/BTrees/tests/testBTrees.py
===================================================================
--- ZODB/trunk/src/BTrees/tests/testBTrees.py	2005-09-02 22:12:23 UTC (rev 38269)
+++ ZODB/trunk/src/BTrees/tests/testBTrees.py	2005-09-02 22:28:17 UTC (rev 38270)
@@ -628,6 +628,44 @@
         # Too many arguments.
         self.assertRaises(TypeError, t.setdefault, 1, 2, 3)
 
+
+    def testPop(self):
+        t = self.t
+
+        # Empty container.
+        # If no default given, raises KeyError.
+        self.assertRaises(KeyError, t.pop, 1)
+        # But if default given, returns that instead.
+        self.assertEqual(t.pop(1, 42), 42)
+
+        t[1] = 3
+        # KeyError when key is not in container and default is not passed.
+        self.assertRaises(KeyError, t.pop, 5)
+        self.assertEqual(list(t.items()), [(1, 3)])
+        # If key is in container, returns the value and deletes the key.
+        self.assertEqual(t.pop(1), 3)
+        self.assertEqual(len(t), 0)
+
+        # If key is present, return value bypassing default.
+        t[1] = 3
+        self.assertEqual(t.pop(1, 7), 3)
+        self.assertEqual(len(t), 0)
+
+        # Pop only one item.
+        t[1] = 3
+        t[2] = 4
+        self.assertEqual(len(t), 2)
+        self.assertEqual(t.pop(1), 3)
+        self.assertEqual(len(t), 1)
+        self.assertEqual(t[2], 4)
+        self.assertEqual(t.pop(1, 3), 3)
+
+        # Too few arguments.
+        self.assertRaises(TypeError, t.pop)
+        # Too many arguments.
+        self.assertRaises(TypeError, t.pop, 1, 2, 3)
+
+
 class NormalSetTests(Base):
     """ Test common to all set types """
 



More information about the Zodb-checkins mailing list