|
14 | 14 | import traceback |
15 | 15 | from inspect import getframeinfo |
16 | 16 | from pathlib import Path |
17 | | -from typing import Dict, NamedTuple, Optional, Tuple, Type |
| 17 | +from typing import Dict |
18 | 18 |
|
19 | 19 | import hypothesis |
20 | 20 | from hypothesis.errors import ( |
@@ -103,35 +103,29 @@ def get_trimmed_traceback(exception=None): |
103 | 103 | return tb |
104 | 104 |
|
105 | 105 |
|
106 | | -class InterestingOrigin(NamedTuple): |
| 106 | +def get_interesting_origin(exception): |
107 | 107 | # The `interesting_origin` is how Hypothesis distinguishes between multiple |
108 | 108 | # failures, for reporting and also to replay from the example database (even |
109 | 109 | # if report_multiple_bugs=False). We traditionally use the exception type and |
110 | 110 | # location, but have extracted this logic in order to see through `except ...:` |
111 | 111 | # blocks and understand the __cause__ (`raise x from y`) or __context__ that |
112 | 112 | # first raised an exception as well as PEP-654 exception groups. |
113 | | - type_: Type[BaseException] |
114 | | - filename: str |
115 | | - lineno: int |
116 | | - context: "Optional[InterestingOrigin]" |
117 | | - exceptiongroup_contents: "Optional[Tuple[InterestingOrigin, ...]]" |
118 | | - |
119 | | - @classmethod |
120 | | - def from_exception(cls, exception: BaseException) -> "InterestingOrigin": |
121 | | - tb = get_trimmed_traceback(exception) |
122 | | - filename, lineno, *_ = traceback.extract_tb(tb)[-1] |
| 113 | + tb = get_trimmed_traceback(exception) |
| 114 | + filename, lineno, *_ = traceback.extract_tb(tb)[-1] |
| 115 | + return ( |
| 116 | + type(exception), |
| 117 | + filename, |
| 118 | + lineno, |
123 | 119 | # Note that if __cause__ is set it is always equal to __context__, explicitly |
124 | 120 | # to support introspection when debugging, so we can use that unconditionally. |
125 | | - chained_from = exception.__context__ |
126 | | - return cls( |
127 | | - type(exception), |
128 | | - filename, |
129 | | - lineno, |
130 | | - cls.from_exception(chained_from) if chained_from else None, |
131 | | - tuple(map(cls.from_exception, exception.exceptions)) |
| 121 | + get_interesting_origin(exception.__context__) if exception.__context__ else (), |
| 122 | + # We distinguish exception groups by the inner exceptions, as for __context__ |
| 123 | + tuple( |
| 124 | + map(get_interesting_origin, exception.exceptions) |
132 | 125 | if isinstance(exception, BaseExceptionGroup) |
133 | | - else None, |
134 | | - ) |
| 126 | + else [] |
| 127 | + ), |
| 128 | + ) |
135 | 129 |
|
136 | 130 |
|
137 | 131 | current_pytest_item = DynamicVariable(None) |
|
0 commit comments