summaryrefslogtreecommitdiff
path: root/Lib/doctest.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/doctest.py')
-rw-r--r--Lib/doctest.py153
1 files changed, 112 insertions, 41 deletions
diff --git a/Lib/doctest.py b/Lib/doctest.py
index fae333e4f6..e189c8feba 100644
--- a/Lib/doctest.py
+++ b/Lib/doctest.py
@@ -92,10 +92,15 @@ __all__ = [
]
import __future__
-
-import sys, traceback, inspect, linecache, os, re
-import unittest, difflib, pdb, tempfile
-import warnings
+import difflib
+import inspect
+import linecache
+import os
+import pdb
+import re
+import sys
+import traceback
+import unittest
from io import StringIO
from collections import namedtuple
@@ -318,7 +323,8 @@ class _OutputRedirectingPdb(pdb.Pdb):
def __init__(self, out):
self.__out = out
self.__debugger_used = False
- pdb.Pdb.__init__(self, stdout=out)
+ # do not play signal games in the pdb
+ pdb.Pdb.__init__(self, stdout=out, nosigint=True)
# still use input() to get user input
self.use_rawinput = 1
@@ -434,6 +440,25 @@ class Example:
self.options = options
self.exc_msg = exc_msg
+ def __eq__(self, other):
+ if type(self) is not type(other):
+ return NotImplemented
+
+ return self.source == other.source and \
+ self.want == other.want and \
+ self.lineno == other.lineno and \
+ self.indent == other.indent and \
+ self.options == other.options and \
+ self.exc_msg == other.exc_msg
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __hash__(self):
+ return hash((self.source, self.want, self.lineno, self.indent,
+ self.exc_msg))
+
+
class DocTest:
"""
A collection of doctest examples that should be run in a single
@@ -482,6 +507,22 @@ class DocTest:
return ('<DocTest %s from %s:%s (%s)>' %
(self.name, self.filename, self.lineno, examples))
+ def __eq__(self, other):
+ if type(self) is not type(other):
+ return NotImplemented
+
+ return self.examples == other.examples and \
+ self.docstring == other.docstring and \
+ self.globs == other.globs and \
+ self.name == other.name and \
+ self.filename == other.filename and \
+ self.lineno == other.lineno
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __hash__(self):
+ return hash((self.docstring, self.name, self.filename, self.lineno))
# This lets us sort tests by name:
def __lt__(self, other):
@@ -1280,9 +1321,9 @@ class DocTestRunner:
# Another chance if they didn't care about the detail.
elif self.optionflags & IGNORE_EXCEPTION_DETAIL:
- m1 = re.match(r'[^:]*:', example.exc_msg)
- m2 = re.match(r'[^:]*:', exc_msg)
- if m1 and m2 and check(m1.group(0), m2.group(0),
+ m1 = re.match(r'(?:[^:]*\.)?([^:]*:)', example.exc_msg)
+ m2 = re.match(r'(?:[^:]*\.)?([^:]*:)', exc_msg)
+ if m1 and m2 and check(m1.group(1), m2.group(1),
self.optionflags):
outcome = SUCCESS
@@ -1320,7 +1361,7 @@ class DocTestRunner:
self.tries += t
__LINECACHE_FILENAME_RE = re.compile(r'<doctest '
- r'(?P<name>[\w\.]+)'
+ r'(?P<name>.+)'
r'\[(?P<examplenum>\d+)\]>$')
def __patched_linecache_getlines(self, filename, module_globals=None):
m = self.__LINECACHE_FILENAME_RE.match(filename)
@@ -2198,6 +2239,23 @@ class DocTestCase(unittest.TestCase):
def id(self):
return self._dt_test.name
+ def __eq__(self, other):
+ if type(self) is not type(other):
+ return NotImplemented
+
+ return self._dt_test == other._dt_test and \
+ self._dt_optionflags == other._dt_optionflags and \
+ self._dt_setUp == other._dt_setUp and \
+ self._dt_tearDown == other._dt_tearDown and \
+ self._dt_checker == other._dt_checker
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __hash__(self):
+ return hash((self._dt_optionflags, self._dt_setUp, self._dt_tearDown,
+ self._dt_checker))
+
def __repr__(self):
name = self._dt_test.name.split('.')
return "%s (%s)" % (name[-1], '.'.join(name[:-1]))
@@ -2207,6 +2265,23 @@ class DocTestCase(unittest.TestCase):
def shortDescription(self):
return "Doctest: " + self._dt_test.name
+class SkipDocTestCase(DocTestCase):
+ def __init__(self, module):
+ self.module = module
+ DocTestCase.__init__(self, None)
+
+ def setUp(self):
+ self.skipTest("DocTestSuite will not work with -O2 and above")
+
+ def test_skip(self):
+ pass
+
+ def shortDescription(self):
+ return "Skipping tests from %s" % self.module.__name__
+
+ __str__ = shortDescription
+
+
def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
**options):
"""
@@ -2249,13 +2324,25 @@ def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
module = _normalize_module(module)
tests = test_finder.find(module, globs=globs, extraglobs=extraglobs)
- if not tests:
+
+ if not tests and sys.flags.optimize >=2:
+ # Skip doctests when running with -O2
+ suite = unittest.TestSuite()
+ suite.addTest(SkipDocTestCase(module))
+ return suite
+ elif not tests:
# Why do we want to do this? Because it reveals a bug that might
# otherwise be hidden.
- raise ValueError(module, "has no tests")
+ # It is probably a bug that this exception is not also raised if the
+ # number of doctest examples in tests is zero (i.e. if no doctest
+ # examples were found). However, we should probably not be raising
+ # an exception at all here, though it is too late to make this change
+ # for a maintenance release. See also issue #14649.
+ raise ValueError(module, "has no docstrings")
tests.sort()
suite = unittest.TestSuite()
+
for test in tests:
if len(test.examples) == 0:
continue
@@ -2488,37 +2575,21 @@ def debug_script(src, pm=False, globs=None):
"Debug a test script. `src` is the script, as a string."
import pdb
- # Note that tempfile.NameTemporaryFile() cannot be used. As the
- # docs say, a file so created cannot be opened by name a second time
- # on modern Windows boxes, and exec() needs to open and read it.
- srcfilename = tempfile.mktemp(".py", "doctestdebug")
- f = open(srcfilename, 'w')
- f.write(src)
- f.close()
-
- try:
- if globs:
- globs = globs.copy()
- else:
- globs = {}
-
- if pm:
- try:
- with open(srcfilename) as f:
- exec(f.read(), globs, globs)
- except:
- print(sys.exc_info()[1])
- pdb.post_mortem(sys.exc_info()[2])
- else:
- fp = open(srcfilename)
- try:
- script = fp.read()
- finally:
- fp.close()
- pdb.run("exec(%r)" % script, globs, globs)
+ if globs:
+ globs = globs.copy()
+ else:
+ globs = {}
- finally:
- os.remove(srcfilename)
+ if pm:
+ try:
+ exec(src, globs, globs)
+ except:
+ print(sys.exc_info()[1])
+ p = pdb.Pdb(nosigint=True)
+ p.reset()
+ p.interaction(None, sys.exc_info()[2])
+ else:
+ pdb.Pdb(nosigint=True).run("exec(%r)" % src, globs, globs)
def debug(module, name, pm=False):
"""Debug a single doctest docstring.