Kaydet (Commit) c6cbab0d authored tarafından Tim Peters's avatar Tim Peters

Added NDIFF_DIFF option.

üst 94607dd5
...@@ -356,6 +356,15 @@ can also be used in doctest directives (see below). ...@@ -356,6 +356,15 @@ can also be used in doctest directives (see below).
actual outputs will be displayed using a context diff. actual outputs will be displayed using a context diff.
\end{datadesc} \end{datadesc}
\begin{datadesc}{NDIFF_DIFF}
When specified, differences are computed by \code{difflib.Differ},
using the same algorithm as the popular \file{ndiff.py} utility.
This is the only method that marks differences within lines as
well as across lines. For example, if a line of expected output
contains digit \code{1} where actual output contains letter \code{l},
a line is inserted with a caret marking the mismatching column
positions.
\end{datadesc}
A "doctest directive" is a trailing Python comment on a line of a doctest A "doctest directive" is a trailing Python comment on a line of a doctest
example: example:
...@@ -414,7 +423,8 @@ can be useful. ...@@ -414,7 +423,8 @@ can be useful.
\versionchanged[Constants \constant{DONT_ACCEPT_BLANKLINE}, \versionchanged[Constants \constant{DONT_ACCEPT_BLANKLINE},
\constant{NORMALIZE_WHITESPACE}, \constant{ELLIPSIS}, \constant{NORMALIZE_WHITESPACE}, \constant{ELLIPSIS},
\constant{UNIFIED_DIFF}, and \constant{CONTEXT_DIFF} \constant{UNIFIED_DIFF}, \constant{CONTEXT_DIFF}, and
\constant{NDIFF_DIFF}
were added; by default \code{<BLANKLINE>} in expected output were added; by default \code{<BLANKLINE>} in expected output
matches an empty line in actual output; and doctest directives matches an empty line in actual output; and doctest directives
were added]{2.4} were added]{2.4}
......
...@@ -178,6 +178,7 @@ __all__ = [ ...@@ -178,6 +178,7 @@ __all__ = [
'ELLIPSIS', 'ELLIPSIS',
'UNIFIED_DIFF', 'UNIFIED_DIFF',
'CONTEXT_DIFF', 'CONTEXT_DIFF',
'NDIFF_DIFF',
# 1. Utility Functions # 1. Utility Functions
'is_private', 'is_private',
# 2. Example & DocTest # 2. Example & DocTest
...@@ -253,6 +254,7 @@ NORMALIZE_WHITESPACE = register_optionflag('NORMALIZE_WHITESPACE') ...@@ -253,6 +254,7 @@ NORMALIZE_WHITESPACE = register_optionflag('NORMALIZE_WHITESPACE')
ELLIPSIS = register_optionflag('ELLIPSIS') ELLIPSIS = register_optionflag('ELLIPSIS')
UNIFIED_DIFF = register_optionflag('UNIFIED_DIFF') UNIFIED_DIFF = register_optionflag('UNIFIED_DIFF')
CONTEXT_DIFF = register_optionflag('CONTEXT_DIFF') CONTEXT_DIFF = register_optionflag('CONTEXT_DIFF')
NDIFF_DIFF = register_optionflag('NDIFF_DIFF')
# Special string markers for use in `want` strings: # Special string markers for use in `want` strings:
BLANKLINE_MARKER = '<BLANKLINE>' BLANKLINE_MARKER = '<BLANKLINE>'
...@@ -1569,6 +1571,24 @@ class OutputChecker: ...@@ -1569,6 +1571,24 @@ class OutputChecker:
# We didn't find any match; return false. # We didn't find any match; return false.
return False return False
# Should we do a fancy diff?
def _do_a_fancy_diff(self, want, got, optionflags):
# Not unless they asked for a fancy diff.
if not optionflags & (UNIFIED_DIFF |
CONTEXT_DIFF |
NDIFF_DIFF):
return False
# If expected output uses ellipsis, a meaningful fancy diff is
# too hard.
if optionflags & ELLIPSIS and ELLIPSIS_MARKER in want:
return False
# ndiff does intraline difference marking, so can be useful even
# for 1-line inputs.
if optionflags & NDIFF_DIFF:
return True
# The other diff types need at least a few lines to be helpful.
return want.count('\n') > 2 and got.count('\n') > 2
def output_difference(self, want, got, optionflags): def output_difference(self, want, got, optionflags):
""" """
Return a string describing the differences between the Return a string describing the differences between the
...@@ -1586,9 +1606,7 @@ class OutputChecker: ...@@ -1586,9 +1606,7 @@ class OutputChecker:
# Check if we should use diff. Don't use diff if the actual # Check if we should use diff. Don't use diff if the actual
# or expected outputs are too short, or if the expected output # or expected outputs are too short, or if the expected output
# contains an ellipsis marker. # contains an ellipsis marker.
if ((optionflags & (UNIFIED_DIFF | CONTEXT_DIFF)) and if self._do_a_fancy_diff(want, got, optionflags):
want.count('\n') > 2 and got.count('\n') > 2 and
not (optionflags & ELLIPSIS and '...' in want)):
# Split want & got into lines. # Split want & got into lines.
want_lines = [l+'\n' for l in want.split('\n')] want_lines = [l+'\n' for l in want.split('\n')]
got_lines = [l+'\n' for l in got.split('\n')] got_lines = [l+'\n' for l in got.split('\n')]
...@@ -1596,16 +1614,20 @@ class OutputChecker: ...@@ -1596,16 +1614,20 @@ class OutputChecker:
if optionflags & UNIFIED_DIFF: if optionflags & UNIFIED_DIFF:
diff = difflib.unified_diff(want_lines, got_lines, n=2, diff = difflib.unified_diff(want_lines, got_lines, n=2,
fromfile='Expected', tofile='Got') fromfile='Expected', tofile='Got')
kind = 'unified' kind = 'unified diff'
elif optionflags & CONTEXT_DIFF: elif optionflags & CONTEXT_DIFF:
diff = difflib.context_diff(want_lines, got_lines, n=2, diff = difflib.context_diff(want_lines, got_lines, n=2,
fromfile='Expected', tofile='Got') fromfile='Expected', tofile='Got')
kind = 'context' kind = 'context diff'
elif optionflags & NDIFF_DIFF:
engine = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK)
diff = list(engine.compare(want_lines, got_lines))
kind = 'ndiff with -expected +actual'
else: else:
assert 0, 'Bad diff option' assert 0, 'Bad diff option'
# Remove trailing whitespace on diff output. # Remove trailing whitespace on diff output.
diff = [line.rstrip() + '\n' for line in diff] diff = [line.rstrip() + '\n' for line in diff]
return _tag_msg("Differences (" + kind + " diff)", return _tag_msg("Differences (" + kind + ")",
''.join(diff)) ''.join(diff))
# If we're not using diff, then simply list the expected # If we're not using diff, then simply list the expected
......
...@@ -283,7 +283,7 @@ We'll simulate a __file__ attr that ends in pyc: ...@@ -283,7 +283,7 @@ We'll simulate a __file__ attr that ends in pyc:
'test_doctest.py' 'test_doctest.py'
>>> test.test_doctest.__file__ = old >>> test.test_doctest.__file__ = old
>>> e = tests[0].examples[0] >>> e = tests[0].examples[0]
>>> (e.source, e.want, e.lineno) >>> (e.source, e.want, e.lineno)
...@@ -931,7 +931,33 @@ and actual outputs to be displayed using a context diff: ...@@ -931,7 +931,33 @@ and actual outputs to be displayed using a context diff:
g g
<BLANKLINE> <BLANKLINE>
(1, 1) (1, 1)
"""
The NDIFF_DIFF flag causes failures to use the difflib.Differ algorithm
used by the popular ndiff.py utility. This does intraline difference
marking, as well as interline differences.
>>> def f(x):
... r'''
... >>> print "a b c d e f g h i j k l m"
... a b c d e f g h i j k 1 m
... '''
>>> test = doctest.DocTestFinder().find(f)[0]
>>> flags = doctest.NDIFF_DIFF
>>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test)
**********************************************************************
Line 2, in f
Failed example:
print "a b c d e f g h i j k l m"
Differences (ndiff with -expected +actual):
- a b c d e f g h i j k 1 m
? ^
+ a b c d e f g h i j k l m
? + ++ ^
<BLANKLINE>
(1, 1)
"""
def option_directives(): r""" def option_directives(): r"""
Tests of `DocTestRunner`'s option directive mechanism. Tests of `DocTestRunner`'s option directive mechanism.
...@@ -1468,7 +1494,7 @@ def test_DocFileSuite(): ...@@ -1468,7 +1494,7 @@ def test_DocFileSuite():
def test_trailing_space_in_test(): def test_trailing_space_in_test():
""" """
Trailing spaces in expcted output are significant: Trailing spaces in expcted output are significant:
>>> x, y = 'foo', '' >>> x, y = 'foo', ''
>>> print x, y >>> print x, y
foo \n foo \n
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment