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
42 changes: 40 additions & 2 deletions kernels/src/kernels/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from huggingface_hub.hf_api import HfApi
from kernels_data import KernelDependency, KernelLocks, KernelPaths, Metadata

from kernels._versions import resolve_kernel_version
from kernels._versions import _get_available_versions, resolve_kernel_version
from kernels.hf_hub import CACHE_DIR, _check_trust_remote_code
from kernels.variants import (
Variant,
Expand Down Expand Up @@ -123,8 +123,14 @@ def resolve_hub_kernel(
)
variant, trace = resolve_variant(variants, backend)
if variant is None:
suggestion = _latest_compatible_version_suggestion(
api=api,
repo_id=repo_id,
backend=backend,
)
raise FileNotFoundError(
f"Cannot find a build variant for this system in {repo_id} (revision: {revision}):\n\n{variants_trace_str(trace)}"
f"Cannot find a build variant for this system in {repo_id} (revision: {revision}):\n\n"
f"{variants_trace_str(trace)}{suggestion}"
)

metadata_path = Path(
Expand All @@ -144,6 +150,38 @@ def resolve_hub_kernel(
return location


def _latest_compatible_version_suggestion(
*,
api: HfApi,
repo_id: str,
backend: str | None,
) -> str:
"""
This runs only after variant resolution has failed. Version discovery and
variant inspection involve additional Hub requests, so the lookup is
best-effort and hence, we never mask the original resolution error.
"""
try:
versions = _get_available_versions(repo_id, local_files_only=False)
if not versions:
return ""

latest_version = max(versions)
ref = versions[latest_version]
variants = get_variants(api, repo_id=repo_id, revision=ref.ref)
compatible_variant, _ = resolve_variant(variants, backend)
if compatible_variant is not None:
return (
f"\n\nHowever, version v{latest_version} of '{repo_id}' has a build compatible with your "
f"system ({compatible_variant.variant_str}). Consider upgrading to that version by specifying "
"the `version` argument."
)
except Exception:
return ""

return ""


def resolve_hub_cache_kernel(
api: HfApi,
repo_id: str,
Expand Down
110 changes: 110 additions & 0 deletions kernels/tests/test_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pathlib import Path

import pytest
from huggingface_hub.hf_api import GitRefInfo

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I have covered most bases here but I have a feeling I am missing 1/2 tests.

from kernels_data import (
KernelDependency,
KernelLock,
Expand All @@ -14,6 +15,7 @@
Metadata,
)

import kernels.resolver as resolver_module
from kernels._versions import resolve_version_spec_as_ref
from kernels.hf_hub import _get_hf_api
from kernels.install import install_kernel
Expand All @@ -30,6 +32,7 @@
Resolver,
SequentialResolver,
_locked_revision,
resolve_hub_kernel,
)
from kernels.variants import parse_variant

Expand Down Expand Up @@ -277,6 +280,113 @@ def test_hub_resolver_no_matching_variant(api):
)


def test_resolve_hub_kernel_suggests_latest_compatible_version_for_unknown_revision(monkeypatch):
versions = {
version: GitRefInfo(name=f"v{version}", ref=f"refs/heads/v{version}", target_commit=str(version) * 40)
for version in (1, 2, 3)
}
checked_revisions = []

monkeypatch.setattr(
resolver_module,
"_get_available_versions",
lambda repo_id, *, local_files_only: versions,
)

def fake_get_variants(api, *, repo_id, revision):
checked_revisions.append(revision)
return [parse_variant("torch-cpu" if revision == "refs/heads/v3" else "torch-cuda")]

monkeypatch.setattr(resolver_module, "get_variants", fake_get_variants)

with pytest.raises(FileNotFoundError) as exc_info:
resolve_hub_kernel(
"test/kernel",
api=object(),
backend="cpu",
revision="unknown-revision",
)

message = str(exc_info.value)
assert "Cannot find a build variant for this system" in message
assert (
"However, version v3 of 'test/kernel' has a build compatible with your system (torch-cpu). "
"Consider upgrading to that version by specifying the `version` argument."
) in message
assert checked_revisions == ["unknown-revision", "refs/heads/v3"]


def test_resolve_hub_kernel_only_checks_latest_version(monkeypatch):
versions = {
version: GitRefInfo(name=f"v{version}", ref=f"refs/heads/v{version}", target_commit=str(version) * 40)
for version in (1, 2, 3)
}
checked_revisions = []
monkeypatch.setattr(
resolver_module,
"_get_available_versions",
lambda repo_id, *, local_files_only: versions,
)

def fake_get_variants(api, *, repo_id, revision):
checked_revisions.append(revision)
return [parse_variant("torch-cpu" if revision == "refs/heads/v2" else "torch-cuda")]

monkeypatch.setattr(resolver_module, "get_variants", fake_get_variants)

with pytest.raises(FileNotFoundError) as exc_info:
resolve_hub_kernel("test/kernel", api=object(), backend="cpu", revision="locked-commit")

assert "However, version" not in str(exc_info.value)
assert checked_revisions == ["locked-commit", "refs/heads/v3"]


def test_resolve_hub_kernel_preserves_original_error_when_latest_version_lookup_fails(monkeypatch):
versions = {
1: GitRefInfo(name="v1", ref="refs/heads/v1", target_commit="1" * 40),
2: GitRefInfo(name="v2", ref="refs/heads/v2", target_commit="2" * 40),
}
monkeypatch.setattr(
resolver_module,
"_get_available_versions",
lambda repo_id, *, local_files_only: versions,
)

def fake_get_variants(api, *, repo_id, revision):
if revision == "refs/heads/v2":
raise OSError("latest branch is temporarily unavailable")
return [parse_variant("torch-cuda")]

monkeypatch.setattr(resolver_module, "get_variants", fake_get_variants)

with pytest.raises(FileNotFoundError) as exc_info:
resolve_hub_kernel("test/kernel", api=object(), backend="cpu", revision="locked-commit")

message = str(exc_info.value)
assert "Cannot find a build variant for this system" in message
assert "However, version" not in message


def test_resolve_hub_kernel_preserves_original_error_when_version_lookup_fails(monkeypatch):
monkeypatch.setattr(
resolver_module,
"_get_available_versions",
lambda repo_id, *, local_files_only: (_ for _ in ()).throw(OSError("offline")),
)
monkeypatch.setattr(
resolver_module,
"get_variants",
lambda api, *, repo_id, revision: [parse_variant("torch-cuda")],
)

with pytest.raises(FileNotFoundError) as exc_info:
resolve_hub_kernel("test/kernel", api=object(), backend="cpu", revision="locked-commit")

message = str(exc_info.value)
assert "Cannot find a build variant for this system" in message
assert "However, version" not in message


def test_hub_cache_resolver_resolves_cached_kernel(api, installed_relu_cpu):
location = HubCacheResolver(trust_remote_code=False).resolve(
api=api, backend="cpu", kernel=_dep("kernels-community/relu", version=1)
Expand Down
Loading