Skip to content

Commit a643b65

Browse files
author
Jim Fulton
committed
Ported some features from zope:
- Fixed the display of tests in verbose output - Allow setUp and tearDown functions to be provided for DocTestSuites.
1 parent b91af52 commit a643b65

File tree

1 file changed

+214
-101
lines changed

1 file changed

+214
-101
lines changed

Lib/doctest.py

Lines changed: 214 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,6 @@ def _test():
289289
'run_docstring_examples',
290290
'is_private',
291291
'Tester',
292-
'DocTestTestFailure',
293292
'DocTestSuite',
294293
'testsource',
295294
'debug',
@@ -1289,66 +1288,92 @@ def _find_tests(module, prefix=None):
12891288
_extract_doctests(mdict.items(), module, mdict, tests, prefix)
12901289
return tests
12911290

1292-
# unittest helpers.
1291+
###############################################################################
1292+
# unitest support
12931293

1294-
# A function passed to unittest, for unittest to drive.
1295-
# tester is doctest Tester instance. doc is the docstring whose
1296-
# doctests are to be run.
1294+
from StringIO import StringIO
1295+
import os
1296+
import sys
1297+
import tempfile
1298+
import unittest
12971299

1298-
def _utest(tester, name, doc, filename, lineno):
1299-
import sys
1300-
from StringIO import StringIO
1300+
class DocTestTestCase(unittest.TestCase):
1301+
"""A test case that wraps a test function.
13011302
1302-
old = sys.stdout
1303-
sys.stdout = new = StringIO()
1304-
try:
1305-
failures, tries = tester.runstring(doc, name)
1306-
finally:
1307-
sys.stdout = old
1308-
1309-
if failures:
1310-
msg = new.getvalue()
1311-
lname = '.'.join(name.split('.')[-1:])
1312-
if not lineno:
1313-
lineno = "0 (don't know line number)"
1314-
# Don't change this format! It was designed so that Emacs can
1315-
# parse it naturally.
1316-
raise DocTestTestFailure('Failed doctest test for %s\n'
1317-
' File "%s", line %s, in %s\n\n%s' %
1318-
(name, filename, lineno, lname, msg))
1319-
1320-
class DocTestTestFailure(Exception):
1321-
"""A doctest test failed"""
1322-
1323-
def DocTestSuite(module=None):
1324-
"""Convert doctest tests for a module to a unittest TestSuite.
1325-
1326-
The returned TestSuite is to be run by the unittest framework, and
1327-
runs each doctest in the module. If any of the doctests fail,
1328-
then the synthesized unit test fails, and an error is raised showing
1329-
the name of the file containing the test and a (sometimes approximate)
1330-
line number.
1331-
1332-
The optional module argument provides the module to be tested. It
1333-
can be a module object or a (possibly dotted) module name. If not
1334-
specified, the module calling DocTestSuite() is used.
1335-
1336-
Example (although note that unittest supplies many ways to use the
1337-
TestSuite returned; see the unittest docs):
1338-
1339-
import unittest
1340-
import doctest
1341-
import my_module_with_doctests
1342-
1343-
suite = doctest.DocTestSuite(my_module_with_doctests)
1344-
runner = unittest.TextTestRunner()
1345-
runner.run(suite)
1303+
This is useful for slipping pre-existing test functions into the
1304+
PyUnit framework. Optionally, set-up and tidy-up functions can be
1305+
supplied. As with TestCase, the tidy-up ('tearDown') function will
1306+
always be called if the set-up ('setUp') function ran successfully.
13461307
"""
13471308

1348-
import unittest
1309+
def __init__(self, tester, name, doc, filename, lineno,
1310+
setUp=None, tearDown=None):
1311+
unittest.TestCase.__init__(self)
1312+
(self.__tester, self.__name, self.__doc,
1313+
self.__filename, self.__lineno,
1314+
self.__setUp, self.__tearDown
1315+
) = tester, name, doc, filename, lineno, setUp, tearDown
1316+
1317+
def setUp(self):
1318+
if self.__setUp is not None:
1319+
self.__setUp()
1320+
1321+
def tearDown(self):
1322+
if self.__tearDown is not None:
1323+
self.__tearDown()
1324+
1325+
def runTest(self):
1326+
old = sys.stdout
1327+
new = StringIO()
1328+
try:
1329+
sys.stdout = new
1330+
failures, tries = self.__tester.runstring(self.__doc, self.__name)
1331+
finally:
1332+
sys.stdout = old
1333+
1334+
if failures:
1335+
lname = '.'.join(self.__name.split('.')[-1:])
1336+
lineno = self.__lineno or "0 (don't know line no)"
1337+
raise self.failureException(
1338+
'Failed doctest test for %s\n'
1339+
' File "%s", line %s, in %s\n\n%s'
1340+
% (self.__name, self.__filename, lineno, lname, new.getvalue())
1341+
)
1342+
1343+
def id(self):
1344+
return self.__name
1345+
1346+
def __repr__(self):
1347+
name = self.__name.split('.')
1348+
return "%s (%s)" % (name[-1], '.'.join(name[:-1]))
1349+
1350+
__str__ = __repr__
1351+
1352+
def shortDescription(self):
1353+
return "Doctest: " + self.__name
1354+
1355+
1356+
def DocTestSuite(module=None,
1357+
setUp=lambda: None,
1358+
tearDown=lambda: None,
1359+
):
1360+
"""Convert doctest tests for a mudule to a unittest test suite
1361+
1362+
This tests convers each documentation string in a module that
1363+
contains doctest tests to a unittest test case. If any of the
1364+
tests in a doc string fail, then the test case fails. An error is
1365+
raised showing the name of the file containing the test and a
1366+
(sometimes approximate) line number.
1367+
1368+
A module argument provides the module to be tested. The argument
1369+
can be either a module or a module name.
1370+
1371+
If no argument is given, the calling module is used.
1372+
1373+
"""
1374+
module = _normalizeModule(module)
1375+
tests = _findTests(module)
13491376

1350-
module = _normalize_module(module)
1351-
tests = _find_tests(module)
13521377
if not tests:
13531378
raise ValueError(module, "has no tests")
13541379

@@ -1362,78 +1387,166 @@ def DocTestSuite(module=None):
13621387
filename = filename[:-1]
13631388
elif filename.endswith(".pyo"):
13641389
filename = filename[:-1]
1365-
def runit(name=name, doc=doc, filename=filename, lineno=lineno):
1366-
_utest(tester, name, doc, filename, lineno)
1367-
suite.addTest(unittest.FunctionTestCase(
1368-
runit,
1369-
description="doctest of " + name))
1390+
1391+
suite.addTest(DocTestTestCase(
1392+
tester, name, doc, filename, lineno,
1393+
setUp, tearDown))
1394+
13701395
return suite
13711396

1372-
# Debugging support.
1397+
def _normalizeModule(module):
1398+
# Normalize a module
1399+
if module is None:
1400+
# Test the calling module
1401+
module = sys._getframe(2).f_globals['__name__']
1402+
module = sys.modules[module]
1403+
1404+
elif isinstance(module, (str, unicode)):
1405+
module = __import__(module, globals(), locals(), ["*"])
1406+
1407+
return module
1408+
1409+
def _doc(name, object, tests, prefix, filename='', lineno=''):
1410+
doc = getattr(object, '__doc__', '')
1411+
if doc and doc.find('>>>') >= 0:
1412+
tests.append((prefix+name, doc, filename, lineno))
1413+
1414+
1415+
def _findTests(module, prefix=None):
1416+
if prefix is None:
1417+
prefix = module.__name__
1418+
dict = module.__dict__
1419+
tests = []
1420+
_doc(prefix, module, tests, '',
1421+
lineno="1 (or below)")
1422+
prefix = prefix and (prefix + ".")
1423+
_find(dict.items(), module, dict, tests, prefix)
1424+
return tests
1425+
1426+
def _find(items, module, dict, tests, prefix, minlineno=0):
1427+
for name, object in items:
1428+
1429+
# Only interested in named objects
1430+
if not hasattr(object, '__name__'):
1431+
continue
1432+
1433+
if hasattr(object, 'func_globals'):
1434+
# Looks like a func
1435+
if object.func_globals is not dict:
1436+
# Non-local func
1437+
continue
1438+
code = getattr(object, 'func_code', None)
1439+
filename = getattr(code, 'co_filename', '')
1440+
lineno = getattr(code, 'co_firstlineno', -1) + 1
1441+
if minlineno:
1442+
minlineno = min(lineno, minlineno)
1443+
else:
1444+
minlineno = lineno
1445+
_doc(name, object, tests, prefix, filename, lineno)
1446+
1447+
elif hasattr(object, "__module__"):
1448+
# Maybe a class-like things. In which case, we care
1449+
if object.__module__ != module.__name__:
1450+
continue # not the same module
1451+
if not (hasattr(object, '__dict__')
1452+
and hasattr(object, '__bases__')):
1453+
continue # not a class
1454+
1455+
lineno = _find(object.__dict__.items(), module, dict, tests,
1456+
prefix+name+".")
1457+
1458+
_doc(name, object, tests, prefix,
1459+
lineno="%s (or above)" % (lineno-3))
1460+
1461+
return minlineno
1462+
1463+
# end unitest support
1464+
###############################################################################
1465+
1466+
###############################################################################
1467+
# debugger
13731468

13741469
def _expect(expect):
1375-
# Return the expected output (if any), formatted as a Python
1376-
# comment block.
1470+
# Return the expected output, if any
13771471
if expect:
13781472
expect = "\n# ".join(expect.split("\n"))
13791473
expect = "\n# Expect:\n# %s" % expect
13801474
return expect
13811475

13821476
def testsource(module, name):
1383-
"""Extract the doctest examples from a docstring.
1477+
"""Extract the test sources from a doctest test docstring as a script
13841478
13851479
Provide the module (or dotted name of the module) containing the
1386-
tests to be extracted, and the name (within the module) of the object
1387-
with the docstring containing the tests to be extracted.
1480+
test to be debugged and the name (within the module) of the object
1481+
with the doc string with tests to be debugged.
13881482
1389-
The doctest examples are returned as a string containing Python
1390-
code. The expected output blocks in the examples are converted
1391-
to Python comments.
13921483
"""
1393-
1394-
module = _normalize_module(module)
1395-
tests = _find_tests(module, "")
1396-
test = [doc for (tname, doc, dummy, dummy) in tests
1397-
if tname == name]
1484+
module = _normalizeModule(module)
1485+
tests = _findTests(module, "")
1486+
test = [doc for (tname, doc, f, l) in tests if tname == name]
13981487
if not test:
13991488
raise ValueError(name, "not found in tests")
14001489
test = test[0]
1401-
examples = [source + _expect(expect)
1402-
for source, expect, dummy in _extract_examples(test)]
1403-
return '\n'.join(examples)
1404-
1405-
def debug(module, name):
1406-
"""Debug a single docstring containing doctests.
1407-
1408-
Provide the module (or dotted name of the module) containing the
1409-
docstring to be debugged, and the name (within the module) of the
1410-
object with the docstring to be debugged.
1411-
1412-
The doctest examples are extracted (see function testsource()),
1413-
and written to a temp file. The Python debugger (pdb) is then
1414-
invoked on that file.
1490+
# XXX we rely on an internal doctest function:
1491+
examples = _extract_examples(test)
1492+
testsrc = '\n'.join([
1493+
"%s%s" % (source, _expect(expect))
1494+
for (source, expect, lineno) in examples
1495+
])
1496+
return testsrc
1497+
1498+
def debug_src(src, pm=False, globs=None):
1499+
"""Debug a single doctest test doc string
1500+
1501+
The string is provided directly
14151502
"""
1416-
1417-
import os
1503+
# XXX we rely on an internal doctest function:
1504+
examples = _extract_examples(src)
1505+
src = '\n'.join([
1506+
"%s%s" % (source, _expect(expect))
1507+
for (source, expect, lineno) in examples
1508+
])
1509+
debug_script(src, pm, globs)
1510+
1511+
def debug_script(src, pm=False, globs=None):
1512+
"Debug a test script"
14181513
import pdb
1419-
import tempfile
14201514

1421-
module = _normalize_module(module)
1422-
testsrc = testsource(module, name)
14231515
srcfilename = tempfile.mktemp("doctestdebug.py")
1424-
f = file(srcfilename, 'w')
1425-
f.write(testsrc)
1426-
f.close()
1516+
open(srcfilename, 'w').write(src)
1517+
if globs:
1518+
globs = globs.copy()
1519+
else:
1520+
globs = {}
14271521

1428-
globs = {}
1429-
globs.update(module.__dict__)
14301522
try:
1431-
# Note that %r is vital here. '%s' instead can, e.g., cause
1432-
# backslashes to get treated as metacharacters on Windows.
1433-
pdb.run("execfile(%r)" % srcfilename, globs, globs)
1523+
if pm:
1524+
try:
1525+
execfile(srcfilename, globs, globs)
1526+
except:
1527+
print sys.exc_info()[1]
1528+
pdb.post_mortem(sys.exc_info()[2])
1529+
else:
1530+
# Note that %r is vital here. '%s' instead can, e.g., cause
1531+
# backslashes to get treated as metacharacters on Windows.
1532+
pdb.run("execfile(%r)" % srcfilename, globs, globs)
14341533
finally:
14351534
os.remove(srcfilename)
14361535

1536+
def debug(module, name, pm=False):
1537+
"""Debug a single doctest test doc string
1538+
1539+
Provide the module (or dotted name of the module) containing the
1540+
test to be debugged and the name (within the module) of the object
1541+
with the doc string with tests to be debugged.
1542+
1543+
"""
1544+
module = _normalizeModule(module)
1545+
testsrc = testsource(module, name)
1546+
debug_script(testsrc, pm, module.__dict__)
1547+
1548+
# end debugger
1549+
###############################################################################
14371550

14381551

14391552
class _TestClass:

0 commit comments

Comments
 (0)