excepthook.py 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import sys
  2. import sentry_sdk
  3. from sentry_sdk.utils import (
  4. capture_internal_exceptions,
  5. event_from_exception,
  6. )
  7. from sentry_sdk.integrations import Integration
  8. from typing import TYPE_CHECKING
  9. if TYPE_CHECKING:
  10. from typing import Callable
  11. from typing import Any
  12. from typing import Type
  13. from typing import Optional
  14. from types import TracebackType
  15. Excepthook = Callable[
  16. [Type[BaseException], BaseException, Optional[TracebackType]],
  17. Any,
  18. ]
  19. class ExcepthookIntegration(Integration):
  20. identifier = "excepthook"
  21. always_run = False
  22. def __init__(self, always_run: bool = False) -> None:
  23. if not isinstance(always_run, bool):
  24. raise ValueError(
  25. "Invalid value for always_run: %s (must be type boolean)"
  26. % (always_run,)
  27. )
  28. self.always_run = always_run
  29. @staticmethod
  30. def setup_once() -> None:
  31. sys.excepthook = _make_excepthook(sys.excepthook)
  32. def _make_excepthook(old_excepthook: "Excepthook") -> "Excepthook":
  33. def sentry_sdk_excepthook(
  34. type_: "Type[BaseException]",
  35. value: BaseException,
  36. traceback: "Optional[TracebackType]",
  37. ) -> None:
  38. integration = sentry_sdk.get_client().get_integration(ExcepthookIntegration)
  39. # Note: If we replace this with ensure_integration_enabled then
  40. # we break the exceptiongroup backport;
  41. # See: https://github.com/getsentry/sentry-python/issues/3097
  42. if integration is None:
  43. return old_excepthook(type_, value, traceback)
  44. if _should_send(integration.always_run):
  45. with capture_internal_exceptions():
  46. event, hint = event_from_exception(
  47. (type_, value, traceback),
  48. client_options=sentry_sdk.get_client().options,
  49. mechanism={"type": "excepthook", "handled": False},
  50. )
  51. sentry_sdk.capture_event(event, hint=hint)
  52. return old_excepthook(type_, value, traceback)
  53. return sentry_sdk_excepthook
  54. def _should_send(always_run: bool = False) -> bool:
  55. if always_run:
  56. return True
  57. if hasattr(sys, "ps1"):
  58. # Disable the excepthook for interactive Python shells, otherwise
  59. # every typo gets sent to Sentry.
  60. return False
  61. return True