Issue #19138: doctest's IGNORE_EXCEPTION_DETAIL now allows no detail at all.
(grafted from c80083ad142db2939507800c755082293a87f2de)
diff --git a/Lib/doctest.py b/Lib/doctest.py
index 0b485f1..3f0d9d9 100644
--- a/Lib/doctest.py
+++ b/Lib/doctest.py
@@ -314,6 +314,32 @@
else:
return '#'
+def _strip_exception_details(msg):
+ # Support for IGNORE_EXCEPTION_DETAIL.
+ # Get rid of everything except the exception name; in particular, drop
+ # the possibly dotted module path (if any) and the exception message (if
+ # any). We assume that a colon is never part of a dotted name, or of an
+ # exception name.
+ # E.g., given
+ # "foo.bar.MyError: la di da"
+ # return "MyError"
+ # Or for "abc.def" or "abc.def:\n" return "def".
+
+ start, end = 0, len(msg)
+ # The exception name must appear on the first line.
+ i = msg.find("\n")
+ if i >= 0:
+ end = i
+ # retain up to the first colon (if any)
+ i = msg.find(':', 0, end)
+ if i >= 0:
+ end = i
+ # retain just the exception name
+ i = msg.rfind('.', 0, end)
+ if i >= 0:
+ start = i+1
+ return msg[start: end]
+
class _OutputRedirectingPdb(pdb.Pdb):
"""
A specialized version of the python debugger that redirects stdout
@@ -1320,10 +1346,9 @@
# 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(1), m2.group(1),
- self.optionflags):
+ if check(_strip_exception_details(example.exc_msg),
+ _strip_exception_details(exc_msg),
+ self.optionflags):
outcome = SUCCESS
# Report the outcome.
diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py
index 8f8c7c7..86259c3 100644
--- a/Lib/test/test_doctest.py
+++ b/Lib/test/test_doctest.py
@@ -1020,6 +1020,33 @@
ValueError: message
TestResults(failed=1, attempted=1)
+If the exception does not have a message, you can still use
+IGNORE_EXCEPTION_DETAIL to normalize the modules between Python 2 and 3:
+
+ >>> def f(x):
+ ... r'''
+ ... >>> from http.client import HTTPException
+ ... >>> raise HTTPException() #doctest: +IGNORE_EXCEPTION_DETAIL
+ ... Traceback (most recent call last):
+ ... foo.bar.HTTPException
+ ... '''
+ >>> test = doctest.DocTestFinder().find(f)[0]
+ >>> doctest.DocTestRunner(verbose=False).run(test)
+ TestResults(failed=0, attempted=2)
+
+Note that a trailing colon doesn't matter either:
+
+ >>> def f(x):
+ ... r'''
+ ... >>> from http.client import HTTPException
+ ... >>> raise HTTPException() #doctest: +IGNORE_EXCEPTION_DETAIL
+ ... Traceback (most recent call last):
+ ... foo.bar.HTTPException:
+ ... '''
+ >>> test = doctest.DocTestFinder().find(f)[0]
+ >>> doctest.DocTestRunner(verbose=False).run(test)
+ TestResults(failed=0, attempted=2)
+
If an exception is raised but not expected, then it is reported as an
unexpected exception:
diff --git a/Misc/NEWS b/Misc/NEWS
index 1af0788..b2f18ad 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,6 +18,10 @@
Library
-------
+- Issue #19138: doctest's IGNORE_EXCEPTION_DETAIL now allows a match when
+ no exception detail exists (no colon following the exception's name, or
+ a colon does follow but no text follows the colon).
+
- Issue #19834: Support unpickling of exceptions pickled by Python 2.
- Issue #15798: Fixed subprocess.Popen() to no longer fail if file