diff options
author | Irit Katriel <1055913+iritkatriel@users.noreply.github.com> | 2021-12-03 22:01:15 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-03 22:01:15 (GMT) |
commit | 5bb7ef2768be5979b306e4c7552862b1746c251d (patch) | |
tree | 2c5d4d96a1e5bba8d0d918cc413d882f10308e63 /Lib | |
parent | d9301703fb1086cafbd730c17e3d450a192485d6 (diff) | |
download | cpython-5bb7ef2768be5979b306e4c7552862b1746c251d.zip cpython-5bb7ef2768be5979b306e4c7552862b1746c251d.tar.gz cpython-5bb7ef2768be5979b306e4c7552862b1746c251d.tar.bz2 |
bpo-45607: Make it possible to enrich exception displays via setting their __note__ field (GH-29880)
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/test/test_exceptions.py | 21 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 8 | ||||
-rw-r--r-- | Lib/test/test_traceback.py | 69 | ||||
-rw-r--r-- | Lib/traceback.py | 4 |
4 files changed, 98 insertions, 4 deletions
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index c666004..e4b7b8f 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -516,6 +516,27 @@ class ExceptionTests(unittest.TestCase): 'pickled "%r", attribute "%s' % (e, checkArgName)) + def test_note(self): + for e in [BaseException(1), Exception(2), ValueError(3)]: + with self.subTest(e=e): + self.assertIsNone(e.__note__) + e.__note__ = "My Note" + self.assertEqual(e.__note__, "My Note") + + with self.assertRaises(TypeError): + e.__note__ = 42 + self.assertEqual(e.__note__, "My Note") + + e.__note__ = "Your Note" + self.assertEqual(e.__note__, "Your Note") + + with self.assertRaises(TypeError): + del e.__note__ + self.assertEqual(e.__note__, "Your Note") + + e.__note__ = None + self.assertIsNone(e.__note__) + def testWithTraceback(self): try: raise IndexError(4) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index db8d008..2b1ba24 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1298,13 +1298,13 @@ class SizeofTest(unittest.TestCase): class C(object): pass check(C.__dict__, size('P')) # BaseException - check(BaseException(), size('5Pb')) + check(BaseException(), size('6Pb')) # UnicodeEncodeError - check(UnicodeEncodeError("", "", 0, 0, ""), size('5Pb 2P2nP')) + check(UnicodeEncodeError("", "", 0, 0, ""), size('6Pb 2P2nP')) # UnicodeDecodeError - check(UnicodeDecodeError("", b"", 0, 0, ""), size('5Pb 2P2nP')) + check(UnicodeDecodeError("", b"", 0, 0, ""), size('6Pb 2P2nP')) # UnicodeTranslateError - check(UnicodeTranslateError("", 0, 1, ""), size('5Pb 2P2nP')) + check(UnicodeTranslateError("", 0, 1, ""), size('6Pb 2P2nP')) # ellipses check(Ellipsis, size('')) # EncodingMap diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index cde35f5..a458b21 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -1224,6 +1224,22 @@ class BaseExceptionReportingTests: exp = "\n".join(expected) self.assertEqual(exp, err) + def test_exception_with_note(self): + e = ValueError(42) + vanilla = self.get_report(e) + + e.__note__ = 'My Note' + self.assertEqual(self.get_report(e), vanilla + 'My Note\n') + + e.__note__ = '' + self.assertEqual(self.get_report(e), vanilla + '\n') + + e.__note__ = 'Your Note' + self.assertEqual(self.get_report(e), vanilla + 'Your Note\n') + + e.__note__ = None + self.assertEqual(self.get_report(e), vanilla) + def test_exception_qualname(self): class A: class B: @@ -1566,6 +1582,59 @@ class BaseExceptionReportingTests: report = self.get_report(exc) self.assertEqual(report, expected) + def test_exception_group_with_notes(self): + def exc(): + try: + excs = [] + for msg in ['bad value', 'terrible value']: + try: + raise ValueError(msg) + except ValueError as e: + e.__note__ = f'the {msg}' + excs.append(e) + raise ExceptionGroup("nested", excs) + except ExceptionGroup as e: + e.__note__ = ('>> Multi line note\n' + '>> Because I am such\n' + '>> an important exception.\n' + '>> empty lines work too\n' + '\n' + '(that was an empty line)') + raise + + expected = (f' + Exception Group Traceback (most recent call last):\n' + f' | File "{__file__}", line {self.callable_line}, in get_exception\n' + f' | exception_or_callable()\n' + f' | ^^^^^^^^^^^^^^^^^^^^^^^\n' + f' | File "{__file__}", line {exc.__code__.co_firstlineno + 9}, in exc\n' + f' | raise ExceptionGroup("nested", excs)\n' + f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' + f' | ExceptionGroup: nested\n' + f' | >> Multi line note\n' + f' | >> Because I am such\n' + f' | >> an important exception.\n' + f' | >> empty lines work too\n' + f' | \n' + f' | (that was an empty line)\n' + f' +-+---------------- 1 ----------------\n' + f' | Traceback (most recent call last):\n' + f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n' + f' | raise ValueError(msg)\n' + f' | ^^^^^^^^^^^^^^^^^^^^^\n' + f' | ValueError: bad value\n' + f' | the bad value\n' + f' +---------------- 2 ----------------\n' + f' | Traceback (most recent call last):\n' + f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n' + f' | raise ValueError(msg)\n' + f' | ^^^^^^^^^^^^^^^^^^^^^\n' + f' | ValueError: terrible value\n' + f' | the terrible value\n' + f' +------------------------------------\n') + + report = self.get_report(exc) + self.assertEqual(report, expected) + class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): # diff --git a/Lib/traceback.py b/Lib/traceback.py index 77f8590..b244750 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -685,6 +685,8 @@ class TracebackException: # Capture now to permit freeing resources: only complication is in the # unofficial API _format_final_exc_line self._str = _some_str(exc_value) + self.__note__ = exc_value.__note__ if exc_value else None + if exc_type and issubclass(exc_type, SyntaxError): # Handle SyntaxError's specially self.filename = exc_value.filename @@ -816,6 +818,8 @@ class TracebackException: yield _format_final_exc_line(stype, self._str) else: yield from self._format_syntax_error(stype) + if self.__note__ is not None: + yield from [l + '\n' for l in self.__note__.split('\n')] def _format_syntax_error(self, stype): """Format SyntaxError exceptions (internal helper).""" |