summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormayeut <mayeut@users.noreply.github.com>2021-12-28 23:42:46 +0100
committermayeut <mayeut@users.noreply.github.com>2021-12-29 10:46:55 +0100
commitdbb6347e23ab30196d05fc3f0102a704b80d7a49 (patch)
tree389aff67a2181c19e2da0a00b2a8984b5d92c26f
parent7307ea1cf37bad6e4508925ae14587fde7a6c6de (diff)
downloadvirtualenv-dbb6347e23ab30196d05fc3f0102a704b80d7a49.tar.gz
fix: pinned version are not working once periodic / manual updates kick-in
Fixes pypa#2203
-rw-r--r--docs/changelog/2203.bugfix.rst1
-rw-r--r--src/virtualenv/seed/wheels/bundle.py4
-rw-r--r--src/virtualenv/seed/wheels/periodic_update.py28
-rw-r--r--tests/unit/seed/wheels/test_bundle.py71
-rw-r--r--tests/unit/seed/wheels/test_periodic_update.py8
5 files changed, 97 insertions, 15 deletions
diff --git a/docs/changelog/2203.bugfix.rst b/docs/changelog/2203.bugfix.rst
new file mode 100644
index 0000000..f27c59f
--- /dev/null
+++ b/docs/changelog/2203.bugfix.rst
@@ -0,0 +1 @@
+Fix installation of pinned versions of ``pip``, ``setuptools`` & ``wheel`` - by :user:`mayeut`.
diff --git a/src/virtualenv/seed/wheels/bundle.py b/src/virtualenv/seed/wheels/bundle.py
index ab2fe5f..39cd3d3 100644
--- a/src/virtualenv/seed/wheels/bundle.py
+++ b/src/virtualenv/seed/wheels/bundle.py
@@ -15,7 +15,9 @@ def from_bundle(distribution, version, for_py_version, search_dirs, app_data, do
if version != Version.embed:
# 2. check if we have upgraded embed
if app_data.can_update:
- wheel = periodic_update(distribution, for_py_version, wheel, search_dirs, app_data, do_periodic_update, env)
+ wheel = periodic_update(
+ distribution, of_version, for_py_version, wheel, search_dirs, app_data, do_periodic_update, env
+ )
# 3. acquire from extra search dir
found_wheel = from_dir(distribution, of_version, for_py_version, search_dirs)
diff --git a/src/virtualenv/seed/wheels/periodic_update.py b/src/virtualenv/seed/wheels/periodic_update.py
index 45584f9..33a85a7 100644
--- a/src/virtualenv/seed/wheels/periodic_update.py
+++ b/src/virtualenv/seed/wheels/periodic_update.py
@@ -36,7 +36,7 @@ if PY2:
pass # pragma: no cov
-def periodic_update(distribution, for_py_version, wheel, search_dirs, app_data, do_periodic_update, env):
+def periodic_update(distribution, of_version, for_py_version, wheel, search_dirs, app_data, do_periodic_update, env):
if do_periodic_update:
handle_auto_update(distribution, for_py_version, wheel, search_dirs, app_data, env)
@@ -44,15 +44,23 @@ def periodic_update(distribution, for_py_version, wheel, search_dirs, app_data,
u_log = UpdateLog.from_app_data(app_data, distribution, for_py_version)
u_log_older_than_hour = now - u_log.completed > timedelta(hours=1) if u_log.completed is not None else False
- for _, group in groupby(u_log.versions, key=lambda v: v.wheel.version_tuple[0:2]):
- version = next(group) # use only latest patch version per minor, earlier assumed to be buggy
- if wheel is not None and Path(version.filename).name == wheel.name:
- break
- if u_log.periodic is False or (u_log_older_than_hour and version.use(now)):
- updated_wheel = Wheel(app_data.house / version.filename)
- logging.debug("using %supdated wheel %s", "periodically " if updated_wheel else "", updated_wheel)
- wheel = updated_wheel
- break
+ if of_version is None:
+ for _, group in groupby(u_log.versions, key=lambda v: v.wheel.version_tuple[0:2]):
+ version = next(group) # use only latest patch version per minor, earlier assumed to be buggy
+ if wheel is not None and Path(version.filename).name == wheel.name:
+ break
+ if u_log.periodic is False or (u_log_older_than_hour and version.use(now)):
+ updated_wheel = Wheel(app_data.house / version.filename)
+ logging.debug("using %supdated wheel %s", "periodically " if updated_wheel else "", updated_wheel)
+ wheel = updated_wheel
+ break
+ elif u_log.periodic is False or u_log_older_than_hour:
+ for version in u_log.versions:
+ if version.wheel.version == of_version:
+ updated_wheel = Wheel(app_data.house / version.filename)
+ logging.debug("using %supdated wheel %s", "periodically " if updated_wheel else "", updated_wheel)
+ wheel = updated_wheel
+ break
return wheel
diff --git a/tests/unit/seed/wheels/test_bundle.py b/tests/unit/seed/wheels/test_bundle.py
new file mode 100644
index 0000000..16ea2fd
--- /dev/null
+++ b/tests/unit/seed/wheels/test_bundle.py
@@ -0,0 +1,71 @@
+from __future__ import absolute_import, unicode_literals
+
+import os
+
+import pytest
+
+from virtualenv.app_data import AppDataDiskFolder
+from virtualenv.seed.wheels.bundle import from_bundle
+from virtualenv.seed.wheels.embed import get_embed_wheel
+from virtualenv.seed.wheels.util import Version, Wheel
+from virtualenv.util.path import Path
+
+
+@pytest.fixture(scope="module")
+def next_pip_wheel(for_py_version):
+ wheel = get_embed_wheel("pip", for_py_version)
+ new_version = list(wheel.version_tuple)
+ new_version[-1] += 1
+ new_name = wheel.name.replace(wheel.version, ".".join(str(i) for i in new_version))
+ return Wheel.from_path(Path(new_name))
+
+
+@pytest.fixture(scope="module")
+def app_data(tmp_path_factory, for_py_version, next_pip_wheel):
+ temp_folder = tmp_path_factory.mktemp("module-app-data")
+ app_data_ = AppDataDiskFolder(str(temp_folder))
+ app_data_.embed_update_log("pip", for_py_version).write(
+ {
+ "completed": "2000-01-01T00:00:00.000000Z",
+ "periodic": True,
+ "started": "2000-01-01T00:00:00.000000Z",
+ "versions": [
+ {
+ "filename": next_pip_wheel.name,
+ "found_date": "2000-01-01T00:00:00.000000Z",
+ "release_date": "2000-01-01T00:00:00.000000Z",
+ }
+ ],
+ }
+ )
+ yield app_data_
+
+
+def test_version_embed(app_data, for_py_version):
+ wheel = from_bundle("pip", Version.embed, for_py_version, [], app_data, False, os.environ)
+ assert wheel is not None
+ assert wheel.name == get_embed_wheel("pip", for_py_version).name
+
+
+def test_version_bundle(app_data, for_py_version, next_pip_wheel):
+ wheel = from_bundle("pip", Version.bundle, for_py_version, [], app_data, False, os.environ)
+ assert wheel is not None
+ assert wheel.name == next_pip_wheel.name
+
+
+def test_version_pinned_not_found(app_data, for_py_version):
+ wheel = from_bundle("pip", "0.0.0", for_py_version, [], app_data, False, os.environ)
+ assert wheel is None
+
+
+def test_version_pinned_is_embed(app_data, for_py_version):
+ expected_wheel = get_embed_wheel("pip", for_py_version)
+ wheel = from_bundle("pip", expected_wheel.version, for_py_version, [], app_data, False, os.environ)
+ assert wheel is not None
+ assert wheel.name == expected_wheel.name
+
+
+def test_version_pinned_in_app_data(app_data, for_py_version, next_pip_wheel):
+ wheel = from_bundle("pip", next_pip_wheel.version, for_py_version, [], app_data, False, os.environ)
+ assert wheel is not None
+ assert wheel.name == next_pip_wheel.name
diff --git a/tests/unit/seed/wheels/test_periodic_update.py b/tests/unit/seed/wheels/test_periodic_update.py
index f308aee..9b32770 100644
--- a/tests/unit/seed/wheels/test_periodic_update.py
+++ b/tests/unit/seed/wheels/test_periodic_update.py
@@ -101,7 +101,7 @@ def test_periodic_update_stops_at_current(mocker, session_app_data, for_py_versi
)
mocker.patch("virtualenv.app_data.via_disk_folder.JSONStoreDisk.read", return_value=u_log.to_dict())
- result = periodic_update("setuptools", for_py_version, current, [], session_app_data, False, os.environ)
+ result = periodic_update("setuptools", None, for_py_version, current, [], session_app_data, False, os.environ)
assert result.path == current.path
@@ -120,7 +120,7 @@ def test_periodic_update_latest_per_patch(mocker, session_app_data, for_py_versi
)
mocker.patch("virtualenv.app_data.via_disk_folder.JSONStoreDisk.read", return_value=u_log.to_dict())
- result = periodic_update("setuptools", for_py_version, current, [], session_app_data, False, os.environ)
+ result = periodic_update("setuptools", None, for_py_version, current, [], session_app_data, False, os.environ)
assert result.path == current.path
@@ -166,7 +166,7 @@ def test_periodic_update_skip(u_log, mocker, for_py_version, session_app_data, f
mocker.patch("virtualenv.app_data.via_disk_folder.JSONStoreDisk.read", return_value=u_log.to_dict())
mocker.patch("virtualenv.seed.wheels.periodic_update.trigger_update", side_effect=RuntimeError)
- result = periodic_update("setuptools", for_py_version, None, [], session_app_data, os.environ, True)
+ result = periodic_update("setuptools", None, for_py_version, None, [], session_app_data, os.environ, True)
assert result is None
@@ -194,7 +194,7 @@ def test_periodic_update_trigger(u_log, mocker, for_py_version, session_app_data
write = mocker.patch("virtualenv.app_data.via_disk_folder.JSONStoreDisk.write")
trigger_update_ = mocker.patch("virtualenv.seed.wheels.periodic_update.trigger_update")
- result = periodic_update("setuptools", for_py_version, None, [], session_app_data, os.environ, True)
+ result = periodic_update("setuptools", None, for_py_version, None, [], session_app_data, os.environ, True)
assert result is None
assert trigger_update_.call_count