https://github.com/python/cpython/commit/cb8a72b301f47e76d93a7fe5b259e9a5758792e1
commit: cb8a72b301f47e76d93a7fe5b259e9a5758792e1
branch: main
author: Serhiy Storchaka <storch...@gmail.com>
committer: serhiy-storchaka <storch...@gmail.com>
date: 2025-05-30T00:32:44+03:00
summary:

gh-134857: Improve error report for doctests run with unittest (GH-134858)

Remove doctest module frames from tracebacks and redundant newline
character from a failure message.

files:
A Misc/NEWS.d/next/Library/2025-05-28-20-49-29.gh-issue-134857.dVYXVO.rst
M Lib/doctest.py
M Lib/test/test_doctest/test_doctest.py

diff --git a/Lib/doctest.py b/Lib/doctest.py
index 2acb6cb79f394d..dec10a345165da 100644
--- a/Lib/doctest.py
+++ b/Lib/doctest.py
@@ -108,6 +108,8 @@ def _test():
 from _colorize import ANSIColors, can_colorize
 
 
+__unittest = True
+
 class TestResults(namedtuple('TestResults', 'failed attempted')):
     def __new__(cls, failed, attempted, *, skipped=0):
         results = super().__new__(cls, failed, attempted)
@@ -1395,11 +1397,11 @@ def __run(self, test, compileflags, out):
                 exec(compile(example.source, filename, "single",
                              compileflags, True), test.globs)
                 self.debugger.set_continue() # ==== Example Finished ====
-                exception = None
+                exc_info = None
             except KeyboardInterrupt:
                 raise
-            except:
-                exception = sys.exc_info()
+            except BaseException as exc:
+                exc_info = type(exc), exc, exc.__traceback__.tb_next
                 self.debugger.set_continue() # ==== Example Finished ====
 
             got = self._fakeout.getvalue()  # the actual output
@@ -1408,21 +1410,21 @@ def __run(self, test, compileflags, out):
 
             # If the example executed without raising any exceptions,
             # verify its output.
-            if exception is None:
+            if exc_info is None:
                 if check(example.want, got, self.optionflags):
                     outcome = SUCCESS
 
             # The example raised an exception:  check if it was expected.
             else:
-                formatted_ex = traceback.format_exception_only(*exception[:2])
-                if issubclass(exception[0], SyntaxError):
+                formatted_ex = traceback.format_exception_only(*exc_info[:2])
+                if issubclass(exc_info[0], SyntaxError):
                     # SyntaxError / IndentationError is special:
                     # we don't care about the carets / suggestions / etc
                     # We only care about the error message and notes.
                     # They start with `SyntaxError:` (or any other class name)
                     exception_line_prefixes = (
-                        f"{exception[0].__qualname__}:",
-                        
f"{exception[0].__module__}.{exception[0].__qualname__}:",
+                        f"{exc_info[0].__qualname__}:",
+                        
f"{exc_info[0].__module__}.{exc_info[0].__qualname__}:",
                     )
                     exc_msg_index = next(
                         index
@@ -1433,7 +1435,7 @@ def __run(self, test, compileflags, out):
 
                 exc_msg = "".join(formatted_ex)
                 if not quiet:
-                    got += _exception_traceback(exception)
+                    got += _exception_traceback(exc_info)
 
                 # If `example.exc_msg` is None, then we weren't expecting
                 # an exception.
@@ -1462,7 +1464,7 @@ def __run(self, test, compileflags, out):
             elif outcome is BOOM:
                 if not quiet:
                     self.report_unexpected_exception(out, test, example,
-                                                     exception)
+                                                     exc_info)
                 failures += 1
             else:
                 assert False, ("unknown outcome", outcome)
@@ -2324,7 +2326,7 @@ def runTest(self):
             sys.stdout = old
 
         if results.failed:
-            raise self.failureException(self.format_failure(new.getvalue()))
+            raise 
self.failureException(self.format_failure(new.getvalue().rstrip('\n')))
 
     def format_failure(self, err):
         test = self._dt_test
diff --git a/Lib/test/test_doctest/test_doctest.py 
b/Lib/test/test_doctest/test_doctest.py
index c5b247797c321d..2bfaa6c599cd47 100644
--- a/Lib/test/test_doctest/test_doctest.py
+++ b/Lib/test/test_doctest/test_doctest.py
@@ -2411,9 +2411,6 @@ def test_DocTestSuite_errors():
          >>> result
          <unittest.result.TestResult run=4 errors=0 failures=4>
          >>> print(result.failures[0][1]) # doctest: +ELLIPSIS
-         Traceback (most recent call last):
-           File ...
-             raise self.failureException(self.format_failure(new.getvalue()))
          AssertionError: Failed doctest test for 
test.test_doctest.sample_doctest_errors
            File "...sample_doctest_errors.py", line 0, in sample_doctest_errors
          <BLANKLINE>
@@ -2431,21 +2428,12 @@ def test_DocTestSuite_errors():
              1/0
          Exception raised:
              Traceback (most recent call last):
-               File ...
-                 exec(compile(example.source, filename, "single",
-                 ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                              compileflags, True), test.globs)
-                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                File "<doctest test.test_doctest.sample_doctest_errors[1]>", 
line 1, in <module>
                  1/0
                  ~^~
              ZeroDivisionError: division by zero
          <BLANKLINE>
-         <BLANKLINE>
          >>> print(result.failures[1][1]) # doctest: +ELLIPSIS
-         Traceback (most recent call last):
-           File ...
-             raise self.failureException(self.format_failure(new.getvalue()))
          AssertionError: Failed doctest test for 
test.test_doctest.sample_doctest_errors.__test__.bad
            File "...sample_doctest_errors.py", line unknown line number, in bad
          <BLANKLINE>
@@ -2463,21 +2451,12 @@ def test_DocTestSuite_errors():
              1/0
          Exception raised:
              Traceback (most recent call last):
-               File ...
-                 exec(compile(example.source, filename, "single",
-                 ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                              compileflags, True), test.globs)
-                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                File "<doctest 
test.test_doctest.sample_doctest_errors.__test__.bad[1]>", line 1, in <module>
                  1/0
                  ~^~
              ZeroDivisionError: division by zero
          <BLANKLINE>
-         <BLANKLINE>
          >>> print(result.failures[2][1]) # doctest: +ELLIPSIS
-         Traceback (most recent call last):
-           File ...
-             raise self.failureException(self.format_failure(new.getvalue()))
          AssertionError: Failed doctest test for 
test.test_doctest.sample_doctest_errors.errors
            File "...sample_doctest_errors.py", line 14, in errors
          <BLANKLINE>
@@ -2495,11 +2474,6 @@ def test_DocTestSuite_errors():
              1/0
          Exception raised:
              Traceback (most recent call last):
-               File ...
-                 exec(compile(example.source, filename, "single",
-                 ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                              compileflags, True), test.globs)
-                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                File "<doctest 
test.test_doctest.sample_doctest_errors.errors[1]>", line 1, in <module>
                  1/0
                  ~^~
@@ -2510,11 +2484,6 @@ def test_DocTestSuite_errors():
              f()
          Exception raised:
              Traceback (most recent call last):
-               File ...
-                 exec(compile(example.source, filename, "single",
-                 ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                              compileflags, True), test.globs)
-                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                File "<doctest 
test.test_doctest.sample_doctest_errors.errors[3]>", line 1, in <module>
                  f()
                  ~^^
@@ -2528,11 +2497,6 @@ def test_DocTestSuite_errors():
              g()
          Exception raised:
              Traceback (most recent call last):
-               File ...
-                 exec(compile(example.source, filename, "single",
-                 ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                              compileflags, True), test.globs)
-                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                File "<doctest 
test.test_doctest.sample_doctest_errors.errors[4]>", line 1, in <module>
                  g()
                  ~^^
@@ -2541,11 +2505,7 @@ def test_DocTestSuite_errors():
                  ~~^^^
              IndexError: list index out of range
          <BLANKLINE>
-         <BLANKLINE>
          >>> print(result.failures[3][1]) # doctest: +ELLIPSIS
-         Traceback (most recent call last):
-           File ...
-             raise self.failureException(self.format_failure(new.getvalue()))
          AssertionError: Failed doctest test for 
test.test_doctest.sample_doctest_errors.syntax_error
            File "...sample_doctest_errors.py", line 29, in syntax_error
          <BLANKLINE>
@@ -2554,18 +2514,11 @@ def test_DocTestSuite_errors():
          Failed example:
              2+*3
          Exception raised:
-             Traceback (most recent call last):
-               File ...
-                 exec(compile(example.source, filename, "single",
-                      ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                              compileflags, True), test.globs)
-                              ^^^^^^^^^^^^^^^^^^^
                File "<doctest 
test.test_doctest.sample_doctest_errors.syntax_error[0]>", line 1
                  2+*3
                    ^
              SyntaxError: invalid syntax
          <BLANKLINE>
-         <BLANKLINE>
     """
 
 def test_DocFileSuite():
@@ -2740,9 +2693,6 @@ def test_DocFileSuite_errors():
         >>> result
         <unittest.result.TestResult run=1 errors=0 failures=1>
         >>> print(result.failures[0][1]) # doctest: +ELLIPSIS
-        Traceback (most recent call last):
-          File ...
-            raise self.failureException(self.format_failure(new.getvalue()))
         AssertionError: Failed doctest test for test_doctest_errors.txt
           File "...test_doctest_errors.txt", line 0
         <BLANKLINE>
@@ -2760,11 +2710,6 @@ def test_DocFileSuite_errors():
             1/0
         Exception raised:
             Traceback (most recent call last):
-              File ...
-                exec(compile(example.source, filename, "single",
-                ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                             compileflags, True), test.globs)
-                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
               File "<doctest test_doctest_errors.txt[1]>", line 1, in <module>
                 1/0
                 ~^~
@@ -2775,11 +2720,6 @@ def test_DocFileSuite_errors():
             f()
         Exception raised:
             Traceback (most recent call last):
-              File ...
-                exec(compile(example.source, filename, "single",
-                ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                             compileflags, True), test.globs)
-                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
               File "<doctest test_doctest_errors.txt[3]>", line 1, in <module>
                 f()
                 ~^^
@@ -2792,18 +2732,11 @@ def test_DocFileSuite_errors():
         Failed example:
             2+*3
         Exception raised:
-            Traceback (most recent call last):
-              File ...
-                exec(compile(example.source, filename, "single",
-                     ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                             compileflags, True), test.globs)
-                             ^^^^^^^^^^^^^^^^^^^
               File "<doctest test_doctest_errors.txt[4]>", line 1
                 2+*3
                   ^
             SyntaxError: invalid syntax
         <BLANKLINE>
-        <BLANKLINE>
     """
 
 def test_trailing_space_in_test():
@@ -2876,7 +2809,8 @@ def test_unittest_reportflags():
       >>> result
       <unittest.result.TestResult run=1 errors=0 failures=1>
       >>> print(result.failures[0][1]) # doctest: +ELLIPSIS
-      Traceback ...
+      AssertionError: Failed doctest test for test_doctest.txt
+      ...
       Failed example:
           favorite_color
       ...
@@ -2895,14 +2829,14 @@ def test_unittest_reportflags():
       >>> result
       <unittest.result.TestResult run=1 errors=0 failures=1>
       >>> print(result.failures[0][1]) # doctest: +ELLIPSIS
-      Traceback ...
+      AssertionError: Failed doctest test for test_doctest.txt
+      ...
       Failed example:
           favorite_color
       Exception raised:
           ...
           NameError: name 'favorite_color' is not defined
       <BLANKLINE>
-      <BLANKLINE>
 
     We get only the first failure.
 
@@ -2922,7 +2856,8 @@ def test_unittest_reportflags():
     the trailing whitespace using `\x20` in the diff below.
 
       >>> print(result.failures[0][1]) # doctest: +ELLIPSIS
-      Traceback ...
+      AssertionError: Failed doctest test for test_doctest.txt
+      ...
       Failed example:
           favorite_color
       ...
@@ -2937,7 +2872,6 @@ def test_unittest_reportflags():
           +\x20
             b
       <BLANKLINE>
-      <BLANKLINE>
 
 
     Test runners can restore the formatting flags after they run:
@@ -3145,11 +3079,6 @@ def test_testfile_errors(): r"""
         1/0
     Exception raised:
         Traceback (most recent call last):
-          File ...
-            exec(compile(example.source, filename, "single",
-            ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                         compileflags, True), test.globs)
-                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           File "<doctest test_doctest_errors.txt[1]>", line 1, in <module>
             1/0
             ~^~
@@ -3160,11 +3089,6 @@ def test_testfile_errors(): r"""
         f()
     Exception raised:
         Traceback (most recent call last):
-          File ...
-            exec(compile(example.source, filename, "single",
-            ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                         compileflags, True), test.globs)
-                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           File "<doctest test_doctest_errors.txt[3]>", line 1, in <module>
             f()
             ~^^
@@ -3177,12 +3101,6 @@ def test_testfile_errors(): r"""
     Failed example:
         2+*3
     Exception raised:
-        Traceback (most recent call last):
-          File ...
-            exec(compile(example.source, filename, "single",
-                 ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                         compileflags, True), test.globs)
-                         ^^^^^^^^^^^^^^^^^^^
           File "<doctest test_doctest_errors.txt[4]>", line 1
             2+*3
               ^
@@ -3343,11 +3261,6 @@ def test_testmod_errors(): r"""
         1/0
     Exception raised:
         Traceback (most recent call last):
-          File ...
-            exec(compile(example.source, filename, "single",
-            ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                         compileflags, True), test.globs)
-                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           File "<doctest test.test_doctest.sample_doctest_errors[1]>", line 1, 
in <module>
             1/0
             ~^~
@@ -3366,11 +3279,6 @@ def test_testmod_errors(): r"""
         1/0
     Exception raised:
         Traceback (most recent call last):
-          File ...
-            exec(compile(example.source, filename, "single",
-            ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                         compileflags, True), test.globs)
-                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           File "<doctest 
test.test_doctest.sample_doctest_errors.__test__.bad[1]>", line 1, in <module>
             1/0
             ~^~
@@ -3389,11 +3297,6 @@ def test_testmod_errors(): r"""
         1/0
     Exception raised:
         Traceback (most recent call last):
-          File ...
-            exec(compile(example.source, filename, "single",
-            ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                         compileflags, True), test.globs)
-                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           File "<doctest test.test_doctest.sample_doctest_errors.errors[1]>", 
line 1, in <module>
             1/0
             ~^~
@@ -3404,11 +3307,6 @@ def test_testmod_errors(): r"""
         f()
     Exception raised:
         Traceback (most recent call last):
-          File ...
-            exec(compile(example.source, filename, "single",
-            ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                         compileflags, True), test.globs)
-                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           File "<doctest test.test_doctest.sample_doctest_errors.errors[3]>", 
line 1, in <module>
             f()
             ~^^
@@ -3422,11 +3320,6 @@ def test_testmod_errors(): r"""
         g()
     Exception raised:
         Traceback (most recent call last):
-          File ...
-            exec(compile(example.source, filename, "single",
-            ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                         compileflags, True), test.globs)
-                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           File "<doctest test.test_doctest.sample_doctest_errors.errors[4]>", 
line 1, in <module>
             g()
             ~^^
@@ -3439,12 +3332,6 @@ def test_testmod_errors(): r"""
     Failed example:
         2+*3
     Exception raised:
-        Traceback (most recent call last):
-          File ...
-            exec(compile(example.source, filename, "single",
-                 ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                         compileflags, True), test.globs)
-                         ^^^^^^^^^^^^^^^^^^^
           File "<doctest 
test.test_doctest.sample_doctest_errors.syntax_error[0]>", line 1
             2+*3
               ^
@@ -3490,11 +3377,6 @@ def test_unicode(): """
         raise Exception('clé')
     Exception raised:
         Traceback (most recent call last):
-          File ...
-            exec(compile(example.source, filename, "single",
-            ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-                         compileflags, True), test.globs)
-                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           File "<doctest foo-bär@baz[0]>", line 1, in <module>
             raise Exception('clé')
         Exception: clé
diff --git 
a/Misc/NEWS.d/next/Library/2025-05-28-20-49-29.gh-issue-134857.dVYXVO.rst 
b/Misc/NEWS.d/next/Library/2025-05-28-20-49-29.gh-issue-134857.dVYXVO.rst
new file mode 100644
index 00000000000000..92e38c0bb5ac87
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-28-20-49-29.gh-issue-134857.dVYXVO.rst
@@ -0,0 +1,3 @@
+Improve error report for :mod:`doctest`\ s run with :mod:`unittest`. Remove
+:mod:`!doctest` module frames from tracebacks and redundant newline
+character from a failure message.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to