Skip to content

Commit 18c66de

Browse files
committed
fix: pinned version are not working once periodic / manual updates kick-in
Fixes pypa#2203
1 parent 1594bae commit 18c66de

File tree

4 files changed

+96
-15
lines changed

4 files changed

+96
-15
lines changed

src/virtualenv/seed/wheels/bundle.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ def from_bundle(distribution, version, for_py_version, search_dirs, app_data, do
1515
if version != Version.embed:
1616
# 2. check if we have upgraded embed
1717
if app_data.can_update:
18-
wheel = periodic_update(distribution, for_py_version, wheel, search_dirs, app_data, do_periodic_update, env)
18+
wheel = periodic_update(
19+
distribution, of_version, for_py_version, wheel, search_dirs, app_data, do_periodic_update, env
20+
)
1921

2022
# 3. acquire from extra search dir
2123
found_wheel = from_dir(distribution, of_version, for_py_version, search_dirs)

src/virtualenv/seed/wheels/periodic_update.py

+18-10
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,31 @@
3636
pass # pragma: no cov
3737

3838

39-
def periodic_update(distribution, for_py_version, wheel, search_dirs, app_data, do_periodic_update, env):
39+
def periodic_update(distribution, of_version, for_py_version, wheel, search_dirs, app_data, do_periodic_update, env):
4040
if do_periodic_update:
4141
handle_auto_update(distribution, for_py_version, wheel, search_dirs, app_data, env)
4242

4343
now = datetime.now()
4444

4545
u_log = UpdateLog.from_app_data(app_data, distribution, for_py_version)
4646
u_log_older_than_hour = now - u_log.completed > timedelta(hours=1) if u_log.completed is not None else False
47-
for _, group in groupby(u_log.versions, key=lambda v: v.wheel.version_tuple[0:2]):
48-
version = next(group) # use only latest patch version per minor, earlier assumed to be buggy
49-
if wheel is not None and Path(version.filename).name == wheel.name:
50-
break
51-
if u_log.periodic is False or (u_log_older_than_hour and version.use(now)):
52-
updated_wheel = Wheel(app_data.house / version.filename)
53-
logging.debug("using %supdated wheel %s", "periodically " if updated_wheel else "", updated_wheel)
54-
wheel = updated_wheel
55-
break
47+
if of_version is None:
48+
for _, group in groupby(u_log.versions, key=lambda v: v.wheel.version_tuple[0:2]):
49+
version = next(group) # use only latest patch version per minor, earlier assumed to be buggy
50+
if wheel is not None and Path(version.filename).name == wheel.name:
51+
break
52+
if u_log.periodic is False or (u_log_older_than_hour and version.use(now)):
53+
updated_wheel = Wheel(app_data.house / version.filename)
54+
logging.debug("using %supdated wheel %s", "periodically " if updated_wheel else "", updated_wheel)
55+
wheel = updated_wheel
56+
break
57+
elif u_log.periodic is False or u_log_older_than_hour:
58+
for version in u_log.versions:
59+
if version.wheel.version == of_version:
60+
updated_wheel = Wheel(app_data.house / version.filename)
61+
logging.debug("using %supdated wheel %s", "periodically " if updated_wheel else "", updated_wheel)
62+
wheel = updated_wheel
63+
break
5664

5765
return wheel
5866

tests/unit/seed/wheels/test_bundle.py

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from __future__ import absolute_import, unicode_literals
2+
3+
import os
4+
5+
import pytest
6+
7+
from virtualenv.app_data import AppDataDiskFolder
8+
from virtualenv.seed.wheels.bundle import from_bundle
9+
from virtualenv.seed.wheels.embed import get_embed_wheel
10+
from virtualenv.seed.wheels.util import Version, Wheel
11+
from virtualenv.util.path import Path
12+
13+
14+
@pytest.fixture(scope="module")
15+
def next_pip_wheel(for_py_version):
16+
wheel = get_embed_wheel("pip", for_py_version)
17+
new_version = list(wheel.version_tuple)
18+
new_version[-1] += 1
19+
new_name = wheel.name.replace(wheel.version, ".".join(str(i) for i in new_version))
20+
return Wheel.from_path(Path(new_name))
21+
22+
23+
@pytest.fixture(scope="module")
24+
def app_data(tmp_path_factory, for_py_version, next_pip_wheel):
25+
temp_folder = tmp_path_factory.mktemp("module-app-data")
26+
app_data_ = AppDataDiskFolder(str(temp_folder))
27+
app_data_.embed_update_log("pip", for_py_version).write(
28+
{
29+
"completed": "2000-01-01T00:00:00.000000Z",
30+
"periodic": True,
31+
"started": "2000-01-01T00:00:00.000000Z",
32+
"versions": [
33+
{
34+
"filename": next_pip_wheel.name,
35+
"found_date": "2000-01-01T00:00:00.000000Z",
36+
"release_date": "2000-01-01T00:00:00.000000Z",
37+
}
38+
],
39+
}
40+
)
41+
yield app_data_
42+
43+
44+
def test_version_embed(app_data, for_py_version):
45+
wheel = from_bundle("pip", Version.embed, for_py_version, [], app_data, False, os.environ)
46+
assert wheel is not None
47+
assert wheel.name == get_embed_wheel("pip", for_py_version).name
48+
49+
50+
def test_version_bundle(app_data, for_py_version, next_pip_wheel):
51+
wheel = from_bundle("pip", Version.bundle, for_py_version, [], app_data, False, os.environ)
52+
assert wheel is not None
53+
assert wheel.name == next_pip_wheel.name
54+
55+
56+
def test_version_pinned_not_found(app_data, for_py_version):
57+
wheel = from_bundle("pip", "0.0.0", for_py_version, [], app_data, False, os.environ)
58+
assert wheel is None
59+
60+
61+
def test_version_pinned_is_embed(app_data, for_py_version):
62+
expected_wheel = get_embed_wheel("pip", for_py_version)
63+
wheel = from_bundle("pip", expected_wheel.version, for_py_version, [], app_data, False, os.environ)
64+
assert wheel is not None
65+
assert wheel.name == expected_wheel.name
66+
67+
68+
def test_version_pinned_in_app_data(app_data, for_py_version, next_pip_wheel):
69+
wheel = from_bundle("pip", next_pip_wheel.version, for_py_version, [], app_data, False, os.environ)
70+
assert wheel is not None
71+
assert wheel.name == next_pip_wheel.name

tests/unit/seed/wheels/test_periodic_update.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def test_periodic_update_stops_at_current(mocker, session_app_data, for_py_versi
101101
)
102102
mocker.patch("virtualenv.app_data.via_disk_folder.JSONStoreDisk.read", return_value=u_log.to_dict())
103103

104-
result = periodic_update("setuptools", for_py_version, current, [], session_app_data, False, os.environ)
104+
result = periodic_update("setuptools", None, for_py_version, current, [], session_app_data, False, os.environ)
105105
assert result.path == current.path
106106

107107

@@ -120,7 +120,7 @@ def test_periodic_update_latest_per_patch(mocker, session_app_data, for_py_versi
120120
)
121121
mocker.patch("virtualenv.app_data.via_disk_folder.JSONStoreDisk.read", return_value=u_log.to_dict())
122122

123-
result = periodic_update("setuptools", for_py_version, current, [], session_app_data, False, os.environ)
123+
result = periodic_update("setuptools", None, for_py_version, current, [], session_app_data, False, os.environ)
124124
assert result.path == current.path
125125

126126

@@ -166,7 +166,7 @@ def test_periodic_update_skip(u_log, mocker, for_py_version, session_app_data, f
166166
mocker.patch("virtualenv.app_data.via_disk_folder.JSONStoreDisk.read", return_value=u_log.to_dict())
167167
mocker.patch("virtualenv.seed.wheels.periodic_update.trigger_update", side_effect=RuntimeError)
168168

169-
result = periodic_update("setuptools", for_py_version, None, [], session_app_data, os.environ, True)
169+
result = periodic_update("setuptools", None, for_py_version, None, [], session_app_data, os.environ, True)
170170
assert result is None
171171

172172

@@ -194,7 +194,7 @@ def test_periodic_update_trigger(u_log, mocker, for_py_version, session_app_data
194194
write = mocker.patch("virtualenv.app_data.via_disk_folder.JSONStoreDisk.write")
195195
trigger_update_ = mocker.patch("virtualenv.seed.wheels.periodic_update.trigger_update")
196196

197-
result = periodic_update("setuptools", for_py_version, None, [], session_app_data, os.environ, True)
197+
result = periodic_update("setuptools", None, for_py_version, None, [], session_app_data, os.environ, True)
198198

199199
assert result is None
200200
assert trigger_update_.call_count

0 commit comments

Comments
 (0)