Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[py3] Ported django.test.doctest.

Based on Vinay Sajip's branch.
  • Loading branch information...
commit 67646dc28d6fc7b4032f1f5a18a5347e81755638 1 parent a8b3dde
@aaugustin aaugustin authored
Showing with 103 additions and 16 deletions.
  1. +103 −16 django/test/_doctest.py
View
119 django/test/_doctest.py
@@ -105,7 +105,7 @@ def _test():
import warnings
from django.utils import six
-from django.utils.six import StringIO
+from django.utils.six.moves import StringIO, xrange
if sys.platform.startswith('java'):
# On Jython, isclass() reports some modules as classes. Patch it.
@@ -501,11 +501,31 @@ def __repr__(self):
# This lets us sort tests by name:
+ def _cmpkey(self):
+ return (self.name, self.filename, self.lineno, id(self))
def __cmp__(self, other):
if not isinstance(other, DocTest):
return -1
- return cmp((self.name, self.filename, self.lineno, id(self)),
- (other.name, other.filename, other.lineno, id(other)))
+ return cmp(self._cmpkey(), other._cmpkey())
+
+ 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
@@ -1229,6 +1249,57 @@ def __run(self, test, compileflags, out):
# __patched_linecache_getlines).
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
# any exception that gets raised. (But don't intercept
# keyboard interrupts.)
@@ -1243,9 +1314,14 @@ def __run(self, test, compileflags, out):
except:
exception = sys.exc_info()
self.debugger.set_continue() # ==== Example Finished ====
+ finally:
+ # restore the original displayhook
+ sys.displayhook = original_displayhook
got = self._fakeout.getvalue() # the actual output
self._fakeout.truncate(0)
+ # Python 3.1 requires seek after truncate
+ self._fakeout.seek(0)
outcome = FAILURE # guilty until proved innocent or insane
# If the example executed without raising any exceptions,
@@ -1256,10 +1332,21 @@ def __run(self, test, compileflags, out):
# The example raised an exception: check if it was expected.
else:
- exc_info = sys.exc_info()
- exc_msg = traceback.format_exception_only(*exc_info[:2])[-1]
+ exc_msg = traceback.format_exception_only(*exception[: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:
- got += _exception_traceback(exc_info)
+ got += _exception_traceback(exception)
# If `example.exc_msg` is None, then we weren't expecting
# an exception.
@@ -1289,7 +1376,7 @@ def __run(self, test, compileflags, out):
elif outcome is BOOM:
if not quiet:
self.report_unexpected_exception(out, test, example,
- exc_info)
+ exception)
failures += 1
else:
assert False, ("unknown outcome", outcome)
@@ -1629,8 +1716,8 @@ class DebugRunner(DocTestRunner):
... {}, 'foo', 'foo.py', 0)
>>> try:
... runner.run(test)
- ... except UnexpectedException as failure:
- ... pass
+ ... except UnexpectedException as e:
+ ... failure = e
>>> failure.test is test
True
@@ -1657,8 +1744,8 @@ class DebugRunner(DocTestRunner):
>>> try:
... runner.run(test)
- ... except DocTestFailure as failure:
- ... pass
+ ... except DocTestFailure as e:
+ ... failure = e
DocTestFailure objects provide access to the test:
@@ -2167,8 +2254,8 @@ def debug(self):
>>> case = DocTestCase(test)
>>> try:
... case.debug()
- ... except UnexpectedException as failure:
- ... pass
+ ... except UnexpectedException as e:
+ ... failure = e
The UnexpectedException contains the test, the example, and
the original exception:
@@ -2196,8 +2283,8 @@ def debug(self):
>>> try:
... case.debug()
- ... except DocTestFailure as failure:
- ... pass
+ ... except DocTestFailure as e:
+ ... failure = e
DocTestFailure objects provide access to the test:
@@ -2646,7 +2733,7 @@ def get(self):
"whitespace normalization": r"""
If the whitespace normalization flag is used, then
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,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29]
Please sign in to comment.
Something went wrong with that request. Please try again.