diff --git a/Doc/lib/libdoctest.tex b/Doc/lib/libdoctest.tex index 0741aa0bfae..89518213763 100644 --- a/Doc/lib/libdoctest.tex +++ b/Doc/lib/libdoctest.tex @@ -235,57 +235,57 @@ Traceback (most recent call last): ValueError: list.remove(x): x not in list \end{verbatim} -That doctest succeeds if, and only if, \exception{ValueError} is raised, -with the \samp{list.remove(x): x not in list} detail as shown. +That doctest succeeds if \exception{ValueError} is raised, with the +\samp{list.remove(x): x not in list} detail as shown.\footnote{The + doctest also succeeds if it prints the exact text of the traceback + message; otherwise, it fails.} -The expected output for an exception is divided into four parts. -First, an example may produce some normal output before an exception -is raised, although that's unusual. The "normal output" is taken to -be everything until the first "Traceback" line, and is usually an -empty string. Next, the traceback line must be one of these two, and -indented the same as the first line in the example: +The expected output for an exception must start with a traceback +header, which may be either of the following two lines, indented the +same as the first line of the example: \begin{verbatim} Traceback (most recent call last): Traceback (innermost last): \end{verbatim} -The most interesting part is the last part: the line(s) starting with the -exception type and detail. This is usually the last line of a traceback, -but can extend across any number of lines. After the "Traceback" line, -doctest simply ignores everything until the first line indented the same as -the first line of the example, \emph{and} starting with an alphanumeric -character. This example illustrates the complexities that are possible: +The traceback header is followed by an optional traceback stack, whose +contents are ignored by doctest. Each line of the traceback stack +must be indented further than the first line of the example, \emph{or} +start with a non-alphanumeric character. Typically, the traceback +stack is either omitted or copied verbatim from an interactive +session. + +The traceback stack is followed by the most interesting part: the +line(s) containing the exception type and detail. This is usually the +last line of a traceback, but can extend across multiple lines if the +exception has a multi-line detail, as illustrated in the following +example: \begin{verbatim} ->>> print 1, 2; raise ValueError('printed 1\nand 2\n but not 3') -1 2 +>>> raise ValueError('multi\n line\ndetail') Traceback (most recent call last): -... indented the same, but doesn't start with an alphanumeric - not indented the same, so ignored too - File "/Python23/lib/doctest.py", line 442, in _run_examples_inner - compileflags, 1) in globs - File "", line 1, in ? # and all these are ignored -ValueError: printed 1 -and 2 - but not 3 + File "", line 1, in ? +ValueError: multi + line +detail \end{verbatim} -The first (\samp{1 2}) and last three (starting with -\exception{ValueError}) lines are compared, and the rest are ignored. +The last three (starting with \exception{ValueError}) lines are +compared against the exception's type and detail, and the rest are +ignored. -Best practice is to omit the ``File'' lines, unless they add +Best practice is to omit the traceback stack, unless it adds significant documentation value to the example. So the example above is probably better as: \begin{verbatim} ->>> print 1, 2; raise ValueError('printed 1\nand 2\n but not 3') -1 2 +>>> raise ValueError('multi\n line\ndetail') Traceback (most recent call last): - ... -ValueError: printed 1 -and 2 - but not 3 + ... +ValueError: multi + line +detail \end{verbatim} Note the tracebacks are treated very specially. In particular, in the @@ -293,11 +293,6 @@ rewritten example, the use of \samp{...} is independent of doctest's \constant{ELLIPSIS} option. The ellipsis in that example could be left out, or could just as well be three (or three hundred) commas. -\versionchanged[The abilities to check both normal output and an - exception in a single example, and to have a multi-line - exception detail, were added]{2.4} - - \subsection{Option Flags and Directives\label{doctest-options}} A number of option flags control various aspects of doctest's comparison @@ -634,7 +629,7 @@ Backslashes in a raw docstring: m\n \end{verbatim} Otherwise, the backslash will be interpreted as part of the string. - E.g., the "\textbackslash" above would be interpreted as a newline + E.g., the "{\textbackslash}" above would be interpreted as a newline character. Alternatively, you can double each backslash in the doctest version (and not use a raw string): diff --git a/Lib/doctest.py b/Lib/doctest.py index 43f21b2e1c6..01f7cb32367 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -1281,15 +1281,14 @@ class DocTestRunner: # A regular expression for handling `want` strings that contain # expected exceptions. It divides `want` into three pieces: - # - the pre-exception output (`want`) # - the traceback header line (`hdr`) + # - the traceback stack (`stack`) # - the exception message (`msg`), as generated by # traceback.format_exception_only() # `msg` may have multiple lines. We assume/require that the # exception message is the first non-indented line starting with a word # character following the traceback header line. _EXCEPTION_RE = re.compile(r""" - (?P .*?) # suck up everything until traceback header # Grab the traceback header. Different versions of Python have # said different things on the first traceback line. ^(?P Traceback\ \( @@ -1297,9 +1296,9 @@ class DocTestRunner: | innermost\ last ) \) : ) - \s* $ # toss trailing whitespace on traceback header - .*? # don't blink: absorb stuff until a line *starts* with \w - ^ (?P \w+ .*) + \s* $ # toss trailing whitespace on the header. + (?P .*?) # don't blink: absorb stuff until... + ^ (?P \w+ .*) # a line *starts* with alphanum. """, re.VERBOSE | re.MULTILINE | re.DOTALL) def __run(self, test, compileflags, out): @@ -1374,13 +1373,10 @@ class DocTestRunner: exc_info) failures += 1 else: - e_want, e_msg = m.group('want', 'msg') - # The test passes iff the pre-exception output and - # the exception description match the values given - # in `want`. - if (self._checker.check_output(e_want, got, - self.optionflags) and - self._checker.check_output(e_msg, exc_msg, + # The test passes iff the expected exception + # message (`m.group('msg')`) matches the actual + # exception message (`exc_msg`). + if (self._checker.check_output(m.group('msg'), exc_msg, self.optionflags)): self.report_success(out, test, example, got + _exception_traceback(exc_info)) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index d5e9ef50d45..5d0cf90be5a 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -623,8 +623,10 @@ replaced with any other string: >>> doctest.DocTestRunner(verbose=False).run(test) (0, 2) -An example may generate output before it raises an exception; if it -does, then the output must match the expected output: +An example may not generate output before it raises an exception; if +it does, then the traceback message will not be recognized as +signaling an expected exception, so the example will be reported as an +unexpected exception: >>> def f(x): ... ''' @@ -636,7 +638,15 @@ does, then the output must match the expected output: ... ''' >>> test = doctest.DocTestFinder().find(f)[0] >>> doctest.DocTestRunner(verbose=False).run(test) - (0, 2) + ... # doctest: +ELLIPSIS + ********************************************************************** + Line 3, in f + Failed example: + print 'pre-exception output', x/0 + Exception raised: + ... + ZeroDivisionError: integer division or modulo by zero + (1, 2) Exception messages may contain newlines: