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