excepthook.py 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  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=False):
  23. # type: (bool) -> None
  24. if not isinstance(always_run, bool):
  25. raise ValueError(
  26. "Invalid value for always_run: %s (must be type boolean)"
  27. % (always_run,)
  28. )
  29. self.always_run = always_run
  30. @staticmethod
  31. def setup_once():
  32. # type: () -> None
  33. sys.excepthook = _make_excepthook(sys.excepthook)
  34. def _make_excepthook(old_excepthook):
  35. # type: (Excepthook) -> Excepthook
  36. def sentry_sdk_excepthook(type_, value, traceback):
  37. # type: (Type[BaseException], BaseException, Optional[TracebackType]) -> 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=False):
  55. # type: (bool) -> bool
  56. if always_run:
  57. return True
  58. if hasattr(sys, "ps1"):
  59. # Disable the excepthook for interactive Python shells, otherwise
  60. # every typo gets sent to Sentry.
  61. return False
  62. return True