_result.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. """
  2. Hook wrapper "result" utilities.
  3. """
  4. from __future__ import annotations
  5. from types import TracebackType
  6. from typing import Callable
  7. from typing import cast
  8. from typing import Generator
  9. from typing import Generic
  10. from typing import Optional
  11. from typing import Tuple
  12. from typing import Type
  13. from typing import TYPE_CHECKING
  14. from typing import TypeVar
  15. if TYPE_CHECKING:
  16. from typing import NoReturn
  17. _ExcInfo = Tuple[Type[BaseException], BaseException, Optional[TracebackType]]
  18. _T = TypeVar("_T")
  19. def _raise_wrapfail(
  20. wrap_controller: (
  21. Generator[None, _Result[_T], None] | Generator[None, object, object]
  22. ),
  23. msg: str,
  24. ) -> NoReturn:
  25. co = wrap_controller.gi_code
  26. raise RuntimeError(
  27. "wrap_controller at %r %s:%d %s"
  28. % (co.co_name, co.co_filename, co.co_firstlineno, msg)
  29. )
  30. class HookCallError(Exception):
  31. """Hook was called incorrectly."""
  32. class _Result(Generic[_T]):
  33. __slots__ = ("_result", "_exception")
  34. def __init__(
  35. self,
  36. result: _T | None,
  37. exception: BaseException | None,
  38. ) -> None:
  39. self._result = result
  40. self._exception = exception
  41. @property
  42. def excinfo(self) -> _ExcInfo | None:
  43. exc = self._exception
  44. if exc is None:
  45. return None
  46. else:
  47. return (type(exc), exc, exc.__traceback__)
  48. @property
  49. def exception(self) -> BaseException | None:
  50. return self._exception
  51. @classmethod
  52. def from_call(cls, func: Callable[[], _T]) -> _Result[_T]:
  53. __tracebackhide__ = True
  54. result = exception = None
  55. try:
  56. result = func()
  57. except BaseException as exc:
  58. exception = exc
  59. return cls(result, exception)
  60. def force_result(self, result: _T) -> None:
  61. """Force the result(s) to ``result``.
  62. If the hook was marked as a ``firstresult`` a single value should
  63. be set, otherwise set a (modified) list of results. Any exceptions
  64. found during invocation will be deleted.
  65. This overrides any previous result or exception.
  66. """
  67. self._result = result
  68. self._exception = None
  69. def force_exception(self, exception: BaseException) -> None:
  70. """Force the result to fail with ``exception``.
  71. This overrides any previous result or exception.
  72. .. versionadded:: 1.1.0
  73. """
  74. self._result = None
  75. self._exception = exception
  76. def get_result(self) -> _T:
  77. """Get the result(s) for this hook call.
  78. If the hook was marked as a ``firstresult`` only a single value
  79. will be returned, otherwise a list of results.
  80. """
  81. __tracebackhide__ = True
  82. exc = self._exception
  83. if exc is None:
  84. return cast(_T, self._result)
  85. else:
  86. raise exc.with_traceback(exc.__traceback__)