diff options
author | Tim Peters <tim.peters@gmail.com> | 2003-06-27 20:48:05 (GMT) |
---|---|---|
committer | Tim Peters <tim.peters@gmail.com> | 2003-06-27 20:48:05 (GMT) |
commit | 6ebe61fa807d250ccab419473abd8d746a932e75 (patch) | |
tree | 18350da8265076dbebafc9fca16fe1b5506f6206 | |
parent | 6cf26195c6e206cba1bb3ebe554e82ab52304bd5 (diff) | |
download | cpython-6ebe61fa807d250ccab419473abd8d746a932e75.zip cpython-6ebe61fa807d250ccab419473abd8d746a932e75.tar.gz cpython-6ebe61fa807d250ccab419473abd8d746a932e75.tar.bz2 |
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.
-rw-r--r-- | Doc/lib/libdoctest.tex | 17 | ||||
-rw-r--r-- | Lib/doctest.py | 75 | ||||
-rw-r--r-- | Misc/NEWS | 8 |
3 files changed, 83 insertions, 17 deletions
diff --git a/Doc/lib/libdoctest.tex b/Doc/lib/libdoctest.tex index 9a795e9..253d6b4 100644 --- a/Doc/lib/libdoctest.tex +++ b/Doc/lib/libdoctest.tex @@ -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} diff --git a/Lib/doctest.py b/Lib/doctest.py index c01606d..8b379cd 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -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(): @@ -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). |