def test_popitem(self): the_list = [("a", 1), ("b", 2), ("a", 3)] # Removes the last item old = OrderedMultiDict(the_list) self.assertTupleEqual(old.popitem(), ("a", 3)) self.assertEqual(len(old), 2) self.assertTupleEqual(old.popitem(), ("b", 2)) self.assertEqual(len(old), 1) self.assertTupleEqual(old.popitem(), ("a", 1)) self.assertEqual(len(old), 0) self.assertRaises(KeyError, old.popitem) try: from pvl.collections import PVLMultiDict # Removes a random item, in proper dict-like fashion new = PVLMultiDict(the_list) self.assertIn(new.popitem(), the_list) self.assertEqual(len(new), 2) new.popitem() new.popitem() self.assertRaises(KeyError, new.popitem) except ImportError: pass
def test_discard(self): the_list = [("a", 1), ("b", 2), ("a", 3)] # Has a set-like .discard() function old = OrderedMultiDict(the_list) old.discard("a") self.assertEqual(len(old), 1) self.assertRaises(KeyError, old.getall, "a") self.assertRaises(KeyError, old.__getitem__, "a") self.assertEqual(old.__getitem__("b"), 2) old.discard("b") self.assertEqual(len(old), 0) self.assertRaises(KeyError, old.__getitem__, "b") old.discard("c") self.assertEqual(len(old), 0) try: from pvl.collections import PVLMultiDict # Does not have a set-like .discard() function, # because it isn't a set! new = PVLMultiDict(the_list) self.assertRaises(AttributeError, getattr, new, "discard") except ImportError: pass
def test_repr(self): # Original repr old = OrderedMultiDict() self.assertEqual(repr(old), 'OrderedMultiDict([])') try: from pvl.collections import PVLMultiDict # MultiDict repr new = PVLMultiDict() self.assertEqual(repr(new), 'PVLMultiDict()') except ImportError: pass
def test_py3_items(self): the_list = [("a", 1), ("b", 2), ("a", 3)] # These views are returned as lists! old = OrderedMultiDict(the_list) self.assertIsInstance(old.items(), pvl.collections.ItemsView) self.assertIsInstance(old.keys(), pvl.collections.KeysView) self.assertIsInstance(old.values(), pvl.collections.ValuesView) views = [ (old.items(), old.keys(), old.values()), ] try: from pvl.collections import PVLMultiDict # These are proper Python 3 views: new = PVLMultiDict(the_list) self.assertIsInstance(new.items(), abc.ItemsView) self.assertIsInstance(new.keys(), abc.KeysView) self.assertIsInstance(new.values(), abc.ValuesView) views.append( (list(new.items()), list(new.keys()), list(new.values()))) except ImportError: pass # However, if you wrap the new items in a list (as above), this is # the same: for items, keys, values in views: self.assertTupleEqual(items[0], ("a", 1)) self.assertTupleEqual(items[1], ("b", 2)) self.assertTupleEqual(items[2], ("a", 3)) self.assertEqual(items.index(("a", 1)), 0) self.assertEqual(items.index(("b", 2)), 1) self.assertEqual(items.index(("a", 3)), 2) self.assertEqual(keys[0], "a") self.assertEqual(keys[1], "b") self.assertEqual(keys[2], "a") self.assertEqual(keys.index("a"), 0) self.assertEqual(keys.index("b"), 1) self.assertEqual(values[0], 1) self.assertEqual(values[1], 2) self.assertEqual(values[2], 3) self.assertEqual(values.index(1), 0) self.assertEqual(values.index(2), 1) self.assertEqual(values.index(3), 2)
def test_as_list(self): the_list = [('a', 1), ('b', 2)] # Returns list of tuples: old = OrderedMultiDict(the_list) self.assertListEqual(list(old), [('a', 1), ('b', 2)]) try: from pvl.collections import PVLMultiDict # Returns list of keys, which is semantically identical to calling # list() on a dict. new = PVLMultiDict(the_list) self.assertListEqual(list(new), ['a', 'b']) except ImportError: pass
def test_pop(self): the_list = [("a", 1), ("b", 2), ("a", 3)] # Removes all keys that match, but returns only the first value, which # is weird old = OrderedMultiDict(the_list) self.assertEqual(old.pop("a"), 1) self.assertEqual(len(old), 1) self.assertRaises(KeyError, old.getall, "a") self.assertRaises(KeyError, old.pop, "a") self.assertEqual(old.pop("a", 42), 42) self.assertEqual(old.pop("b"), 2) self.assertEqual(len(old), 0) self.assertRaises(KeyError, old.pop, "b") self.assertRaises(KeyError, old.__getitem__, "b") self.assertRaises(KeyError, old.pop, "c") self.assertEqual(old.pop("c", 42), 42) try: from pvl.collections import PVLMultiDict # Removes only the first key new = PVLMultiDict(the_list) self.assertEqual(new.pop("a"), 1) self.assertEqual(len(new), 2) self.assertListEqual(new.getall("a"), [ 3, ]) self.assertEqual(new.pop("a"), 3) self.assertEqual(new.pop("a", 42), 42) self.assertEqual(new.pop("b"), 2) self.assertEqual(len(new), 0) self.assertRaises(KeyError, new.pop, "b") self.assertRaises(KeyError, new.__getitem__, "b") self.assertRaises(KeyError, new.pop, "c") self.assertEqual(new.pop("c", 42), 42) except ImportError: pass
def test_conversion(self): the_list = [("a", 1), ("b", 2), ("a", 3)] # This returns a list of key, value tuple pairs old = OrderedMultiDict(the_list) self.assertListEqual(list(old), the_list) # === Callling dict(old) === # This is the one test that I could not get to pass from # pvl 0.3, and now I know why: it is because the # OrderedMultiDict inherently carries two copies of the # information added to it: one in the default dict that # it inherits from being directly subclassed from dict, # and the other is an internal list of key, value tuples. # # When I tried to run this test using the regular Python # interpreters, I got # self.assertEqual(dict(old), {'a': 1, 'b': 2}) # This is because when it is passed to the dict constructor, # Python makes it into a regular Mapping object with unique # keys, inevitably loosing the double value of 'a'. # # I could not understand why pvl 0.3 had this test that it # expected to pass: # expected_dict = { # 'a': [1, 3], # 'b': [2], # } # # That was until Travis ran the tests using both the regular # python interpreter and pypy (I don't usually run pypy tests locally). # If you run the test with a regular python interpreter, you get the # first result, with the second value of 'a' being lost. If you run # the test with the pypy3 interpreter, you get the second result, with # the values for the keys being lists! # # This scared the heck out of me. # # This is what I think is happening: # When you run dict() on an OrderedMultiDict, the python interpreter # uses the fact that it is a Mapping item, and internally runs its # .items() function to get the (key, value) pairs and builds a new # dict from them (it encounters a second value for the key 'a', and # looses it). The pypy3 interpreter, must not use the .items() # function on the passed-in Mapping object, but notices that this # object derives from a dict, and so just grabs the internal dict # representation, which OrderedMultiDict hides from the user, but # is implemented as single keys with lists as the values. # # And that's why the same code produces different results with # different interpreters. try: from pvl.collections import PVLMultiDict # This returns the same thing that calling list() on a dict would, # the list of keys new = PVLMultiDict(the_list) self.assertListEqual(list(new), ["a", "b", "a"]) self.assertEqual(dict(new), {"a": 1, "b": 2}) except ImportError: pass