Skip to content

Commit

Permalink
Centralize minimum version checking
Browse files Browse the repository at this point in the history
  • Loading branch information
sentrivana committed Jan 9, 2025
1 parent 4432e26 commit 1e0839a
Show file tree
Hide file tree
Showing 20 changed files with 85 additions and 116 deletions.
42 changes: 41 additions & 1 deletion sentry_sdk/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ def iter_default_integrations(with_auto_enabling_integrations):
"sentry_sdk.integrations.tornado.TornadoIntegration",
]


iter_default_integrations = _generate_default_integrations_iterator(
integrations=_DEFAULT_INTEGRATIONS,
auto_enabling_integrations=_AUTO_ENABLING_INTEGRATIONS,
Expand All @@ -120,6 +119,30 @@ def iter_default_integrations(with_auto_enabling_integrations):
del _generate_default_integrations_iterator


_MIN_VERSIONS = {
"aiohttp": (3, 4),
"anthropic": (0, 16),
"ariadne": (0, 20),
"arq": (0, 23),
"asyncpg": (0, 23),
"boto3": (1, 12), # this is actually the botocore version
"bottle": (0, 12),
"celery": (4, 4, 7),
"clickhouse_driver": (0, 2, 0),
"django": (1, 8),
"falcon": (1, 4),
"flask": (0, 10),
"gql": (3, 4, 1),
"graphene": (3, 3),
"ray": (2, 7, 0),
"rq": (0, 6),
"sanic": (0, 8),
"sqlalchemy": (1, 2),
"strawberry": (0, 209, 5),
"tornado": (6, 0),
}


def setup_integrations(
integrations,
with_defaults=True,
Expand Down Expand Up @@ -195,6 +218,23 @@ def setup_integrations(
return integrations


def _check_minimum_version(integration, version, package=None):
# type: (Integration, Optional[tuple[int]], Optional[str]) -> None
package = package or integration.identifier

if version is None:
raise DidNotEnable(f"Unparsable {package} version: {version}")

min_version = _MIN_VERSIONS.get(integration.identifier)
if min_version is None:
return

if version < min_version:
raise DidNotEnable(
f"Integration only supports {package} {'.'.join(map(str, min_version))} or newer."
)


class DidNotEnable(Exception): # noqa: N818
"""
The integration could not be enabled due to a trivial user error like
Expand Down
8 changes: 2 additions & 6 deletions sentry_sdk/integrations/aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA
from sentry_sdk.integrations import (
_DEFAULT_FAILED_REQUEST_STATUS_CODES,
_check_minimum_version,
Integration,
DidNotEnable,
)
Expand Down Expand Up @@ -91,12 +92,7 @@ def setup_once():
# type: () -> None

version = parse_version(AIOHTTP_VERSION)

if version is None:
raise DidNotEnable("Unparsable AIOHTTP version: {}".format(AIOHTTP_VERSION))

if version < (3, 4):
raise DidNotEnable("AIOHTTP 3.4 or newer required.")
_check_minimum_version(AioHttpIntegration, version)

if not HAS_REAL_CONTEXTVARS:
# We better have contextvars or we're going to leak state between
Expand Down
9 changes: 2 additions & 7 deletions sentry_sdk/integrations/ariadne.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import sentry_sdk
from sentry_sdk import get_client, capture_event
from sentry_sdk.integrations import DidNotEnable, Integration
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
from sentry_sdk.integrations.logging import ignore_logger
from sentry_sdk.integrations._wsgi_common import request_body_within_bounds
from sentry_sdk.scope import should_send_default_pii
Expand Down Expand Up @@ -36,12 +36,7 @@ class AriadneIntegration(Integration):
def setup_once():
# type: () -> None
version = package_version("ariadne")

if version is None:
raise DidNotEnable("Unparsable ariadne version.")

if version < (0, 20):
raise DidNotEnable("ariadne 0.20 or newer required.")
_check_minimum_version(AriadneIntegration, version)

ignore_logger("ariadne")

Expand Down
8 changes: 2 additions & 6 deletions sentry_sdk/integrations/arq.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import sentry_sdk
from sentry_sdk.consts import OP, SPANSTATUS
from sentry_sdk.integrations import DidNotEnable, Integration
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
from sentry_sdk.integrations.logging import ignore_logger
from sentry_sdk.scope import should_send_default_pii
from sentry_sdk.tracing import Transaction, TRANSACTION_SOURCE_TASK
Expand Down Expand Up @@ -55,11 +55,7 @@ def setup_once():
except (TypeError, ValueError):
version = None

if version is None:
raise DidNotEnable("Unparsable arq version: {}".format(ARQ_VERSION))

if version < (0, 23):
raise DidNotEnable("arq 0.23 or newer required.")
_check_minimum_version(ArqIntegration, version)

patch_enqueue_job()
patch_run_job()
Expand Down
7 changes: 6 additions & 1 deletion sentry_sdk/integrations/asyncpg.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import sentry_sdk
from sentry_sdk.consts import OP, SPANDATA
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
from sentry_sdk.tracing import Span
from sentry_sdk.tracing_utils import add_query_source, record_sql_queries
from sentry_sdk.utils import (
Expand Down Expand Up @@ -37,6 +37,11 @@ def __init__(self, *, record_params: bool = False):

@staticmethod
def setup_once() -> None:
# asyncpg.__version__ is a string containing the semantic version in the form of "<major>.<minor>.<patch>"
asyncpg_version = parse_version(asyncpg.__version__)

_check_minimum_version(AsyncPGIntegration, asyncpg_version)

asyncpg.Connection.execute = _wrap_execute(
asyncpg.Connection.execute,
)
Expand Down
12 changes: 2 additions & 10 deletions sentry_sdk/integrations/boto3.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import sentry_sdk
from sentry_sdk.consts import OP, SPANDATA
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
from sentry_sdk.tracing import Span
from sentry_sdk.utils import (
capture_internal_exceptions,
Expand Down Expand Up @@ -35,16 +35,8 @@ class Boto3Integration(Integration):
@staticmethod
def setup_once():
# type: () -> None

version = parse_version(BOTOCORE_VERSION)

if version is None:
raise DidNotEnable(
"Unparsable botocore version: {}".format(BOTOCORE_VERSION)
)

if version < (1, 12):
raise DidNotEnable("Botocore 1.12 or newer is required.")
_check_minimum_version(Boto3Integration, version, "botocore")

orig_init = BaseClient.__init__

Expand Down
8 changes: 2 additions & 6 deletions sentry_sdk/integrations/bottle.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
Integration,
DidNotEnable,
_DEFAULT_FAILED_REQUEST_STATUS_CODES,
_check_minimum_version,
)
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
from sentry_sdk.integrations._wsgi_common import RequestExtractor
Expand Down Expand Up @@ -72,12 +73,7 @@ def __init__(
def setup_once():
# type: () -> None
version = parse_version(BOTTLE_VERSION)

if version is None:
raise DidNotEnable("Unparsable Bottle version: {}".format(BOTTLE_VERSION))

if version < (0, 12):
raise DidNotEnable("Bottle 0.12 or newer required.")
_check_minimum_version(BottleIntegration, version)

old_app = Bottle.__call__

Expand Down
5 changes: 2 additions & 3 deletions sentry_sdk/integrations/celery/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sentry_sdk import isolation_scope
from sentry_sdk.api import continue_trace
from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
from sentry_sdk.integrations.celery.beat import (
_patch_beat_apply_entry,
_patch_redbeat_maybe_due,
Expand Down Expand Up @@ -79,8 +79,7 @@ def __init__(
@staticmethod
def setup_once():
# type: () -> None
if CELERY_VERSION < (4, 4, 7):
raise DidNotEnable("Celery 4.4.7 or newer required.")
_check_minimum_version(CeleryIntegration, CELERY_VERSION)

_patch_build_tracer()
_patch_task_apply_async()
Expand Down
4 changes: 3 additions & 1 deletion sentry_sdk/integrations/clickhouse_driver.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sentry_sdk
from sentry_sdk.consts import OP, SPANDATA
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
from sentry_sdk.tracing import Span
from sentry_sdk.scope import should_send_default_pii
from sentry_sdk.utils import capture_internal_exceptions, ensure_integration_enabled
Expand Down Expand Up @@ -44,6 +44,8 @@ class ClickhouseDriverIntegration(Integration):

@staticmethod
def setup_once() -> None:
_check_minimum_version(ClickhouseDriverIntegration, clickhouse_driver.VERSION)

# Every query is done using the Connection's `send_query` function
clickhouse_driver.connection.Connection.send_query = _wrap_start(
clickhouse_driver.connection.Connection.send_query
Expand Down
6 changes: 2 additions & 4 deletions sentry_sdk/integrations/django/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
transaction_from_function,
walk_exception_chain,
)
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
from sentry_sdk.integrations.logging import ignore_logger
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
from sentry_sdk.integrations._wsgi_common import (
Expand Down Expand Up @@ -154,9 +154,7 @@ def __init__(
@staticmethod
def setup_once():
# type: () -> None

if DJANGO_VERSION < (1, 8):
raise DidNotEnable("Django 1.8 or newer is required.")
_check_minimum_version(DjangoIntegration, DJANGO_VERSION)

install_sql_hook()
# Patch in our custom middleware.
Expand Down
9 changes: 2 additions & 7 deletions sentry_sdk/integrations/falcon.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sentry_sdk
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
from sentry_sdk.integrations._wsgi_common import RequestExtractor
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
from sentry_sdk.tracing import SOURCE_FOR_STYLE
Expand Down Expand Up @@ -135,12 +135,7 @@ def setup_once():
# type: () -> None

version = parse_version(FALCON_VERSION)

if version is None:
raise DidNotEnable("Unparsable Falcon version: {}".format(FALCON_VERSION))

if version < (1, 4):
raise DidNotEnable("Falcon 1.4 or newer required.")
_check_minimum_version(FalconIntegration, version)

_patch_wsgi_app()
_patch_handle_exception()
Expand Down
9 changes: 2 additions & 7 deletions sentry_sdk/integrations/flask.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sentry_sdk
from sentry_sdk.integrations import DidNotEnable, Integration
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
from sentry_sdk.integrations._wsgi_common import (
DEFAULT_HTTP_METHODS_TO_CAPTURE,
RequestExtractor,
Expand Down Expand Up @@ -73,12 +73,7 @@ def __init__(
def setup_once():
# type: () -> None
version = package_version("flask")

if version is None:
raise DidNotEnable("Unparsable Flask version.")

if version < (0, 10):
raise DidNotEnable("Flask 0.10 or newer is required.")
_check_minimum_version(FlaskIntegration, version)

before_render_template.connect(_add_sentry_trace)
request_started.connect(_request_started)
Expand Down
9 changes: 3 additions & 6 deletions sentry_sdk/integrations/gql.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
parse_version,
)

from sentry_sdk.integrations import DidNotEnable, Integration
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
from sentry_sdk.scope import should_send_default_pii

try:
Expand Down Expand Up @@ -34,11 +34,8 @@ class GQLIntegration(Integration):
def setup_once():
# type: () -> None
gql_version = parse_version(gql.__version__)
if gql_version is None or gql_version < MIN_GQL_VERSION:
raise DidNotEnable(
"GQLIntegration is only supported for GQL versions %s and above."
% ".".join(str(num) for num in MIN_GQL_VERSION)
)
_check_minimum_version(GQLIntegration, gql_version)

_patch_execute()


Expand Down
9 changes: 2 additions & 7 deletions sentry_sdk/integrations/graphene.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import sentry_sdk
from sentry_sdk.consts import OP
from sentry_sdk.integrations import DidNotEnable, Integration
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
from sentry_sdk.scope import should_send_default_pii
from sentry_sdk.utils import (
capture_internal_exceptions,
Expand Down Expand Up @@ -34,12 +34,7 @@ class GrapheneIntegration(Integration):
def setup_once():
# type: () -> None
version = package_version("graphene")

if version is None:
raise DidNotEnable("Unparsable graphene version.")

if version < (3, 3):
raise DidNotEnable("graphene 3.3 or newer required.")
_check_minimum_version(GrapheneIntegration, version)

_patch_graphql()

Expand Down
9 changes: 2 additions & 7 deletions sentry_sdk/integrations/ray.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import sentry_sdk
from sentry_sdk.consts import OP, SPANSTATUS
from sentry_sdk.integrations import DidNotEnable, Integration
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
from sentry_sdk.tracing import TRANSACTION_SOURCE_TASK
from sentry_sdk.utils import (
event_from_exception,
Expand Down Expand Up @@ -136,11 +136,6 @@ class RayIntegration(Integration):
def setup_once():
# type: () -> None
version = package_version("ray")

if version is None:
raise DidNotEnable("Unparsable ray version: {}".format(version))

if version < (2, 7, 0):
raise DidNotEnable("Ray 2.7.0 or newer required")
_check_minimum_version(RayIntegration, version)

_patch_ray_remote()
10 changes: 2 additions & 8 deletions sentry_sdk/integrations/rq.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sentry_sdk
from sentry_sdk.consts import OP
from sentry_sdk.api import continue_trace
from sentry_sdk.integrations import DidNotEnable, Integration
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
from sentry_sdk.integrations.logging import ignore_logger
from sentry_sdk.tracing import TRANSACTION_SOURCE_TASK
from sentry_sdk.utils import (
Expand Down Expand Up @@ -41,14 +41,8 @@ class RqIntegration(Integration):
@staticmethod
def setup_once():
# type: () -> None

version = parse_version(RQ_VERSION)

if version is None:
raise DidNotEnable("Unparsable RQ version: {}".format(RQ_VERSION))

if version < (0, 6):
raise DidNotEnable("RQ 0.6 or newer is required.")
_check_minimum_version(RqIntegration, version)

old_perform_job = Worker.perform_job

Expand Down
Loading

0 comments on commit 1e0839a

Please sign in to comment.