doctest.py

来自「mallet是自然语言处理、机器学习领域的一个开源项目。」· Python 代码 · 共 1,174 行 · 第 1/3 页

PY
1,174
字号
# Module doctest.# Released to the public domain 16-Jan-2001,# by Tim Peters (tim.one@home.com).# Provided as-is; use at your own risk; no warranty; no promises; enjoy!"""Module doctest -- a framework for running examples in docstrings.NORMAL USAGEIn normal use, end each module M with:def _test():    import doctest, M           # replace M with your module's name    return doctest.testmod(M)   # dittoif __name__ == "__main__":    _test()Then running the module as a script will cause the examples in thedocstrings to get executed and verified:python M.pyThis won't display anything unless an example fails, in which case thefailing example(s) and the cause(s) of the failure(s) are printed to stdout(why not stderr? because stderr is a lame hack <0.2 wink>), and the finalline of output is "Test failed.".Run it with the -v switch instead:python M.py -vand a detailed report of all examples tried is printed to stdout, alongwith assorted summaries at the end.You can force verbose mode by passing "verbose=1" to testmod, or prohibitit by passing "verbose=0".  In either of those cases, sys.argv is notexamined by testmod.In any case, testmod returns a 2-tuple of ints (f, t), where f is thenumber of docstring examples that failed and t is the total number ofdocstring examples attempted.WHICH DOCSTRINGS ARE EXAMINED?+ M.__doc__.+ f.__doc__ for all functions f in M.__dict__.values(), except those  with private names and those defined in other modules.+ C.__doc__ for all classes C in M.__dict__.values(), except those with  private names and those defined in other modules.+ If M.__test__ exists and "is true", it must be a dict, and  each entry maps a (string) name to a function object, class object, or  string.  Function and class object docstrings found from M.__test__  are searched even if the name is private, and strings are searched  directly as if they were docstrings.  In output, a key K in M.__test__  appears with name      <name of M>.__test__.KAny classes found are recursively searched similarly, to test docstrings intheir contained methods and nested classes.  Private names reached from M'sglobals are skipped, but all names reached from M.__test__ are searched.By default, a name is considered to be private if it begins with anunderscore (like "_my_func") but doesn't both begin and end with (at least)two underscores (like "__init__").  You can change the default by passingyour own "isprivate" function to testmod.If you want to test docstrings in objects with private names too, stuffthem into an M.__test__ dict, or see ADVANCED USAGE below (e.g., pass yourown isprivate function to Tester's constructor, or call the rundoc methodof a Tester instance).WHAT'S THE EXECUTION CONTEXT?By default, each time testmod finds a docstring to test, it uses a *copy*of M's globals (so that running tests on a module doesn't change themodule's real globals, and so that one test in M can't leave behind crumbsthat accidentally allow another test to work).  This means examples canfreely use any names defined at top-level in M.  It also means that sloppyimports (see above) can cause examples in external docstrings to useglobals inappropriate for them.You can force use of your own dict as the execution context by passing"globs=your_dict" to testmod instead.  Presumably this would be a copy ofM.__dict__ merged with the globals from other imported modules.WHAT IF I WANT TO TEST A WHOLE PACKAGE?Piece o' cake, provided the modules do their testing from docstrings.Here's the test.py I use for the world's most elaborate Rational/floating-base-conversion pkg (which I'll distribute some day):from Rational import Cvtfrom Rational import Formatfrom Rational import machprecfrom Rational import Ratfrom Rational import Roundfrom Rational import utilsmodules = (Cvt,           Format,           machprec,           Rat,           Round,           utils)def _test():    import doctest    import sys    verbose = "-v" in sys.argv    for mod in modules:        doctest.testmod(mod, verbose=verbose, report=0)    doctest.master.summarize()if __name__ == "__main__":    _test()IOW, it just runs testmod on all the pkg modules.  testmod remembers thenames and outcomes (# of failures, # of tries) for each item it's seen, andpassing "report=0" prevents it from printing a summary in verbose mode.Instead, the summary is delayed until all modules have been tested, andthen "doctest.master.summarize()" forces the summary at the end.So this is very nice in practice:  each module can be tested individuallywith almost no work beyond writing up docstring examples, and collectionsof modules can be tested too as a unit with no more work than the above.WHAT ABOUT EXCEPTIONS?No problem, as long as the only output generated by the example is thetraceback itself.  For example:    >>> [1, 2, 3].remove(42)    Traceback (most recent call last):      File "<stdin>", line 1, in ?    ValueError: list.remove(x): x not in list    >>>Note that only the exception type and value are compared (specifically,only the last line in the traceback).ADVANCED USAGEdoctest.testmod() captures the testing policy I find most useful mostoften.  You may want other policies.testmod() actually creates a local instance of class doctest.Tester, runsappropriate methods of that class, and merges the results into globalTester instance doctest.master.You can create your own instances of doctest.Tester, and so build your ownpolicies, or even run methods of doctest.master directly.  Seedoctest.Tester.__doc__ for details.SO WHAT DOES A DOCSTRING EXAMPLE LOOK LIKE ALREADY!?Oh ya.  It's easy!  In most cases a copy-and-paste of an interactiveconsole session works fine -- just make sure the leading whitespace isrigidly consistent (you can mix tabs and spaces if you're too lazy to do itright, but doctest is not in the business of guessing what you think a tabmeans).    >>> # comments are ignored    >>> x = 12    >>> x    12    >>> if x == 13:    ...     print "yes"    ... else:    ...     print "no"    ...     print "NO"    ...     print "NO!!!"    ...    no    NO    NO!!!    >>>Any expected output must immediately follow the final ">>>" or "..." linecontaining the code, and the expected output (if any) extends to the next">>>" or all-whitespace line.  That's it.Bummers:+ Expected output cannot contain an all-whitespace line, since such a line  is taken to signal the end of expected output.+ Output to stdout is captured, but not output to stderr (exception  tracebacks are captured via a different means).+ If you continue a line via backslashing in an interactive session, or for  any other reason use a backslash, you need to double the backslash in the  docstring version.  This is simply because you're in a string, and so the  backslash must be escaped for it to survive intact.  Like:>>> if "yes" == \\...     "y" +   \\...     "es":   # in the source code you'll see the doubled backslashes...     print 'yes'yesThe starting column doesn't matter:>>> assert "Easy!"     >>> import math            >>> math.floor(1.9)            1.0and as many leading whitespace characters are stripped from the expectedoutput as appeared in the initial ">>>" line that triggered it.If you execute this very file, the examples above will be found andexecuted, leading to this output in verbose mode:Running doctest.__doc__Trying: [1, 2, 3].remove(42)Expecting:Traceback (most recent call last):  File "<stdin>", line 1, in ?ValueError: list.remove(x): x not in listokTrying: x = 12Expecting: nothingokTrying: xExpecting: 12okTrying:if x == 13:    print "yes"else:    print "no"    print "NO"    print "NO!!!"Expecting:noNONO!!!ok... and a bunch more like that, with this summary at the end:5 items had no tests:    doctest.Tester.__init__    doctest.Tester.run__test__    doctest.Tester.summarize    doctest.run_docstring_examples    doctest.testmod12 items passed all tests:   8 tests in doctest   6 tests in doctest.Tester  10 tests in doctest.Tester.merge  14 tests in doctest.Tester.rundict   3 tests in doctest.Tester.rundoc   3 tests in doctest.Tester.runstring   2 tests in doctest.__test__._TestClass   2 tests in doctest.__test__._TestClass.__init__   2 tests in doctest.__test__._TestClass.get   1 tests in doctest.__test__._TestClass.square   2 tests in doctest.__test__.string   7 tests in doctest.is_private60 tests in 17 items.60 passed and 0 failed.Test passed."""__all__ = [    'testmod',    'run_docstring_examples',    'is_private',    'Tester',]import __future__import rePS1 = ">>>"PS2 = "..."_isPS1 = re.compile(r"(\s*)" + re.escape(PS1)).match_isPS2 = re.compile(r"(\s*)" + re.escape(PS2)).match_isEmpty = re.compile(r"\s*$").match_isComment = re.compile(r"\s*#").matchdel refrom types import StringTypes as _StringTypesfrom inspect import isclass    as _isclassfrom inspect import isfunction as _isfunctionfrom inspect import ismodule   as _ismodulefrom inspect import classify_class_attrs as _classify_class_attrs# 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# the expected output if any, else an empty string.  When not empty,# outcome always ends with a newline.  "lineno" is the line number,# 0-based wrt the start of the string, of the first source line.def _extract_examples(s):    isPS1, isPS2 = _isPS1, _isPS2    isEmpty, isComment = _isEmpty, _isComment    examples = []    lines = s.split("\n")    i, n = 0, len(lines)    while i < n:        line = lines[i]        i = i + 1        m = isPS1(line)        if m is None:            continue        j = m.end(0)  # beyond the prompt        if isEmpty(line, j) or isComment(line, j):            # a bare prompt or comment -- not interesting            continue        lineno = i - 1        if line[j] != " ":            raise ValueError("line " + `lineno` + " of docstring lacks "                "blank after " + PS1 + ": " + line)        j = j + 1        blanks = m.group(1)        nblanks = len(blanks)        # suck up this and following PS2 lines        source = []        while 1:            source.append(line[j:])            line = lines[i]            m = isPS2(line)            if m:                if m.group(1) != blanks:                    raise ValueError("inconsistent leading whitespace "                        "in line " + `i` + " of docstring: " + line)                i = i + 1            else:                break        if len(source) == 1:            source = source[0]        else:            # get rid of useless null line from trailing empty "..."            if source[-1] == "":                del source[-1]            source = "\n".join(source) + "\n"        # suck up response        if isPS1(line) or isEmpty(line):            expect = ""        else:            expect = []            while 1:                if line[:nblanks] != blanks:                    raise ValueError("inconsistent leading whitespace "                        "in line " + `i` + " of docstring: " + line)                expect.append(line[nblanks:])                i = i + 1                line = lines[i]                if isPS1(line) or isEmpty(line):                    break            expect = "\n".join(expect) + "\n"        examples.append( (source, expect, lineno) )    return examples# Capture stdout when running examples.class _SpoofOut:    def __init__(self):        self.clear()    def write(self, s):        self.buf.append(s)    def get(self):        guts = "".join(self.buf)        # If anything at all was written, make sure there's a trailing        # newline.  There's no way for the expected output to indicate        # that a trailing newline is missing.        if guts and not guts.endswith("\n"):            guts = guts + "\n"        # Prevent softspace from screwing up the next test case, in        # case they used print with a trailing comma in an example.        if hasattr(self, "softspace"):            del self.softspace        return guts    def clear(self):        self.buf = []        if hasattr(self, "softspace"):            del self.softspace    def flush(self):        # JPython calls flush

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?