From d4e87a3a6c57a6d40a70760da9736a6b8c5befc0 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Thu, 13 Aug 2026 09:37:03 +0200 Subject: [PATCH 1/5] fix: Allow to correctly compare parsed versions --- sentry_sdk/utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py index 45e5376d1c..6ebc288c52 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -1314,6 +1314,12 @@ def parse_version(version: str) -> "Optional[Tuple[int, ...]]": except (TypeError, ValueError, AttributeError): return None + if len(release) == 2: + # Pad out to three digits so that version comparison against a three-part + # tuple works as expected (e.g. (0, 1) should be >= than (0, 1, 0) when + # it comes to releases, but tuples don't compare that way). + release_tuple = (release_tuple[0], release_tuple[1], 0) + return release_tuple From cf58a841d043e9af3772fe0c40b59c29761c6258 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Thu, 13 Aug 2026 09:40:02 +0200 Subject: [PATCH 2/5] . --- sentry_sdk/utils.py | 7 +++++-- tests/test_utils.py | 10 +++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py index 6ebc288c52..be0e98d6f7 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -1314,11 +1314,14 @@ def parse_version(version: str) -> "Optional[Tuple[int, ...]]": except (TypeError, ValueError, AttributeError): return None - if len(release) == 2: + if len(release_tuple) < 3: # Pad out to three digits so that version comparison against a three-part # tuple works as expected (e.g. (0, 1) should be >= than (0, 1, 0) when # it comes to releases, but tuples don't compare that way). - release_tuple = (release_tuple[0], release_tuple[1], 0) + if len(release_tuple) == 1: + release_tuple = (release_tuple[0], 0, 0) + elif len(release_tuple) == 2: + release_tuple = (release_tuple[0], release_tuple[1], 0) return release_tuple diff --git a/tests/test_utils.py b/tests/test_utils.py index 64973ea5dd..af08ece0e4 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -535,15 +535,15 @@ def test_match_regex_list(item, regex_list, expected_result): ["2.0.0b3", (2, 0, 0)], ["2.0.0b2", (2, 0, 0)], ["2.0.0b1", (2, 0, 0)], - ["0.6beta3", (0, 6)], - ["0.6beta2", (0, 6)], - ["0.6beta1", (0, 6)], + ["0.6beta3", (0, 6, 0)], + ["0.6beta2", (0, 6, 0)], + ["0.6beta1", (0, 6, 0)], ["0.4.2b", (0, 4, 2)], ["0.4.2a", (0, 4, 2)], ["0.0.1", (0, 0, 1)], ["0.0.0", (0, 0, 0)], - ["1", (1,)], - ["1.0", (1, 0)], + ["1", (1, 0, 0)], + ["1.0", (1, 0, 0)], ["1.0.0", (1, 0, 0)], [" 1.0.0 ", (1, 0, 0)], [" 1.0.0 ", (1, 0, 0)], From 882ff7a8a7daa984245caa2de2012d83e71ded89 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Mon, 24 Aug 2026 11:16:25 +0200 Subject: [PATCH 3/5] move logic to comparison function --- sentry_sdk/integrations/__init__.py | 16 ++++++-- sentry_sdk/utils.py | 9 ----- tests/test_utils.py | 62 ++++++++++++++++++++++++++--- 3 files changed, 68 insertions(+), 19 deletions(-) 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/utils.py b/sentry_sdk/utils.py index be0e98d6f7..45e5376d1c 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -1314,15 +1314,6 @@ def parse_version(version: str) -> "Optional[Tuple[int, ...]]": except (TypeError, ValueError, AttributeError): return None - if len(release_tuple) < 3: - # Pad out to three digits so that version comparison against a three-part - # tuple works as expected (e.g. (0, 1) should be >= than (0, 1, 0) when - # it comes to releases, but tuples don't compare that way). - if len(release_tuple) == 1: - release_tuple = (release_tuple[0], 0, 0) - elif len(release_tuple) == 2: - release_tuple = (release_tuple[0], release_tuple[1], 0) - return release_tuple diff --git a/tests/test_utils.py b/tests/test_utils.py index af08ece0e4..b979c67b73 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, @@ -535,15 +535,15 @@ def test_match_regex_list(item, regex_list, expected_result): ["2.0.0b3", (2, 0, 0)], ["2.0.0b2", (2, 0, 0)], ["2.0.0b1", (2, 0, 0)], - ["0.6beta3", (0, 6, 0)], - ["0.6beta2", (0, 6, 0)], - ["0.6beta1", (0, 6, 0)], + ["0.6beta3", (0, 6)], + ["0.6beta2", (0, 6)], + ["0.6beta1", (0, 6)], ["0.4.2b", (0, 4, 2)], ["0.4.2a", (0, 4, 2)], ["0.0.1", (0, 0, 1)], ["0.0.0", (0, 0, 0)], - ["1", (1, 0, 0)], - ["1.0", (1, 0, 0)], + ["1", (1,)], + ["1.0", (1, 0)], ["1.0.0", (1, 0, 0)], [" 1.0.0 ", (1, 0, 0)], [" 1.0.0 ", (1, 0, 0)], @@ -556,6 +556,56 @@ 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), + ], +) +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), min_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(): """ From 6a5d28b1c7ed1f753448dac97af1d7948d929b86 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Mon, 24 Aug 2026 11:22:17 +0200 Subject: [PATCH 4/5] . --- tests/test_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index b979c67b73..9e5b68e9eb 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -585,6 +585,7 @@ def test_parse_version(version, expected_result): True, ), ("2.0.1", (1, 1, 2), True), + ("1", (1, 0), True), ], ) def test_check_minimum_version(monkeypatch, version, min_version, expected_pass): @@ -593,7 +594,7 @@ class TestIntegration(Integration): monkeypatch.setattr(sentry_sdk.integrations, "_MIN_VERSIONS", {"test": min_version}) try: - _check_minimum_version(TestIntegration, parse_version(version), min_version) + _check_minimum_version(TestIntegration, parse_version(version)) except DidNotEnable: if expected_pass: assert False, ( From 78a98477dc4876787e3e4b357e3321f996b7ee23 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Mon, 24 Aug 2026 11:34:40 +0200 Subject: [PATCH 5/5] fix celery --- sentry_sdk/integrations/celery/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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()