version: Rely on importlib.metadata.version too
Packages are slowly migrating to not having a __version__ attribute anymore, instead relying on importlib.metadata to query the installed version. jinja2 now shows a deprecation warning when accessing the __version__ attribute: https://github.com/pallets/jinja/pull/2098 For now we keep accessing __version__ for other packages (we still need the logic for PyQt and its special version attributes anyways), but we fall back on importlib.metadata.version if we can't get a version that way, and we stop trying __version__ for jinja2.
This commit is contained in:
parent
96e535c7ed
commit
6b86a9072f
|
|
@ -322,8 +322,8 @@ class ModuleInfo:
|
|||
except (ImportError, ValueError):
|
||||
self._installed = False
|
||||
return
|
||||
else:
|
||||
self._installed = True
|
||||
|
||||
self._installed = True
|
||||
|
||||
for attribute_name in self._version_attributes:
|
||||
if hasattr(module, attribute_name):
|
||||
|
|
@ -332,6 +332,13 @@ class ModuleInfo:
|
|||
self._version = str(version)
|
||||
break
|
||||
|
||||
if self._version is None:
|
||||
try:
|
||||
self._version = importlib.metadata.version(self.name)
|
||||
except importlib.metadata.PackageNotFoundError:
|
||||
log.misc.debug(f"{self.name} not found")
|
||||
self._version = None
|
||||
|
||||
self._initialized = True
|
||||
|
||||
def get_version(self) -> Optional[str]:
|
||||
|
|
@ -372,7 +379,7 @@ class ModuleInfo:
|
|||
|
||||
version = self.get_version()
|
||||
if version is None:
|
||||
return f'{self.name}: yes'
|
||||
return f'{self.name}: unknown'
|
||||
|
||||
text = f'{self.name}: {version}'
|
||||
if self.is_outdated():
|
||||
|
|
@ -383,7 +390,7 @@ class ModuleInfo:
|
|||
def _create_module_info() -> dict[str, ModuleInfo]:
|
||||
packages = [
|
||||
('colorama', ['VERSION', '__version__']),
|
||||
('jinja2', ['__version__']),
|
||||
('jinja2', []),
|
||||
('pygments', ['__version__']),
|
||||
('yaml', ['__version__']),
|
||||
('adblock', ['__version__'], "0.3.2"),
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ import textwrap
|
|||
import datetime
|
||||
import dataclasses
|
||||
import importlib.metadata
|
||||
import unittest.mock
|
||||
|
||||
import pytest
|
||||
import pytest_mock
|
||||
import hypothesis
|
||||
import hypothesis.strategies
|
||||
from qutebrowser.qt import machinery
|
||||
|
|
@ -620,27 +622,32 @@ def test_path_info(monkeypatch, equal):
|
|||
assert pathinfo['system data'] == 'SYSTEM DATA PATH'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def import_fake(stubs, monkeypatch):
|
||||
"""Fixture to patch imports using ImportFake."""
|
||||
fake = stubs.ImportFake(dict.fromkeys(version.MODULE_INFO, True), monkeypatch)
|
||||
fake.patch()
|
||||
return fake
|
||||
|
||||
|
||||
class TestModuleVersions:
|
||||
|
||||
"""Tests for _module_versions() and ModuleInfo."""
|
||||
|
||||
@pytest.fixture
|
||||
def import_fake(self, stubs, monkeypatch):
|
||||
"""Fixture to patch imports using ImportFake."""
|
||||
fake = stubs.ImportFake(dict.fromkeys(version.MODULE_INFO, True), monkeypatch)
|
||||
fake.patch()
|
||||
return fake
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def importlib_metadata_mock(
|
||||
self, mocker: pytest_mock.MockerFixture
|
||||
) -> unittest.mock.Mock:
|
||||
return mocker.patch("importlib.metadata.version", return_value="4.5.6")
|
||||
|
||||
def test_all_present(self, import_fake):
|
||||
"""Test with all modules present in version 1.2.3."""
|
||||
"""Test with all modules present in a fixed version."""
|
||||
expected = []
|
||||
for name in import_fake.modules:
|
||||
version.MODULE_INFO[name]._reset_cache()
|
||||
if '__version__' not in version.MODULE_INFO[name]._version_attributes:
|
||||
expected.append('{}: yes'.format(name))
|
||||
expected.append(f"{name}: 4.5.6") # from importlib.metadata
|
||||
else:
|
||||
expected.append('{}: 1.2.3'.format(name))
|
||||
expected.append(f"{name}: 1.2.3")
|
||||
assert version._module_versions() == expected
|
||||
|
||||
@pytest.mark.parametrize('module, idx, expected', [
|
||||
|
|
@ -695,6 +702,14 @@ class TestModuleVersions:
|
|||
expected = f"adblock: {fake_version} (< {mod_info.min_version}, outdated)"
|
||||
assert version._module_versions()[4] == expected
|
||||
|
||||
def test_importlib_not_found(self, importlib_metadata_mock: unittest.mock.Mock):
|
||||
"""Test with no __version__ attribute and missing importlib.metadata."""
|
||||
assert not version.MODULE_INFO["jinja2"]._version_attributes # sanity check
|
||||
importlib_metadata_mock.side_effect = importlib.metadata.PackageNotFoundError
|
||||
version.MODULE_INFO["jinja2"]._reset_cache()
|
||||
idx = list(version.MODULE_INFO).index("jinja2")
|
||||
assert version._module_versions()[idx] == "jinja2: unknown"
|
||||
|
||||
@pytest.mark.parametrize('attribute, expected_modules', [
|
||||
('VERSION', ['colorama']),
|
||||
('SIP_VERSION_STR', ['PyQt5.sip', 'PyQt6.sip']),
|
||||
|
|
@ -722,17 +737,17 @@ class TestModuleVersions:
|
|||
mod_info = version.MODULE_INFO[name]
|
||||
if name in expected_modules:
|
||||
assert mod_info.get_version() == "1.2.3"
|
||||
expected.append('{}: 1.2.3'.format(name))
|
||||
expected.append(f"{name}: 1.2.3")
|
||||
else:
|
||||
assert mod_info.get_version() is None
|
||||
expected.append('{}: yes'.format(name))
|
||||
assert mod_info.get_version() == "4.5.6" # from importlib.metadata
|
||||
expected.append(f"{name}: 4.5.6")
|
||||
|
||||
assert version._module_versions() == expected
|
||||
|
||||
@pytest.mark.parametrize('name, has_version', [
|
||||
('sip', False),
|
||||
('colorama', True),
|
||||
('jinja2', True),
|
||||
# jinja2: removed in 3.3
|
||||
('pygments', True),
|
||||
('yaml', True),
|
||||
('adblock', True),
|
||||
|
|
|
|||
Loading…
Reference in New Issue