From 1e6f92f7865949004f8c2fe0ba8101dd7a7e794e Mon Sep 17 00:00:00 2001 From: Patrick Arminio Date: Wed, 11 Dec 2024 16:06:38 +0000 Subject: [PATCH 1/7] =?UTF-8?q?=E2=9C=A8=20Add=20Typer=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sentry_sdk/integrations/typer.py | 60 ++++++++++++++++++++++++++ tests/integrations/typer/__init__.py | 3 ++ tests/integrations/typer/test_typer.py | 52 ++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 sentry_sdk/integrations/typer.py create mode 100644 tests/integrations/typer/__init__.py create mode 100644 tests/integrations/typer/test_typer.py diff --git a/sentry_sdk/integrations/typer.py b/sentry_sdk/integrations/typer.py new file mode 100644 index 0000000000..9006c420db --- /dev/null +++ b/sentry_sdk/integrations/typer.py @@ -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) + + +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": "excepthook", "handled": False}, + ) + sentry_sdk.capture_event(event, hint=hint) + + return old_excepthook(type_, value, traceback) + + return sentry_sdk_excepthook diff --git a/tests/integrations/typer/__init__.py b/tests/integrations/typer/__init__.py new file mode 100644 index 0000000000..3b7c8011ea --- /dev/null +++ b/tests/integrations/typer/__init__.py @@ -0,0 +1,3 @@ +import pytest + +pytest.importorskip("typer") diff --git a/tests/integrations/typer/test_typer.py b/tests/integrations/typer/test_typer.py new file mode 100644 index 0000000000..34ac0a7c8c --- /dev/null +++ b/tests/integrations/typer/test_typer.py @@ -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 From 7b972221665537d32812c21d7b2b2ca605370c38 Mon Sep 17 00:00:00 2001 From: Patrick Arminio Date: Wed, 11 Dec 2024 16:12:42 +0000 Subject: [PATCH 2/7] Update tox file --- tox.ini | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tox.ini b/tox.ini index d3bd83cb03..a6c8b35655 100644 --- a/tox.ini +++ b/tox.ini @@ -287,6 +287,9 @@ envlist = {py3.8,py3.11,py3.12}-trytond-v{7} {py3.8,py3.12,py3.13}-trytond-latest + # Typer + {py3.7,py3.9,py3.10,py3.11,py3.12,py3.13}-typer-lates + [testenv] deps = # if you change requirements-testing.txt and your change is not being reflected @@ -723,6 +726,10 @@ deps = trytond-v7: trytond~=7.0 trytond-latest: trytond + # Typer + typer: typer + typer-latest: typer + setenv = PYTHONDONTWRITEBYTECODE=1 OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES From adb1183309dc0a22d5d84298d7341058c5e0f9c7 Mon Sep 17 00:00:00 2001 From: Patrick Arminio Date: Fri, 13 Dec 2024 12:49:06 +0000 Subject: [PATCH 3/7] Add typer to Groups --- scripts/split-tox-gh-actions/split-tox-gh-actions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/split-tox-gh-actions/split-tox-gh-actions.py b/scripts/split-tox-gh-actions/split-tox-gh-actions.py index c4b8f3e5e5..26d13390c2 100755 --- a/scripts/split-tox-gh-actions/split-tox-gh-actions.py +++ b/scripts/split-tox-gh-actions/split-tox-gh-actions.py @@ -132,6 +132,7 @@ "potel", "pure_eval", "trytond", + "typer", ], } From 030c13895d6554234c7ee24354e47eb20e6578e2 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 17 Dec 2024 12:44:18 +0100 Subject: [PATCH 4/7] Pin tests, add to CI --- .github/workflows/test-integrations-misc.yml | 10 +++++++++- tox.ini | 6 ++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-integrations-misc.yml b/.github/workflows/test-integrations-misc.yml index fb76a854fb..b88b256384 100644 --- a/.github/workflows/test-integrations-misc.yml +++ b/.github/workflows/test-integrations-misc.yml @@ -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 @@ -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: | @@ -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: | diff --git a/tox.ini b/tox.ini index a6c8b35655..d5d9782bc2 100644 --- a/tox.ini +++ b/tox.ini @@ -288,7 +288,8 @@ envlist = {py3.8,py3.12,py3.13}-trytond-latest # Typer - {py3.7,py3.9,py3.10,py3.11,py3.12,py3.13}-typer-lates + {py3.7,py3.12,py3.13}-typer-v{0.15} + {py3.7,py3.12,py3.13}-typer-latest [testenv] deps = @@ -727,7 +728,7 @@ deps = trytond-latest: trytond # Typer - typer: typer + typer-v0.15: typer~=0.15.0 typer-latest: typer setenv = @@ -792,6 +793,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 = From fa54d1cff32ff7cd875593a9b5187735e9e3356d Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 17 Dec 2024 12:55:06 +0100 Subject: [PATCH 5/7] Small fixes --- requirements-linting.txt | 1 + sentry_sdk/integrations/typer.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements-linting.txt b/requirements-linting.txt index c9d4bd7f5c..c3f39ecd1f 100644 --- a/requirements-linting.txt +++ b/requirements-linting.txt @@ -17,3 +17,4 @@ pre-commit # local linting httpcore openfeature-sdk launchdarkly-server-sdk +typer diff --git a/sentry_sdk/integrations/typer.py b/sentry_sdk/integrations/typer.py index 9006c420db..19631a39e6 100644 --- a/sentry_sdk/integrations/typer.py +++ b/sentry_sdk/integrations/typer.py @@ -41,7 +41,7 @@ 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 + # 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: From a70e7458fa4f82ed4438fcd7fae8d66a6b8db0da Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Wed, 18 Dec 2024 09:58:07 +0100 Subject: [PATCH 6/7] Change mechanism to typer --- sentry_sdk/integrations/typer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/typer.py b/sentry_sdk/integrations/typer.py index 19631a39e6..8986c6d79e 100644 --- a/sentry_sdk/integrations/typer.py +++ b/sentry_sdk/integrations/typer.py @@ -51,7 +51,7 @@ def sentry_sdk_excepthook(type_, value, traceback): event, hint = event_from_exception( (type_, value, traceback), client_options=sentry_sdk.get_client().options, - mechanism={"type": "excepthook", "handled": False}, + mechanism={"type": "typer", "handled": False}, ) sentry_sdk.capture_event(event, hint=hint) From 1837c5f7d09dedc1b72c732789a84a6423ae666c Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Wed, 18 Dec 2024 11:40:39 +0100 Subject: [PATCH 7/7] mypy --- sentry_sdk/integrations/typer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/typer.py b/sentry_sdk/integrations/typer.py index 8986c6d79e..8879d6d0d0 100644 --- a/sentry_sdk/integrations/typer.py +++ b/sentry_sdk/integrations/typer.py @@ -32,7 +32,7 @@ class TyperIntegration(Integration): @staticmethod def setup_once(): # type: () -> None - typer.main.except_hook = _make_excepthook(typer.main.except_hook) + typer.main.except_hook = _make_excepthook(typer.main.except_hook) # type: ignore def _make_excepthook(old_excepthook):