diff --git a/sentry_sdk/integrations/__init__.py b/sentry_sdk/integrations/__init__.py index 677d34a81e..cfd94b80cb 100644 --- a/sentry_sdk/integrations/__init__.py +++ b/sentry_sdk/integrations/__init__.py @@ -1,4 +1,5 @@ from abc import ABC, abstractmethod +from itertools import zip_longest from threading import Lock from typing import TYPE_CHECKING @@ -299,10 +300,17 @@ def _check_minimum_version( if min_version is None: return - if version < min_version: - raise DidNotEnable( - f"Integration only supports {package} {'.'.join(map(str, min_version))} or newer." - ) + # We can't use normal tuple comparison here because the version tuples might + # not have the same length, in which case they wouldn't compare as expected. + for v, min in zip_longest(version, min_version, fillvalue=0): + if v == min: + continue + elif v < min: + raise DidNotEnable( + f"Integration only supports {package} {'.'.join(map(str, min_version))} or newer." + ) + elif v > min: + return class DidNotEnable(Exception): # noqa: N818 diff --git a/sentry_sdk/integrations/celery/__init__.py b/sentry_sdk/integrations/celery/__init__.py index 51eedf976b..b81e622003 100644 --- a/sentry_sdk/integrations/celery/__init__.py +++ b/sentry_sdk/integrations/celery/__init__.py @@ -24,6 +24,7 @@ capture_internal_exceptions, event_from_exception, has_data_collection_enabled, + parse_version, reraise, ) @@ -36,7 +37,7 @@ try: - from celery import VERSION as CELERY_VERSION # type: ignore + from celery import __version__ as CELERY_VERSION # type: ignore from celery.app.task import Task # type: ignore from celery.app.trace import task_has_custom from celery.exceptions import ( # type: ignore @@ -73,7 +74,7 @@ def __init__( @staticmethod def setup_once() -> None: - _check_minimum_version(CeleryIntegration, CELERY_VERSION) + _check_minimum_version(CeleryIntegration, parse_version(CELERY_VERSION)) _patch_build_tracer() _patch_task_apply_async() diff --git a/tests/test_utils.py b/tests/test_utils.py index 64973ea5dd..9e5b68e9eb 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -8,7 +8,7 @@ import sentry_sdk from sentry_sdk._queue import Queue -from sentry_sdk.integrations import Integration +from sentry_sdk.integrations import DidNotEnable, Integration, _check_minimum_version from sentry_sdk.utils import ( Components, Dsn, @@ -556,6 +556,57 @@ def test_parse_version(version, expected_result): assert parse_version(version) == expected_result +@pytest.mark.parametrize( + "version,min_version,expected_pass", + [ + ("1.0.0", (1, 0, 0), True), + ("1.0.1", (2, 0, 0), False), + ("1", (1, 0, 2), False), + ("1.0", (1, 0, 2), False), + ("1.0.1", (1, 0, 2), False), + ("1.0.1", (1, 0, 1), True), + ("1.0", (2,), False), + ( + "1.0.1", + ( + 2, + 0, + ), + False, + ), + ("1.0.1", (2, 0, 0), False), + ("2.0", (1,), True), + ( + "2.0.1", + ( + 1, + 1, + ), + True, + ), + ("2.0.1", (1, 1, 2), True), + ("1", (1, 0), True), + ], +) +def test_check_minimum_version(monkeypatch, version, min_version, expected_pass): + class TestIntegration(Integration): + identifier = "test" + + monkeypatch.setattr(sentry_sdk.integrations, "_MIN_VERSIONS", {"test": min_version}) + try: + _check_minimum_version(TestIntegration, parse_version(version)) + except DidNotEnable: + if expected_pass: + assert False, ( + "_check_minimum_version raised DidNotEnable when it shouldn't have" + ) + else: + if not expected_pass: + assert False, ( + "_check_minimum_version didn't raise DidNotEnable when it was supposed to" + ) + + @pytest.fixture def mock_client_with_dsn_netloc(): """