[Python-checkins] r86828 - in python/branches/py3k: Doc/library/unittest.rst Lib/unittest/case.py Lib/unittest/test/test_assertions.py Lib/unittest/test/test_case.py Misc/NEWS

raymond.hettinger python-checkins at python.org
Sat Nov 27 10:31:37 CET 2010


Author: raymond.hettinger
Date: Sat Nov 27 10:31:37 2010
New Revision: 86828

Log:
Issue 10242: unittest.assertItemsEqual makes too many assumptions.

Modified:
   python/branches/py3k/Doc/library/unittest.rst
   python/branches/py3k/Lib/unittest/case.py
   python/branches/py3k/Lib/unittest/test/test_assertions.py
   python/branches/py3k/Lib/unittest/test/test_case.py
   python/branches/py3k/Misc/NEWS

Modified: python/branches/py3k/Doc/library/unittest.rst
==============================================================================
--- python/branches/py3k/Doc/library/unittest.rst	(original)
+++ python/branches/py3k/Doc/library/unittest.rst	Sat Nov 27 10:31:37 2010
@@ -1068,8 +1068,8 @@
    | :meth:`assertDictContainsSubset(a, b) | all the key/value pairs        | 3.1          |
    | <TestCase.assertDictContainsSubset>`  | in `a` exist in `b`            |              |
    +---------------------------------------+--------------------------------+--------------+
-   | :meth:`assertItemsEqual(a, b)         | `a` and `b` have the same      | 3.2          |
-   | <TestCase.assertItemsEqual>`          | elements in the same number,   |              |
+   | :meth:`assertCountEqual(a, b)         | `a` and `b` have the same      | 3.2          |
+   | <TestCase.assertCountEqual>`          | elements in the same number,   |              |
    |                                       | regardless of their order      |              |
    +---------------------------------------+--------------------------------+--------------+
 
@@ -1130,7 +1130,7 @@
       .. versionadded:: 3.1
 
 
-   .. method:: assertItemsEqual(actual, expected, msg=None)
+   .. method:: assertCountEqual(expected, actual, msg=None)
 
       Test that sequence *expected* contains the same elements as *actual*,
       regardless of their order. When they don't, an error message listing the
@@ -1138,12 +1138,18 @@
 
       Duplicate elements are *not* ignored when comparing *actual* and
       *expected*. It verifies if each element has the same count in both
-      sequences. It is the equivalent of ``assertEqual(sorted(expected),
-      sorted(actual))`` but it works with sequences of unhashable objects as
-      well.
+      sequences. Equivalent to:
+      ``assertEqual(Counter(iter(expected)), Counter(iter(actual)))``
+      but works with sequences of unhashable objects as well.
 
       .. versionadded:: 3.2
 
+   .. method:: assertItemsEqual(actual, expected, msg=None)
+
+      Outdated name for :meth:`assertCountEqual`, kept for compatibility
+      with Python 2.7.
+
+      .. versionadded:: 3.2
 
    .. method:: assertSameElements(actual, expected, msg=None)
 
@@ -1155,7 +1161,7 @@
       It is the equivalent of ``assertEqual(set(expected), set(actual))``
       but it works with sequences of unhashable objects as well. Because
       duplicates are ignored, this method has been deprecated in favour of
-      :meth:`assertItemsEqual`.
+      :meth:`assertCountEqual`.
 
       .. versionadded:: 3.1
       .. deprecated:: 3.2

Modified: python/branches/py3k/Lib/unittest/case.py
==============================================================================
--- python/branches/py3k/Lib/unittest/case.py	(original)
+++ python/branches/py3k/Lib/unittest/case.py	Sat Nov 27 10:31:37 2010
@@ -6,6 +6,7 @@
 import pprint
 import re
 import warnings
+import collections
 
 from . import result
 from .util import (strclass, safe_repr, sorted_list_difference,
@@ -990,15 +991,13 @@
             self.fail(self._formatMessage(msg, standardMsg))
 
 
-    def assertItemsEqual(self, expected_seq, actual_seq, msg=None):
-        """An unordered sequence / set specific comparison. It asserts that
-        expected_seq and actual_seq contain the same elements. It is
-        the equivalent of::
+    def assertCountEqual(self, expected_seq, actual_seq, msg=None):
+        """An unordered sequence specific comparison. It asserts that
+        expected_seq and actual_seq have the same element counts.
+        Equivalent to::
 
-            self.assertEqual(sorted(expected_seq), sorted(actual_seq))
-
-        Raises with an error message listing which elements of expected_seq
-        are missing from actual_seq and vice versa if any.
+            self.assertEqual(Counter(iter(expected_seq)),
+                             Counter(iter(actual_seq)))
 
         Asserts that each element has the same count in both sequences.
         Example:
@@ -1006,15 +1005,18 @@
             - [0, 0, 1] and [0, 1] compare unequal.
         """
         try:
-            expected = sorted(expected_seq)
-            actual = sorted(actual_seq)
+            expected = collections.Counter(iter(expected_seq))
+            actual = collections.Counter(iter(actual_seq))
         except TypeError:
             # Unsortable items (example: set(), complex(), ...)
             expected = list(expected_seq)
             actual = list(actual_seq)
             missing, unexpected = unorderable_list_difference(expected, actual)
         else:
-            return self.assertSequenceEqual(expected, actual, msg=msg)
+            if expected == actual:
+                return
+            missing = list(expected - actual)
+            unexpected = list(actual - expected)
 
         errors = []
         if missing:
@@ -1027,6 +1029,9 @@
             standardMsg = '\n'.join(errors)
             self.fail(self._formatMessage(msg, standardMsg))
 
+    # Old name for assertCountEqual()
+    assertItemsEqual = assertCountEqual
+
     def assertMultiLineEqual(self, first, second, msg=None):
         """Assert that two multi-line strings are equal."""
         self.assertIsInstance(first, str, 'First argument is not a string')

Modified: python/branches/py3k/Lib/unittest/test/test_assertions.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_assertions.py	(original)
+++ python/branches/py3k/Lib/unittest/test/test_assertions.py	Sat Nov 27 10:31:37 2010
@@ -229,8 +229,8 @@
                              "^Missing: 'key'$",
                              "^Missing: 'key' : oops$"])
 
-    def testAssertItemsEqual(self):
-        self.assertMessages('assertItemsEqual', ([], [None]),
+    def testassertCountEqual(self):
+        self.assertMessages('assertCountEqual', ([], [None]),
                             [r"\[None\]$", "^oops$",
                              r"\[None\]$",
                              r"\[None\] : oops$"])

Modified: python/branches/py3k/Lib/unittest/test/test_case.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_case.py	(original)
+++ python/branches/py3k/Lib/unittest/test/test_case.py	Sat Nov 27 10:31:37 2010
@@ -672,46 +672,53 @@
         else:
             self.fail('assertMultiLineEqual did not fail')
 
-    def testAssertItemsEqual(self):
+    def testassertCountEqual(self):
         a = object()
-        self.assertItemsEqual([1, 2, 3], [3, 2, 1])
-        self.assertItemsEqual(['foo', 'bar', 'baz'], ['bar', 'baz', 'foo'])
-        self.assertItemsEqual([a, a, 2, 2, 3], (a, 2, 3, a, 2))
-        self.assertItemsEqual([1, "2", "a", "a"], ["a", "2", True, "a"])
-        self.assertRaises(self.failureException, self.assertItemsEqual,
+        self.assertCountEqual([1, 2, 3], [3, 2, 1])
+        self.assertCountEqual(['foo', 'bar', 'baz'], ['bar', 'baz', 'foo'])
+        self.assertCountEqual([a, a, 2, 2, 3], (a, 2, 3, a, 2))
+        self.assertCountEqual([1, "2", "a", "a"], ["a", "2", True, "a"])
+        self.assertRaises(self.failureException, self.assertCountEqual,
                           [1, 2] + [3] * 100, [1] * 100 + [2, 3])
-        self.assertRaises(self.failureException, self.assertItemsEqual,
+        self.assertRaises(self.failureException, self.assertCountEqual,
                           [1, "2", "a", "a"], ["a", "2", True, 1])
-        self.assertRaises(self.failureException, self.assertItemsEqual,
+        self.assertRaises(self.failureException, self.assertCountEqual,
                           [10], [10, 11])
-        self.assertRaises(self.failureException, self.assertItemsEqual,
+        self.assertRaises(self.failureException, self.assertCountEqual,
                           [10, 11], [10])
-        self.assertRaises(self.failureException, self.assertItemsEqual,
+        self.assertRaises(self.failureException, self.assertCountEqual,
                           [10, 11, 10], [10, 11])
 
         # Test that sequences of unhashable objects can be tested for sameness:
-        self.assertItemsEqual([[1, 2], [3, 4], 0], [False, [3, 4], [1, 2]])
+        self.assertCountEqual([[1, 2], [3, 4], 0], [False, [3, 4], [1, 2]])
 
         # hashable types, but not orderable
-        self.assertRaises(self.failureException, self.assertItemsEqual,
+        self.assertRaises(self.failureException, self.assertCountEqual,
                           [], [divmod, 'x', 1, 5j, 2j, frozenset()])
         # comparing dicts
-        self.assertItemsEqual([{'a': 1}, {'b': 2}], [{'b': 2}, {'a': 1}])
+        self.assertCountEqual([{'a': 1}, {'b': 2}], [{'b': 2}, {'a': 1}])
         # comparing heterogenous non-hashable sequences
-        self.assertItemsEqual([1, 'x', divmod, []], [divmod, [], 'x', 1])
-        self.assertRaises(self.failureException, self.assertItemsEqual,
+        self.assertCountEqual([1, 'x', divmod, []], [divmod, [], 'x', 1])
+        self.assertRaises(self.failureException, self.assertCountEqual,
                           [], [divmod, [], 'x', 1, 5j, 2j, set()])
-        self.assertRaises(self.failureException, self.assertItemsEqual,
+        self.assertRaises(self.failureException, self.assertCountEqual,
                           [[1]], [[2]])
 
         # Same elements, but not same sequence length
-        self.assertRaises(self.failureException, self.assertItemsEqual,
+        self.assertRaises(self.failureException, self.assertCountEqual,
                           [1, 1, 2], [2, 1])
-        self.assertRaises(self.failureException, self.assertItemsEqual,
+        self.assertRaises(self.failureException, self.assertCountEqual,
                           [1, 1, "2", "a", "a"], ["2", "2", True, "a"])
-        self.assertRaises(self.failureException, self.assertItemsEqual,
+        self.assertRaises(self.failureException, self.assertCountEqual,
                           [1, {'b': 2}, None, True], [{'b': 2}, True, None])
 
+        # Same elements which don't reliably compare, in
+        # different order, see issue 10242
+        a = [{2,4}, {1,2}]
+        b = a[::-1]
+        self.assertCountEqual(a, b)
+
+
     def testAssertSetEqual(self):
         set1 = set()
         set2 = set()

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Sat Nov 27 10:31:37 2010
@@ -41,6 +41,9 @@
 Library
 -------
 
+- Issue #10242: Fixed implementation of unittest.ItemsEqual and gave it
+  a new more informative name, unittest.CountEqual.
+
 - Issue #2986: difflib.SequenceMatcher gets a new parameter, autojunk, which
   can be set to False to turn off the previously undocumented 'popularity'
   heuristic. Patch by Terry Reedy and Eli Bendersky.


More information about the Python-checkins mailing list