Attached is a patch for review.

As of revision 57341 (only a couple hours old as of this writing), test_shelve was failing on my machine. This was because I didn't have any swell databases available, so anydbm was falling back to dumbdbm, and dumbdbm had a bug. In Py3k, dumbdbm's dict-like interface now requires byte objects, which it internally encodes to "latin-1" then uses with a real dict. But dumbdbm.__contains__ was missing the conversion, so it was trying to use a bytes object with a real dict, and that failed with an error (as bytes objects are not hashable). This patch fixes dumbdbm.__contains__ so it encodes the key, fixing test_shelve on my machine.

But there's more! Neil Norvitz pointed out that test_shelve /didn't/ fail on his machine. That's because dumbdbm is the last resort of anydbm, and he had a superior database module available. So the regression test suite was really missing two things:

   * test_dumbdbm should test dumbdbm.__contains__.
   * test_anydbm should test all the database modules available, not
     merely its first choice.

So this patch also adds test_write_contains() to test_dumbdbm, and a new external function to test_anydbm: dbm_iterate(), which returns an iterator over all database modules available to anydbm, /and/ internally forces anydbm to use that database module, restoring anydbm to its first choice when it finishes iteration. I also renamed _delete_files() to delete_files() so it could be the canonical dbm cleanup function for other tests.

While I was at it, I noticed that test_whichdbm.py did a good job of testing all the databases available, but with a slightly odd approach: it iterated over all the possible databases, and created new test methods--inserting them into the class object--for each one that was available. I changed it to use dbm_iterate() and delete_files() from test.test_anydbm, so that that logic can live in only one place. I didn't preserve the setattr() approach; I simply iterate over all the modules and run the tests inside one conventional method.

One final thought, for the folks who defined this "in Py3k, database-backed dict-like objects use byte objects as keys" interface. dumbdbm.keys() returns self._index.keys(), which means that it's serving up real strings, not byte objects. Shouldn't it return [k.encode("latin-1") for k in self._index.keys()] ? (Or perhaps change iterkeys to return that as a generator expression, and change keys() to return list(self.iterkeys())?)

Thanks,


/larry/
Index: Lib/dumbdbm.py
===================================================================
--- Lib/dumbdbm.py      (revision 57346)
+++ Lib/dumbdbm.py      (working copy)
@@ -203,6 +203,7 @@
         return self._index.keys()
 
     def __contains__(self, key):
+        key = key.decode("latin-1")
         return key in self._index
 
     def iterkeys(self):
Index: Lib/test/test_dumbdbm.py
===================================================================
--- Lib/test/test_dumbdbm.py    (revision 57346)
+++ Lib/test/test_dumbdbm.py    (working copy)
@@ -89,6 +89,12 @@
         keys = self.keys_helper(f)
         f.close()
 
+    def test_write_contains(self):
+        f = dumbdbm.open(_fname)
+        f[b'1'] = b'hello'
+        assert b'1' in f
+        f.close()
+
     def test_write_write_read(self):
         # test for bug #482460
         f = dumbdbm.open(_fname)
Index: Lib/test/test_whichdb.py
===================================================================
--- Lib/test/test_whichdb.py    (revision 57346)
+++ Lib/test/test_whichdb.py    (working copy)
@@ -10,57 +10,47 @@
 import anydbm
 import tempfile
 import glob
+from test.test_anydbm import delete_files, dbm_iterator
 
 _fname = test.test_support.TESTFN
 
-def _delete_files():
-    # we don't know the precise name the underlying database uses
-    # so we use glob to locate all names
-    for f in glob.glob(_fname + "*"):
-        try:
-            os.unlink(f)
-        except OSError:
-            pass
-
 class WhichDBTestCase(unittest.TestCase):
     # Actual test methods are added to namespace
     # after class definition.
     def __init__(self, *args):
         unittest.TestCase.__init__(self, *args)
 
+    def test_whichdb(self):
+        for module in dbm_iterator():
+            # Check whether whichdb correctly guesses module name
+            # for databases opened with "module" module.
+            # Try with empty files first
+            name = module.__name__
+            f = module.open(_fname, 'c')
+            f.close()
+            self.assertEqual(name, whichdb.whichdb(_fname))
+            # Now add a key
+            f = module.open(_fname, 'w')
+            f[b"1"] = b"1"
+            # and test that we can find it
+            assert b"1" in f
+            # and read it
+            assert f[b"1"] == b"1"
+            f.close()
+            self.assertEqual(name, whichdb.whichdb(_fname))
+
     def tearDown(self):
-        _delete_files()
+        delete_files()
 
     def setUp(self):
-        _delete_files()
+        delete_files()
 
-for name in anydbm._names:
-    # we define a new test method for each
-    # candidate database module.
-    try:
-        mod = __import__(name)
-    except ImportError:
-        continue
 
-    def test_whichdb_name(self, name=name, mod=mod):
-        # Check whether whichdb correctly guesses module name
-        # for databases opened with module mod.
-        # Try with empty files first
-        f = mod.open(_fname, 'c')
-        f.close()
-        self.assertEqual(name, whichdb.whichdb(_fname))
-        # Now add a key
-        f = mod.open(_fname, 'w')
-        f[b"1"] = b"1"
-        f.close()
-        self.assertEqual(name, whichdb.whichdb(_fname))
-    setattr(WhichDBTestCase,"test_whichdb_%s" % name, test_whichdb_name)
-
 def test_main():
     try:
         test.test_support.run_unittest(WhichDBTestCase)
     finally:
-        _delete_files()
+        delete_files()
 
 if __name__ == "__main__":
     test_main()
Index: Lib/test/test_shelve.py
===================================================================
--- Lib/test/test_shelve.py     (revision 57346)
+++ Lib/test/test_shelve.py     (working copy)
@@ -3,6 +3,7 @@
 import glob
 from test import test_support
 from UserDict import DictMixin
+from test.test_anydbm import dbm_iterator
 
 def L1(s):
     return s.decode("latin-1")
@@ -148,15 +149,16 @@
     _in_mem = True
 
 def test_main():
-    test_support.run_unittest(
-        TestAsciiFileShelve,
-        TestBinaryFileShelve,
-        TestProto2FileShelve,
-        TestAsciiMemShelve,
-        TestBinaryMemShelve,
-        TestProto2MemShelve,
-        TestCase
-    )
+    for module in dbm_iterator():
+        test_support.run_unittest(
+            TestAsciiFileShelve,
+            TestBinaryFileShelve,
+            TestProto2FileShelve,
+            TestAsciiMemShelve,
+            TestBinaryMemShelve,
+            TestProto2MemShelve,
+            TestCase
+        )
 
 if __name__ == "__main__":
     test_main()
Index: Lib/test/test_anydbm.py
===================================================================
--- Lib/test/test_anydbm.py     (revision 57346)
+++ Lib/test/test_anydbm.py     (working copy)
@@ -11,7 +11,33 @@
 
 _fname = test_support.TESTFN
 
-def _delete_files():
+_all_modules = []
+
+for _name in anydbm._names:
+    try:
+        _module = __import__(_name)
+    except ImportError:
+        continue
+    _all_modules.append(_module)
+
+
+#
+# Iterates over every database module supported by anydbm
+# currently available, setting anydbm to use each in turn,
+# and yielding that module
+#
+def dbm_iterator():
+    old_default = anydbm._defaultmod
+    for module in _all_modules:
+        anydbm._defaultmod = module
+        yield module
+    anydbm._defaultmod = old_default
+
+#
+# Clean up all scratch databases we might have created
+# during testing
+#
+def delete_files():
     # we don't know the precise name the underlying database uses
     # so we use glob to locate all names
     for f in glob.glob(_fname + "*"):
@@ -60,6 +86,14 @@
         keys = self.keys_helper(f)
         f.close()
 
+    def test_anydbm_access(self):
+        self.init_db()
+        f = anydbm.open(_fname, 'r')
+        key = "a".encode("ascii")
+        assert(key in f)
+        assert(f[key] == b"Python:")
+        f.close()
+
     def read_helper(self, f):
         keys = self.keys_helper(f)
         for key in self._dict:
@@ -78,16 +112,18 @@
         return keys
 
     def tearDown(self):
-        _delete_files()
+        delete_files()
 
     def setUp(self):
-        _delete_files()
+        delete_files()
 
+
 def test_main():
     try:
-        test_support.run_unittest(AnyDBMTestCase)
+        for module in dbm_iterator():
+            test_support.run_unittest(AnyDBMTestCase)
     finally:
-        _delete_files()
+        delete_files()
 
 if __name__ == "__main__":
     test_main()
_______________________________________________
Python-3000 mailing list
[email protected]
http://mail.python.org/mailman/listinfo/python-3000
Unsubscribe: 
http://mail.python.org/mailman/options/python-3000/archive%40mail-archive.com

Reply via email to