[Python-checkins] python/dist/src/Lib doctest.py, 1.36.2.13,
1.36.2.14
dcjim at users.sourceforge.net
dcjim at users.sourceforge.net
Thu Aug 5 23:38:54 CEST 2004
- Previous message: [Python-checkins] python/dist/src/Lib/test sample_doctest.py, NONE,
1.1.2.1 test_doctest.txt, NONE, 1.1.2.1 test_doctest2.txt,
NONE, 1.1.2.1 test_doctest.py, 1.5.18.9, 1.5.18.10
- Next message: [Python-checkins] python/dist/src/Lib doctest.py, 1.36.2.14,
1.36.2.15
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
Update of /cvsroot/python/python/dist/src/Lib
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv28896
Modified Files:
Tag: tim-doctest-branch
doctest.py
Log Message:
- Added DocFileSuite
- Added a script_from_examples method to convert examples to a script.
- Changed the way the debugger works to include all of the text around
the exaples. Having the explanatory text can make debugging a lot
easier.
- Added a DebugRunner for that raises an error on the first unexpected
error or failure.
- Wrote tests for DocTestSuite, DocFileSuite, script_from_examples,
and DebugRunner.
- Added a debug method to DocTestCase that uses the DebugRunner to
raise an error on the first failure, to support post-mortem
debugging.
Index: doctest.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/doctest.py,v
retrieving revision 1.36.2.13
retrieving revision 1.36.2.14
diff -C2 -d -r1.36.2.13 -r1.36.2.14
*** doctest.py 4 Aug 2004 14:05:14 -0000 1.36.2.13
--- doctest.py 5 Aug 2004 21:38:50 -0000 1.36.2.14
***************
*** 306,310 ****
import __future__
! import sys, traceback, inspect, linecache, re, types
import unittest, difflib, tempfile
from StringIO import StringIO
--- 306,310 ----
import __future__
! import sys, traceback, inspect, linecache, os, re, types
import unittest, difflib, tempfile
from StringIO import StringIO
***************
*** 499,502 ****
--- 499,504 ----
the object whose docstring this DocTest was extracted from).
+ - docstring: The docstring being tested
+
- filename: The name of the file that this DocTest was extracted
from.
***************
*** 518,521 ****
--- 520,524 ----
self.lineno = lineno
# Parse the docstring.
+ self.docstring = docstring
self.examples = self._parse(docstring)
***************
*** 1414,1417 ****
--- 1417,1484 ----
return totalf, totalt
+ class DocTestFailure(Exception):
+ """A DocTest example has failed in debugging mode
+
+ The exeption instance has variables:
+
+ - test: The DocTest object being run
+
+ - excample: The Example object that failed
+
+ - got: the actual output
+ """
+ def __init__(self, test, example, got):
+ self.test = test
+ self.example = example
+ self.got = got
+
+ class DebugRunner(DocTestRunner):
+ r"""Run doc tests but raie an exception as soon as there is a failure
+
+ If an unexpected exception occurs, the exception is merely propigated
+ to the caller:
+
+ >>> runner = DebugRunner(verbose=False)
+ >>> test = DocTest('>>> raise KeyError', {}, 'foo', 'foo.py', 0)
+ >>> runner.run(test)
+ Traceback (most recent call last):
+ ...
+ KeyError
+
+ If the output doesn't match, then a DocTestFailure is raised:
+
+ >>> try:
+ ... test = DocTest('''
+ ... >>> x = 1
+ ... >>> x
+ ... 2
+ ... ''', {}, 'foo', 'foo.py', 0)
+ ... runner.run(test)
+ ... except DocTestFailure, failure:
+ ... pass
+
+ DocTestFailure objects provide access to the test:
+
+ >>> failure.test is test
+ True
+
+ As well as to the example:
+
+ >>> failure.example.want
+ '2\n'
+
+ and the actual output:
+
+ >>> failure.got
+ '1\n'
+
+ """
+
+ def report_unexpected_exception(self, out, test, example, exc_info):
+ raise exc_info[0], exc_info[1], exc_info[2]
+
+ def report_failure(self, out, test, example, got):
+ raise DocTestFailure(test, example, got)
+
######################################################################
## 5. Test Functions
***************
*** 1420,1424 ****
def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
! report=True, optionflags=0, extraglobs=None):
"""m=None, name=None, globs=None, verbose=None, isprivate=None,
report=True, optionflags=0, extraglobs=None
--- 1487,1492 ----
def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
! report=True, optionflags=0, extraglobs=None,
! raise_on_error=False):
"""m=None, name=None, globs=None, verbose=None, isprivate=None,
report=True, optionflags=0, extraglobs=None
***************
*** 1500,1503 ****
--- 1568,1576 ----
multi-line expected and actual outputs will be displayed
using a context diff.
+
+ Optional keyword arg "raise_on_error" raises an exception on the
+ first unexpected exception or failure. This allows failures to be
+ post-mortem debugged.
+
"""
***************
*** 1528,1532 ****
# Find, parse, and run all tests in the given module.
finder = DocTestFinder(namefilter=isprivate)
! runner = DocTestRunner(verbose=verbose, optionflags=optionflags)
for test in finder.find(m, name, globs=globs, extraglobs=extraglobs):
runner.run(test)
--- 1601,1610 ----
# Find, parse, and run all tests in the given module.
finder = DocTestFinder(namefilter=isprivate)
!
! if raise_on_error:
! runner = DebugRunner(verbose=verbose, optionflags=optionflags)
! else:
! runner = DocTestRunner(verbose=verbose, optionflags=optionflags)
!
for test in finder.find(m, name, globs=globs, extraglobs=extraglobs):
runner.run(test)
***************
*** 1648,1652 ****
unittest.TestCase.__init__(self)
self.__test_runner = test_runner
! self.__test = test
self.__setUp = setUp
self.__tearDown = tearDown
--- 1726,1730 ----
unittest.TestCase.__init__(self)
self.__test_runner = test_runner
! self._dt_test = test
self.__setUp = setUp
self.__tearDown = tearDown
***************
*** 1661,1665 ****
def runTest(self):
! test = self.__test
old = sys.stdout
new = StringIO()
--- 1739,1743 ----
def runTest(self):
! test = self._dt_test
old = sys.stdout
new = StringIO()
***************
*** 1671,1691 ****
if failures:
! lname = '.'.join(test.name.split('.')[-1:])
! if test.lineno is None:
! lineno = 'unknown line number'
! else:
! lineno = 'line %s' % test.lineno
! err = new.getvalue()
! raise self.failureException(
! 'Failed doctest test for %s\n'
! ' File "%s", %s, in %s\n\n%s'
! % (test.name, test.filename, lineno, lname, err))
def id(self):
! return self.__test.name
def __repr__(self):
! name = self.__test.name.split('.')
return "%s (%s)" % (name[-1], '.'.join(name[:-1]))
--- 1749,1776 ----
if failures:
! raise self.failureException(self.format_failure(new.getvalue()))
! def format_failure(self, err):
! test = self._dt_test
! if test.lineno is None:
! lineno = 'unknown line number'
! else:
! lineno = 'line %s' % test.lineno
! lname = '.'.join(test.name.split('.')[-1:])
! return ('Failed doctest test for %s\n'
! ' File "%s", line %s, in %s\n\n%s'
! % (test.name, test.filename, lineno, lname, err)
! )
!
! def debug(self):
! runner = DebugRunner(verbose = False,
! optionflags=self.__test_runner.optionflags)
! runner.run(self._dt_test, nooutput)
def id(self):
! return self._dt_test.name
def __repr__(self):
! name = self._dt_test.name.split('.')
return "%s (%s)" % (name[-1], '.'.join(name[:-1]))
***************
*** 1693,1702 ****
def shortDescription(self):
! return "Doctest: " + self.__test.name
! def DocTestSuite(module=None, filename=None, globs=None, extraglobs=None,
! optionflags=0,
! test_finder=None, test_runner=None,
setUp=lambda: None, tearDown=lambda: None):
"""
--- 1778,1788 ----
def shortDescription(self):
! return "Doctest: " + self._dt_test.name
+ def nooutput(*args):
+ pass
! def DocTestSuite(module=None, globs=None, extraglobs=None,
! optionflags=0, test_finder=None,
setUp=lambda: None, tearDown=lambda: None):
"""
***************
*** 1714,1737 ****
If no argument is given, the calling module is used.
"""
- if module is not None and filename is not None:
- raise ValueError('Specify module or filename, not both.')
if test_finder is None:
test_finder = DocTestFinder()
! if test_runner is None:
! test_runner = DocTestRunner(optionflags=optionflags)
! if filename is not None:
! name = os.path.basename(filename)
! test = Test(open(filename).read(),name,filename,0)
! if globs is None:
! globs = {}
! else:
! module = _normalize_module(module)
! tests = test_finder.find(module, globs=globs, extraglobs=extraglobs)
! if globs is None:
! globs = module.__dict__
! if not tests: # [XX] why do we want to do this?
! raise ValueError(module, "has no tests")
tests.sort()
--- 1800,1814 ----
If no argument is given, the calling module is used.
"""
if test_finder is None:
test_finder = DocTestFinder()
! test_runner = DocTestRunner(optionflags=optionflags, verbose=False)
! module = _normalize_module(module)
! tests = test_finder.find(module, globs=globs, extraglobs=extraglobs)
! if globs is None:
! globs = module.__dict__
! if not tests: # [XX] why do we want to do this?
! raise ValueError(module, "has no tests")
tests.sort()
***************
*** 1751,1758 ****
--- 1828,2022 ----
return suite
+ class DocTestFileTestCase(DocTestTestCase):
+
+ def id(self):
+ return '_'.join(self._dt_test.name.split('.'))
+
+ def __repr__(self):
+ return self._dt_test.filename
+ __str__ = __repr__
+
+ def format_failure(self, err):
+ return ('Failed doctest test for %s\n File "%s", line 0\n\n%s'
+ % (self._dt_test.name, self._dt_test.filename, err)
+ )
+
+ def DocFileTest(path, package=None, globs=None,
+ setUp=None, tearDown=None,
+ optionflags=0):
+ package = _normalize_module(package)
+ name = path.split('/')[-1]
+ dir = os.path.split(package.__file__)[0]
+ path = os.path.join(dir, *(path.split('/')))
+ doc = open(path).read()
+
+ if globs is None:
+ globs = {}
+
+ test_runner = DocTestRunner(optionflags=optionflags, verbose=False)
+ test = DocTest(doc, globs, name, path, 0)
+
+ return DocTestFileTestCase(test_runner, test, setUp, tearDown)
+
+ def DocFileSuite(*paths, **kw):
+ """Creates a suite of doctest files.
+
+ One or more text file paths are given as strings. These should
+ use "/" characters to separate path segments. Paths are relative
+ to the directory of the calling module, or relative to the package
+ passed as a keyword argument.
+
+ A number of options may be provided as keyword arguments:
+
+ package
+ The name of a Python package. Text-file paths will be
+ interpreted relative to the directory containing this package.
+ The package may be supplied as a package object or as a dotted
+ package name.
+
+ setUp
+ The name of a set-up function. This is called before running the
+ tests in each file.
+
+ tearDown
+ The name of a tear-down function. This is called after running the
+ tests in each file.
+
+ globs
+ A dictionary containing initial global variables for the tests.
+ """
+ suite = unittest.TestSuite()
+
+ # We do this here so that _normalize_module is called at the right
+ # level. If it were called in DocFileTest, then this function
+ # would be the caller and we might guess the package incorrectly.
+ kw['package'] = _normalize_module(kw.get('package'))
+
+ for path in paths:
+ suite.addTest(DocFileTest(path, **kw))
+
+ return suite
+
######################################################################
## 8. Debugging Support
######################################################################
+ def script_from_examples(s):
+ r"""Extract script from text with examples
+
+ The script_from_examples function converts text with examples
+ into a Python script. Example input is converted to regular
+ code. Example output and all other words are converted to
+ comments:
+
+ >>> text = '''
+ ... Here are examples of simple math.
+ ...
+ ... Python has super accurate integer addition
+ ...
+ ... >>> 2 + 2
+ ... 5
+ ...
+ ... And very friendly error messages:
+ ...
+ ... >>> 1/0
+ ... To Infinity
+ ... And
+ ... Beyond
+ ...
+ ... You can use logic if you want:
+ ...
+ ... >>> if 0:
+ ... ... blah
+ ... ... blah
+ ... ...
+ ...
+ ... Ho hum
+ ... '''
+
+ >>> print script_from_examples(text)
+ #
+ # Here are examples of simple math.
+ #
+ # Python has super accurate integer addition
+ #
+ 2 + 2
+ # Expected:
+ # 5
+ #
+ # And very friendly error messages:
+ #
+ 1/0
+ # Expected:
+ # To Infinity
+ # And
+ # Beyond
+ #
+ # You can use logic if you want:
+ #
+ if 0:
+ blah
+ blah
+ <BLANKLINE>
+ #
+ # Ho hum
+ #
+ """
+ isPS1, isPS2 = DocTest._isPS1, DocTest._isPS2
+ isEmpty, isComment = DocTest._isEmpty, DocTest._isComment
+ output = []
+ 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:
+ line = line.rstrip()
+ if line:
+ line = ' ' + line
+ output.append('#'+line)
+ continue
+ j = m.end(0) # beyond the prompt
+ if isEmpty(line, j) or isComment(line, j):
+ # a bare prompt or comment -- not interesting
+ output.append('# '+line[j:])
+
+ lineno = i - 1
+ if line[j] != " ":
+ raise ValueError("line %r of docstring lacks blank after %s: %s" %
+ (lineno, PS1, line))
+ j = j + 1
+ blanks = m.group(1)
+ nblanks = len(blanks)
+ # suck up this and following PS2 lines
+ while 1:
+ output.append(line[j:])
+ line = lines[i]
+ m = isPS2(line)
+ if m:
+ if m.group(1) != blanks:
+ raise ValueError("inconsistent leading whitespace "
+ "in line %r of docstring: %s" % (i, line))
+ i = i + 1
+ else:
+ break
+
+ # suck up response
+ if not (isPS1(line) or isEmpty(line)):
+ output.append('# Expected:')
+ while 1:
+ if line[:nblanks] != blanks:
+ raise ValueError("inconsistent leading whitespace "
+ "in line %r of docstring: %s" % (i, line))
+ output.append('# '+line[nblanks:])
+ i = i + 1
+ line = lines[i]
+ if isPS1(line) or isEmpty(line):
+ break
+
+ return '\n'.join(output)
+
+
def _want_comment(example):
"""
***************
*** 1782,1789 ****
raise ValueError(name, "not found in tests")
test = test[0]
! testsrc = '\n'.join([
! "%s%s" % (example.source, _want_comment(example))
! for example in test.examples
! ])
return testsrc
--- 2046,2050 ----
raise ValueError(name, "not found in tests")
test = test[0]
! testsrc = script_from_examples(test.docstring)
return testsrc
***************
*** 1793,1802 ****
The string is provided directly
"""
! test = DocTest(src, globs or {}, 'debug', None, None)
!
! testsrc = '\n'.join([
! "%s%s" % (example.source, _want_comment(example))
! for example in test.examples
! ])
debug_script(testsrc, pm, globs)
--- 2054,2058 ----
The string is provided directly
"""
! testsrc = script_from_examples(src)
debug_script(testsrc, pm, globs)
- Previous message: [Python-checkins] python/dist/src/Lib/test sample_doctest.py, NONE,
1.1.2.1 test_doctest.txt, NONE, 1.1.2.1 test_doctest2.txt,
NONE, 1.1.2.1 test_doctest.py, 1.5.18.9, 1.5.18.10
- Next message: [Python-checkins] python/dist/src/Lib doctest.py, 1.36.2.14,
1.36.2.15
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the Python-checkins
mailing list