From ea2c4c6d8c31fd341d506b615cd71d328a2f34b6 Mon Sep 17 00:00:00 2001 From: Jack Evans Date: Thu, 5 Nov 2026 11:39:01 -0300 Subject: [PATCH 1/3] perf: Avoid scanning installed packages in package_version Co-Authored-By: Claude Opus 5.5 --- sentry_sdk/utils.py | 11 +++++++++++ tests/test_utils.py | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py index b9be3d8936..551328e739 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -1837,6 +1837,17 @@ def _get_installed_modules() -> "Dict[str, str]": def package_version(package: str) -> "Optional[Tuple[int, ...]]": + try: + from importlib import metadata + + package_metadata_version = metadata.version(package) + if package_metadata_version is None: + return None + return parse_version(package_metadata_version) + except ImportError: + # < py3.8, or no metadata found for this package + pass + normalized_package = _normalize_module_name( _replace_hyphens_dots_and_underscores_with_dashes(package) ) diff --git a/tests/test_utils.py b/tests/test_utils.py index 77def0eb6e..a09d63a499 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1412,6 +1412,14 @@ def test_package_version_is_none(): assert package_version("non_existent_package") is None +def test_package_version_does_not_scan_installed_packages(): + with mock.patch("sentry_sdk.utils._generate_installed_modules") as generate: + with mock.patch("sentry_sdk.utils._installed_modules", None): + assert package_version("pytest") is not None + + generate.assert_not_called() + + @pytest.mark.parametrize( "kwargs, expected", [ From f47e6a429365627d039aca7c5f6b796e4c32562f Mon Sep 17 00:00:00 2001 From: Jack Evans Date: Thu, 5 Nov 2026 11:45:27 -0300 Subject: [PATCH 2/3] fix: Fall back to the existing lookup when package metadata is broken Co-Authored-By: Claude Opus 5.5 --- sentry_sdk/utils.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py index 551328e739..d70a18eb9c 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -1840,12 +1840,11 @@ def package_version(package: str) -> "Optional[Tuple[int, ...]]": try: from importlib import metadata - package_metadata_version = metadata.version(package) - if package_metadata_version is None: - return None - return parse_version(package_metadata_version) - except ImportError: - # < py3.8, or no metadata found for this package + metadata_version = metadata.version(package) + if metadata_version is not None: + return parse_version(metadata_version) + except Exception: + # < py3.8, or missing or broken metadata for this package pass normalized_package = _normalize_module_name( From 06ac9941f1384376ea05ff0e9eb16101ae51ea9f Mon Sep 17 00:00:00 2001 From: Jack Evans Date: Thu, 5 Nov 2026 14:53:49 -0300 Subject: [PATCH 3/3] test: Skip the package_version scan test below Python 3.8 Co-Authored-By: Claude Opus 5.5 --- tests/test_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_utils.py b/tests/test_utils.py index a09d63a499..03c4373c24 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1412,6 +1412,7 @@ def test_package_version_is_none(): assert package_version("non_existent_package") is None +@pytest.mark.skipif(sys.version_info < (3, 8), reason="requires importlib.metadata") def test_package_version_does_not_scan_installed_packages(): with mock.patch("sentry_sdk.utils._generate_installed_modules") as generate: with mock.patch("sentry_sdk.utils._installed_modules", None):