Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions src/manage/install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ def _expand_versions_by_tag(versions):
yield {**v, "tag": t}


def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False):
def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False, allow_pre=True):
"""Finds suitable package from index.json that looks like:
{"versions": [
{"id": ..., "company": ..., "tag": ..., "url": ..., "hash": {"sha256": hexdigest}},
Expand All@@ -63,19 +63,31 @@ def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by
try:
if by_id:
for v in index.versions:
if not allow_pre and v["sort-version"].is_prerelease:
continue
if v["id"].casefold() == tag.casefold():
return v
raise LookupError("Could not find a runtime matching '{}' at '{}'".format(
tag, sanitise_url(index.source_url)
))
if platform:
try:
return index.find_to_install(tag + platform)
v = index.find_to_install(tag + platform)
except LookupError:
pass
return index.find_to_install(tag)
else:
if allow_pre or not v["sort-version"].is_prerelease:
return v
v = index.find_to_install(tag)
if v["sort-version"].is_prerelease and not allow_pre:
raise LookupError("Found '{}' matching '{}' but excluded "
"because we are now allowing prereleases.".format(
v["display-name"], tag))
return v
except LookupError as ex:
first_exc = ex
LOGGER.debug("Potential error: %s", ex)
if not first_exc:
first_exc = ex

if first_exc:
raise first_exc
Expand DownExpand Up@@ -367,7 +379,7 @@ def _same_install(i, j):
return i["id"] == j["id"] and i["sort-version"] == j["sort-version"]


def _find_one(cmd, source, tag, *, installed=None, by_id=False):
def _find_one(cmd, source, tag, *, installed=None, by_id=False, allow_pre=True):
if by_id:
LOGGER.debug("Searching for Python with ID %s", tag)
elif tag:
Expand All@@ -377,7 +389,7 @@ def _find_one(cmd, source, tag, *, installed=None, by_id=False):

download_cache = cmd.scratch.setdefault("install_command.download_cache", {})
downloader = IndexDownloader(cmd, source, Index, {}, download_cache)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id, allow_pre=allow_pre)

# Ensure the requested source URL is in the install
if install and source:
Expand DownExpand Up@@ -829,14 +841,16 @@ def execute(cmd):
# Fallthrough is safe - cmd.tags is empty
elif cmd.update:
LOGGER.verbose("No tags provided, updating all installs:")
from .verutils import Version
for install in installed:
first_exc = None
update = None
allow_pre = Version(install['sort-version']).is_prerelease
for source in [install.get('source'), cmd.source, cmd.fallback_source]:
if not source:
continue
try:
update = _find_one(cmd, source, install['id'], by_id=True)
update = _find_one(cmd, source, install['id'], by_id=True, allow_pre=allow_pre)
if update:
break
except LookupError:
Expand Down
110 changes: 85 additions & 25 deletions tests/test_install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@
from manage import install_command as IC
from manage import installs
from manage.exceptions import NoInstallFoundError
from manage.logging import LOGGER


def test_print_cli_shortcuts(patched_installs, assert_log, monkeypatch, tmp_path):
Expand DownExpand Up@@ -225,50 +226,51 @@ def __init__(self, tmp_path, *args, **kwargs):
self.scratch = {
"install_command.download_cache": self.download_cache,
}
self.automatic = kwargs.get("automatic", False)
self.by_id = kwargs.get("by_id", False)
self.default_install_tag = kwargs.get("default_install_tag", "1")
self.default_platform = kwargs.get("default_platform", "-32")
self.default_tag = kwargs.get("default_tag", "1")
self.download = kwargs.get("download")
self.automatic = kwargs.pop("automatic", False)
self.by_id = kwargs.pop("by_id", False)
self.default_install_tag = kwargs.pop("default_install_tag", "1")
self.default_platform = kwargs.pop("default_platform", "-32")
self.default_tag = kwargs.pop("default_tag", "1")
self.download = kwargs.pop("download", None)
if self.download:
self.download = tmp_path / self.download
self.download_dir = tmp_path / kwargs.get("download_dir", "_cache")
self.dry_run = kwargs.get("dry_run", True)
self.fallback_source = kwargs.get("fallback_source")
self.force = kwargs.get("force", True)
self.from_script = kwargs.get("from_script")
self.log_file = kwargs.get("log_file")
self.refresh = kwargs.get("refresh", False)
self.repair = kwargs.get("repair", False)
self.shebang_can_run_anything = kwargs.get("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.get("shebang_can_run_anything_silently", False)
self.source = kwargs.get("source", "http://example.com/index.json")
self.target = kwargs.get("target")
self.download_dir = tmp_path / kwargs.pop("download_dir", "_cache")
self.dry_run = kwargs.pop("dry_run", True)
self.fallback_source = kwargs.pop("fallback_source", None)
self.force = kwargs.pop("force", True)
self.from_script = kwargs.pop("from_script", None)
self.log_file = kwargs.pop("log_file", None)
self.refresh = kwargs.pop("refresh", False)
self.repair = kwargs.pop("repair", False)
self.shebang_can_run_anything = kwargs.pop("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.pop("shebang_can_run_anything_silently", False)
self.source = kwargs.pop("source", "http://example.com/index.json")
self.target = kwargs.pop("target", None)
if self.target:
self.target = tmp_path / self.target
self.update = kwargs.get("update", False)
self.virtual_env = kwargs.get("virtual_env")
self.update = kwargs.pop("update", False)
self.virtual_env = kwargs.pop("virtual_env", None)

self.index_installs = [
*kwargs.pop("index_installs", ()),
{
"schema": 1,
"id": "test-1.1-32",
"id": "test-32",
"sort-version": "1.1",
"company": "Test",
"tag": "1.1-32",
"install-for": ["1", "1.1", "1.1-32"],
"install-for": ["1-32", "1.1-32", "1.1-32"],
"display-name": "Test 1.1 (32)",
"executable": "test.exe",
"url": "about:blank",
},
{
"schema": 1,
"id": "test-1.0-32",
"id": "test-32",
"sort-version": "1.0",
"company": "Test",
"tag": "1.0-32",
"install-for": ["1", "1.0", "1.0-32"],
"install-for": ["1-32", "1.0-32", "1.0-32"],
"display-name": "Test 1.0 (32)",
"executable": "test.exe",
"url": "about:blank",
Expand All@@ -280,8 +282,13 @@ def __init__(self, tmp_path, *args, **kwargs):
self.installs = [{
**self.index_installs[-1],
"source": self.source,
"prefix": tmp_path / "test-1.0-32",
"prefix": tmp_path / "test-32",
}]
assert not kwargs

def ask_yn(self, prompt, *args):
LOGGER.info(prompt, *args)
return True

def get_log_file(self):
return self.log_file
Expand All@@ -302,6 +309,7 @@ def test_install_simple(tmp_path, assert_log):
IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1.1"]),
assert_log.skip_until(".*Your existing %s install.*", ["Test 1.0 (32)", "Test 1.1 (32)"]),
assert_log.skip_until("Installing %s", ["Test 1.1 (32)"]),
("Tag: %s\\\\%s", ["Test", "1.1-32"]),
)
Expand All@@ -317,6 +325,58 @@ def test_install_already_installed(tmp_path, assert_log):
)



def test_install_update(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python with ID %s", ["test-32"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit_pre(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1rc", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1rc"]),
assert_log.skip_until("Updating to %s", ["Test 1.2rc1 (32)"]),
)


def test_install_from_script(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, from_script=tmp_path / "t.py")

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Restrict prerelease versions when updating by id. by zooba · Pull Request #352 · python/pymanager · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions src/manage/install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ def _expand_versions_by_tag(versions):
yield {**v, "tag": t}


def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False):
def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False, allow_pre=True):
"""Finds suitable package from index.json that looks like:
{"versions": [
{"id": ..., "company": ..., "tag": ..., "url": ..., "hash": {"sha256": hexdigest}},
Expand All@@ -63,19 +63,31 @@ def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by
try:
if by_id:
for v in index.versions:
if not allow_pre and v["sort-version"].is_prerelease:
continue
if v["id"].casefold() == tag.casefold():
return v
raise LookupError("Could not find a runtime matching '{}' at '{}'".format(
tag, sanitise_url(index.source_url)
))
if platform:
try:
return index.find_to_install(tag + platform)
v = index.find_to_install(tag + platform)
except LookupError:
pass
return index.find_to_install(tag)
else:
if allow_pre or not v["sort-version"].is_prerelease:
return v
v = index.find_to_install(tag)
if v["sort-version"].is_prerelease and not allow_pre:
raise LookupError("Found '{}' matching '{}' but excluded "
"because we are now allowing prereleases.".format(
v["display-name"], tag))
return v
except LookupError as ex:
first_exc = ex
LOGGER.debug("Potential error: %s", ex)
if not first_exc:
first_exc = ex

if first_exc:
raise first_exc
Expand DownExpand Up@@ -367,7 +379,7 @@ def _same_install(i, j):
return i["id"] == j["id"] and i["sort-version"] == j["sort-version"]


def _find_one(cmd, source, tag, *, installed=None, by_id=False):
def _find_one(cmd, source, tag, *, installed=None, by_id=False, allow_pre=True):
if by_id:
LOGGER.debug("Searching for Python with ID %s", tag)
elif tag:
Expand All@@ -377,7 +389,7 @@ def _find_one(cmd, source, tag, *, installed=None, by_id=False):

download_cache = cmd.scratch.setdefault("install_command.download_cache", {})
downloader = IndexDownloader(cmd, source, Index, {}, download_cache)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id, allow_pre=allow_pre)

# Ensure the requested source URL is in the install
if install and source:
Expand DownExpand Up@@ -829,14 +841,16 @@ def execute(cmd):
# Fallthrough is safe - cmd.tags is empty
elif cmd.update:
LOGGER.verbose("No tags provided, updating all installs:")
from .verutils import Version
for install in installed:
first_exc = None
update = None
allow_pre = Version(install['sort-version']).is_prerelease
for source in [install.get('source'), cmd.source, cmd.fallback_source]:
if not source:
continue
try:
update = _find_one(cmd, source, install['id'], by_id=True)
update = _find_one(cmd, source, install['id'], by_id=True, allow_pre=allow_pre)
if update:
break
except LookupError:
Expand Down
110 changes: 85 additions & 25 deletions tests/test_install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@
from manage import install_command as IC
from manage import installs
from manage.exceptions import NoInstallFoundError
from manage.logging import LOGGER


def test_print_cli_shortcuts(patched_installs, assert_log, monkeypatch, tmp_path):
Expand DownExpand Up@@ -225,50 +226,51 @@ def __init__(self, tmp_path, *args, **kwargs):
self.scratch = {
"install_command.download_cache": self.download_cache,
}
self.automatic = kwargs.get("automatic", False)
self.by_id = kwargs.get("by_id", False)
self.default_install_tag = kwargs.get("default_install_tag", "1")
self.default_platform = kwargs.get("default_platform", "-32")
self.default_tag = kwargs.get("default_tag", "1")
self.download = kwargs.get("download")
self.automatic = kwargs.pop("automatic", False)
self.by_id = kwargs.pop("by_id", False)
self.default_install_tag = kwargs.pop("default_install_tag", "1")
self.default_platform = kwargs.pop("default_platform", "-32")
self.default_tag = kwargs.pop("default_tag", "1")
self.download = kwargs.pop("download", None)
if self.download:
self.download = tmp_path / self.download
self.download_dir = tmp_path / kwargs.get("download_dir", "_cache")
self.dry_run = kwargs.get("dry_run", True)
self.fallback_source = kwargs.get("fallback_source")
self.force = kwargs.get("force", True)
self.from_script = kwargs.get("from_script")
self.log_file = kwargs.get("log_file")
self.refresh = kwargs.get("refresh", False)
self.repair = kwargs.get("repair", False)
self.shebang_can_run_anything = kwargs.get("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.get("shebang_can_run_anything_silently", False)
self.source = kwargs.get("source", "http://example.com/index.json")
self.target = kwargs.get("target")
self.download_dir = tmp_path / kwargs.pop("download_dir", "_cache")
self.dry_run = kwargs.pop("dry_run", True)
self.fallback_source = kwargs.pop("fallback_source", None)
self.force = kwargs.pop("force", True)
self.from_script = kwargs.pop("from_script", None)
self.log_file = kwargs.pop("log_file", None)
self.refresh = kwargs.pop("refresh", False)
self.repair = kwargs.pop("repair", False)
self.shebang_can_run_anything = kwargs.pop("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.pop("shebang_can_run_anything_silently", False)
self.source = kwargs.pop("source", "http://example.com/index.json")
self.target = kwargs.pop("target", None)
if self.target:
self.target = tmp_path / self.target
self.update = kwargs.get("update", False)
self.virtual_env = kwargs.get("virtual_env")
self.update = kwargs.pop("update", False)
self.virtual_env = kwargs.pop("virtual_env", None)

self.index_installs = [
*kwargs.pop("index_installs", ()),
{
"schema": 1,
"id": "test-1.1-32",
"id": "test-32",
"sort-version": "1.1",
"company": "Test",
"tag": "1.1-32",
"install-for": ["1", "1.1", "1.1-32"],
"install-for": ["1-32", "1.1-32", "1.1-32"],
"display-name": "Test 1.1 (32)",
"executable": "test.exe",
"url": "about:blank",
},
{
"schema": 1,
"id": "test-1.0-32",
"id": "test-32",
"sort-version": "1.0",
"company": "Test",
"tag": "1.0-32",
"install-for": ["1", "1.0", "1.0-32"],
"install-for": ["1-32", "1.0-32", "1.0-32"],
"display-name": "Test 1.0 (32)",
"executable": "test.exe",
"url": "about:blank",
Expand All@@ -280,8 +282,13 @@ def __init__(self, tmp_path, *args, **kwargs):
self.installs = [{
**self.index_installs[-1],
"source": self.source,
"prefix": tmp_path / "test-1.0-32",
"prefix": tmp_path / "test-32",
}]
assert not kwargs

def ask_yn(self, prompt, *args):
LOGGER.info(prompt, *args)
return True

def get_log_file(self):
return self.log_file
Expand All@@ -302,6 +309,7 @@ def test_install_simple(tmp_path, assert_log):
IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1.1"]),
assert_log.skip_until(".*Your existing %s install.*", ["Test 1.0 (32)", "Test 1.1 (32)"]),
assert_log.skip_until("Installing %s", ["Test 1.1 (32)"]),
("Tag: %s\\\\%s", ["Test", "1.1-32"]),
)
Expand All@@ -317,6 +325,58 @@ def test_install_already_installed(tmp_path, assert_log):
)



def test_install_update(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python with ID %s", ["test-32"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit_pre(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1rc", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1rc"]),
assert_log.skip_until("Updating to %s", ["Test 1.2rc1 (32)"]),
)


def test_install_from_script(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, from_script=tmp_path / "t.py")

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Restrict prerelease versions when updating by id. by zooba · Pull Request #352 · python/pymanager · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions src/manage/install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ def _expand_versions_by_tag(versions):
yield {**v, "tag": t}


def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False):
def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False, allow_pre=True):
"""Finds suitable package from index.json that looks like:
{"versions": [
{"id": ..., "company": ..., "tag": ..., "url": ..., "hash": {"sha256": hexdigest}},
Expand All@@ -63,19 +63,31 @@ def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by
try:
if by_id:
for v in index.versions:
if not allow_pre and v["sort-version"].is_prerelease:
continue
if v["id"].casefold() == tag.casefold():
return v
raise LookupError("Could not find a runtime matching '{}' at '{}'".format(
tag, sanitise_url(index.source_url)
))
if platform:
try:
return index.find_to_install(tag + platform)
v = index.find_to_install(tag + platform)
except LookupError:
pass
return index.find_to_install(tag)
else:
if allow_pre or not v["sort-version"].is_prerelease:
return v
v = index.find_to_install(tag)
if v["sort-version"].is_prerelease and not allow_pre:
raise LookupError("Found '{}' matching '{}' but excluded "
"because we are now allowing prereleases.".format(
v["display-name"], tag))
return v
except LookupError as ex:
first_exc = ex
LOGGER.debug("Potential error: %s", ex)
if not first_exc:
first_exc = ex

if first_exc:
raise first_exc
Expand DownExpand Up@@ -367,7 +379,7 @@ def _same_install(i, j):
return i["id"] == j["id"] and i["sort-version"] == j["sort-version"]


def _find_one(cmd, source, tag, *, installed=None, by_id=False):
def _find_one(cmd, source, tag, *, installed=None, by_id=False, allow_pre=True):
if by_id:
LOGGER.debug("Searching for Python with ID %s", tag)
elif tag:
Expand All@@ -377,7 +389,7 @@ def _find_one(cmd, source, tag, *, installed=None, by_id=False):

download_cache = cmd.scratch.setdefault("install_command.download_cache", {})
downloader = IndexDownloader(cmd, source, Index, {}, download_cache)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id, allow_pre=allow_pre)

# Ensure the requested source URL is in the install
if install and source:
Expand DownExpand Up@@ -829,14 +841,16 @@ def execute(cmd):
# Fallthrough is safe - cmd.tags is empty
elif cmd.update:
LOGGER.verbose("No tags provided, updating all installs:")
from .verutils import Version
for install in installed:
first_exc = None
update = None
allow_pre = Version(install['sort-version']).is_prerelease
for source in [install.get('source'), cmd.source, cmd.fallback_source]:
if not source:
continue
try:
update = _find_one(cmd, source, install['id'], by_id=True)
update = _find_one(cmd, source, install['id'], by_id=True, allow_pre=allow_pre)
if update:
break
except LookupError:
Expand Down
110 changes: 85 additions & 25 deletions tests/test_install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@
from manage import install_command as IC
from manage import installs
from manage.exceptions import NoInstallFoundError
from manage.logging import LOGGER


def test_print_cli_shortcuts(patched_installs, assert_log, monkeypatch, tmp_path):
Expand DownExpand Up@@ -225,50 +226,51 @@ def __init__(self, tmp_path, *args, **kwargs):
self.scratch = {
"install_command.download_cache": self.download_cache,
}
self.automatic = kwargs.get("automatic", False)
self.by_id = kwargs.get("by_id", False)
self.default_install_tag = kwargs.get("default_install_tag", "1")
self.default_platform = kwargs.get("default_platform", "-32")
self.default_tag = kwargs.get("default_tag", "1")
self.download = kwargs.get("download")
self.automatic = kwargs.pop("automatic", False)
self.by_id = kwargs.pop("by_id", False)
self.default_install_tag = kwargs.pop("default_install_tag", "1")
self.default_platform = kwargs.pop("default_platform", "-32")
self.default_tag = kwargs.pop("default_tag", "1")
self.download = kwargs.pop("download", None)
if self.download:
self.download = tmp_path / self.download
self.download_dir = tmp_path / kwargs.get("download_dir", "_cache")
self.dry_run = kwargs.get("dry_run", True)
self.fallback_source = kwargs.get("fallback_source")
self.force = kwargs.get("force", True)
self.from_script = kwargs.get("from_script")
self.log_file = kwargs.get("log_file")
self.refresh = kwargs.get("refresh", False)
self.repair = kwargs.get("repair", False)
self.shebang_can_run_anything = kwargs.get("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.get("shebang_can_run_anything_silently", False)
self.source = kwargs.get("source", "http://example.com/index.json")
self.target = kwargs.get("target")
self.download_dir = tmp_path / kwargs.pop("download_dir", "_cache")
self.dry_run = kwargs.pop("dry_run", True)
self.fallback_source = kwargs.pop("fallback_source", None)
self.force = kwargs.pop("force", True)
self.from_script = kwargs.pop("from_script", None)
self.log_file = kwargs.pop("log_file", None)
self.refresh = kwargs.pop("refresh", False)
self.repair = kwargs.pop("repair", False)
self.shebang_can_run_anything = kwargs.pop("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.pop("shebang_can_run_anything_silently", False)
self.source = kwargs.pop("source", "http://example.com/index.json")
self.target = kwargs.pop("target", None)
if self.target:
self.target = tmp_path / self.target
self.update = kwargs.get("update", False)
self.virtual_env = kwargs.get("virtual_env")
self.update = kwargs.pop("update", False)
self.virtual_env = kwargs.pop("virtual_env", None)

self.index_installs = [
*kwargs.pop("index_installs", ()),
{
"schema": 1,
"id": "test-1.1-32",
"id": "test-32",
"sort-version": "1.1",
"company": "Test",
"tag": "1.1-32",
"install-for": ["1", "1.1", "1.1-32"],
"install-for": ["1-32", "1.1-32", "1.1-32"],
"display-name": "Test 1.1 (32)",
"executable": "test.exe",
"url": "about:blank",
},
{
"schema": 1,
"id": "test-1.0-32",
"id": "test-32",
"sort-version": "1.0",
"company": "Test",
"tag": "1.0-32",
"install-for": ["1", "1.0", "1.0-32"],
"install-for": ["1-32", "1.0-32", "1.0-32"],
"display-name": "Test 1.0 (32)",
"executable": "test.exe",
"url": "about:blank",
Expand All@@ -280,8 +282,13 @@ def __init__(self, tmp_path, *args, **kwargs):
self.installs = [{
**self.index_installs[-1],
"source": self.source,
"prefix": tmp_path / "test-1.0-32",
"prefix": tmp_path / "test-32",
}]
assert not kwargs

def ask_yn(self, prompt, *args):
LOGGER.info(prompt, *args)
return True

def get_log_file(self):
return self.log_file
Expand All@@ -302,6 +309,7 @@ def test_install_simple(tmp_path, assert_log):
IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1.1"]),
assert_log.skip_until(".*Your existing %s install.*", ["Test 1.0 (32)", "Test 1.1 (32)"]),
assert_log.skip_until("Installing %s", ["Test 1.1 (32)"]),
("Tag: %s\\\\%s", ["Test", "1.1-32"]),
)
Expand All@@ -317,6 +325,58 @@ def test_install_already_installed(tmp_path, assert_log):
)



def test_install_update(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python with ID %s", ["test-32"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit_pre(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1rc", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1rc"]),
assert_log.skip_until("Updating to %s", ["Test 1.2rc1 (32)"]),
)


def test_install_from_script(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, from_script=tmp_path / "t.py")

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Restrict prerelease versions when updating by id. by zooba · Pull Request #352 · python/pymanager · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions src/manage/install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ def _expand_versions_by_tag(versions):
yield {**v, "tag": t}


def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False):
def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False, allow_pre=True):
"""Finds suitable package from index.json that looks like:
{"versions": [
{"id": ..., "company": ..., "tag": ..., "url": ..., "hash": {"sha256": hexdigest}},
Expand All@@ -63,19 +63,31 @@ def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by
try:
if by_id:
for v in index.versions:
if not allow_pre and v["sort-version"].is_prerelease:
continue
if v["id"].casefold() == tag.casefold():
return v
raise LookupError("Could not find a runtime matching '{}' at '{}'".format(
tag, sanitise_url(index.source_url)
))
if platform:
try:
return index.find_to_install(tag + platform)
v = index.find_to_install(tag + platform)
except LookupError:
pass
return index.find_to_install(tag)
else:
if allow_pre or not v["sort-version"].is_prerelease:
return v
v = index.find_to_install(tag)
if v["sort-version"].is_prerelease and not allow_pre:
raise LookupError("Found '{}' matching '{}' but excluded "
"because we are now allowing prereleases.".format(
v["display-name"], tag))
return v
except LookupError as ex:
first_exc = ex
LOGGER.debug("Potential error: %s", ex)
if not first_exc:
first_exc = ex

if first_exc:
raise first_exc
Expand DownExpand Up@@ -367,7 +379,7 @@ def _same_install(i, j):
return i["id"] == j["id"] and i["sort-version"] == j["sort-version"]


def _find_one(cmd, source, tag, *, installed=None, by_id=False):
def _find_one(cmd, source, tag, *, installed=None, by_id=False, allow_pre=True):
if by_id:
LOGGER.debug("Searching for Python with ID %s", tag)
elif tag:
Expand All@@ -377,7 +389,7 @@ def _find_one(cmd, source, tag, *, installed=None, by_id=False):

download_cache = cmd.scratch.setdefault("install_command.download_cache", {})
downloader = IndexDownloader(cmd, source, Index, {}, download_cache)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id, allow_pre=allow_pre)

# Ensure the requested source URL is in the install
if install and source:
Expand DownExpand Up@@ -829,14 +841,16 @@ def execute(cmd):
# Fallthrough is safe - cmd.tags is empty
elif cmd.update:
LOGGER.verbose("No tags provided, updating all installs:")
from .verutils import Version
for install in installed:
first_exc = None
update = None
allow_pre = Version(install['sort-version']).is_prerelease
for source in [install.get('source'), cmd.source, cmd.fallback_source]:
if not source:
continue
try:
update = _find_one(cmd, source, install['id'], by_id=True)
update = _find_one(cmd, source, install['id'], by_id=True, allow_pre=allow_pre)
if update:
break
except LookupError:
Expand Down
110 changes: 85 additions & 25 deletions tests/test_install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@
from manage import install_command as IC
from manage import installs
from manage.exceptions import NoInstallFoundError
from manage.logging import LOGGER


def test_print_cli_shortcuts(patched_installs, assert_log, monkeypatch, tmp_path):
Expand DownExpand Up@@ -225,50 +226,51 @@ def __init__(self, tmp_path, *args, **kwargs):
self.scratch = {
"install_command.download_cache": self.download_cache,
}
self.automatic = kwargs.get("automatic", False)
self.by_id = kwargs.get("by_id", False)
self.default_install_tag = kwargs.get("default_install_tag", "1")
self.default_platform = kwargs.get("default_platform", "-32")
self.default_tag = kwargs.get("default_tag", "1")
self.download = kwargs.get("download")
self.automatic = kwargs.pop("automatic", False)
self.by_id = kwargs.pop("by_id", False)
self.default_install_tag = kwargs.pop("default_install_tag", "1")
self.default_platform = kwargs.pop("default_platform", "-32")
self.default_tag = kwargs.pop("default_tag", "1")
self.download = kwargs.pop("download", None)
if self.download:
self.download = tmp_path / self.download
self.download_dir = tmp_path / kwargs.get("download_dir", "_cache")
self.dry_run = kwargs.get("dry_run", True)
self.fallback_source = kwargs.get("fallback_source")
self.force = kwargs.get("force", True)
self.from_script = kwargs.get("from_script")
self.log_file = kwargs.get("log_file")
self.refresh = kwargs.get("refresh", False)
self.repair = kwargs.get("repair", False)
self.shebang_can_run_anything = kwargs.get("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.get("shebang_can_run_anything_silently", False)
self.source = kwargs.get("source", "http://example.com/index.json")
self.target = kwargs.get("target")
self.download_dir = tmp_path / kwargs.pop("download_dir", "_cache")
self.dry_run = kwargs.pop("dry_run", True)
self.fallback_source = kwargs.pop("fallback_source", None)
self.force = kwargs.pop("force", True)
self.from_script = kwargs.pop("from_script", None)
self.log_file = kwargs.pop("log_file", None)
self.refresh = kwargs.pop("refresh", False)
self.repair = kwargs.pop("repair", False)
self.shebang_can_run_anything = kwargs.pop("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.pop("shebang_can_run_anything_silently", False)
self.source = kwargs.pop("source", "http://example.com/index.json")
self.target = kwargs.pop("target", None)
if self.target:
self.target = tmp_path / self.target
self.update = kwargs.get("update", False)
self.virtual_env = kwargs.get("virtual_env")
self.update = kwargs.pop("update", False)
self.virtual_env = kwargs.pop("virtual_env", None)

self.index_installs = [
*kwargs.pop("index_installs", ()),
{
"schema": 1,
"id": "test-1.1-32",
"id": "test-32",
"sort-version": "1.1",
"company": "Test",
"tag": "1.1-32",
"install-for": ["1", "1.1", "1.1-32"],
"install-for": ["1-32", "1.1-32", "1.1-32"],
"display-name": "Test 1.1 (32)",
"executable": "test.exe",
"url": "about:blank",
},
{
"schema": 1,
"id": "test-1.0-32",
"id": "test-32",
"sort-version": "1.0",
"company": "Test",
"tag": "1.0-32",
"install-for": ["1", "1.0", "1.0-32"],
"install-for": ["1-32", "1.0-32", "1.0-32"],
"display-name": "Test 1.0 (32)",
"executable": "test.exe",
"url": "about:blank",
Expand All@@ -280,8 +282,13 @@ def __init__(self, tmp_path, *args, **kwargs):
self.installs = [{
**self.index_installs[-1],
"source": self.source,
"prefix": tmp_path / "test-1.0-32",
"prefix": tmp_path / "test-32",
}]
assert not kwargs

def ask_yn(self, prompt, *args):
LOGGER.info(prompt, *args)
return True

def get_log_file(self):
return self.log_file
Expand All@@ -302,6 +309,7 @@ def test_install_simple(tmp_path, assert_log):
IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1.1"]),
assert_log.skip_until(".*Your existing %s install.*", ["Test 1.0 (32)", "Test 1.1 (32)"]),
assert_log.skip_until("Installing %s", ["Test 1.1 (32)"]),
("Tag: %s\\\\%s", ["Test", "1.1-32"]),
)
Expand All@@ -317,6 +325,58 @@ def test_install_already_installed(tmp_path, assert_log):
)



def test_install_update(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python with ID %s", ["test-32"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit_pre(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1rc", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1rc"]),
assert_log.skip_until("Updating to %s", ["Test 1.2rc1 (32)"]),
)


def test_install_from_script(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, from_script=tmp_path / "t.py")

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Restrict prerelease versions when updating by id. by zooba · Pull Request #352 · python/pymanager · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions src/manage/install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ def _expand_versions_by_tag(versions):
yield {**v, "tag": t}


def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False):
def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False, allow_pre=True):
"""Finds suitable package from index.json that looks like:
{"versions": [
{"id": ..., "company": ..., "tag": ..., "url": ..., "hash": {"sha256": hexdigest}},
Expand All@@ -63,19 +63,31 @@ def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by
try:
if by_id:
for v in index.versions:
if not allow_pre and v["sort-version"].is_prerelease:
continue
if v["id"].casefold() == tag.casefold():
return v
raise LookupError("Could not find a runtime matching '{}' at '{}'".format(
tag, sanitise_url(index.source_url)
))
if platform:
try:
return index.find_to_install(tag + platform)
v = index.find_to_install(tag + platform)
except LookupError:
pass
return index.find_to_install(tag)
else:
if allow_pre or not v["sort-version"].is_prerelease:
return v
v = index.find_to_install(tag)
if v["sort-version"].is_prerelease and not allow_pre:
raise LookupError("Found '{}' matching '{}' but excluded "
"because we are now allowing prereleases.".format(
v["display-name"], tag))
return v
except LookupError as ex:
first_exc = ex
LOGGER.debug("Potential error: %s", ex)
if not first_exc:
first_exc = ex

if first_exc:
raise first_exc
Expand DownExpand Up@@ -367,7 +379,7 @@ def _same_install(i, j):
return i["id"] == j["id"] and i["sort-version"] == j["sort-version"]


def _find_one(cmd, source, tag, *, installed=None, by_id=False):
def _find_one(cmd, source, tag, *, installed=None, by_id=False, allow_pre=True):
if by_id:
LOGGER.debug("Searching for Python with ID %s", tag)
elif tag:
Expand All@@ -377,7 +389,7 @@ def _find_one(cmd, source, tag, *, installed=None, by_id=False):

download_cache = cmd.scratch.setdefault("install_command.download_cache", {})
downloader = IndexDownloader(cmd, source, Index, {}, download_cache)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id, allow_pre=allow_pre)

# Ensure the requested source URL is in the install
if install and source:
Expand DownExpand Up@@ -829,14 +841,16 @@ def execute(cmd):
# Fallthrough is safe - cmd.tags is empty
elif cmd.update:
LOGGER.verbose("No tags provided, updating all installs:")
from .verutils import Version
for install in installed:
first_exc = None
update = None
allow_pre = Version(install['sort-version']).is_prerelease
for source in [install.get('source'), cmd.source, cmd.fallback_source]:
if not source:
continue
try:
update = _find_one(cmd, source, install['id'], by_id=True)
update = _find_one(cmd, source, install['id'], by_id=True, allow_pre=allow_pre)
if update:
break
except LookupError:
Expand Down
110 changes: 85 additions & 25 deletions tests/test_install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@
from manage import install_command as IC
from manage import installs
from manage.exceptions import NoInstallFoundError
from manage.logging import LOGGER


def test_print_cli_shortcuts(patched_installs, assert_log, monkeypatch, tmp_path):
Expand DownExpand Up@@ -225,50 +226,51 @@ def __init__(self, tmp_path, *args, **kwargs):
self.scratch = {
"install_command.download_cache": self.download_cache,
}
self.automatic = kwargs.get("automatic", False)
self.by_id = kwargs.get("by_id", False)
self.default_install_tag = kwargs.get("default_install_tag", "1")
self.default_platform = kwargs.get("default_platform", "-32")
self.default_tag = kwargs.get("default_tag", "1")
self.download = kwargs.get("download")
self.automatic = kwargs.pop("automatic", False)
self.by_id = kwargs.pop("by_id", False)
self.default_install_tag = kwargs.pop("default_install_tag", "1")
self.default_platform = kwargs.pop("default_platform", "-32")
self.default_tag = kwargs.pop("default_tag", "1")
self.download = kwargs.pop("download", None)
if self.download:
self.download = tmp_path / self.download
self.download_dir = tmp_path / kwargs.get("download_dir", "_cache")
self.dry_run = kwargs.get("dry_run", True)
self.fallback_source = kwargs.get("fallback_source")
self.force = kwargs.get("force", True)
self.from_script = kwargs.get("from_script")
self.log_file = kwargs.get("log_file")
self.refresh = kwargs.get("refresh", False)
self.repair = kwargs.get("repair", False)
self.shebang_can_run_anything = kwargs.get("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.get("shebang_can_run_anything_silently", False)
self.source = kwargs.get("source", "http://example.com/index.json")
self.target = kwargs.get("target")
self.download_dir = tmp_path / kwargs.pop("download_dir", "_cache")
self.dry_run = kwargs.pop("dry_run", True)
self.fallback_source = kwargs.pop("fallback_source", None)
self.force = kwargs.pop("force", True)
self.from_script = kwargs.pop("from_script", None)
self.log_file = kwargs.pop("log_file", None)
self.refresh = kwargs.pop("refresh", False)
self.repair = kwargs.pop("repair", False)
self.shebang_can_run_anything = kwargs.pop("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.pop("shebang_can_run_anything_silently", False)
self.source = kwargs.pop("source", "http://example.com/index.json")
self.target = kwargs.pop("target", None)
if self.target:
self.target = tmp_path / self.target
self.update = kwargs.get("update", False)
self.virtual_env = kwargs.get("virtual_env")
self.update = kwargs.pop("update", False)
self.virtual_env = kwargs.pop("virtual_env", None)

self.index_installs = [
*kwargs.pop("index_installs", ()),
{
"schema": 1,
"id": "test-1.1-32",
"id": "test-32",
"sort-version": "1.1",
"company": "Test",
"tag": "1.1-32",
"install-for": ["1", "1.1", "1.1-32"],
"install-for": ["1-32", "1.1-32", "1.1-32"],
"display-name": "Test 1.1 (32)",
"executable": "test.exe",
"url": "about:blank",
},
{
"schema": 1,
"id": "test-1.0-32",
"id": "test-32",
"sort-version": "1.0",
"company": "Test",
"tag": "1.0-32",
"install-for": ["1", "1.0", "1.0-32"],
"install-for": ["1-32", "1.0-32", "1.0-32"],
"display-name": "Test 1.0 (32)",
"executable": "test.exe",
"url": "about:blank",
Expand All@@ -280,8 +282,13 @@ def __init__(self, tmp_path, *args, **kwargs):
self.installs = [{
**self.index_installs[-1],
"source": self.source,
"prefix": tmp_path / "test-1.0-32",
"prefix": tmp_path / "test-32",
}]
assert not kwargs

def ask_yn(self, prompt, *args):
LOGGER.info(prompt, *args)
return True

def get_log_file(self):
return self.log_file
Expand All@@ -302,6 +309,7 @@ def test_install_simple(tmp_path, assert_log):
IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1.1"]),
assert_log.skip_until(".*Your existing %s install.*", ["Test 1.0 (32)", "Test 1.1 (32)"]),
assert_log.skip_until("Installing %s", ["Test 1.1 (32)"]),
("Tag: %s\\\\%s", ["Test", "1.1-32"]),
)
Expand All@@ -317,6 +325,58 @@ def test_install_already_installed(tmp_path, assert_log):
)



def test_install_update(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python with ID %s", ["test-32"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit_pre(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1rc", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1rc"]),
assert_log.skip_until("Updating to %s", ["Test 1.2rc1 (32)"]),
)


def test_install_from_script(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, from_script=tmp_path / "t.py")

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Restrict prerelease versions when updating by id. by zooba · Pull Request #352 · python/pymanager · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions src/manage/install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ def _expand_versions_by_tag(versions):
yield {**v, "tag": t}


def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False):
def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False, allow_pre=True):
"""Finds suitable package from index.json that looks like:
{"versions": [
{"id": ..., "company": ..., "tag": ..., "url": ..., "hash": {"sha256": hexdigest}},
Expand All@@ -63,19 +63,31 @@ def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by
try:
if by_id:
for v in index.versions:
if not allow_pre and v["sort-version"].is_prerelease:
continue
if v["id"].casefold() == tag.casefold():
return v
raise LookupError("Could not find a runtime matching '{}' at '{}'".format(
tag, sanitise_url(index.source_url)
))
if platform:
try:
return index.find_to_install(tag + platform)
v = index.find_to_install(tag + platform)
except LookupError:
pass
return index.find_to_install(tag)
else:
if allow_pre or not v["sort-version"].is_prerelease:
return v
v = index.find_to_install(tag)
if v["sort-version"].is_prerelease and not allow_pre:
raise LookupError("Found '{}' matching '{}' but excluded "
"because we are now allowing prereleases.".format(
v["display-name"], tag))
return v
except LookupError as ex:
first_exc = ex
LOGGER.debug("Potential error: %s", ex)
if not first_exc:
first_exc = ex

if first_exc:
raise first_exc
Expand DownExpand Up@@ -367,7 +379,7 @@ def _same_install(i, j):
return i["id"] == j["id"] and i["sort-version"] == j["sort-version"]


def _find_one(cmd, source, tag, *, installed=None, by_id=False):
def _find_one(cmd, source, tag, *, installed=None, by_id=False, allow_pre=True):
if by_id:
LOGGER.debug("Searching for Python with ID %s", tag)
elif tag:
Expand All@@ -377,7 +389,7 @@ def _find_one(cmd, source, tag, *, installed=None, by_id=False):

download_cache = cmd.scratch.setdefault("install_command.download_cache", {})
downloader = IndexDownloader(cmd, source, Index, {}, download_cache)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id, allow_pre=allow_pre)

# Ensure the requested source URL is in the install
if install and source:
Expand DownExpand Up@@ -829,14 +841,16 @@ def execute(cmd):
# Fallthrough is safe - cmd.tags is empty
elif cmd.update:
LOGGER.verbose("No tags provided, updating all installs:")
from .verutils import Version
for install in installed:
first_exc = None
update = None
allow_pre = Version(install['sort-version']).is_prerelease
for source in [install.get('source'), cmd.source, cmd.fallback_source]:
if not source:
continue
try:
update = _find_one(cmd, source, install['id'], by_id=True)
update = _find_one(cmd, source, install['id'], by_id=True, allow_pre=allow_pre)
if update:
break
except LookupError:
Expand Down
110 changes: 85 additions & 25 deletions tests/test_install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@
from manage import install_command as IC
from manage import installs
from manage.exceptions import NoInstallFoundError
from manage.logging import LOGGER


def test_print_cli_shortcuts(patched_installs, assert_log, monkeypatch, tmp_path):
Expand DownExpand Up@@ -225,50 +226,51 @@ def __init__(self, tmp_path, *args, **kwargs):
self.scratch = {
"install_command.download_cache": self.download_cache,
}
self.automatic = kwargs.get("automatic", False)
self.by_id = kwargs.get("by_id", False)
self.default_install_tag = kwargs.get("default_install_tag", "1")
self.default_platform = kwargs.get("default_platform", "-32")
self.default_tag = kwargs.get("default_tag", "1")
self.download = kwargs.get("download")
self.automatic = kwargs.pop("automatic", False)
self.by_id = kwargs.pop("by_id", False)
self.default_install_tag = kwargs.pop("default_install_tag", "1")
self.default_platform = kwargs.pop("default_platform", "-32")
self.default_tag = kwargs.pop("default_tag", "1")
self.download = kwargs.pop("download", None)
if self.download:
self.download = tmp_path / self.download
self.download_dir = tmp_path / kwargs.get("download_dir", "_cache")
self.dry_run = kwargs.get("dry_run", True)
self.fallback_source = kwargs.get("fallback_source")
self.force = kwargs.get("force", True)
self.from_script = kwargs.get("from_script")
self.log_file = kwargs.get("log_file")
self.refresh = kwargs.get("refresh", False)
self.repair = kwargs.get("repair", False)
self.shebang_can_run_anything = kwargs.get("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.get("shebang_can_run_anything_silently", False)
self.source = kwargs.get("source", "http://example.com/index.json")
self.target = kwargs.get("target")
self.download_dir = tmp_path / kwargs.pop("download_dir", "_cache")
self.dry_run = kwargs.pop("dry_run", True)
self.fallback_source = kwargs.pop("fallback_source", None)
self.force = kwargs.pop("force", True)
self.from_script = kwargs.pop("from_script", None)
self.log_file = kwargs.pop("log_file", None)
self.refresh = kwargs.pop("refresh", False)
self.repair = kwargs.pop("repair", False)
self.shebang_can_run_anything = kwargs.pop("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.pop("shebang_can_run_anything_silently", False)
self.source = kwargs.pop("source", "http://example.com/index.json")
self.target = kwargs.pop("target", None)
if self.target:
self.target = tmp_path / self.target
self.update = kwargs.get("update", False)
self.virtual_env = kwargs.get("virtual_env")
self.update = kwargs.pop("update", False)
self.virtual_env = kwargs.pop("virtual_env", None)

self.index_installs = [
*kwargs.pop("index_installs", ()),
{
"schema": 1,
"id": "test-1.1-32",
"id": "test-32",
"sort-version": "1.1",
"company": "Test",
"tag": "1.1-32",
"install-for": ["1", "1.1", "1.1-32"],
"install-for": ["1-32", "1.1-32", "1.1-32"],
"display-name": "Test 1.1 (32)",
"executable": "test.exe",
"url": "about:blank",
},
{
"schema": 1,
"id": "test-1.0-32",
"id": "test-32",
"sort-version": "1.0",
"company": "Test",
"tag": "1.0-32",
"install-for": ["1", "1.0", "1.0-32"],
"install-for": ["1-32", "1.0-32", "1.0-32"],
"display-name": "Test 1.0 (32)",
"executable": "test.exe",
"url": "about:blank",
Expand All@@ -280,8 +282,13 @@ def __init__(self, tmp_path, *args, **kwargs):
self.installs = [{
**self.index_installs[-1],
"source": self.source,
"prefix": tmp_path / "test-1.0-32",
"prefix": tmp_path / "test-32",
}]
assert not kwargs

def ask_yn(self, prompt, *args):
LOGGER.info(prompt, *args)
return True

def get_log_file(self):
return self.log_file
Expand All@@ -302,6 +309,7 @@ def test_install_simple(tmp_path, assert_log):
IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1.1"]),
assert_log.skip_until(".*Your existing %s install.*", ["Test 1.0 (32)", "Test 1.1 (32)"]),
assert_log.skip_until("Installing %s", ["Test 1.1 (32)"]),
("Tag: %s\\\\%s", ["Test", "1.1-32"]),
)
Expand All@@ -317,6 +325,58 @@ def test_install_already_installed(tmp_path, assert_log):
)



def test_install_update(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python with ID %s", ["test-32"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit_pre(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1rc", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1rc"]),
assert_log.skip_until("Updating to %s", ["Test 1.2rc1 (32)"]),
)


def test_install_from_script(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, from_script=tmp_path / "t.py")

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Restrict prerelease versions when updating by id. by zooba · Pull Request #352 · python/pymanager · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions src/manage/install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,7 @@ def _expand_versions_by_tag(versions):
yield {**v, "tag": t}


def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False):
def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by_id=False, allow_pre=True):
"""Finds suitable package from index.json that looks like:
{"versions": [
{"id": ..., "company": ..., "tag": ..., "url": ..., "hash": {"sha256": hexdigest}},
Expand All@@ -63,19 +63,31 @@ def select_package(index_downloader, tag, platform=None, *, urlopen=_urlopen, by
try:
if by_id:
for v in index.versions:
if not allow_pre and v["sort-version"].is_prerelease:
continue
if v["id"].casefold() == tag.casefold():
return v
raise LookupError("Could not find a runtime matching '{}' at '{}'".format(
tag, sanitise_url(index.source_url)
))
if platform:
try:
return index.find_to_install(tag + platform)
v = index.find_to_install(tag + platform)
except LookupError:
pass
return index.find_to_install(tag)
else:
if allow_pre or not v["sort-version"].is_prerelease:
return v
v = index.find_to_install(tag)
if v["sort-version"].is_prerelease and not allow_pre:
raise LookupError("Found '{}' matching '{}' but excluded "
"because we are now allowing prereleases.".format(
v["display-name"], tag))
return v
except LookupError as ex:
first_exc = ex
LOGGER.debug("Potential error: %s", ex)
if not first_exc:
first_exc = ex

if first_exc:
raise first_exc
Expand DownExpand Up@@ -367,7 +379,7 @@ def _same_install(i, j):
return i["id"] == j["id"] and i["sort-version"] == j["sort-version"]


def _find_one(cmd, source, tag, *, installed=None, by_id=False):
def _find_one(cmd, source, tag, *, installed=None, by_id=False, allow_pre=True):
if by_id:
LOGGER.debug("Searching for Python with ID %s", tag)
elif tag:
Expand All@@ -377,7 +389,7 @@ def _find_one(cmd, source, tag, *, installed=None, by_id=False):

download_cache = cmd.scratch.setdefault("install_command.download_cache", {})
downloader = IndexDownloader(cmd, source, Index, {}, download_cache)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id)
install = select_package(downloader, tag, cmd.default_platform, by_id=by_id, allow_pre=allow_pre)

# Ensure the requested source URL is in the install
if install and source:
Expand DownExpand Up@@ -829,14 +841,16 @@ def execute(cmd):
# Fallthrough is safe - cmd.tags is empty
elif cmd.update:
LOGGER.verbose("No tags provided, updating all installs:")
from .verutils import Version
for install in installed:
first_exc = None
update = None
allow_pre = Version(install['sort-version']).is_prerelease
for source in [install.get('source'), cmd.source, cmd.fallback_source]:
if not source:
continue
try:
update = _find_one(cmd, source, install['id'], by_id=True)
update = _find_one(cmd, source, install['id'], by_id=True, allow_pre=allow_pre)
if update:
break
except LookupError:
Expand Down
110 changes: 85 additions & 25 deletions tests/test_install_command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@
from manage import install_command as IC
from manage import installs
from manage.exceptions import NoInstallFoundError
from manage.logging import LOGGER


def test_print_cli_shortcuts(patched_installs, assert_log, monkeypatch, tmp_path):
Expand DownExpand Up@@ -225,50 +226,51 @@ def __init__(self, tmp_path, *args, **kwargs):
self.scratch = {
"install_command.download_cache": self.download_cache,
}
self.automatic = kwargs.get("automatic", False)
self.by_id = kwargs.get("by_id", False)
self.default_install_tag = kwargs.get("default_install_tag", "1")
self.default_platform = kwargs.get("default_platform", "-32")
self.default_tag = kwargs.get("default_tag", "1")
self.download = kwargs.get("download")
self.automatic = kwargs.pop("automatic", False)
self.by_id = kwargs.pop("by_id", False)
self.default_install_tag = kwargs.pop("default_install_tag", "1")
self.default_platform = kwargs.pop("default_platform", "-32")
self.default_tag = kwargs.pop("default_tag", "1")
self.download = kwargs.pop("download", None)
if self.download:
self.download = tmp_path / self.download
self.download_dir = tmp_path / kwargs.get("download_dir", "_cache")
self.dry_run = kwargs.get("dry_run", True)
self.fallback_source = kwargs.get("fallback_source")
self.force = kwargs.get("force", True)
self.from_script = kwargs.get("from_script")
self.log_file = kwargs.get("log_file")
self.refresh = kwargs.get("refresh", False)
self.repair = kwargs.get("repair", False)
self.shebang_can_run_anything = kwargs.get("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.get("shebang_can_run_anything_silently", False)
self.source = kwargs.get("source", "http://example.com/index.json")
self.target = kwargs.get("target")
self.download_dir = tmp_path / kwargs.pop("download_dir", "_cache")
self.dry_run = kwargs.pop("dry_run", True)
self.fallback_source = kwargs.pop("fallback_source", None)
self.force = kwargs.pop("force", True)
self.from_script = kwargs.pop("from_script", None)
self.log_file = kwargs.pop("log_file", None)
self.refresh = kwargs.pop("refresh", False)
self.repair = kwargs.pop("repair", False)
self.shebang_can_run_anything = kwargs.pop("shebang_can_run_anything", False)
self.shebang_can_run_anything_silently = kwargs.pop("shebang_can_run_anything_silently", False)
self.source = kwargs.pop("source", "http://example.com/index.json")
self.target = kwargs.pop("target", None)
if self.target:
self.target = tmp_path / self.target
self.update = kwargs.get("update", False)
self.virtual_env = kwargs.get("virtual_env")
self.update = kwargs.pop("update", False)
self.virtual_env = kwargs.pop("virtual_env", None)

self.index_installs = [
*kwargs.pop("index_installs", ()),
{
"schema": 1,
"id": "test-1.1-32",
"id": "test-32",
"sort-version": "1.1",
"company": "Test",
"tag": "1.1-32",
"install-for": ["1", "1.1", "1.1-32"],
"install-for": ["1-32", "1.1-32", "1.1-32"],
"display-name": "Test 1.1 (32)",
"executable": "test.exe",
"url": "about:blank",
},
{
"schema": 1,
"id": "test-1.0-32",
"id": "test-32",
"sort-version": "1.0",
"company": "Test",
"tag": "1.0-32",
"install-for": ["1", "1.0", "1.0-32"],
"install-for": ["1-32", "1.0-32", "1.0-32"],
"display-name": "Test 1.0 (32)",
"executable": "test.exe",
"url": "about:blank",
Expand All@@ -280,8 +282,13 @@ def __init__(self, tmp_path, *args, **kwargs):
self.installs = [{
**self.index_installs[-1],
"source": self.source,
"prefix": tmp_path / "test-1.0-32",
"prefix": tmp_path / "test-32",
}]
assert not kwargs

def ask_yn(self, prompt, *args):
LOGGER.info(prompt, *args)
return True

def get_log_file(self):
return self.log_file
Expand All@@ -302,6 +309,7 @@ def test_install_simple(tmp_path, assert_log):
IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1.1"]),
assert_log.skip_until(".*Your existing %s install.*", ["Test 1.0 (32)", "Test 1.1 (32)"]),
assert_log.skip_until("Installing %s", ["Test 1.1 (32)"]),
("Tag: %s\\\\%s", ["Test", "1.1-32"]),
)
Expand All@@ -317,6 +325,58 @@ def test_install_already_installed(tmp_path, assert_log):
)



def test_install_update(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python with ID %s", ["test-32"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1"]),
assert_log.skip_until("Updating to %s", ["Test 1.1 (32)"]),
)


def test_install_update_explicit_pre(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, "1rc", update=True, force=False,
index_installs=[
{"schema": 1, "id": "test-32", "sort-version": "1.2rc1",
"display-name": "Test 1.2rc1 (32)",
"company": "Test", "tag": "1.2rc-32",
"install-for": ["1-32", "1.2-32", "1.2rc1-32"]}
],
)

IC.execute(cmd)
assert_log(
assert_log.skip_until("Searching for Python matching %s", ["1rc"]),
assert_log.skip_until("Updating to %s", ["Test 1.2rc1 (32)"]),
)


def test_install_from_script(tmp_path, assert_log):
cmd = InstallCommandTestCmd(tmp_path, from_script=tmp_path / "t.py")

Expand Down
Loading