feat: Remove special cases for error messages
Description
Always stringify exception values using the safe_str() method.
The special-casing of the message and detail attributes resulted in missing detail. The message attribute is a legacy from Python 2, and the detail attribute was returned directly because an old version of Starlette did not implement __str__ for some exceptions.
Issues
Closes https://github.com/getsentry/sentry-python/issues/5050
Reminders
- Please add tests to validate your changes, and lint your code using
tox -e linters. - Add GH Issue ID & Linear ID (if applicable)
- PR title should use conventional commit style (
feat:,fix:,ref:,meta:) - For external contributors: CONTRIBUTING.md, Sentry SDK development docs, Discord community
:x: 14 Tests Failed:
| Tests completed | Failed | Passed | Skipped |
|---|---|---|---|
| 24481 | 14 | 24467 | 1876 |
View the top 3 failed test(s) by shortest run time
tests.test_exceptiongroup::test_exceptiongroup_simpleStack Traces | 0.001s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple assert exception_values[1]["value"] == "simple" E AssertionError: assert 'simple (1 sub-exception)' == 'simple' E E - simple E + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup_simpleStack Traces | 0.001s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple assert exception_values[1]["value"] == "simple" E AssertionError: assert 'simple (1 sub-exception)' == 'simple' E E - simple E + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup_simpleStack Traces | 0.001s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple assert exception_values[1]["value"] == "simple" E AssertionError: assert 'simple (1 sub-exception)' == 'simple' E E - simple E + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup_simpleStack Traces | 0.001s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple assert exception_values[1]["value"] == "simple" E AssertionError: assert 'simple (1 sub-exception)' == 'simple' E E - simple E + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup_simpleStack Traces | 0.001s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple assert exception_values[1]["value"] == "simple" E AssertionError: assert 'simple (1 sub-exception)' == 'simple' E E - simple E + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup_simpleStack Traces | 0.002s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple assert exception_values[1]["value"] == "simple" E AssertionError: assert 'simple (1 sub-exception)' == 'simple' E E - simple E + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup_simpleStack Traces | 0.002s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple assert exception_values[1]["value"] == "simple" E AssertionError: assert 'simple (1 sub-exception)' == 'simple' E E - simple E + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroupStack Traces | 0.004s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup assert values == expected_values E AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}] E E At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'} E E Full diff: E [ E { E 'mechanism': { E 'exception_id': 6, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[2]', E 'type': 'chained', E }, E 'type': 'TypeError', E 'value': 'int', E }, E { E 'mechanism': { E 'exception_id': 5, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ModuleNotFoundError', E 'value': 'another_module', E }, E { E 'mechanism': { E 'exception_id': 4, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ImportError', E 'value': 'no_such_module', E }, E { E 'mechanism': { E 'exception_id': 3, E 'handled': False, E 'is_exception_group': True, E 'parent_id': 0, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ExceptionGroup', E - 'value': 'imports', E + 'value': 'imports (2 sub-exceptions)', E }, E { E 'mechanism': { E 'exception_id': 2, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ValueError', E 'value': '654', E }, E { E 'mechanism': { E 'exception_id': 1, E 'handled': False, E 'parent_id': 0, E 'source': '__context__', E 'type': 'chained', E }, E 'type': 'RuntimeError', E 'value': 'something', E }, E { E 'mechanism': { E 'exception_id': 0, E 'handled': False, E 'is_exception_group': True, E 'type': 'test_suite', E }, E 'type': 'ExceptionGroup', E - 'value': 'nested', E + 'value': 'nested (3 sub-exceptions)', E }, E ]
tests.test_exceptiongroup::test_exceptiongroupStack Traces | 0.004s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup assert values == expected_values E AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}] E E At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'} E E Full diff: E [ E { E 'mechanism': { E 'exception_id': 6, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[2]', E 'type': 'chained', E }, E 'type': 'TypeError', E 'value': 'int', E }, E { E 'mechanism': { E 'exception_id': 5, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ModuleNotFoundError', E 'value': 'another_module', E }, E { E 'mechanism': { E 'exception_id': 4, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ImportError', E 'value': 'no_such_module', E }, E { E 'mechanism': { E 'exception_id': 3, E 'handled': False, E 'is_exception_group': True, E 'parent_id': 0, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ExceptionGroup', E - 'value': 'imports', E + 'value': 'imports (2 sub-exceptions)', E }, E { E 'mechanism': { E 'exception_id': 2, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ValueError', E 'value': '654', E }, E { E 'mechanism': { E 'exception_id': 1, E 'handled': False, E 'parent_id': 0, E 'source': '__context__', E 'type': 'chained', E }, E 'type': 'RuntimeError', E 'value': 'something', E }, E { E 'mechanism': { E 'exception_id': 0, E 'handled': False, E 'is_exception_group': True, E 'type': 'test_suite', E }, E 'type': 'ExceptionGroup', E - 'value': 'nested', E + 'value': 'nested (3 sub-exceptions)', E }, E ]
tests.test_exceptiongroup::test_exceptiongroupStack Traces | 0.004s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup assert values == expected_values E AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}] E E At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'} E E Full diff: E [ E { E 'mechanism': { E 'exception_id': 6, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[2]', E 'type': 'chained', E }, E 'type': 'TypeError', E 'value': 'int', E }, E { E 'mechanism': { E 'exception_id': 5, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ModuleNotFoundError', E 'value': 'another_module', E }, E { E 'mechanism': { E 'exception_id': 4, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ImportError', E 'value': 'no_such_module', E }, E { E 'mechanism': { E 'exception_id': 3, E 'handled': False, E 'is_exception_group': True, E 'parent_id': 0, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ExceptionGroup', E - 'value': 'imports', E + 'value': 'imports (2 sub-exceptions)', E }, E { E 'mechanism': { E 'exception_id': 2, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ValueError', E 'value': '654', E }, E { E 'mechanism': { E 'exception_id': 1, E 'handled': False, E 'parent_id': 0, E 'source': '__context__', E 'type': 'chained', E }, E 'type': 'RuntimeError', E 'value': 'something', E }, E { E 'mechanism': { E 'exception_id': 0, E 'handled': False, E 'is_exception_group': True, E 'type': 'test_suite', E }, E 'type': 'ExceptionGroup', E - 'value': 'nested', E + 'value': 'nested (3 sub-exceptions)', E }, E ]
tests.test_exceptiongroup::test_exceptiongroupStack Traces | 0.004s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup assert values == expected_values E AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}] E E At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'} E E Full diff: E [ E { E 'mechanism': { E 'exception_id': 6, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[2]', E 'type': 'chained', E }, E 'type': 'TypeError', E 'value': 'int', E }, E { E 'mechanism': { E 'exception_id': 5, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ModuleNotFoundError', E 'value': 'another_module', E }, E { E 'mechanism': { E 'exception_id': 4, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ImportError', E 'value': 'no_such_module', E }, E { E 'mechanism': { E 'exception_id': 3, E 'handled': False, E 'is_exception_group': True, E 'parent_id': 0, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ExceptionGroup', E - 'value': 'imports', E + 'value': 'imports (2 sub-exceptions)', E }, E { E 'mechanism': { E 'exception_id': 2, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ValueError', E 'value': '654', E }, E { E 'mechanism': { E 'exception_id': 1, E 'handled': False, E 'parent_id': 0, E 'source': '__context__', E 'type': 'chained', E }, E 'type': 'RuntimeError', E 'value': 'something', E }, E { E 'mechanism': { E 'exception_id': 0, E 'handled': False, E 'is_exception_group': True, E 'type': 'test_suite', E }, E 'type': 'ExceptionGroup', E - 'value': 'nested', E + 'value': 'nested (3 sub-exceptions)', E }, E ]
tests.test_exceptiongroup::test_exceptiongroupStack Traces | 0.004s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup assert values == expected_values E AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}] E E At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'} E E Full diff: E [ E { E 'mechanism': { E 'exception_id': 6, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[2]', E 'type': 'chained', E }, E 'type': 'TypeError', E 'value': 'int', E }, E { E 'mechanism': { E 'exception_id': 5, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ModuleNotFoundError', E 'value': 'another_module', E }, E { E 'mechanism': { E 'exception_id': 4, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ImportError', E 'value': 'no_such_module', E }, E { E 'mechanism': { E 'exception_id': 3, E 'handled': False, E 'is_exception_group': True, E 'parent_id': 0, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ExceptionGroup', E - 'value': 'imports', E + 'value': 'imports (2 sub-exceptions)', E }, E { E 'mechanism': { E 'exception_id': 2, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ValueError', E 'value': '654', E }, E { E 'mechanism': { E 'exception_id': 1, E 'handled': False, E 'parent_id': 0, E 'source': '__context__', E 'type': 'chained', E }, E 'type': 'RuntimeError', E 'value': 'something', E }, E { E 'mechanism': { E 'exception_id': 0, E 'handled': False, E 'is_exception_group': True, E 'type': 'test_suite', E }, E 'type': 'ExceptionGroup', E - 'value': 'nested', E + 'value': 'nested (3 sub-exceptions)', E }, E ]
tests.test_exceptiongroup::test_exceptiongroupStack Traces | 0.005s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup assert values == expected_values E AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}] E E At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'} E E Full diff: E [ E { E 'mechanism': { E 'exception_id': 6, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[2]', E 'type': 'chained', E }, E 'type': 'TypeError', E 'value': 'int', E }, E { E 'mechanism': { E 'exception_id': 5, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ModuleNotFoundError', E 'value': 'another_module', E }, E { E 'mechanism': { E 'exception_id': 4, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ImportError', E 'value': 'no_such_module', E }, E { E 'mechanism': { E 'exception_id': 3, E 'handled': False, E 'is_exception_group': True, E 'parent_id': 0, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ExceptionGroup', E - 'value': 'imports', E + 'value': 'imports (2 sub-exceptions)', E }, E { E 'mechanism': { E 'exception_id': 2, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ValueError', E 'value': '654', E }, E { E 'mechanism': { E 'exception_id': 1, E 'handled': False, E 'parent_id': 0, E 'source': '__context__', E 'type': 'chained', E }, E 'type': 'RuntimeError', E 'value': 'something', E }, E { E 'mechanism': { E 'exception_id': 0, E 'handled': False, E 'is_exception_group': True, E 'type': 'test_suite', E }, E 'type': 'ExceptionGroup', E - 'value': 'nested', E + 'value': 'nested (3 sub-exceptions)', E }, E ]
tests.test_exceptiongroup::test_exceptiongroupStack Traces | 0.005s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup assert values == expected_values E AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}] E E At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'} E E Full diff: E [ E { E 'mechanism': { E 'exception_id': 6, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[2]', E 'type': 'chained', E }, E 'type': 'TypeError', E 'value': 'int', E }, E { E 'mechanism': { E 'exception_id': 5, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ModuleNotFoundError', E 'value': 'another_module', E }, E { E 'mechanism': { E 'exception_id': 4, E 'handled': False, E 'parent_id': 3, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ImportError', E 'value': 'no_such_module', E }, E { E 'mechanism': { E 'exception_id': 3, E 'handled': False, E 'is_exception_group': True, E 'parent_id': 0, E 'source': 'exceptions[1]', E 'type': 'chained', E }, E 'type': 'ExceptionGroup', E - 'value': 'imports', E + 'value': 'imports (2 sub-exceptions)', E }, E { E 'mechanism': { E 'exception_id': 2, E 'handled': False, E 'parent_id': 0, E 'source': 'exceptions[0]', E 'type': 'chained', E }, E 'type': 'ValueError', E 'value': '654', E }, E { E 'mechanism': { E 'exception_id': 1, E 'handled': False, E 'parent_id': 0, E 'source': '__context__', E 'type': 'chained', E }, E 'type': 'RuntimeError', E 'value': 'something', E }, E { E 'mechanism': { E 'exception_id': 0, E 'handled': False, E 'is_exception_group': True, E 'type': 'test_suite', E }, E 'type': 'ExceptionGroup', E - 'value': 'nested', E + 'value': 'nested (3 sub-exceptions)', E }, E ]
To view more test analytics, go to the Test Analytics Dashboard 📋 Got 3 mins? Take this short survey to help us improve Test Analytics.
Good point regarding the old Starlette version.
Their HTTPException has a __str__ method since version 0.29 but we support version 0.16 and up.
So our fix to add special handling of a detail property fixed a Starlette and FastAPI specific problem. At the same time, users that happen to have detail properties on their exception lost information, as before https://github.com/getsentry/sentry-python/pull/2193 __str__ was used to generate the Sentry exception value.
To keep both worlds happy we could use __repr__ instead of __str__ when
CustomException.__str__ is Exception.__str__; andCustomException.__repr__ is not Exception.__repr__.
Starlette users would see a more meaningful value since __repr__ is added in Starlette 0.12.
Happy to hear other suggestions as well, especially since it'll be hard to change later.
Hmm so if we detect someone explicitly redefined __repr__ but not __str__, we use __repr__? I'm unsure how often that'd trigger in situations where __str__ would still be more valuable.