From cd5676adc4db7c19dc08b0d905ce48c140b8574d Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Thu, 30 Sep 2010 23:15:41 +0100 Subject: [PATCH] Truncate the text passed to difflib where possible This stops difflib from printing many lines which had no change in them anyway. It also avoids a bug in difflib which fails or hangs when there are many trailing lines which are all identical. --HG-- branch : trunk --- doc/example/assertion/failure_demo.py | 18 ++++++++++ py/_plugin/pytest_assertion.py | 52 +++++++++++++++++++-------- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/doc/example/assertion/failure_demo.py b/doc/example/assertion/failure_demo.py index b99253ee8..f0266b82c 100644 --- a/doc/example/assertion/failure_demo.py +++ b/doc/example/assertion/failure_demo.py @@ -128,15 +128,33 @@ class TestSpecialisedExplanations(object): def test_eq_multiline_text(self): assert 'foo\nspam\nbar' == 'foo\neggs\nbar' + def test_eq_long_text(self): + a = '1'*100 + 'a' + '2'*100 + b = '1'*100 + 'b' + '2'*100 + assert a == b + + def test_eq_long_text_multiline(self): + a = '1\n'*100 + 'a' + '2\n'*100 + b = '1\n'*100 + 'b' + '2\n'*100 + assert a == b + def test_eq_list(self): assert [0, 1, 2] == [0, 1, 3] + def test_eq_list_long(self): + a = [0]*100 + [1] + [3]*100 + b = [0]*100 + [2] + [3]*100 + assert a == b + def test_eq_dict(self): assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2} def test_eq_set(self): assert set([0, 10, 11, 12]) == set([0, 20, 21]) + def test_in_list(self): + assert 1 in [0, 2, 3, 4, 5] + def globf(x): return x+1 diff --git a/py/_plugin/pytest_assertion.py b/py/_plugin/pytest_assertion.py index 9e6355f41..b58b91985 100644 --- a/py/_plugin/pytest_assertion.py +++ b/py/_plugin/pytest_assertion.py @@ -54,18 +54,16 @@ def pytest_assert_binrepr(op, left, right): explanation = None if op == '==': if istext(left) and istext(right): - explanation = [line.strip('\n') for line in - py.std.difflib.ndiff(left.splitlines(), - right.splitlines())] + explanation = _diff_text(left, right) elif issequence(left) and issequence(right): explanation = _compare_eq_sequence(left, right) elif isset(left) and isset(right): explanation = _compare_eq_set(left, right) elif isdict(left) and isdict(right): - explanation = _pprint_diff(left, right) + explanation = _diff_text(py.std.pprint.pformat(left), + py.std.pprint.pformat(right)) elif op == 'in': - # XXX - pass + pass # XXX if not explanation: return None @@ -77,6 +75,38 @@ def pytest_assert_binrepr(op, left, right): return [summary] + explanation +def _diff_text(left, right): + """Return the explanation for the diff between text + + This will skip leading and trailing characters which are + identical to keep the diff minimal. + """ + explanation = [] + for i in range(min(len(left), len(right))): + if left[i] != right[i]: + break + if i > 42: + i -= 10 # Provide some context + explanation = ['Skipping %s identical ' + 'leading characters in diff' % i] + left = left[i:] + right = right[i:] + if len(left) == len(right): + for i in range(len(left)): + if left[-i] != right[-i]: + break + if i > 42: + i -= 10 # Provide some context + explanation += ['Skipping %s identical ' + 'trailing characters in diff' % i] + left = left[:-i] + right = right[:-i] + explanation += [line.strip('\n') + for line in py.std.difflib.ndiff(left.splitlines(), + right.splitlines())] + return explanation + + def _compare_eq_sequence(left, right): explanation = [] for i in range(min(len(left), len(right))): @@ -90,14 +120,8 @@ def _compare_eq_sequence(left, right): elif len(left) < len(right): explanation += ['Right contains more items, ' 'first extra item: %s' % right[len(left)]] - return explanation + _pprint_diff(left, right) - - -def _pprint_diff(left, right): - """Make explanation using pprint and difflib""" - return [line.strip('\n') for line in - py.std.difflib.ndiff(py.std.pprint.pformat(left).splitlines(), - py.std.pprint.pformat(right).splitlines())] + return explanation + _diff_text(py.std.pprint.pformat(left), + py.std.pprint.pformat(right)) def _compare_eq_set(left, right):