Commit 07a349c4 authored by Jim Fulton's avatar Jim Fulton

Bugs fixed:

  - Test filenames sometimes had trailing .pyc or .pyo sufixes
    (when module __file__ did).

  - Trailing spaces spaces in expected output were dropped.

New default failure format:

  - Separation of examples from file info makes examples easier to see

  - More vertical separation, improving readability

  - Emacs-recognized file info (also closer to Python exception format)
parent 8ffe9abd
...@@ -740,7 +740,15 @@ class DocTestParser: ...@@ -740,7 +740,15 @@ class DocTestParser:
# Divide want into lines; check that it's properly # Divide want into lines; check that it's properly
# indented; and then strip the indentation. # indented; and then strip the indentation.
want_lines = m.group('want').rstrip().split('\n') want = m.group('want')
# Strip trailing newline and following spaces
l = len(want.rstrip())
l = want.find('\n', l)
if l >= 0:
want = want[:l]
want_lines = want.split('\n')
self._check_prefix(want_lines, ' '*indent, name, self._check_prefix(want_lines, ' '*indent, name,
lineno+len(source_lines)) lineno+len(source_lines))
want = '\n'.join([wl[indent:] for wl in want_lines]) want = '\n'.join([wl[indent:] for wl in want_lines])
...@@ -1063,6 +1071,8 @@ class DocTestFinder: ...@@ -1063,6 +1071,8 @@ class DocTestFinder:
filename = None filename = None
else: else:
filename = getattr(module, '__file__', module.__name__) filename = getattr(module, '__file__', module.__name__)
if filename[-4:] in (".pyc", ".pyo"):
filename = filename[:-1]
return self._parser.get_doctest(docstring, globs, name, return self._parser.get_doctest(docstring, globs, name,
filename, lineno) filename, lineno)
...@@ -1199,6 +1209,7 @@ class DocTestRunner: ...@@ -1199,6 +1209,7 @@ class DocTestRunner:
verbose = '-v' in sys.argv verbose = '-v' in sys.argv
self._verbose = verbose self._verbose = verbose
self.optionflags = optionflags self.optionflags = optionflags
self.original_optionflags = optionflags
# Keep track of the examples we've run. # Keep track of the examples we've run.
self.tries = 0 self.tries = 0
...@@ -1246,20 +1257,22 @@ class DocTestRunner: ...@@ -1246,20 +1257,22 @@ class DocTestRunner:
_tag_msg("Exception raised", _exception_traceback(exc_info))) _tag_msg("Exception raised", _exception_traceback(exc_info)))
def _failure_header(self, test, example): def _failure_header(self, test, example):
s = (self.DIVIDER + "\n" + out = [self.DIVIDER]
_tag_msg("Failure in example", example.source)) if test.filename:
if test.filename is None: if test.lineno is not None and example.lineno is not None:
# [XX] I'm not putting +1 here, to give the same output lineno = test.lineno + example.lineno + 1
# as the old version. But I think it *should* go here. else:
return s + ("from line #%s of %s\n" % lineno = '?'
(example.lineno, test.name)) out.append('File "%s", line %s, in %s' %
elif test.lineno is None: (test.filename, lineno, test.name))
return s + ("from line #%s of %s in %s\n" %
(example.lineno+1, test.name, test.filename))
else: else:
lineno = test.lineno+example.lineno+1 out.append('Line %s, in %s' % (example.lineno+1, test.name))
return s + ("from line #%s of %s (%s)\n" % out.append('Failed example:')
(lineno, test.filename, test.name)) source = example.source
if source.endswith('\n'):
source = source[:-1]
out.append(' ' + '\n '.join(source.split('\n')))
return '\n'.join(out)+'\n'
#///////////////////////////////////////////////////////////////// #/////////////////////////////////////////////////////////////////
# DocTest Running # DocTest Running
...@@ -1568,6 +1581,7 @@ class OutputChecker: ...@@ -1568,6 +1581,7 @@ class OutputChecker:
compare `want` and `got`. `indent` is the indentation of the compare `want` and `got`. `indent` is the indentation of the
original example. original example.
""" """
# If <BLANKLINE>s are being used, then replace blank lines # If <BLANKLINE>s are being used, then replace blank lines
# with <BLANKLINE> in the actual output string. # with <BLANKLINE> in the actual output string.
if not (optionflags & DONT_ACCEPT_BLANKLINE): if not (optionflags & DONT_ACCEPT_BLANKLINE):
...@@ -1600,8 +1614,13 @@ class OutputChecker: ...@@ -1600,8 +1614,13 @@ class OutputChecker:
# If we're not using diff, then simply list the expected # If we're not using diff, then simply list the expected
# output followed by the actual output. # output followed by the actual output.
return (_tag_msg("Expected", want or "Nothing") + if want.endswith('\n'):
_tag_msg("Got", got)) want = want[:-1]
want = ' ' + '\n '.join(want.split('\n'))
if got.endswith('\n'):
got = got[:-1]
got = ' ' + '\n '.join(got.split('\n'))
return "Expected:\n%s\nGot:\n%s\n" % (want, got)
class DocTestFailure(Exception): class DocTestFailure(Exception):
"""A DocTest example has failed in debugging mode. """A DocTest example has failed in debugging mode.
...@@ -1989,6 +2008,7 @@ class DocTestCase(unittest.TestCase): ...@@ -1989,6 +2008,7 @@ class DocTestCase(unittest.TestCase):
def __init__(self, test, optionflags=0, setUp=None, tearDown=None, def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
checker=None): checker=None):
unittest.TestCase.__init__(self) unittest.TestCase.__init__(self)
self._dt_optionflags = optionflags self._dt_optionflags = optionflags
self._dt_checker = checker self._dt_checker = checker
...@@ -2025,7 +2045,7 @@ class DocTestCase(unittest.TestCase): ...@@ -2025,7 +2045,7 @@ class DocTestCase(unittest.TestCase):
if test.lineno is None: if test.lineno is None:
lineno = 'unknown line number' lineno = 'unknown line number'
else: else:
lineno = 'line %s' % test.lineno lineno = '%s' % test.lineno
lname = '.'.join(test.name.split('.')[-1:]) lname = '.'.join(test.name.split('.')[-1:])
return ('Failed doctest test for %s\n' return ('Failed doctest test for %s\n'
' File "%s", line %s, in %s\n\n%s' ' File "%s", line %s, in %s\n\n%s'
...@@ -2150,9 +2170,7 @@ def DocTestSuite(module=None, globs=None, extraglobs=None, ...@@ -2150,9 +2170,7 @@ def DocTestSuite(module=None, globs=None, extraglobs=None,
continue continue
if not test.filename: if not test.filename:
filename = module.__file__ filename = module.__file__
if filename.endswith(".pyc"): if filename[-4:] in (".pyc", ".pyo"):
filename = filename[:-1]
elif filename.endswith(".pyo"):
filename = filename[:-1] filename = filename[:-1]
test.filename = filename test.filename = filename
suite.addTest(DocTestCase(test, optionflags, setUp, tearDown, suite.addTest(DocTestCase(test, optionflags, setUp, tearDown,
...@@ -2469,10 +2487,13 @@ def test1(): r""" ...@@ -2469,10 +2487,13 @@ def test1(): r"""
... 42 ... 42
... ''', 'XYZ') ... ''', 'XYZ')
********************************************************************** **********************************************************************
Failure in example: print x Line 3, in XYZ
from line #2 of XYZ Failed example:
Expected: 42 print x
Got: 84 Expected:
42
Got:
84
(1, 2) (1, 2)
>>> t.runstring(">>> x = x * 2\n>>> print x\n84\n", 'example2') >>> t.runstring(">>> x = x * 2\n>>> print x\n84\n", 'example2')
(0, 2) (0, 2)
......
...@@ -267,11 +267,24 @@ For a function whose docstring contains examples, DocTestFinder.find() ...@@ -267,11 +267,24 @@ For a function whose docstring contains examples, DocTestFinder.find()
will return a single test (for that function's docstring): will return a single test (for that function's docstring):
>>> finder = doctest.DocTestFinder() >>> finder = doctest.DocTestFinder()
We'll simulate a __file__ attr that ends in pyc:
>>> import test.test_doctest
>>> old = test.test_doctest.__file__
>>> test.test_doctest.__file__ = 'test_doctest.pyc'
>>> tests = finder.find(sample_func) >>> tests = finder.find(sample_func)
>>> print tests # doctest: +ELLIPSIS >>> print tests # doctest: +ELLIPSIS
[<DocTest sample_func from ...:12 (1 example)>] [<DocTest sample_func from ...:12 (1 example)>]
>>> tests[0].filename
'test_doctest.py'
>>> 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)
('print sample_func(22)\n', '44\n', 3) ('print sample_func(22)\n', '44\n', 3)
...@@ -519,10 +532,13 @@ the failure and proceeds to the next example: ...@@ -519,10 +532,13 @@ the failure and proceeds to the next example:
Trying: print x Trying: print x
Expecting: 14 Expecting: 14
********************************************************************** **********************************************************************
Failure in example: print x Line 3, in f
from line #2 of f Failed example:
Expected: 14 print x
Got: 12 Expected:
14
Got:
12
Trying: x/2 Trying: x/2
Expecting: 6 Expecting: 6
ok ok
...@@ -645,8 +661,9 @@ message is raised, then it is reported as a failure: ...@@ -645,8 +661,9 @@ message is raised, then it is reported as a failure:
>>> doctest.DocTestRunner(verbose=False).run(test) >>> doctest.DocTestRunner(verbose=False).run(test)
... # doctest: +ELLIPSIS ... # doctest: +ELLIPSIS
********************************************************************** **********************************************************************
Failure in example: raise ValueError, 'message' Line 2, in f
from line #1 of f Failed example:
raise ValueError, 'message'
Expected: Expected:
Traceback (most recent call last): Traceback (most recent call last):
ValueError: wrong message ValueError: wrong message
...@@ -668,11 +685,12 @@ unexpected exception: ...@@ -668,11 +685,12 @@ unexpected exception:
>>> doctest.DocTestRunner(verbose=False).run(test) >>> doctest.DocTestRunner(verbose=False).run(test)
... # doctest: +ELLIPSIS ... # doctest: +ELLIPSIS
********************************************************************** **********************************************************************
Failure in example: 1/0 Line 2, in f
from line #1 of f Failed example:
1/0
Exception raised: Exception raised:
Traceback (most recent call last): Traceback (most recent call last):
... ...
ZeroDivisionError: integer division or modulo by zero ZeroDivisionError: integer division or modulo by zero
(1, 1) (1, 1)
""" """
...@@ -700,10 +718,13 @@ and 1/0: ...@@ -700,10 +718,13 @@ and 1/0:
>>> flags = doctest.DONT_ACCEPT_TRUE_FOR_1 >>> flags = doctest.DONT_ACCEPT_TRUE_FOR_1
>>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test)
********************************************************************** **********************************************************************
Failure in example: True Line 1, in f
from line #0 of f Failed example:
Expected: 1 True
Got: True Expected:
1
Got:
True
(1, 1) (1, 1)
The DONT_ACCEPT_BLANKLINE flag disables the match between blank lines The DONT_ACCEPT_BLANKLINE flag disables the match between blank lines
...@@ -722,8 +743,9 @@ and the '<BLANKLINE>' marker: ...@@ -722,8 +743,9 @@ and the '<BLANKLINE>' marker:
>>> flags = doctest.DONT_ACCEPT_BLANKLINE >>> flags = doctest.DONT_ACCEPT_BLANKLINE
>>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test)
********************************************************************** **********************************************************************
Failure in example: print "a\n\nb" Line 1, in f
from line #0 of f Failed example:
print "a\n\nb"
Expected: Expected:
a a
<BLANKLINE> <BLANKLINE>
...@@ -744,12 +766,14 @@ treated as equal: ...@@ -744,12 +766,14 @@ treated as equal:
>>> test = doctest.DocTestFinder().find(f)[0] >>> test = doctest.DocTestFinder().find(f)[0]
>>> doctest.DocTestRunner(verbose=False).run(test) >>> doctest.DocTestRunner(verbose=False).run(test)
********************************************************************** **********************************************************************
Failure in example: print 1, 2, 3 Line 1, in f
from line #0 of f Failed example:
print 1, 2, 3
Expected: Expected:
1 2 1 2
3 3
Got: 1 2 3 Got:
1 2 3
(1, 1) (1, 1)
>>> # With the flag: >>> # With the flag:
...@@ -773,10 +797,13 @@ output to match any substring in the actual output: ...@@ -773,10 +797,13 @@ output to match any substring in the actual output:
>>> test = doctest.DocTestFinder().find(f)[0] >>> test = doctest.DocTestFinder().find(f)[0]
>>> doctest.DocTestRunner(verbose=False).run(test) >>> doctest.DocTestRunner(verbose=False).run(test)
********************************************************************** **********************************************************************
Failure in example: print range(15) Line 1, in f
from line #0 of f Failed example:
Expected: [0, 1, 2, ..., 14] print range(15)
Got: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] Expected:
[0, 1, 2, ..., 14]
Got:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
(1, 1) (1, 1)
>>> # With the flag: >>> # With the flag:
...@@ -825,8 +852,9 @@ and actual outputs to be displayed using a unified diff: ...@@ -825,8 +852,9 @@ and actual outputs to be displayed using a unified diff:
>>> test = doctest.DocTestFinder().find(f)[0] >>> test = doctest.DocTestFinder().find(f)[0]
>>> doctest.DocTestRunner(verbose=False).run(test) >>> doctest.DocTestRunner(verbose=False).run(test)
********************************************************************** **********************************************************************
Failure in example: print '\n'.join('abcdefg') Line 2, in f
from line #1 of f Failed example:
print '\n'.join('abcdefg')
Expected: Expected:
a a
B B
...@@ -850,8 +878,9 @@ and actual outputs to be displayed using a unified diff: ...@@ -850,8 +878,9 @@ and actual outputs to be displayed using a unified diff:
>>> flags = doctest.UNIFIED_DIFF >>> flags = doctest.UNIFIED_DIFF
>>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test)
********************************************************************** **********************************************************************
Failure in example: print '\n'.join('abcdefg') Line 2, in f
from line #1 of f Failed example:
print '\n'.join('abcdefg')
Differences (unified diff): Differences (unified diff):
--- Expected --- Expected
+++ Got +++ Got
...@@ -876,8 +905,9 @@ and actual outputs to be displayed using a context diff: ...@@ -876,8 +905,9 @@ and actual outputs to be displayed using a context diff:
>>> flags = doctest.CONTEXT_DIFF >>> flags = doctest.CONTEXT_DIFF
>>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test)
********************************************************************** **********************************************************************
Failure in example: print '\n'.join('abcdefg') Line 2, in f
from line #1 of f Failed example:
print '\n'.join('abcdefg')
Differences (context diff): Differences (context diff):
*** Expected *** Expected
--- Got --- Got
...@@ -919,10 +949,13 @@ example with a comment of the form ``# doctest: +OPTION``: ...@@ -919,10 +949,13 @@ example with a comment of the form ``# doctest: +OPTION``:
>>> test = doctest.DocTestFinder().find(f)[0] >>> test = doctest.DocTestFinder().find(f)[0]
>>> doctest.DocTestRunner(verbose=False).run(test) >>> doctest.DocTestRunner(verbose=False).run(test)
********************************************************************** **********************************************************************
Failure in example: print range(10) # should fail: no ellipsis Line 2, in f
from line #1 of f Failed example:
Expected: [0, 1, ..., 9] print range(10) # should fail: no ellipsis
Got: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Expected:
[0, 1, ..., 9]
Got:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
(1, 2) (1, 2)
To turn an option off for an example, follow that example with a To turn an option off for an example, follow that example with a
...@@ -940,10 +973,13 @@ comment of the form ``# doctest: -OPTION``: ...@@ -940,10 +973,13 @@ comment of the form ``# doctest: -OPTION``:
>>> doctest.DocTestRunner(verbose=False, >>> doctest.DocTestRunner(verbose=False,
... optionflags=doctest.ELLIPSIS).run(test) ... optionflags=doctest.ELLIPSIS).run(test)
********************************************************************** **********************************************************************
Failure in example: print range(10) # doctest: -ELLIPSIS Line 6, in f
from line #5 of f Failed example:
Expected: [0, 1, ..., 9] print range(10) # doctest: -ELLIPSIS
Got: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Expected:
[0, 1, ..., 9]
Got:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
(1, 2) (1, 2)
Option directives affect only the example that they appear with; they Option directives affect only the example that they appear with; they
...@@ -962,15 +998,21 @@ do not change the options for surrounding examples: ...@@ -962,15 +998,21 @@ do not change the options for surrounding examples:
>>> test = doctest.DocTestFinder().find(f)[0] >>> test = doctest.DocTestFinder().find(f)[0]
>>> doctest.DocTestRunner(verbose=False).run(test) >>> doctest.DocTestRunner(verbose=False).run(test)
********************************************************************** **********************************************************************
Failure in example: print range(10) # Should fail: no ellipsis Line 2, in f
from line #1 of f Failed example:
Expected: [0, 1, ..., 9] print range(10) # Should fail: no ellipsis
Got: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Expected:
[0, 1, ..., 9]
Got:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
********************************************************************** **********************************************************************
Failure in example: print range(10) # Should fail: no ellipsis Line 8, in f
from line #7 of f Failed example:
Expected: [0, 1, ..., 9] print range(10) # Should fail: no ellipsis
Got: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Expected:
[0, 1, ..., 9]
Got:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
(2, 3) (2, 3)
Multiple options may be modified by a single option directive. They Multiple options may be modified by a single option directive. They
...@@ -986,10 +1028,13 @@ may be separated by whitespace, commas, or both: ...@@ -986,10 +1028,13 @@ may be separated by whitespace, commas, or both:
>>> test = doctest.DocTestFinder().find(f)[0] >>> test = doctest.DocTestFinder().find(f)[0]
>>> doctest.DocTestRunner(verbose=False).run(test) >>> doctest.DocTestRunner(verbose=False).run(test)
********************************************************************** **********************************************************************
Failure in example: print range(10) # Should fail Line 2, in f
from line #1 of f Failed example:
Expected: [0, 1, ..., 9] print range(10) # Should fail
Got: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Expected:
[0, 1, ..., 9]
Got:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
(1, 2) (1, 2)
>>> def f(x): r''' >>> def f(x): r'''
...@@ -1002,10 +1047,13 @@ may be separated by whitespace, commas, or both: ...@@ -1002,10 +1047,13 @@ may be separated by whitespace, commas, or both:
>>> test = doctest.DocTestFinder().find(f)[0] >>> test = doctest.DocTestFinder().find(f)[0]
>>> doctest.DocTestRunner(verbose=False).run(test) >>> doctest.DocTestRunner(verbose=False).run(test)
********************************************************************** **********************************************************************
Failure in example: print range(10) # Should fail Line 2, in f
from line #1 of f Failed example:
Expected: [0, 1, ..., 9] print range(10) # Should fail
Got: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Expected:
[0, 1, ..., 9]
Got:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
(1, 2) (1, 2)
>>> def f(x): r''' >>> def f(x): r'''
...@@ -1018,10 +1066,13 @@ may be separated by whitespace, commas, or both: ...@@ -1018,10 +1066,13 @@ may be separated by whitespace, commas, or both:
>>> test = doctest.DocTestFinder().find(f)[0] >>> test = doctest.DocTestFinder().find(f)[0]
>>> doctest.DocTestRunner(verbose=False).run(test) >>> doctest.DocTestRunner(verbose=False).run(test)
********************************************************************** **********************************************************************
Failure in example: print range(10) # Should fail Line 2, in f
from line #1 of f Failed example:
Expected: [0, 1, ..., 9] print range(10) # Should fail
Got: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Expected:
[0, 1, ..., 9]
Got:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
(1, 2) (1, 2)
The option directive may be put on the line following the source, as The option directive may be put on the line following the source, as
...@@ -1414,6 +1465,14 @@ def test_DocFileSuite(): ...@@ -1414,6 +1465,14 @@ def test_DocFileSuite():
""" """
def test_trailing_space_in_test():
"""
Trailing spaces in expcted output are significant:
>>> x, y = 'foo', ''
>>> print x, y
foo \n
"""
###################################################################### ######################################################################
## Main ## Main
......
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