Commit 41a65ea7 authored by Tim Peters's avatar Tim Peters

Doctest has new traceback gimmicks in 2.4. While trying to document

them (which they are now), I had to rewrite the code to understand
it.  This has got to be the most DWIM part of doctest -- but in context
is really necessary.
parent f076953e
...@@ -108,7 +108,8 @@ Expecting: [1, 1, 2, 6, 24, 120] ...@@ -108,7 +108,8 @@ Expecting: [1, 1, 2, 6, 24, 120]
ok ok
Trying: [factorial(long(n)) for n in range(6)] Trying: [factorial(long(n)) for n in range(6)]
Expecting: [1, 1, 2, 6, 24, 120] Expecting: [1, 1, 2, 6, 24, 120]
ok\end{verbatim} ok
\end{verbatim}
And so on, eventually ending with: And so on, eventually ending with:
...@@ -129,12 +130,14 @@ $ ...@@ -129,12 +130,14 @@ $
\end{verbatim} \end{verbatim}
That's all you need to know to start making productive use of That's all you need to know to start making productive use of
\module{doctest}! Jump in. \module{doctest}! Jump in. The following sections provide full
details. Note that there are many examples of doctests in
the standard Python test suite and libraries.
\subsection{Simple Usage} \subsection{Simple Usage}
The simplest (not necessarily the best) way to start using doctest is to The simplest way to start using doctest (but not necessarily the way
end each module \module{M} with: you'll continue to do it) is to end each module \module{M} with:
\begin{verbatim} \begin{verbatim}
def _test(): def _test():
...@@ -146,8 +149,7 @@ if __name__ == "__main__": ...@@ -146,8 +149,7 @@ if __name__ == "__main__":
\end{verbatim} \end{verbatim}
\module{doctest} then examines docstrings in the module calling \module{doctest} then examines docstrings in the module calling
\function{testmod()}. If you want to test a different module, you can \function{testmod()}.
pass that module object to \function{testmod()}.
Running the module as a script causes the examples in the docstrings Running the module as a script causes the examples in the docstrings
to get executed and verified: to get executed and verified:
...@@ -292,35 +294,95 @@ their contained methods and nested classes. ...@@ -292,35 +294,95 @@ their contained methods and nested classes.
\subsection{What's the Execution Context?} \subsection{What's the Execution Context?}
By default, each time \function{testmod()} finds a docstring to test, it uses By default, each time \function{testmod()} finds a docstring to test, it
a \emph{copy} of \module{M}'s globals, so that running tests on a module uses a \emph{shallow copy} of \module{M}'s globals, so that running tests
doesn't change the module's real globals, and so that one test in doesn't change the module's real globals, and so that one test in
\module{M} can't leave behind crumbs that accidentally allow another test \module{M} can't leave behind crumbs that accidentally allow another test
to work. This means examples can freely use any names defined at top-level to work. This means examples can freely use any names defined at top-level
in \module{M}, and names defined earlier in the docstring being run. in \module{M}, and names defined earlier in the docstring being run.
Examples cannot see names defined in other docstrings.
You can force use of your own dict as the execution context by passing You can force use of your own dict as the execution context by passing
\code{globs=your_dict} to \function{testmod()} instead. Presumably this \code{globs=your_dict} to \function{testmod()} instead.
would be a copy of \code{M.__dict__} merged with the globals from other
imported modules.
\subsection{What About Exceptions?} \subsection{What About Exceptions?}
No problem, as long as the only output generated by the example is the No problem: just paste in the expected traceback. Since
traceback itself. For example: tracebacks contain details that are likely to change
rapidly (for example, exact file paths and line numbers), this is one
case where doctest works hard to be flexible in what it accepts.
This makes the full story involved, but you really don't have
to remember much. Simple example:
\begin{verbatim} \begin{verbatim}
>>> [1, 2, 3].remove(42) >>> [1, 2, 3].remove(42)
Traceback (most recent call last): Traceback (most recent call last):
File "<stdin>", line 1, in ? File "<stdin>", line 1, in ?
ValueError: list.remove(x): x not in list ValueError: list.remove(x): x not in list
>>>
\end{verbatim} \end{verbatim}
Note that only the exception type and value are compared (specifically, That doctest succeeds if, and only if, \exception{ValueError} is raised,
only the last line in the traceback). The various ``File'' lines in with the \samp{list.remove(x): x not in list} detail as shown.
between can be left out (unless they add significantly to the documentation
value of the example). 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:
\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:
\begin{verbatim}
>>> print 1, 2; raise ValueError('printed 1\nand 2\n but not 3')
1 2
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 "<string>", line 1, in ? # and all these are ignored
ValueError: printed 1
and 2
but not 3
\end{verbatim}
The first (\samp{1 2}) and last three (starting with
\exception{ValueError}) lines are compared, and the rest are ignored.
Best practice is to omit the ``File'' lines, unless they add
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
Traceback (most recent call last):
...
ValueError: printed 1
and 2
but not 3
\end{verbatim}
Note the tracebacks are treated very specially. In particular, in the
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 Directive Names\label{doctest-options}} \subsection{Option Flags and Directive Names\label{doctest-options}}
......
...@@ -1191,16 +1191,27 @@ class DocTestRunner: ...@@ -1191,16 +1191,27 @@ class DocTestRunner:
#///////////////////////////////////////////////////////////////// #/////////////////////////////////////////////////////////////////
# A regular expression for handling `want` strings that contain # A regular expression for handling `want` strings that contain
# expected exceptions. It divides `want` into two pieces: the # expected exceptions. It divides `want` into three pieces:
# pre-exception output (`out`) and the exception message (`exc`), # - the pre-exception output (`want`)
# as generated by traceback.format_exception_only(). (I assume # - the traceback header line (`hdr`)
# that the exception_only message is the first non-indented line # - the exception message (`msg`), as generated by
# starting with word characters after the "Traceback ...".) # traceback.format_exception_only()
_EXCEPTION_RE = re.compile(('^(?P<out>.*)' # `msg` may have multiple lines. We assume/require that the
'^(?P<hdr>Traceback \((?:%s|%s)\):)\s*$.*?' # exception message is the first non-indented line starting with a word
'^(?P<exc>\w+.*)') % # character following the traceback header line.
('most recent call last', 'innermost last'), _EXCEPTION_RE = re.compile(r"""
re.MULTILINE | re.DOTALL) (?P<want> .*?) # suck up everything until traceback header
# Grab the traceback header. Different versions of Python have
# said different things on the first traceback line.
^(?P<hdr> Traceback\ \(
(?: most\ recent\ call\ last
| innermost\ last
) \) :
)
\s* $ # toss trailing whitespace on traceback header
.*? # don't blink: absorb stuff until a line *starts* with \w
^ (?P<msg> \w+ .*)
""", re.VERBOSE | re.MULTILINE | re.DOTALL)
def __run(self, test, compileflags, out): def __run(self, test, compileflags, out):
""" """
...@@ -1274,20 +1285,19 @@ class DocTestRunner: ...@@ -1274,20 +1285,19 @@ class DocTestRunner:
exc_info) exc_info)
failures += 1 failures += 1
else: else:
exc_hdr = m.group('hdr')+'\n' # Exception header e_want, e_msg = m.group('want', 'msg')
# The test passes iff the pre-exception output and # The test passes iff the pre-exception output and
# the exception description match the values given # the exception description match the values given
# in `want`. # in `want`.
if (self._checker.check_output(m.group('out'), got, if (self._checker.check_output(e_want, got,
self.optionflags) and self.optionflags) and
self._checker.check_output(m.group('exc'), exc_msg, self._checker.check_output(e_msg, exc_msg,
self.optionflags)): self.optionflags)):
# Is +exc_msg the right thing here??
self.report_success(out, test, example, self.report_success(out, test, example,
got+_exception_traceback(exc_info)) got + _exception_traceback(exc_info))
else: else:
self.report_failure(out, test, example, self.report_failure(out, test, example,
got+_exception_traceback(exc_info)) got + _exception_traceback(exc_info))
failures += 1 failures += 1
# Restore the option flags (in case they were modified) # Restore the option flags (in case they were modified)
......
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