Skip to content

Commit 56085d7

Browse files
authored
Merge pull request #504 from bluetech/reraise
result: keep original traceback and reraise with it
2 parents 313eace + 93ac1e9 commit 56085d7

File tree

4 files changed

+37
-4
lines changed

4 files changed

+37
-4
lines changed

changelog/504.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a regression in pluggy 1.1.0 where using :func:`result.get_result() <pluggy.Result.get_result>` on the same failed :class:`~pluggy.Result` causes the exception's traceback to get longer and longer.

src/pluggy/_callers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def _multicall(
136136
_raise_wrapfail(teardown, "has second yield") # type: ignore[arg-type]
137137

138138
if exception is not None:
139-
raise exception.with_traceback(exception.__traceback__)
139+
raise exception
140140
else:
141141
return result
142142

src/pluggy/_result.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class Result(Generic[ResultType]):
2828
"""An object used to inspect and set the result in a :ref:`hook wrapper
2929
<hookwrappers>`."""
3030

31-
__slots__ = ("_result", "_exception")
31+
__slots__ = ("_result", "_exception", "_traceback")
3232

3333
def __init__(
3434
self,
@@ -38,6 +38,8 @@ def __init__(
3838
""":meta private:"""
3939
self._result = result
4040
self._exception = exception
41+
# Exception __traceback__ is mutable, this keeps the original.
42+
self._traceback = exception.__traceback__ if exception is not None else None
4143

4244
@property
4345
def excinfo(self) -> _ExcInfo | None:
@@ -46,7 +48,7 @@ def excinfo(self) -> _ExcInfo | None:
4648
if exc is None:
4749
return None
4850
else:
49-
return (type(exc), exc, exc.__traceback__)
51+
return (type(exc), exc, self._traceback)
5052

5153
@property
5254
def exception(self) -> BaseException | None:
@@ -75,6 +77,7 @@ def force_result(self, result: ResultType) -> None:
7577
"""
7678
self._result = result
7779
self._exception = None
80+
self._traceback = None
7881

7982
def force_exception(self, exception: BaseException) -> None:
8083
"""Force the result to fail with ``exception``.
@@ -85,6 +88,7 @@ def force_exception(self, exception: BaseException) -> None:
8588
"""
8689
self._result = None
8790
self._exception = exception
91+
self._traceback = exception.__traceback__ if exception is not None else None
8892

8993
def get_result(self) -> ResultType:
9094
"""Get the result(s) for this hook call.
@@ -94,10 +98,11 @@ def get_result(self) -> ResultType:
9498
"""
9599
__tracebackhide__ = True
96100
exc = self._exception
101+
tb = self._traceback
97102
if exc is None:
98103
return cast(ResultType, self._result)
99104
else:
100-
raise exc.with_traceback(exc.__traceback__)
105+
raise exc.with_traceback(tb)
101106

102107

103108
# Historical name (pluggy<=1.2), kept for backward compatibility.

testing/test_result.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import traceback
2+
3+
from pluggy import Result
4+
5+
6+
def test_exceptions_traceback_doesnt_get_longer_and_longer() -> None:
7+
def bad() -> None:
8+
1 / 0
9+
10+
result = Result.from_call(bad)
11+
12+
try:
13+
result.get_result()
14+
except Exception as exc:
15+
tb1 = traceback.extract_tb(exc.__traceback__)
16+
17+
try:
18+
result.get_result()
19+
except Exception as exc:
20+
tb2 = traceback.extract_tb(exc.__traceback__)
21+
22+
try:
23+
result.get_result()
24+
except Exception as exc:
25+
tb3 = traceback.extract_tb(exc.__traceback__)
26+
27+
assert len(tb1) == len(tb2) == len(tb3)

0 commit comments

Comments
 (0)