diff options
Diffstat (limited to 'Lib/doctest.py')
-rw-r--r-- | Lib/doctest.py | 153 |
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. |