[py3] Ported django.test.doctest.
Based on Vinay Sajip's branch.
This commit is contained in:
parent
a8b3ddec5f
commit
67646dc28d
|
@ -105,7 +105,7 @@ import unittest, difflib, pdb, tempfile
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.six import StringIO
|
from django.utils.six.moves import StringIO, xrange
|
||||||
|
|
||||||
if sys.platform.startswith('java'):
|
if sys.platform.startswith('java'):
|
||||||
# On Jython, isclass() reports some modules as classes. Patch it.
|
# On Jython, isclass() reports some modules as classes. Patch it.
|
||||||
|
@ -501,11 +501,31 @@ class DocTest:
|
||||||
|
|
||||||
|
|
||||||
# This lets us sort tests by name:
|
# This lets us sort tests by name:
|
||||||
|
def _cmpkey(self):
|
||||||
|
return (self.name, self.filename, self.lineno, id(self))
|
||||||
def __cmp__(self, other):
|
def __cmp__(self, other):
|
||||||
if not isinstance(other, DocTest):
|
if not isinstance(other, DocTest):
|
||||||
return -1
|
return -1
|
||||||
return cmp((self.name, self.filename, self.lineno, id(self)),
|
return cmp(self._cmpkey(), other._cmpkey())
|
||||||
(other.name, other.filename, other.lineno, id(other)))
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self._cmpkey() < other._cmpkey()
|
||||||
|
|
||||||
|
def __le__(self, other):
|
||||||
|
return self._cmpkey() <= other._cmpkey()
|
||||||
|
|
||||||
|
def __gt__(self, other):
|
||||||
|
return self._cmpkey() > other._cmpkey()
|
||||||
|
|
||||||
|
def __ge__(self, other):
|
||||||
|
return self._cmpkey() >= other._cmpkey()
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self._cmpkey() == other._cmpkey()
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return self._cmpkey() != other._cmpkey()
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
## 3. DocTestParser
|
## 3. DocTestParser
|
||||||
|
@ -1229,6 +1249,57 @@ class DocTestRunner:
|
||||||
# __patched_linecache_getlines).
|
# __patched_linecache_getlines).
|
||||||
filename = '<doctest %s[%d]>' % (test.name, examplenum)
|
filename = '<doctest %s[%d]>' % (test.name, examplenum)
|
||||||
|
|
||||||
|
# Doctest and Py3 issue:
|
||||||
|
# If the current example that we wish to run is going to fail
|
||||||
|
# because it expects a leading u"", then use an alternate displayhook
|
||||||
|
original_displayhook = sys.displayhook
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
# only set alternate displayhook if Python 3.x or after
|
||||||
|
lines = []
|
||||||
|
def py3_displayhook(value):
|
||||||
|
if value is None:
|
||||||
|
# None should not be considered at all
|
||||||
|
return original_displayhook(value)
|
||||||
|
|
||||||
|
# Collect the repr output in one variable
|
||||||
|
s = repr(value)
|
||||||
|
# Strip b"" and u"" prefixes from the repr and expected output
|
||||||
|
# TODO: better way of stripping the prefixes?
|
||||||
|
expected = example.want
|
||||||
|
expected = expected.strip() # be wary of newlines
|
||||||
|
s = s.replace("u", "")
|
||||||
|
s = s.replace("b", "")
|
||||||
|
expected = expected.replace("u", "")
|
||||||
|
expected = expected.replace("b", "")
|
||||||
|
# single quote vs. double quote should not matter
|
||||||
|
# default all quote marks to double quote
|
||||||
|
s = s.replace("'", '"')
|
||||||
|
expected = expected.replace("'", '"')
|
||||||
|
|
||||||
|
# In case of multi-line expected result
|
||||||
|
lines.append(s)
|
||||||
|
|
||||||
|
# let them match
|
||||||
|
if s == expected: # be wary of false positives here
|
||||||
|
# they should be the same, print expected value
|
||||||
|
sys.stdout.write("%s\n" % example.want.strip())
|
||||||
|
|
||||||
|
# multi-line expected output, doctest uses loop
|
||||||
|
elif len(expected.split("\n")) == len(lines):
|
||||||
|
if "\n".join(lines) == expected:
|
||||||
|
sys.stdout.write("%s\n" % example.want.strip())
|
||||||
|
else:
|
||||||
|
sys.stdout.write("%s\n" % repr(value))
|
||||||
|
elif len(expected.split("\n")) != len(lines):
|
||||||
|
# we are not done looping yet, do not print anything!
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
sys.stdout.write("%s\n" % repr(value))
|
||||||
|
|
||||||
|
sys.displayhook = py3_displayhook
|
||||||
|
|
||||||
# Run the example in the given context (globs), and record
|
# Run the example in the given context (globs), and record
|
||||||
# any exception that gets raised. (But don't intercept
|
# any exception that gets raised. (But don't intercept
|
||||||
# keyboard interrupts.)
|
# keyboard interrupts.)
|
||||||
|
@ -1243,9 +1314,14 @@ class DocTestRunner:
|
||||||
except:
|
except:
|
||||||
exception = sys.exc_info()
|
exception = sys.exc_info()
|
||||||
self.debugger.set_continue() # ==== Example Finished ====
|
self.debugger.set_continue() # ==== Example Finished ====
|
||||||
|
finally:
|
||||||
|
# restore the original displayhook
|
||||||
|
sys.displayhook = original_displayhook
|
||||||
|
|
||||||
got = self._fakeout.getvalue() # the actual output
|
got = self._fakeout.getvalue() # the actual output
|
||||||
self._fakeout.truncate(0)
|
self._fakeout.truncate(0)
|
||||||
|
# Python 3.1 requires seek after truncate
|
||||||
|
self._fakeout.seek(0)
|
||||||
outcome = FAILURE # guilty until proved innocent or insane
|
outcome = FAILURE # guilty until proved innocent or insane
|
||||||
|
|
||||||
# If the example executed without raising any exceptions,
|
# If the example executed without raising any exceptions,
|
||||||
|
@ -1256,10 +1332,21 @@ class DocTestRunner:
|
||||||
|
|
||||||
# The example raised an exception: check if it was expected.
|
# The example raised an exception: check if it was expected.
|
||||||
else:
|
else:
|
||||||
exc_info = sys.exc_info()
|
exc_msg = traceback.format_exception_only(*exception[:2])[-1]
|
||||||
exc_msg = traceback.format_exception_only(*exc_info[:2])[-1]
|
if six.PY3:
|
||||||
|
# module name will be in group(1) and the expected
|
||||||
|
# exception message will be in group(2)
|
||||||
|
m = re.match(r'(.*)\.(\w+:.+\s)', exc_msg)
|
||||||
|
# make sure there's a match
|
||||||
|
if m != None:
|
||||||
|
f_name = m.group(1)
|
||||||
|
# check to see if m.group(1) contains the module name
|
||||||
|
if f_name == exception[0].__module__:
|
||||||
|
# strip the module name from exc_msg
|
||||||
|
exc_msg = m.group(2)
|
||||||
|
|
||||||
if not quiet:
|
if not quiet:
|
||||||
got += _exception_traceback(exc_info)
|
got += _exception_traceback(exception)
|
||||||
|
|
||||||
# If `example.exc_msg` is None, then we weren't expecting
|
# If `example.exc_msg` is None, then we weren't expecting
|
||||||
# an exception.
|
# an exception.
|
||||||
|
@ -1289,7 +1376,7 @@ class DocTestRunner:
|
||||||
elif outcome is BOOM:
|
elif outcome is BOOM:
|
||||||
if not quiet:
|
if not quiet:
|
||||||
self.report_unexpected_exception(out, test, example,
|
self.report_unexpected_exception(out, test, example,
|
||||||
exc_info)
|
exception)
|
||||||
failures += 1
|
failures += 1
|
||||||
else:
|
else:
|
||||||
assert False, ("unknown outcome", outcome)
|
assert False, ("unknown outcome", outcome)
|
||||||
|
@ -1629,8 +1716,8 @@ class DebugRunner(DocTestRunner):
|
||||||
... {}, 'foo', 'foo.py', 0)
|
... {}, 'foo', 'foo.py', 0)
|
||||||
>>> try:
|
>>> try:
|
||||||
... runner.run(test)
|
... runner.run(test)
|
||||||
... except UnexpectedException as failure:
|
... except UnexpectedException as e:
|
||||||
... pass
|
... failure = e
|
||||||
|
|
||||||
>>> failure.test is test
|
>>> failure.test is test
|
||||||
True
|
True
|
||||||
|
@ -1657,8 +1744,8 @@ class DebugRunner(DocTestRunner):
|
||||||
|
|
||||||
>>> try:
|
>>> try:
|
||||||
... runner.run(test)
|
... runner.run(test)
|
||||||
... except DocTestFailure as failure:
|
... except DocTestFailure as e:
|
||||||
... pass
|
... failure = e
|
||||||
|
|
||||||
DocTestFailure objects provide access to the test:
|
DocTestFailure objects provide access to the test:
|
||||||
|
|
||||||
|
@ -2167,8 +2254,8 @@ class DocTestCase(unittest.TestCase):
|
||||||
>>> case = DocTestCase(test)
|
>>> case = DocTestCase(test)
|
||||||
>>> try:
|
>>> try:
|
||||||
... case.debug()
|
... case.debug()
|
||||||
... except UnexpectedException as failure:
|
... except UnexpectedException as e:
|
||||||
... pass
|
... failure = e
|
||||||
|
|
||||||
The UnexpectedException contains the test, the example, and
|
The UnexpectedException contains the test, the example, and
|
||||||
the original exception:
|
the original exception:
|
||||||
|
@ -2196,8 +2283,8 @@ class DocTestCase(unittest.TestCase):
|
||||||
|
|
||||||
>>> try:
|
>>> try:
|
||||||
... case.debug()
|
... case.debug()
|
||||||
... except DocTestFailure as failure:
|
... except DocTestFailure as e:
|
||||||
... pass
|
... failure = e
|
||||||
|
|
||||||
DocTestFailure objects provide access to the test:
|
DocTestFailure objects provide access to the test:
|
||||||
|
|
||||||
|
@ -2646,7 +2733,7 @@ __test__ = {"_TestClass": _TestClass,
|
||||||
"whitespace normalization": r"""
|
"whitespace normalization": r"""
|
||||||
If the whitespace normalization flag is used, then
|
If the whitespace normalization flag is used, then
|
||||||
differences in whitespace are ignored.
|
differences in whitespace are ignored.
|
||||||
>>> print(range(30)) #doctest: +NORMALIZE_WHITESPACE
|
>>> print(list(xrange(30))) #doctest: +NORMALIZE_WHITESPACE
|
||||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
||||||
27, 28, 29]
|
27, 28, 29]
|
||||||
|
|
Loading…
Reference in New Issue