From 2c2d322884ee72077a256ec3cd0aa9ce3580eedc Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 9 Mar 2003 07:05:43 +0000 Subject: [PATCH] SF patch #667730: More DictMixin * Adds missing pop() methods to weakref.py * Expands test suite to broaden coverage of objects with a mapping interface. Contributed by Sebastien Keim. --- Lib/test/test_os.py | 18 ++++++ Lib/test/test_shelve.py | 49 ++++++++++++++- Lib/test/test_userdict.py | 126 +++++++++++++++++++++++++++++++++++++- Lib/test/test_weakref.py | 17 +++++ Lib/weakref.py | 15 +++++ Misc/ACKS | 1 + 6 files changed, 222 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 2b922552084..2956d73bce6 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -185,10 +185,28 @@ class StatAttributeTests(unittest.TestCase): except TypeError: pass +from test_userdict import TestMappingProtocol + +class EnvironTests(TestMappingProtocol): + """check that os.environ object conform to mapping protocol""" + _tested_class = None + def _reference(self): + return {"KEY1":"VALUE1", "KEY2":"VALUE2", "KEY3":"VALUE3"} + def _empty_mapping(self): + os.environ.clear() + return os.environ + def setUp(self): + self.__save = dict(os.environ) + os.environ.clear() + def tearDown(self): + os.environ.clear() + os.environ.update(self.__save) + def test_main(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(TemporaryFileTests)) suite.addTest(unittest.makeSuite(StatAttributeTests)) + suite.addTest(unittest.makeSuite(EnvironTests)) run_suite(suite) if __name__ == "__main__": diff --git a/Lib/test/test_shelve.py b/Lib/test/test_shelve.py index e69e311ed9c..e7c4b50cab5 100644 --- a/Lib/test/test_shelve.py +++ b/Lib/test/test_shelve.py @@ -43,9 +43,54 @@ class TestCase(unittest.TestCase): self.assertEqual(len(d1), 1) self.assertNotEqual(d1, d2) -def test_main(): - test_support.run_unittest(TestCase) +from test_userdict import TestMappingProtocol +class TestShelveBase(TestMappingProtocol): + fn = "shelftemp.db" + counter = 0 + def __init__(self, *args, **kw): + self._db = [] + TestMappingProtocol.__init__(self, *args, **kw) + _tested_class = shelve.Shelf + def _reference(self): + return {"key1":"value1", "key2":2, "key3":(1,2,3)} + def _empty_mapping(self): + if self._in_mem: + x= shelve.Shelf({}, binary = self._binary) + else: + self.counter+=1 + x= shelve.open(self.fn+str(self.counter), binary=self._binary) + self._db.append(x) + return x + def tearDown(self): + for db in self._db: + db.close() + self._db = [] + if not self._in_mem: + for f in glob.glob(self.fn+"*"): + os.unlink(f) + +class TestAsciiFileShelve(TestShelveBase): + _binary = False + _in_mem = False +class TestBinaryFileShelve(TestShelveBase): + _binary = True + _in_mem = False +class TestAsciiMemShelve(TestShelveBase): + _binary = False + _in_mem = True +class TestBinaryMemShelve(TestShelveBase): + _binary = True + _in_mem = True + +def test_main(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestAsciiFileShelve)) + suite.addTest(unittest.makeSuite(TestBinaryFileShelve)) + suite.addTest(unittest.makeSuite(TestAsciiMemShelve)) + suite.addTest(unittest.makeSuite(TestBinaryMemShelve)) + suite.addTest(unittest.makeSuite(TestCase)) + test_support.run_suite(suite) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_userdict.py b/Lib/test/test_userdict.py index cb983083db7..cbd3bd1d464 100644 --- a/Lib/test/test_userdict.py +++ b/Lib/test/test_userdict.py @@ -4,6 +4,123 @@ import test.test_support, unittest import UserDict +class TestMappingProtocol(unittest.TestCase): + # This base class can be used to check that an object conforms to the + # mapping protocol + + # Functions that can be useful to override to adapt to dictionary + # semantics + _tested_class = dict # which class is being tested + + def _reference(self): + """Return a dictionary of values which are invariant by storage + in the object under test.""" + return {1:2, "key1":"value1", "key2":(1,2,3)} + def _empty_mapping(self): + """Return an empty mapping object""" + return self._tested_class() + def _full_mapping(self, data): + """Return a mapping object with the value contained in data + dictionary""" + x = self._empty_mapping() + for key, value in data.items(): + x[key] = value + return x + + def __init__(self, *args, **kw): + unittest.TestCase.__init__(self, *args, **kw) + self.reference = self._reference().copy() + key, value = self.reference.popitem() + self.other = {key:value} + + def test_read(self): + # Test for read only operations on mapping + p = self._empty_mapping() + p1 = dict(p) #workaround for singleton objects + d = self._full_mapping(self.reference) + if d is p: + p = p1 + #Indexing + for key, value in self.reference.items(): + self.assertEqual(d[key], value) + knownkey = self.other.keys()[0] + self.failUnlessRaises(KeyError, lambda:d[knownkey]) + #len + self.assertEqual(len(p), 0) + self.assertEqual(len(d), len(self.reference)) + #has_key + for k in self.reference: + self.assert_(d.has_key(k)) + self.assert_(k in d) + for k in self.other: + self.failIf(d.has_key(k)) + self.failIf(k in d) + #cmp + self.assertEqual(cmp(p,p), 0) + self.assertEqual(cmp(d,d), 0) + self.assertEqual(cmp(p,d), -1) + self.assertEqual(cmp(d,p), 1) + #__non__zero__ + if p: self.fail("Empty mapping must compare to False") + if not d: self.fail("Full mapping must compare to True") + # keys(), items(), iterkeys() ... + def check_iterandlist(iter, lst, ref): + self.assert_(hasattr(iter, 'next')) + self.assert_(hasattr(iter, '__iter__')) + x = list(iter) + x.sort() + lst.sort() + ref.sort() + self.assert_(x==lst==ref) + check_iterandlist(d.iterkeys(), d.keys(), self.reference.keys()) + check_iterandlist(iter(d), d.keys(), self.reference.keys()) + check_iterandlist(d.itervalues(), d.values(), self.reference.values()) + check_iterandlist(d.iteritems(), d.items(), self.reference.items()) + #get + key, value = d.iteritems().next() + knownkey, knownvalue = self.other.iteritems().next() + self.assertEqual(d.get(key, knownvalue), value) + self.assertEqual(d.get(knownkey, knownvalue), knownvalue) + self.failIf(knownkey in d) + + def test_write(self): + # Test for write operations on mapping + p = self._empty_mapping() + #Indexing + for key, value in self.reference.items(): + p[key] = value + self.assertEqual(p[key], value) + for key in self.reference.keys(): + del p[key] + self.failUnlessRaises(KeyError, lambda:p[key]) + p = self._empty_mapping() + #update + p.update(self.reference) + self.assertEqual(dict(p), self.reference) + d = self._full_mapping(self.reference) + #setdefaullt + key, value = d.iteritems().next() + knownkey, knownvalue = self.other.iteritems().next() + self.assertEqual(d.setdefault(key, knownvalue), value) + self.assertEqual(d[key], value) + self.assertEqual(d.setdefault(knownkey, knownvalue), knownvalue) + self.assertEqual(d[knownkey], knownvalue) + #pop + self.assertEqual(d.pop(knownkey), knownvalue) + self.failIf(knownkey in d) + self.assertRaises(KeyError, d.pop, knownkey) + default = 909 + d[knownkey] = knownvalue + self.assertEqual(d.pop(knownkey, default), knownvalue) + self.failIf(knownkey in d) + self.assertEqual(d.pop(knownkey, default), default) + #popitem + key, value = d.popitem() + self.failIf(key in d) + self.assertEqual(value, self.reference[key]) + p=self._empty_mapping() + self.assertRaises(KeyError, p.popitem) + d0 = {} d1 = {"one": 1} d2 = {"one": 1, "two": 2} @@ -11,7 +128,9 @@ d3 = {"one": 1, "two": 3, "three": 5} d4 = {"one": None, "two": None} d5 = {"one": 1, "two": 1} -class UserDictTest(unittest.TestCase): +class UserDictTest(TestMappingProtocol): + _tested_class = UserDict.IterableUserDict + def test_all(self): # Test constructors u = UserDict.UserDict() @@ -182,7 +301,9 @@ class SeqDict(UserDict.DictMixin): def keys(self): return list(self.keylist) -class UserDictMixinTest(unittest.TestCase): +class UserDictMixinTest(TestMappingProtocol): + _tested_class = SeqDict + def test_all(self): ## Setup test and verify working of the test class @@ -275,6 +396,7 @@ class UserDictMixinTest(unittest.TestCase): def test_main(): suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestMappingProtocol)) suite.addTest(unittest.makeSuite(UserDictTest)) suite.addTest(unittest.makeSuite(UserDictMixinTest)) test.test_support.run_suite(suite) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 3a548cdfd74..b078b17472c 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -517,11 +517,28 @@ class MappingTestCase(TestBase): self.assert_(len(d) == 1) self.assert_(d.items() == [('something else', o2)]) +from test_userdict import TestMappingProtocol + +class WeakValueDictionaryTestCase(TestMappingProtocol): + """Check that WeakValueDictionary class conforms to the mapping protocol""" + __ref = {"key1":Object(1), "key2":Object(2), "key3":Object(3)} + _tested_class = weakref.WeakValueDictionary + def _reference(self): + return self.__ref.copy() + +class WeakKeyDictionaryTestCase(TestMappingProtocol): + """Check that WeakKeyDictionary class conforms to the mapping protocol""" + __ref = {Object("key1"):1, Object("key2"):2, Object("key3"):3} + _tested_class = weakref.WeakKeyDictionary + def _reference(self): + return self.__ref.copy() def test_main(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(ReferencesTestCase)) suite.addTest(unittest.makeSuite(MappingTestCase)) + suite.addTest(unittest.makeSuite(WeakValueDictionaryTestCase)) + suite.addTest(unittest.makeSuite(WeakKeyDictionaryTestCase)) test_support.run_suite(suite) diff --git a/Lib/weakref.py b/Lib/weakref.py index 6153bd966d6..838ff5ef7a5 100644 --- a/Lib/weakref.py +++ b/Lib/weakref.py @@ -101,6 +101,18 @@ class WeakValueDictionary(UserDict.UserDict): if o is not None: return key, o + def pop(self, key, *args): + try: + o = self.data.pop(key)() + except KeyError: + if args: + return args[0] + raise + if o is None: + raise KeyError, key + else: + return o + def setdefault(self, key, default): try: wr = self.data[key] @@ -225,6 +237,9 @@ class WeakKeyDictionary(UserDict.UserDict): if o is not None: return o, value + def pop(self, key, *args): + return self.data.pop(ref(key), *args) + def setdefault(self, key, default): return self.data.setdefault(ref(key, self._remove),default) diff --git a/Misc/ACKS b/Misc/ACKS index f4ecd10d5e0..abc1c75995d 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -281,6 +281,7 @@ Bob Kahn Tamito Kajiyama Jacob Kaplan-Moss Lou Kates +Sebastien Keim Randall Kern Robert Kern Magnus Kessler