Skip to content

Commit 4cf1ee5

Browse files
authored
fix(utils): look up package versions without scanning all installed packages (#8215)
### Description `package_version()` in `sentry_sdk/utils.py` rebuilt the full installed-package mapping on every call: `_get_installed_modules()` iterates `importlib.metadata.distributions()` (parsing every installed distribution's metadata), and `package_version` additionally re-normalized that whole mapping per call. Integrations call `package_version` during `sentry_sdk.init()` to check minimum versions, so in environments with a few hundred installed packages this scan measurably slowed SDK startup. This change makes `package_version` look up the single requested distribution with `importlib.metadata.version()` instead: - Top-level guarded `from importlib import metadata as importlib_metadata` (no inline imports, per the repo guidelines); `PackageNotFoundError` maps to `None`, matching the previous behaviour for missing packages. - The existing `_get_installed_modules()` scan remains as the fallback for environments without `importlib.metadata` (Python < 3.8), so supported old interpreters keep working. - Name handling is unchanged: the requested name is PEP 503-normalized first, which is also how `importlib.metadata.version()` resolves distribution names. Tests in `tests/test_utils.py`: - `test_package_version_installed_package` asserts an installed package (`pytest`) resolves to the same parsed version as `importlib.metadata`. - `test_package_version_does_not_scan_all_installed_packages` monkeypatches `_get_installed_modules` to fail if invoked (the only way to assert the scan no longer happens) and asserts both hit and miss lookups still work. Validation: - `TESTPATH=tests/test_utils.py uv run tox -e py3.12-common -- -k package_version` — 3 passed. - `uv run ruff check` / `ruff format --check` on the changed files — clean. - `uv run --group typing mypy sentry_sdk` — no issues in 203 files. #### Issues * resolves: #8190 #### Reminders - Tests added and linted with `uv run ruff` as requested. - PR title uses conventional-commit style. Co-authored-by: Adityaaa <144815351+addydude@users.noreply.github.com>
1 parent 05c32d3 commit 4cf1ee5

2 files changed

Lines changed: 31 additions & 0 deletions

File tree

‎sentry_sdk/utils.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
# Python 3.10 and below
2929
BaseExceptionGroup = None # type: ignore
3030

31+
try:
32+
from importlib import metadata as importlib_metadata
33+
except ImportError:
34+
importlib_metadata = None # type: ignore[assignment]
35+
3136
from typing import TYPE_CHECKING
3237

3338
import sentry_sdk
@@ -1841,6 +1846,14 @@ def package_version(package: str) -> "Optional[Tuple[int, ...]]":
18411846
_replace_hyphens_dots_and_underscores_with_dashes(package)
18421847
)
18431848

1849+
if importlib_metadata is not None:
1850+
# Look up the single distribution instead of scanning (and parsing
1851+
# the metadata of) every installed package on each call.
1852+
try:
1853+
return parse_version(importlib_metadata.version(normalized_package))
1854+
except importlib_metadata.PackageNotFoundError:
1855+
return None
1856+
18441857
installed_packages = {
18451858
_replace_hyphens_dots_and_underscores_with_dashes(module): v
18461859
for module, v in _get_installed_modules().items()

‎tests/test_utils.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,6 +1412,24 @@ def test_package_version_is_none():
14121412
assert package_version("non_existent_package") is None
14131413

14141414

1415+
@pytest.mark.skipif(sys.version_info < (3, 8), reason="requires importlib.metadata")
1416+
def test_package_version_installed_package():
1417+
from importlib import metadata
1418+
1419+
assert package_version("pytest") == parse_version(metadata.version("pytest"))
1420+
1421+
1422+
@pytest.mark.skipif(sys.version_info < (3, 8), reason="requires importlib.metadata")
1423+
def test_package_version_does_not_scan_all_installed_packages(monkeypatch):
1424+
def _fail():
1425+
raise AssertionError("package_version must not scan all installed packages")
1426+
1427+
monkeypatch.setattr("sentry_sdk.utils._get_installed_modules", _fail)
1428+
1429+
assert package_version("pytest") is not None
1430+
assert package_version("non_existent_package") is None
1431+
1432+
14151433
@pytest.mark.parametrize(
14161434
"kwargs, expected",
14171435
[

0 commit comments

Comments
 (0)