A hack to ease compatibility with pre-2.3 Pythons: by default, doctest

now accepts "True" when a test expects "1", and similarly for "False"
versus "0".  This is un-doctest-like, but on balance makes it much
more pleasant to write doctests that pass under 2.2 and 2.3.  I expect
it to go away again, when 2.2 is forgotten.  In the meantime, there's
a new doctest module constant that can be passed to a new optional
argument, if you want to turn this behavior off.

Note that this substitution is very simple-minded:  the expected and
actual outputs have to consist of single tokens.  No attempt is made,
e.g., to accept [True, False] when a test expects [1, 0].  This is a
simple hack for simple tests, and I intend to keep it that way.
This commit is contained in:
Tim Peters 2003-06-27 20:48:05 +00:00
parent 6cf26195c6
commit 6ebe61fa80
3 changed files with 83 additions and 17 deletions

View File

@ -398,6 +398,23 @@ def _test():
import doctest, sys
doctest.testmod()
\end{verbatim}
\item WYSIWYG isn't always the case, starting in Python 2.3. The
string form of boolean results changed from \code{"0"} and
\code{"1"} to \code{"False"} and \code{"True"} in Python 2.3.
This makes it clumsy to write a doctest showing boolean results that
passes under multiple versions of Python. In Python 2.3, by default,
and as a special case, if an expected output block consists solely
of \code{"0"} and the actual output block consists solely of
\code{"False"}, that's accepted as an exact match, and similarly for
\code{"1"} versus \code{"True"}. This behavior can be turned off by
passing the new (in 2.3) module constant
\constant{DONT_ACCEPT_TRUE_FOR_1} as the value of \function{testmod()}'s
new (in 2.3) optional \var{optionflags} argument. Some years after
the integer spellings of booleans are history, this hack will
probably be removed again.
\end{enumerate}

View File

@ -297,6 +297,9 @@ from inspect import isfunction as _isfunction
from inspect import ismodule as _ismodule
from inspect import classify_class_attrs as _classify_class_attrs
# Option constants.
DONT_ACCEPT_TRUE_FOR_1 = 1 << 0
# Extract interactive examples from a string. Return a list of triples,
# (source, outcome, lineno). "source" is the source code, and ends
# with a newline iff the source spans more than one line. "outcome" is
@ -414,7 +417,7 @@ def _tag_out(printer, *tag_msg_pairs):
# that captures the examples' std output. Return (#failures, #tries).
def _run_examples_inner(out, fakeout, examples, globs, verbose, name,
compileflags):
compileflags, optionflags):
import sys, traceback
OK, BOOM, FAIL = range(3)
NADA = "nothing"
@ -449,7 +452,11 @@ def _run_examples_inner(out, fakeout, examples, globs, verbose, name,
state = BOOM
if state == OK:
if got == want:
if (got == want or
(not (optionflags & DONT_ACCEPT_TRUE_FOR_1) and
(got, want) in (("True\n", "1\n"), ("False\n", "0\n"))
)
):
if verbose:
out("ok\n")
continue
@ -482,14 +489,16 @@ def _extract_future_flags(globs):
# Run list of examples, in a shallow copy of context (dict) globs.
# Return (#failures, #tries).
def _run_examples(examples, globs, verbose, name, compileflags):
def _run_examples(examples, globs, verbose, name, compileflags,
optionflags):
import sys
saveout = sys.stdout
globs = globs.copy()
try:
sys.stdout = fakeout = _SpoofOut()
x = _run_examples_inner(saveout.write, fakeout, examples,
globs, verbose, name, compileflags)
globs, verbose, name, compileflags,
optionflags)
finally:
sys.stdout = saveout
# While Python gc can clean up most cycles on its own, it doesn't
@ -504,7 +513,7 @@ def _run_examples(examples, globs, verbose, name, compileflags):
return x
def run_docstring_examples(f, globs, verbose=0, name="NoName",
compileflags=None):
compileflags=None, optionflags=0):
"""f, globs, verbose=0, name="NoName" -> run examples from f.__doc__.
Use (a shallow copy of) dict globs as the globals for execution.
@ -533,7 +542,7 @@ def run_docstring_examples(f, globs, verbose=0, name="NoName",
return 0, 0
if compileflags is None:
compileflags = _extract_future_flags(globs)
return _run_examples(e, globs, verbose, name, compileflags)
return _run_examples(e, globs, verbose, name, compileflags, optionflags)
def is_private(prefix, base):
"""prefix, base -> true iff name prefix + "." + base is "private".
@ -637,8 +646,9 @@ Got: 84
"""
def __init__(self, mod=None, globs=None, verbose=None,
isprivate=None):
"""mod=None, globs=None, verbose=None, isprivate=None
isprivate=None, optionflags=0):
"""mod=None, globs=None, verbose=None, isprivate=None,
optionflags=0
See doctest.__doc__ for an overview.
@ -658,6 +668,8 @@ failures if false; by default, it's true iff "-v" is in sys.argv.
Optional keyword arg "isprivate" specifies a function used to determine
whether a name is private. The default function is doctest.is_private;
see its docs for details.
See doctest.testmod docs for the meaning of optionflags.
"""
if mod is None and globs is None:
@ -678,6 +690,8 @@ see its docs for details.
isprivate = is_private
self.isprivate = isprivate
self.optionflags = optionflags
self.name2ft = {} # map name to (#failures, #trials) pair
self.compileflags = _extract_future_flags(globs)
@ -714,7 +728,7 @@ see its docs for details.
e = _extract_examples(s)
if e:
f, t = _run_examples(e, self.globs, self.verbose, name,
self.compileflags)
self.compileflags, self.optionflags)
if self.verbose:
print f, "of", t, "examples failed in string", name
self.__record_outcome(name, f, t)
@ -1045,8 +1059,9 @@ see its docs for details.
master = None
def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
report=1):
"""m=None, name=None, globs=None, verbose=None, isprivate=None, report=1
report=True, optionflags=0):
"""m=None, name=None, globs=None, verbose=None, isprivate=None,
report=True, optionflags=0
Test examples in docstrings in functions and classes reachable
from module m (or the current module if m is not supplied), starting
@ -1080,6 +1095,16 @@ def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
else prints nothing at the end. In verbose mode, the summary is
detailed, else very brief (in fact, empty if all tests passed).
Optional keyword arg "optionflags" or's together module constants,
and defaults to 0. This is new in 2.3. Possible values:
DONT_ACCEPT_TRUE_FOR_1
By default, if an expected output block contains just "1",
an actual output block containing just "True" is considered
to be a match, and similarly for "0" versus "False". When
DONT_ACCEPT_TRUE_FOR_1 is specified, neither substitution
is allowed.
Advanced tomfoolery: testmod runs methods of a local instance of
class doctest.Tester, then merges the results into (or creates)
global Tester instance doctest.master. Methods of doctest.master
@ -1102,11 +1127,12 @@ def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
raise TypeError("testmod: module required; " + `m`)
if name is None:
name = m.__name__
tester = Tester(m, globs=globs, verbose=verbose, isprivate=isprivate)
tester = Tester(m, globs=globs, verbose=verbose, isprivate=isprivate,
optionflags=optionflags)
failures, tries = tester.rundoc(m, name)
f, t = tester.rundict(m.__dict__, name, m)
failures = failures + f
tries = tries + t
failures += f
tries += t
if hasattr(m, "__test__"):
testdict = m.__test__
if testdict:
@ -1114,8 +1140,8 @@ def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
raise TypeError("testmod: module.__test__ must support "
".items(); " + `testdict`)
f, t = tester.run__test__(testdict, name + ".__test__")
failures = failures + f
tries = tries + t
failures += f
tries += t
if report:
tester.summarize()
if master is None:
@ -1174,7 +1200,22 @@ __test__ = {"_TestClass": _TestClass,
>>> x = 1; y = 2
>>> x + y, x * y
(3, 2)
"""
""",
"bool-int equivalence": r"""
In 2.2, boolean expressions displayed
0 or 1. By default, we still accept
them. This can be disabled by passing
DONT_ACCEPT_TRUE_FOR_1 to the new
optionflags argument.
>>> 4 == 4
1
>>> 4 == 4
True
>>> 4 > 4
0
>>> 4 > 4
False
""",
}
def _test():

View File

@ -83,6 +83,14 @@ Extension modules
Library
-------
- For compatibility with doctests created before 2.3, if an expected
output block consists solely of "1" and the actual output block
consists solely of "True", it's accepted as a match; similarly
for "0" and "False". This is quite un-doctest-like, but is practical.
The behavior can be disabled by passing the new doctest module
constant DONT_ACCEPT_TRUE_FOR_1 to the new optionflags optional
argument.
- The cgitb module has been extended to support plain text display (SF patch
569574).