Skip to content

Commit

Permalink
Merge pull request #388 from bluetech/no-excinfo
Browse files Browse the repository at this point in the history
result: use exception instead excinfo triplet
  • Loading branch information
bluetech committed Jun 12, 2023
2 parents 53efdc3 + fbc4442 commit 250081a
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 32 deletions.
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,7 @@ hook invocation point:
outcome.get_result()
except RuntimeError:
# log the error details
print(outcome.excinfo)
print(outcome.exception)
pm = PluginManager("myproject")
Expand Down
15 changes: 5 additions & 10 deletions src/pluggy/_callers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""
Call loop machinery
"""
import sys
from typing import cast
from typing import Generator
from typing import List
Expand Down Expand Up @@ -31,7 +30,7 @@ def _multicall(
"""
__tracebackhide__ = True
results: List[object] = []
excinfo = None
exception = None
try: # run impl and wrapper setup functions in a loop
teardowns = []
try:
Expand Down Expand Up @@ -61,19 +60,15 @@ def _multicall(
results.append(res)
if firstresult: # halt further impl calls
break
except BaseException:
_excinfo = sys.exc_info()
assert _excinfo[0] is not None
assert _excinfo[1] is not None
assert _excinfo[2] is not None
excinfo = (_excinfo[0], _excinfo[1], _excinfo[2])
except BaseException as exc:
exception = exc
finally:
if firstresult: # first result hooks return a single value
outcome: _Result[Union[object, List[object]]] = _Result(
results[0] if results else None, excinfo
results[0] if results else None, exception
)
else:
outcome = _Result(results, excinfo)
outcome = _Result(results, exception)

# run all wrapper post-yield blocks
for gen in reversed(teardowns):
Expand Down
2 changes: 1 addition & 1 deletion src/pluggy/_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ def after(
methods: Sequence[HookImpl],
kwargs: Mapping[str, object],
) -> None:
if outcome.excinfo is None:
if outcome.exception is None:
hooktrace("finish", hook_name, "-->", outcome.get_result())
hooktrace.root.indent -= 1

Expand Down
44 changes: 25 additions & 19 deletions src/pluggy/_result.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""
Hook wrapper "result" utilities.
"""
import sys
from types import TracebackType
from typing import Callable
from typing import cast
Expand All @@ -17,7 +16,7 @@
from typing import NoReturn


_ExcInfo = Tuple[Type[BaseException], BaseException, TracebackType]
_ExcInfo = Tuple[Type[BaseException], BaseException, Optional[TracebackType]]
_T = TypeVar("_T")


Expand All @@ -36,30 +35,37 @@ class HookCallError(Exception):


class _Result(Generic[_T]):
__slots__ = ("_result", "_excinfo")
__slots__ = ("_result", "_exception")

def __init__(self, result: Optional[_T], excinfo: Optional[_ExcInfo]) -> None:
def __init__(
self,
result: Optional[_T],
exception: Optional[BaseException],
) -> None:
self._result = result
self._excinfo = excinfo
self._exception = exception

@property
def excinfo(self) -> Optional[_ExcInfo]:
return self._excinfo
exc = self._exception
if exc is None:
return None
else:
return (type(exc), exc, exc.__traceback__)

@property
def exception(self) -> Optional[BaseException]:
return self._exception

@classmethod
def from_call(cls, func: Callable[[], _T]) -> "_Result[_T]":
__tracebackhide__ = True
result = excinfo = None
result = exception = None
try:
result = func()
except BaseException:
_excinfo = sys.exc_info()
assert _excinfo[0] is not None
assert _excinfo[1] is not None
assert _excinfo[2] is not None
excinfo = (_excinfo[0], _excinfo[1], _excinfo[2])

return cls(result, excinfo)
except BaseException as exc:
exception = exc
return cls(result, exception)

def force_result(self, result: _T) -> None:
"""Force the result(s) to ``result``.
Expand All @@ -69,7 +75,7 @@ def force_result(self, result: _T) -> None:
found during invocation will be deleted.
"""
self._result = result
self._excinfo = None
self._exception = None

def get_result(self) -> _T:
"""Get the result(s) for this hook call.
Expand All @@ -78,8 +84,8 @@ def get_result(self) -> _T:
will be returned, otherwise a list of results.
"""
__tracebackhide__ = True
if self._excinfo is None:
exc = self._exception
if exc is None:
return cast(_T, self._result)
else:
ex = self._excinfo
raise ex[1].with_traceback(ex[2])
raise exc.with_traceback(exc.__traceback__)
2 changes: 2 additions & 0 deletions testing/test_invocations.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ def hello(self, arg):
assert arg == 0
outcome = yield
assert outcome.get_result() == [3, 2, 1]
assert outcome.exception is None
assert outcome.excinfo is None

pm.register(Plugin1())
pm.register(Plugin2())
Expand Down
4 changes: 3 additions & 1 deletion testing/test_multicall.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ def test_hookwrapper_exception(exc: "Type[BaseException]") -> None:
@hookimpl(hookwrapper=True)
def m1():
out.append("m1 init")
yield None
result = yield
assert isinstance(result.exception, exc)
assert result.excinfo[0] == exc
out.append("m1 finish")

@hookimpl
Expand Down

0 comments on commit 250081a

Please sign in to comment.