Skip to content

Commit be53273

Browse files
authored
Centralize minimum version checking (#3910)
For [populating tox automatically](#3808), we need to store min versions of frameworks/libraries in a programmatically accessible place. The obvious place for this would be in each integration; however, since integrations can't be imported unless the respective framework is installed, this couldn't be used from the script (unless we'd always install all requirements of all integrations prior to running it, which takes a non trivial amount of time). So instead I've opted for a central place within `sentry_sdk/integrations/__init__.py`. Note: the min versions probably need updating. Not sure when this was last done, but some of them look quite ancient and we probably don't support them because we'd already dropped the last Python version they'd be able to run on.
1 parent 4432e26 commit be53273

21 files changed

+87
-136
lines changed

sentry_sdk/integrations/__init__.py

+41-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ def iter_default_integrations(with_auto_enabling_integrations):
111111
"sentry_sdk.integrations.tornado.TornadoIntegration",
112112
]
113113

114-
115114
iter_default_integrations = _generate_default_integrations_iterator(
116115
integrations=_DEFAULT_INTEGRATIONS,
117116
auto_enabling_integrations=_AUTO_ENABLING_INTEGRATIONS,
@@ -120,6 +119,30 @@ def iter_default_integrations(with_auto_enabling_integrations):
120119
del _generate_default_integrations_iterator
121120

122121

122+
_MIN_VERSIONS = {
123+
"aiohttp": (3, 4),
124+
"anthropic": (0, 16),
125+
"ariadne": (0, 20),
126+
"arq": (0, 23),
127+
"asyncpg": (0, 23),
128+
"boto3": (1, 12), # this is actually the botocore version
129+
"bottle": (0, 12),
130+
"celery": (4, 4, 7),
131+
"clickhouse_driver": (0, 2, 0),
132+
"django": (1, 8),
133+
"falcon": (1, 4),
134+
"flask": (0, 10),
135+
"gql": (3, 4, 1),
136+
"graphene": (3, 3),
137+
"ray": (2, 7, 0),
138+
"rq": (0, 6),
139+
"sanic": (0, 8),
140+
"sqlalchemy": (1, 2),
141+
"strawberry": (0, 209, 5),
142+
"tornado": (6, 0),
143+
}
144+
145+
123146
def setup_integrations(
124147
integrations,
125148
with_defaults=True,
@@ -195,6 +218,23 @@ def setup_integrations(
195218
return integrations
196219

197220

221+
def _check_minimum_version(integration, version, package=None):
222+
# type: (type[Integration], Optional[tuple[int, ...]], Optional[str]) -> None
223+
package = package or integration.identifier
224+
225+
if version is None:
226+
raise DidNotEnable(f"Unparsable {package} version.")
227+
228+
min_version = _MIN_VERSIONS.get(integration.identifier)
229+
if min_version is None:
230+
return
231+
232+
if version < min_version:
233+
raise DidNotEnable(
234+
f"Integration only supports {package} {'.'.join(map(str, min_version))} or newer."
235+
)
236+
237+
198238
class DidNotEnable(Exception): # noqa: N818
199239
"""
200240
The integration could not be enabled due to a trivial user error like

sentry_sdk/integrations/aiohttp.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA
88
from sentry_sdk.integrations import (
99
_DEFAULT_FAILED_REQUEST_STATUS_CODES,
10+
_check_minimum_version,
1011
Integration,
1112
DidNotEnable,
1213
)
@@ -91,12 +92,7 @@ def setup_once():
9192
# type: () -> None
9293

9394
version = parse_version(AIOHTTP_VERSION)
94-
95-
if version is None:
96-
raise DidNotEnable("Unparsable AIOHTTP version: {}".format(AIOHTTP_VERSION))
97-
98-
if version < (3, 4):
99-
raise DidNotEnable("AIOHTTP 3.4 or newer required.")
95+
_check_minimum_version(AioHttpIntegration, version)
10096

10197
if not HAS_REAL_CONTEXTVARS:
10298
# We better have contextvars or we're going to leak state between

sentry_sdk/integrations/anthropic.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import sentry_sdk
55
from sentry_sdk.ai.monitoring import record_token_usage
66
from sentry_sdk.consts import OP, SPANDATA
7-
from sentry_sdk.integrations import DidNotEnable, Integration
7+
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
88
from sentry_sdk.scope import should_send_default_pii
99
from sentry_sdk.utils import (
1010
capture_internal_exceptions,
@@ -37,12 +37,7 @@ def __init__(self, include_prompts=True):
3737
def setup_once():
3838
# type: () -> None
3939
version = package_version("anthropic")
40-
41-
if version is None:
42-
raise DidNotEnable("Unparsable anthropic version.")
43-
44-
if version < (0, 16):
45-
raise DidNotEnable("anthropic 0.16 or newer required.")
40+
_check_minimum_version(AnthropicIntegration, version)
4641

4742
Messages.create = _wrap_message_create(Messages.create)
4843
AsyncMessages.create = _wrap_message_create_async(AsyncMessages.create)

sentry_sdk/integrations/ariadne.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import sentry_sdk
44
from sentry_sdk import get_client, capture_event
5-
from sentry_sdk.integrations import DidNotEnable, Integration
5+
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
66
from sentry_sdk.integrations.logging import ignore_logger
77
from sentry_sdk.integrations._wsgi_common import request_body_within_bounds
88
from sentry_sdk.scope import should_send_default_pii
@@ -36,12 +36,7 @@ class AriadneIntegration(Integration):
3636
def setup_once():
3737
# type: () -> None
3838
version = package_version("ariadne")
39-
40-
if version is None:
41-
raise DidNotEnable("Unparsable ariadne version.")
42-
43-
if version < (0, 20):
44-
raise DidNotEnable("ariadne 0.20 or newer required.")
39+
_check_minimum_version(AriadneIntegration, version)
4540

4641
ignore_logger("ariadne")
4742

sentry_sdk/integrations/arq.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import sentry_sdk
44
from sentry_sdk.consts import OP, SPANSTATUS
5-
from sentry_sdk.integrations import DidNotEnable, Integration
5+
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
66
from sentry_sdk.integrations.logging import ignore_logger
77
from sentry_sdk.scope import should_send_default_pii
88
from sentry_sdk.tracing import Transaction, TRANSACTION_SOURCE_TASK
@@ -55,11 +55,7 @@ def setup_once():
5555
except (TypeError, ValueError):
5656
version = None
5757

58-
if version is None:
59-
raise DidNotEnable("Unparsable arq version: {}".format(ARQ_VERSION))
60-
61-
if version < (0, 23):
62-
raise DidNotEnable("arq 0.23 or newer required.")
58+
_check_minimum_version(ArqIntegration, version)
6359

6460
patch_enqueue_job()
6561
patch_run_job()

sentry_sdk/integrations/asyncpg.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import sentry_sdk
66
from sentry_sdk.consts import OP, SPANDATA
7-
from sentry_sdk.integrations import Integration, DidNotEnable
7+
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
88
from sentry_sdk.tracing import Span
99
from sentry_sdk.tracing_utils import add_query_source, record_sql_queries
1010
from sentry_sdk.utils import (
@@ -20,12 +20,6 @@
2020
except ImportError:
2121
raise DidNotEnable("asyncpg not installed.")
2222

23-
# asyncpg.__version__ is a string containing the semantic version in the form of "<major>.<minor>.<patch>"
24-
asyncpg_version = parse_version(asyncpg.__version__)
25-
26-
if asyncpg_version is not None and asyncpg_version < (0, 23, 0):
27-
raise DidNotEnable("asyncpg >= 0.23.0 required")
28-
2923

3024
class AsyncPGIntegration(Integration):
3125
identifier = "asyncpg"
@@ -37,6 +31,10 @@ def __init__(self, *, record_params: bool = False):
3731

3832
@staticmethod
3933
def setup_once() -> None:
34+
# asyncpg.__version__ is a string containing the semantic version in the form of "<major>.<minor>.<patch>"
35+
asyncpg_version = parse_version(asyncpg.__version__)
36+
_check_minimum_version(AsyncPGIntegration, asyncpg_version)
37+
4038
asyncpg.Connection.execute = _wrap_execute(
4139
asyncpg.Connection.execute,
4240
)

sentry_sdk/integrations/boto3.py

+2-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import sentry_sdk
44
from sentry_sdk.consts import OP, SPANDATA
5-
from sentry_sdk.integrations import Integration, DidNotEnable
5+
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
66
from sentry_sdk.tracing import Span
77
from sentry_sdk.utils import (
88
capture_internal_exceptions,
@@ -35,16 +35,8 @@ class Boto3Integration(Integration):
3535
@staticmethod
3636
def setup_once():
3737
# type: () -> None
38-
3938
version = parse_version(BOTOCORE_VERSION)
40-
41-
if version is None:
42-
raise DidNotEnable(
43-
"Unparsable botocore version: {}".format(BOTOCORE_VERSION)
44-
)
45-
46-
if version < (1, 12):
47-
raise DidNotEnable("Botocore 1.12 or newer is required.")
39+
_check_minimum_version(Boto3Integration, version, "botocore")
4840

4941
orig_init = BaseClient.__init__
5042

sentry_sdk/integrations/bottle.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
Integration,
1414
DidNotEnable,
1515
_DEFAULT_FAILED_REQUEST_STATUS_CODES,
16+
_check_minimum_version,
1617
)
1718
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
1819
from sentry_sdk.integrations._wsgi_common import RequestExtractor
@@ -72,12 +73,7 @@ def __init__(
7273
def setup_once():
7374
# type: () -> None
7475
version = parse_version(BOTTLE_VERSION)
75-
76-
if version is None:
77-
raise DidNotEnable("Unparsable Bottle version: {}".format(BOTTLE_VERSION))
78-
79-
if version < (0, 12):
80-
raise DidNotEnable("Bottle 0.12 or newer required.")
76+
_check_minimum_version(BottleIntegration, version)
8177

8278
old_app = Bottle.__call__
8379

sentry_sdk/integrations/celery/__init__.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from sentry_sdk import isolation_scope
77
from sentry_sdk.api import continue_trace
88
from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA
9-
from sentry_sdk.integrations import Integration, DidNotEnable
9+
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
1010
from sentry_sdk.integrations.celery.beat import (
1111
_patch_beat_apply_entry,
1212
_patch_redbeat_maybe_due,
@@ -79,8 +79,7 @@ def __init__(
7979
@staticmethod
8080
def setup_once():
8181
# type: () -> None
82-
if CELERY_VERSION < (4, 4, 7):
83-
raise DidNotEnable("Celery 4.4.7 or newer required.")
82+
_check_minimum_version(CeleryIntegration, CELERY_VERSION)
8483

8584
_patch_build_tracer()
8685
_patch_task_apply_async()

sentry_sdk/integrations/clickhouse_driver.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sentry_sdk
22
from sentry_sdk.consts import OP, SPANDATA
3-
from sentry_sdk.integrations import Integration, DidNotEnable
3+
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
44
from sentry_sdk.tracing import Span
55
from sentry_sdk.scope import should_send_default_pii
66
from sentry_sdk.utils import capture_internal_exceptions, ensure_integration_enabled
@@ -34,16 +34,15 @@ def __getitem__(self, _):
3434
except ImportError:
3535
raise DidNotEnable("clickhouse-driver not installed.")
3636

37-
if clickhouse_driver.VERSION < (0, 2, 0):
38-
raise DidNotEnable("clickhouse-driver >= 0.2.0 required")
39-
4037

4138
class ClickhouseDriverIntegration(Integration):
4239
identifier = "clickhouse_driver"
4340
origin = f"auto.db.{identifier}"
4441

4542
@staticmethod
4643
def setup_once() -> None:
44+
_check_minimum_version(ClickhouseDriverIntegration, clickhouse_driver.VERSION)
45+
4746
# Every query is done using the Connection's `send_query` function
4847
clickhouse_driver.connection.Connection.send_query = _wrap_start(
4948
clickhouse_driver.connection.Connection.send_query

sentry_sdk/integrations/django/__init__.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
transaction_from_function,
2323
walk_exception_chain,
2424
)
25-
from sentry_sdk.integrations import Integration, DidNotEnable
25+
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
2626
from sentry_sdk.integrations.logging import ignore_logger
2727
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
2828
from sentry_sdk.integrations._wsgi_common import (
@@ -154,9 +154,7 @@ def __init__(
154154
@staticmethod
155155
def setup_once():
156156
# type: () -> None
157-
158-
if DJANGO_VERSION < (1, 8):
159-
raise DidNotEnable("Django 1.8 or newer is required.")
157+
_check_minimum_version(DjangoIntegration, DJANGO_VERSION)
160158

161159
install_sql_hook()
162160
# Patch in our custom middleware.

sentry_sdk/integrations/falcon.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import sentry_sdk
2-
from sentry_sdk.integrations import Integration, DidNotEnable
2+
from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
33
from sentry_sdk.integrations._wsgi_common import RequestExtractor
44
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
55
from sentry_sdk.tracing import SOURCE_FOR_STYLE
@@ -135,12 +135,7 @@ def setup_once():
135135
# type: () -> None
136136

137137
version = parse_version(FALCON_VERSION)
138-
139-
if version is None:
140-
raise DidNotEnable("Unparsable Falcon version: {}".format(FALCON_VERSION))
141-
142-
if version < (1, 4):
143-
raise DidNotEnable("Falcon 1.4 or newer required.")
138+
_check_minimum_version(FalconIntegration, version)
144139

145140
_patch_wsgi_app()
146141
_patch_handle_exception()

sentry_sdk/integrations/flask.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import sentry_sdk
2-
from sentry_sdk.integrations import DidNotEnable, Integration
2+
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
33
from sentry_sdk.integrations._wsgi_common import (
44
DEFAULT_HTTP_METHODS_TO_CAPTURE,
55
RequestExtractor,
@@ -73,12 +73,7 @@ def __init__(
7373
def setup_once():
7474
# type: () -> None
7575
version = package_version("flask")
76-
77-
if version is None:
78-
raise DidNotEnable("Unparsable Flask version.")
79-
80-
if version < (0, 10):
81-
raise DidNotEnable("Flask 0.10 or newer is required.")
76+
_check_minimum_version(FlaskIntegration, version)
8277

8378
before_render_template.connect(_add_sentry_trace)
8479
request_started.connect(_request_started)

sentry_sdk/integrations/gql.py

+3-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
parse_version,
66
)
77

8-
from sentry_sdk.integrations import DidNotEnable, Integration
8+
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
99
from sentry_sdk.scope import should_send_default_pii
1010

1111
try:
@@ -24,8 +24,6 @@
2424

2525
EventDataType = Dict[str, Union[str, Tuple[VariableDefinitionNode, ...]]]
2626

27-
MIN_GQL_VERSION = (3, 4, 1)
28-
2927

3028
class GQLIntegration(Integration):
3129
identifier = "gql"
@@ -34,11 +32,8 @@ class GQLIntegration(Integration):
3432
def setup_once():
3533
# type: () -> None
3634
gql_version = parse_version(gql.__version__)
37-
if gql_version is None or gql_version < MIN_GQL_VERSION:
38-
raise DidNotEnable(
39-
"GQLIntegration is only supported for GQL versions %s and above."
40-
% ".".join(str(num) for num in MIN_GQL_VERSION)
41-
)
35+
_check_minimum_version(GQLIntegration, gql_version)
36+
4237
_patch_execute()
4338

4439

sentry_sdk/integrations/graphene.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import sentry_sdk
44
from sentry_sdk.consts import OP
5-
from sentry_sdk.integrations import DidNotEnable, Integration
5+
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
66
from sentry_sdk.scope import should_send_default_pii
77
from sentry_sdk.utils import (
88
capture_internal_exceptions,
@@ -34,12 +34,7 @@ class GrapheneIntegration(Integration):
3434
def setup_once():
3535
# type: () -> None
3636
version = package_version("graphene")
37-
38-
if version is None:
39-
raise DidNotEnable("Unparsable graphene version.")
40-
41-
if version < (3, 3):
42-
raise DidNotEnable("graphene 3.3 or newer required.")
37+
_check_minimum_version(GrapheneIntegration, version)
4338

4439
_patch_graphql()
4540

0 commit comments

Comments
 (0)