Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add Typer integration #3869

Merged
merged 9 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/test-integrations-misc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.6","3.8","3.12","3.13"]
python-version: ["3.6","3.7","3.8","3.12","3.13"]
# python3.6 reached EOL and is no longer being supported on
# new versions of hosted runners on Github Actions
# ubuntu-20.04 is the last version that supported python3.6
Expand Down Expand Up @@ -73,6 +73,10 @@ jobs:
run: |
set -x # print commands that are executed
./scripts/runtox.sh "py${{ matrix.python-version }}-trytond-latest"
- name: Test typer latest
run: |
set -x # print commands that are executed
./scripts/runtox.sh "py${{ matrix.python-version }}-typer-latest"
- name: Generate coverage XML (Python 3.6)
if: ${{ !cancelled() && matrix.python-version == '3.6' }}
run: |
Expand Down Expand Up @@ -153,6 +157,10 @@ jobs:
run: |
set -x # print commands that are executed
./scripts/runtox.sh --exclude-latest "py${{ matrix.python-version }}-trytond"
- name: Test typer pinned
run: |
set -x # print commands that are executed
./scripts/runtox.sh --exclude-latest "py${{ matrix.python-version }}-typer"
- name: Generate coverage XML (Python 3.6)
if: ${{ !cancelled() && matrix.python-version == '3.6' }}
run: |
Expand Down
1 change: 1 addition & 0 deletions requirements-linting.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ pre-commit # local linting
httpcore
openfeature-sdk
launchdarkly-server-sdk
typer
1 change: 1 addition & 0 deletions scripts/split-tox-gh-actions/split-tox-gh-actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
"potel",
"pure_eval",
"trytond",
"typer",
],
}

Expand Down
60 changes: 60 additions & 0 deletions sentry_sdk/integrations/typer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import sentry_sdk
from sentry_sdk.utils import (
capture_internal_exceptions,
event_from_exception,
)
from sentry_sdk.integrations import Integration, DidNotEnable

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing import Callable
from typing import Any
from typing import Type
from typing import Optional

from types import TracebackType

Excepthook = Callable[
[Type[BaseException], BaseException, Optional[TracebackType]],
Any,
]

try:
import typer
except ImportError:
raise DidNotEnable("Typer not installed")


class TyperIntegration(Integration):
identifier = "typer"

@staticmethod
def setup_once():
# type: () -> None
typer.main.except_hook = _make_excepthook(typer.main.except_hook) # type: ignore


def _make_excepthook(old_excepthook):
# type: (Excepthook) -> Excepthook
def sentry_sdk_excepthook(type_, value, traceback):
# type: (Type[BaseException], BaseException, Optional[TracebackType]) -> None
integration = sentry_sdk.get_client().get_integration(TyperIntegration)

# Note: If we replace this with ensure_integration_enabled then
# we break the exceptiongroup backport;
# See: https://github.com/getsentry/sentry-python/issues/3097
if integration is None:
return old_excepthook(type_, value, traceback)

with capture_internal_exceptions():
event, hint = event_from_exception(
(type_, value, traceback),
client_options=sentry_sdk.get_client().options,
mechanism={"type": "typer", "handled": False},
)
sentry_sdk.capture_event(event, hint=hint)

return old_excepthook(type_, value, traceback)

return sentry_sdk_excepthook
3 changes: 3 additions & 0 deletions tests/integrations/typer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import pytest

pytest.importorskip("typer")
52 changes: 52 additions & 0 deletions tests/integrations/typer/test_typer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import subprocess
import sys
from textwrap import dedent
import pytest

from typer.testing import CliRunner

runner = CliRunner()


def test_catch_exceptions(tmpdir):
app = tmpdir.join("app.py")

app.write(
dedent(
"""
import typer
from unittest import mock

from sentry_sdk import init, transport
from sentry_sdk.integrations.typer import TyperIntegration

def capture_envelope(self, envelope):
print("capture_envelope was called")
event = envelope.get_event()
if event is not None:
print(event)

transport.HttpTransport.capture_envelope = capture_envelope

init("http://foobar@localhost/123", integrations=[TyperIntegration()])

app = typer.Typer()

@app.command()
def test():
print("test called")
raise Exception("pollo")

app()
"""
)
)

with pytest.raises(subprocess.CalledProcessError) as excinfo:
subprocess.check_output([sys.executable, str(app)], stderr=subprocess.STDOUT)

output = excinfo.value.output

assert b"capture_envelope was called" in output
assert b"test called" in output
assert b"pollo" in output
9 changes: 9 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ envlist =
{py3.8,py3.11,py3.12}-trytond-v{7}
{py3.8,py3.12,py3.13}-trytond-latest

# Typer
{py3.7,py3.12,py3.13}-typer-v{0.15}
{py3.7,py3.12,py3.13}-typer-latest

[testenv]
deps =
# if you change requirements-testing.txt and your change is not being reflected
Expand Down Expand Up @@ -724,6 +728,10 @@ deps =
trytond-v7: trytond~=7.0
trytond-latest: trytond

# Typer
typer-v0.15: typer~=0.15.0
typer-latest: typer

setenv =
PYTHONDONTWRITEBYTECODE=1
OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
Expand Down Expand Up @@ -786,6 +794,7 @@ setenv =
strawberry: TESTPATH=tests/integrations/strawberry
tornado: TESTPATH=tests/integrations/tornado
trytond: TESTPATH=tests/integrations/trytond
typer: TESTPATH=tests/integrations/typer
socket: TESTPATH=tests/integrations/socket

passenv =
Expand Down
Loading