Repository: adamchainz/django-rich (language: python)

Issue:
Fix traceback printing
### Description

`RichTextTestResult._exc_info_to_string` has drifted from unittest’s version, especially with changes in 3.11: https://github.com/python/cpython/commit/88b7d86a73da9388aa65c96401c2984c8c16f8db . This means that tracebacks are showing noisy frames from unittest and `--pdb` drops into the unittest method.

We need to fix the method to correctly use the start of 


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 98b0d17..f5e680d 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,11 @@ Changelog
 
   THanks to David Smith in `PR #224 <https://github.com/adamchainz/django-rich/pull/224>`__.
 
+* Use unittest’s traceback cleaning.
+  Unnecessary frames will now be hidden, as with vanilla unittest, and ``--pdb`` will skip past them.
+
+  `PR #225 <https://github.com/adamchainz/django-rich/pull/225>`__.
+
 1.10.0 (2024-08-12)
 -------------------
 


Test patch:
diff --git a/src/django_rich/test.py b/src/django_rich/test.py
index e1b6d23..252e102 100644
--- a/src/django_rich/test.py
+++ b/src/django_rich/test.py
@@ -167,9 +167,14 @@ def addSubTest(
     def _exc_info_to_string(self, err: _SysExcInfoType, test: TestCase) -> str:
         """Converts a sys.exc_info()-style tuple of values into a string."""
         exctype, value, tb = err
-        # Skip test runner traceback levels
-        while tb and self._is_relevant_tb_level(tb):  # type: ignore [attr-defined]
-            tb = tb.tb_next
+
+        if hasattr(self, "_clean_tracebacks"):
+            # Post-bpo-24959 - merged to Python 3.11, backported to 3.9 and 3.10
+            tb = self._clean_tracebacks(exctype, value, tb, test)
+        else:
+            # Skip test runner traceback levels
+            while tb and self._is_relevant_tb_level(tb):  # type: ignore [attr-defined]
+                tb = tb.tb_next
 
         msgLines = []
         if exctype is not None:  # pragma: no branch  # can't work when this isn't true
diff --git a/tests/test_test.py b/tests/test_test.py
index d4264cf..1da3be1 100644
--- a/tests/test_test.py
+++ b/tests/test_test.py
@@ -138,7 +138,10 @@ def test_test_runner_base_class(self):
         assert DiscoverRunner.test_runner is unittest.TextTestRunner
 
     def run_test(
-        self, *args: str, input: str | None = None
+        self,
+        *args: str,
+        input: str | None = None,
+        width: int = 80,
     ) -> subprocess.CompletedProcess[str]:
         return subprocess.run(
             [
@@ -157,7 +160,7 @@ def run_test(
                 "COVERAGE_PROCESS_START": str(PYPROJECT_PATH),
                 # Ensure rich uses colouring and consistent width
                 "TERM": "",
-                "COLUMNS": "80",
+                "COLUMNS": str(width),
             },
         )
 
@@ -324,6 +327,18 @@ def test_failure_verbose(self):
             ]
         assert "─ locals ─" in result.stderr
 
+    @pytest.mark.skipif(
+        sys.version_info < (3, 9),
+        reason="Traceback cleaning only on Python 3.10+",
+    )
+    def test_failure_stack_frames(self):
+        result = self.run_test(
+            "-v", "2", f"{__name__}.ExampleTests.test_failure", width=1000
+        )
+        assert result.returncode == 1
+        assert "unittest/case.py" not in result.stderr
+        assert "in _baseAssertEqual" not in result.stderr
+
     def test_skip_quiet(self):
         result = self.run_test("-v", "0", f"{__name__}.ExampleTests.test_skip")
         assert result.returncode == 0
@@ -506,8 +521,12 @@ def test_pdb(self):
             expected.insert(0, "Found 1 test(s).")
         assert lines[: len(expected)] == expected
 
-        assert lines[len(expected) + 1 : len(expected) + 4] == [
-            "-> raise self.failureException(msg)",
+        if sys.version_info >= (3, 9):
+            assert lines[len(expected) + 1] == "-> self.assertEqual(1, 2)"
+        else:
+            assert lines[len(expected) + 1] == "-> raise self.failureException(msg)"
+
+        assert lines[len(expected) + 2 : len(expected) + 4] == [
             "(Pdb) 4",
             "(Pdb) ",
         ]