diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index cc35f9134b..475fa84181 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -158,6 +158,9 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh - `Transport.capture_event` has been removed. Use `Transport.capture_envelope` instead. - Function transports are no longer supported. Subclass the `Transport` instead. - `start_transaction` (`start_span`) no longer takes a `baggage` argument. Use the `continue_trace()` context manager instead to propagate baggage. +- Dropped support for Django versions below 2.0. +- Dropped support for trytond versions below 5.0. +- Dropped support for Falcon versions below 3.0. ### Deprecated diff --git a/scripts/populate_tox/config.py b/scripts/populate_tox/config.py index fbfbf0bbc3..b1c61b6a14 100644 --- a/scripts/populate_tox/config.py +++ b/scripts/populate_tox/config.py @@ -33,6 +33,7 @@ "package": "django", "deps": { "*": [ + "channels[daphne]", "psycopg2-binary", "djangorestframework", "pytest-django", @@ -45,7 +46,6 @@ "Werkzeug<2.1.0", ], "<3.1": ["pytest-django<4.0"], - ">=2.0": ["channels[daphne]"], }, }, "dramatiq": { diff --git a/sentry_sdk/integrations/__init__.py b/sentry_sdk/integrations/__init__.py index fdf93cd203..fcfcccebd0 100644 --- a/sentry_sdk/integrations/__init__.py +++ b/sentry_sdk/integrations/__init__.py @@ -132,9 +132,9 @@ def iter_default_integrations(with_auto_enabling_integrations): "celery": (4, 4, 7), "chalice": (1, 16, 0), "clickhouse_driver": (0, 2, 0), - "django": (1, 8), + "django": (2, 0), "dramatiq": (1, 9), - "falcon": (1, 4), + "falcon": (3, 0), "fastapi": (0, 79, 0), "flask": (1, 1, 4), "gql": (3, 4, 1), @@ -157,6 +157,7 @@ def iter_default_integrations(with_auto_enabling_integrations): "statsig": (0, 55, 3), "strawberry": (0, 209, 5), "tornado": (6, 0), + "trytond": (5, 0), "typer": (0, 15), "unleash": (6, 0, 1), } diff --git a/sentry_sdk/integrations/django/__init__.py b/sentry_sdk/integrations/django/__init__.py index 99cb7704fa..8c6bc887ba 100644 --- a/sentry_sdk/integrations/django/__init__.py +++ b/sentry_sdk/integrations/django/__init__.py @@ -56,6 +56,7 @@ except ImportError: raise DidNotEnable("Django not installed") +from sentry_sdk.integrations.django.caching import patch_caching from sentry_sdk.integrations.django.transactions import LEGACY_RESOLVER from sentry_sdk.integrations.django.templates import ( get_template_frame_from_exception, @@ -65,11 +66,6 @@ from sentry_sdk.integrations.django.signals_handlers import patch_signals from sentry_sdk.integrations.django.views import patch_views -if DJANGO_VERSION[:2] > (1, 8): - from sentry_sdk.integrations.django.caching import patch_caching -else: - patch_caching = None # type: ignore - from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -90,19 +86,6 @@ from sentry_sdk._types import Event, Hint, EventProcessor, NotImplementedType -if DJANGO_VERSION < (1, 10): - - def is_authenticated(request_user): - # type: (Any) -> bool - return request_user.is_authenticated() - -else: - - def is_authenticated(request_user): - # type: (Any) -> bool - return request_user.is_authenticated - - TRANSACTION_STYLE_VALUES = ("function_name", "url") @@ -597,7 +580,7 @@ def _set_user_info(request, event): user = getattr(request, "user", None) - if user is None or not is_authenticated(user): + if user is None or not user.is_authenticated: return try: @@ -624,20 +607,11 @@ def install_sql_hook(): except ImportError: from django.db.backends.util import CursorWrapper - try: - # django 1.6 and 1.7 compatability - from django.db.backends import BaseDatabaseWrapper - except ImportError: - # django 1.8 or later - from django.db.backends.base.base import BaseDatabaseWrapper + from django.db.backends.base.base import BaseDatabaseWrapper - try: - real_execute = CursorWrapper.execute - real_executemany = CursorWrapper.executemany - real_connect = BaseDatabaseWrapper.connect - except AttributeError: - # This won't work on Django versions < 1.6 - return + real_execute = CursorWrapper.execute + real_executemany = CursorWrapper.executemany + real_connect = BaseDatabaseWrapper.connect @ensure_integration_enabled(DjangoIntegration, real_execute) def execute(self, sql, params=None): diff --git a/sentry_sdk/integrations/django/templates.py b/sentry_sdk/integrations/django/templates.py index c9e41e24a0..53ccc60fc6 100644 --- a/sentry_sdk/integrations/django/templates.py +++ b/sentry_sdk/integrations/django/templates.py @@ -1,8 +1,8 @@ import functools from django.template import TemplateSyntaxError +from django.template.base import Origin from django.utils.safestring import mark_safe -from django import VERSION as DJANGO_VERSION import sentry_sdk from sentry_sdk.consts import OP @@ -17,13 +17,6 @@ from typing import Iterator from typing import Tuple -try: - # support Django 1.9 - from django.template.base import Origin -except ImportError: - # backward compatibility - from django.template.loader import LoaderOrigin as Origin - def get_template_frame_from_exception(exc_value): # type: (Optional[BaseException]) -> Optional[Dict[str, Any]] @@ -81,8 +74,6 @@ def rendered_content(self): SimpleTemplateResponse.rendered_content = rendered_content - if DJANGO_VERSION < (1, 7): - return import django.shortcuts real_render = django.shortcuts.render diff --git a/sentry_sdk/integrations/django/transactions.py b/sentry_sdk/integrations/django/transactions.py index 5a7d69f3c9..78b972bc37 100644 --- a/sentry_sdk/integrations/django/transactions.py +++ b/sentry_sdk/integrations/django/transactions.py @@ -19,12 +19,7 @@ from typing import Union from re import Pattern -from django import VERSION as DJANGO_VERSION - -if DJANGO_VERSION >= (2, 0): - from django.urls.resolvers import RoutePattern -else: - RoutePattern = None +from django.urls.resolvers import RoutePattern try: from django.urls import get_resolver diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index ddedcb10de..0b5c9c4fe7 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -19,8 +19,6 @@ from sentry_sdk._types import Event, EventProcessor -# In Falcon 3.0 `falcon.api_helpers` is renamed to `falcon.app_helpers` -# and `falcon.API` to `falcon.App` try: import falcon # type: ignore @@ -29,24 +27,15 @@ except ImportError: raise DidNotEnable("Falcon not installed") -try: - import falcon.app_helpers # type: ignore - - falcon_helpers = falcon.app_helpers - falcon_app_class = falcon.App - FALCON3 = True -except ImportError: - import falcon.api_helpers # type: ignore +import falcon.app_helpers # type: ignore - falcon_helpers = falcon.api_helpers - falcon_app_class = falcon.API - FALCON3 = False +falcon_helpers = falcon.app_helpers +falcon_app_class = falcon.App _FALCON_UNSET = None # type: Optional[object] -if FALCON3: # falcon.request._UNSET is only available in Falcon 3.0+ - with capture_internal_exceptions(): - from falcon.request import _UNSET as _FALCON_UNSET # type: ignore[import-not-found, no-redef] +with capture_internal_exceptions(): + from falcon.request import _UNSET as _FALCON_UNSET # type: ignore[import-not-found, no-redef] class FalconRequestExtractor(RequestExtractor): @@ -232,14 +221,7 @@ def _exception_leads_to_http_5xx(ex, response): ex, (falcon.HTTPError, falcon.http_status.HTTPStatus) ) - # We only check the HTTP status on Falcon 3 because in Falcon 2, the status on the response - # at the stage where we capture it is listed as 200, even though we would expect to see a 500 - # status. Since at the time of this change, Falcon 2 is ca. 4 years old, we have decided to - # only perform this check on Falcon 3+, despite the risk that some handled errors might be - # reported to Sentry as unhandled on Falcon 2. - return (is_server_error or is_unhandled_error) and ( - not FALCON3 or _has_http_5xx_status(response) - ) + return (is_server_error or is_unhandled_error) and _has_http_5xx_status(response) def _has_http_5xx_status(response): diff --git a/sentry_sdk/integrations/trytond.py b/sentry_sdk/integrations/trytond.py index 2c44c593a4..fd2c6f389f 100644 --- a/sentry_sdk/integrations/trytond.py +++ b/sentry_sdk/integrations/trytond.py @@ -1,8 +1,9 @@ import sentry_sdk -from sentry_sdk.integrations import Integration +from sentry_sdk.integrations import _check_minimum_version, Integration from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware from sentry_sdk.utils import ensure_integration_enabled, event_from_exception +from trytond import __version__ as trytond_version # type: ignore from trytond.exceptions import TrytonException # type: ignore from trytond.wsgi import app # type: ignore @@ -19,6 +20,8 @@ def __init__(self): # type: () -> None @staticmethod def setup_once(): # type: () -> None + _check_minimum_version(TrytondWSGIIntegration, trytond_version) + app.wsgi_app = SentryWsgiMiddleware( app.wsgi_app, span_origin=TrytondWSGIIntegration.origin, diff --git a/tox.ini b/tox.ini index 1ddaa3b69c..f7137389ac 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,7 @@ # The file (and all resulting CI YAMLs) then need to be regenerated via # "scripts/generate-test-files.sh". # -# Last generated: 2025-04-04T12:20:40.475012+00:00 +# Last generated: 2025-04-04T13:09:23.106982+00:00 [tox] requires = @@ -227,7 +227,7 @@ envlist = # ~~~ Web 1 ~~~ - {py3.7}-django-v1.11.29 + {py3.7}-django-v2.0.9 {py3.7,py3.8,py3.9}-django-v2.2.28 {py3.7,py3.9,py3.10}-django-v3.2.25 {py3.8,py3.11,py3.12}-django-v4.2.20 @@ -249,7 +249,7 @@ envlist = {py3.7}-bottle-v0.12.25 {py3.7,py3.8,py3.9}-bottle-v0.13.2 - {py3.7}-falcon-v2.0.0 + {py3.7,py3.8,py3.9}-falcon-v3.0.1 {py3.7,py3.11,py3.12}-falcon-v3.1.3 {py3.8,py3.11,py3.12}-falcon-v4.0.2 @@ -601,12 +601,13 @@ deps = # ~~~ Web 1 ~~~ - django-v1.11.29: django==1.11.29 + django-v2.0.9: django==2.0.9 django-v2.2.28: django==2.2.28 django-v3.2.25: django==3.2.25 django-v4.2.20: django==4.2.20 django-v5.0.9: django==5.0.9 django-v5.2: django==5.2 + django: channels[daphne] django: psycopg2-binary django: djangorestframework django: pytest-django @@ -616,19 +617,14 @@ deps = django-v5.0.9: pytest-asyncio django-v5.2: pytest-asyncio django-v2.2.28: six - django-v1.11.29: djangorestframework>=3.0,<4.0 - django-v1.11.29: Werkzeug<2.1.0 + django-v2.0.9: djangorestframework>=3.0,<4.0 + django-v2.0.9: Werkzeug<2.1.0 django-v2.2.28: djangorestframework>=3.0,<4.0 django-v2.2.28: Werkzeug<2.1.0 django-v3.2.25: djangorestframework>=3.0,<4.0 django-v3.2.25: Werkzeug<2.1.0 - django-v1.11.29: pytest-django<4.0 + django-v2.0.9: pytest-django<4.0 django-v2.2.28: pytest-django<4.0 - django-v2.2.28: channels[daphne] - django-v3.2.25: channels[daphne] - django-v4.2.20: channels[daphne] - django-v5.0.9: channels[daphne] - django-v5.2: channels[daphne] flask-v1.1.4: flask==1.1.4 flask-v2.3.3: flask==2.3.3 @@ -660,7 +656,7 @@ deps = bottle-v0.13.2: bottle==0.13.2 bottle: werkzeug<2.1.0 - falcon-v2.0.0: falcon==2.0.0 + falcon-v3.0.1: falcon==3.0.1 falcon-v3.1.3: falcon==3.1.3 falcon-v4.0.2: falcon==4.0.2