Skip to content

Commit 346f12b

Browse files
committed
Allow installing runtimes from a script shebang.
Fixes #235
1 parent e4a4451 commit 346f12b

File tree

3 files changed

+124
-8
lines changed

3 files changed

+124
-8
lines changed

src/manage/install_command.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@
2121
)
2222

2323

24-
# In-process cache to save repeat downloads
25-
DOWNLOAD_CACHE = {}
26-
27-
2824
def _multihash(file, hashes):
2925
import hashlib
3026
LOGGER.debug("Calculating hashes: %s", ", ".join(hashes))
@@ -379,7 +375,8 @@ def _find_one(cmd, source, tag, *, installed=None, by_id=False):
379375
else:
380376
LOGGER.verbose("Searching for default Python version")
381377

382-
downloader = IndexDownloader(source, Index, {}, DOWNLOAD_CACHE)
378+
download_cache = cmd.scratch.setdefault("install_command.download_cache", {})
379+
downloader = IndexDownloader(source, Index, {}, download_cache)
383380
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id)
384381

385382
if by_id:
@@ -423,8 +420,9 @@ def _download_one(cmd, source, install, download_dir, *, must_copy=False):
423420
if install["url"].casefold().endswith(".nupkg".casefold()):
424421
package = package.with_suffix(".nupkg")
425422

423+
download_cache = cmd.scratch.setdefault("install_command.download_cache", {})
426424
with ProgressPrinter("Downloading", maxwidth=CONSOLE_MAX_WIDTH) as on_progress:
427-
package = download_package(cmd, install, package, DOWNLOAD_CACHE, on_progress=on_progress)
425+
package = download_package(cmd, install, package, download_cache, on_progress=on_progress)
428426
validate_package(install, package)
429427
if must_copy and package.parent != download_dir:
430428
import shutil
@@ -757,7 +755,11 @@ def execute(cmd):
757755
# Have already checked that we are not using --by-id
758756
from .scriptutils import find_install_from_script
759757
try:
760-
spec = find_install_from_script(cmd, cmd.from_script)
758+
spec = find_install_from_script(cmd, cmd.from_script)["tag"]
759+
except NoInstallFoundError as ex:
760+
# Usually expect this exception, since we should be installing
761+
# a runtime that wasn't found.
762+
spec = ex.tag
761763
except LookupError:
762764
spec = None
763765
if spec:

tests/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ def __call__(self, *cmp):
107107
else:
108108
assert re.match(pat, x[0], flags=re.S)
109109
if args is not None:
110-
assert tuple(x[1]) == tuple(args)
110+
values = tuple(type(v2)(v1) for v1, v2 in zip(x[1], args))
111+
assert values == tuple(args)
111112
break
112113

113114

tests/test_install_command.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,116 @@ def cleanup_aliases(cmd, preserve):
214214
assert sorted(a.name for a in created) == ["python3.exe", "pythonw3.exe"]
215215
# Ensure we still only have the two targets
216216
assert set(a.target for a in created) == {"p.exe", "pw.exe"}
217+
218+
219+
class InstallCommandTestCmd:
220+
def __init__(self, tmp_path, *args, **kwargs):
221+
self.args = args
222+
self.tags = None
223+
self.download_cache = {}
224+
self.scratch = {
225+
"install_command.download_cache": self.download_cache,
226+
}
227+
self.automatic = kwargs.get("automatic", False)
228+
self.by_id = kwargs.get("by_id", False)
229+
self.default_install_tag = kwargs.get("default_install_tag", "1")
230+
self.default_platform = kwargs.get("default_platform", "-32")
231+
self.default_tag = kwargs.get("default_tag", "1")
232+
self.download = kwargs.get("download")
233+
if self.download:
234+
self.download = tmp_path / self.download
235+
self.dry_run = kwargs.get("dry_run", True)
236+
self.fallback_source = kwargs.get("fallback_source")
237+
self.force = kwargs.get("force", True)
238+
self.from_script = kwargs.get("from_script")
239+
self.log_file = kwargs.get("log_file")
240+
self.refresh = kwargs.get("refresh", False)
241+
self.repair = kwargs.get("repair", False)
242+
self.shebang_can_run_anything = kwargs.get("shebang_can_run_anything", False)
243+
self.shebang_can_run_anything_silently = kwargs.get("shebang_can_run_anything_silently", False)
244+
self.source = kwargs.get("source", "http://example.com/index.json")
245+
self.target = kwargs.get("target")
246+
if self.target:
247+
self.target = tmp_path / self.target
248+
self.update = kwargs.get("update", False)
249+
self.virtual_env = kwargs.get("virtual_env")
250+
251+
self.index_installs = [
252+
{
253+
"schema": 1,
254+
"id": "test-1.1-32",
255+
"sort-version": "1.1",
256+
"company": "Test",
257+
"tag": "1.1-32",
258+
"install-for": ["1", "1.1", "1.1-32"],
259+
"display-name": "Test 1.1 (32)",
260+
"executable": "test.exe",
261+
"url": "about:blank",
262+
},
263+
{
264+
"schema": 1,
265+
"id": "test-1.0-32",
266+
"sort-version": "1.0",
267+
"company": "Test",
268+
"tag": "1.0-32",
269+
"install-for": ["1", "1.0", "1.0-32"],
270+
"display-name": "Test 1.0 (32)",
271+
"executable": "test.exe",
272+
"url": "about:blank",
273+
},
274+
]
275+
self.download_cache["http://example.com/index.json"] = json.dumps({
276+
"versions": self.index_installs,
277+
})
278+
self.installs = [{
279+
**self.index_installs[-1],
280+
"source": self.source,
281+
"prefix": tmp_path / "test-1.0-32",
282+
}]
283+
284+
def get_log_file(self):
285+
return self.log_file
286+
287+
def get_installs(self):
288+
return self.installs
289+
290+
def get_install_to_run(self, tag):
291+
for i in self.installs:
292+
if i["tag"] == tag or f"{i['company']}/{i['tag']}" == tag:
293+
return i
294+
raise LookupError
295+
296+
297+
def test_install_simple(tmp_path, assert_log):
298+
cmd = InstallCommandTestCmd(tmp_path, "1.1", force=False)
299+
300+
IC.execute(cmd)
301+
assert_log(
302+
assert_log.skip_until("Searching for Python matching %s", ["1.1"]),
303+
assert_log.skip_until("Installing %s", ["Test 1.1 (32)"]),
304+
("Tag: %s\\\\%s", ["Test", "1.1-32"]),
305+
)
306+
307+
308+
def test_install_already_installed(tmp_path, assert_log):
309+
cmd = InstallCommandTestCmd(tmp_path, "1.0", force=False)
310+
311+
IC.execute(cmd)
312+
assert_log(
313+
assert_log.skip_until("Searching for Python matching %s", ["1.0"]),
314+
assert_log.skip_until("%s is already installed", ["Test 1.0 (32)"]),
315+
)
316+
317+
318+
def test_install_from_script(tmp_path, assert_log):
319+
cmd = InstallCommandTestCmd(tmp_path, from_script=tmp_path / "t.py")
320+
321+
cmd.from_script.parent.mkdir(parents=True, exist_ok=True)
322+
cmd.from_script.write_text("#! python1.1.exe")
323+
324+
IC.execute(cmd)
325+
assert_log(
326+
assert_log.skip_until("Searching for Python matching"),
327+
assert_log.skip_until("Installing %s", ["Test 1.1 (32)"]),
328+
("Tag: %s\\\\%s", ["Test", "1.1-32"]),
329+
)

0 commit comments

Comments
 (0)