skilltest-sdk 0.1.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/.gitignore +5 -0
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/PKG-INFO +1 -1
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/pyproject.toml +7 -1
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/skilltest_sdk/runner.py +29 -1
- skilltest_sdk-0.2.2/tests/test_resolve.py +48 -0
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/uv.lock +1 -1
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/README.md +0 -0
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/project.json +0 -0
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/skilltest_sdk/__init__.py +0 -0
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/skilltest_sdk/_report.py +0 -0
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/skilltest_sdk/_validation.py +0 -0
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/skilltest_sdk/errors.py +0 -0
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/skilltest_sdk/models.py +0 -0
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/tests/conftest.py +0 -0
- {skilltest_sdk-0.1.1 → skilltest_sdk-0.2.2}/tests/test_api.py +0 -0
|
@@ -15,6 +15,11 @@ dist/
|
|
|
15
15
|
node_modules/
|
|
16
16
|
*.tsbuildinfo
|
|
17
17
|
|
|
18
|
+
# Bundled CLI binaries injected at release time into the per-platform npm
|
|
19
|
+
# packages and the Python wheel; never committed — built/downloaded at publish.
|
|
20
|
+
/sdks/typescript/platforms/*/bin/
|
|
21
|
+
/sdks/python/skilltest_sdk/_bin/
|
|
22
|
+
|
|
18
23
|
# Nx
|
|
19
24
|
.nx/cache
|
|
20
25
|
.nx/workspace-data
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "skilltest-sdk"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.2"
|
|
4
4
|
description = "Python SDK for the skilltest CLI: run AI-skill tests and natural-language evals from Python, with a typed report contract."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -25,6 +25,12 @@ build-backend = "hatchling.build"
|
|
|
25
25
|
[tool.hatch.build.targets.wheel]
|
|
26
26
|
packages = ["skilltest_sdk"]
|
|
27
27
|
|
|
28
|
+
# A platform build drops the prebuilt CLI at skilltest_sdk/_bin/skilltest (which
|
|
29
|
+
# is git-ignored), then `scripts/build-python-wheel.sh` retags the wheel for the
|
|
30
|
+
# target platform. Listing it as an artifact overrides the VCS-ignore so the
|
|
31
|
+
# binary is packed; the pure (py3-none-any) build simply has nothing to match.
|
|
32
|
+
artifacts = ["skilltest_sdk/_bin/*"]
|
|
33
|
+
|
|
28
34
|
[tool.ruff]
|
|
29
35
|
line-length = 100
|
|
30
36
|
target-version = "py312"
|
|
@@ -7,6 +7,7 @@ deterministic checks against the transcript.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import contextlib
|
|
10
11
|
import os
|
|
11
12
|
import subprocess
|
|
12
13
|
from collections.abc import Sequence
|
|
@@ -26,10 +27,37 @@ ENV_PROVIDER = "SKILLTEST_PROVIDER"
|
|
|
26
27
|
_REPORTING_CODES = frozenset({0, 1})
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
#: Name of the bundled binary inside the wheel's ``_bin/`` directory.
|
|
31
|
+
_BIN_NAME = "skilltest.exe" if os.name == "nt" else "skilltest"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _bundled_bin() -> str | None:
|
|
35
|
+
"""Path to the binary bundled in this wheel, or ``None`` when absent.
|
|
36
|
+
|
|
37
|
+
Platform wheels ship the prebuilt CLI at ``skilltest_sdk/_bin/skilltest``;
|
|
38
|
+
the pure (``py3-none-any``) wheel and a source checkout ship none, so callers
|
|
39
|
+
fall back to ``$SKILLTEST_BIN``/``PATH``. Wheel packing can drop the
|
|
40
|
+
executable bit, so restore it best-effort before handing back the path.
|
|
41
|
+
"""
|
|
42
|
+
candidate = Path(__file__).resolve().parent / "_bin" / _BIN_NAME
|
|
43
|
+
if not candidate.is_file():
|
|
44
|
+
return None
|
|
45
|
+
if not os.access(candidate, os.X_OK):
|
|
46
|
+
with contextlib.suppress(OSError):
|
|
47
|
+
candidate.chmod(0o755)
|
|
48
|
+
return str(candidate)
|
|
49
|
+
|
|
50
|
+
|
|
29
51
|
def _resolve_bin(bin: str | Path | None) -> str:
|
|
52
|
+
"""Resolve the binary, most explicit first: an explicit ``bin``, then
|
|
53
|
+
``$SKILLTEST_BIN``, then the binary bundled in a platform wheel, then
|
|
54
|
+
``skilltest`` on ``PATH``."""
|
|
30
55
|
if bin is not None:
|
|
31
56
|
return str(bin)
|
|
32
|
-
|
|
57
|
+
env = os.environ.get(ENV_BIN)
|
|
58
|
+
if env:
|
|
59
|
+
return env
|
|
60
|
+
return _bundled_bin() or "skilltest"
|
|
33
61
|
|
|
34
62
|
|
|
35
63
|
def _resolve_provider(provider: str | Sequence[str] | None) -> str | None:
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Unit tests for binary resolution: the precedence chain (explicit > env >
|
|
2
|
+
bundled wheel binary > PATH) and that a binary bundled in the wheel's ``_bin/``
|
|
3
|
+
is discovered when present.
|
|
4
|
+
|
|
5
|
+
A source checkout and the pure (``py3-none-any``) wheel ship no binary, so
|
|
6
|
+
``_bundled_bin()`` is ``None`` and the runner falls back — exactly how the e2e
|
|
7
|
+
suite reaches the locally built CLI via ``$SKILLTEST_BIN``.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
from skilltest_sdk import runner
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_explicit_bin_wins(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
20
|
+
monkeypatch.setenv(runner.ENV_BIN, "/from/env")
|
|
21
|
+
assert runner._resolve_bin("/explicit") == "/explicit"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_env_beats_bundled_and_path(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
25
|
+
monkeypatch.setenv(runner.ENV_BIN, "/from/env")
|
|
26
|
+
assert runner._resolve_bin(None) == "/from/env"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_falls_back_to_path_when_unset_and_unbundled(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
30
|
+
monkeypatch.delenv(runner.ENV_BIN, raising=False)
|
|
31
|
+
assert runner._bundled_bin() is None
|
|
32
|
+
assert runner._resolve_bin(None) == "skilltest"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_bundled_binary_is_found_and_preferred(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
36
|
+
monkeypatch.delenv(runner.ENV_BIN, raising=False)
|
|
37
|
+
bin_dir = Path(runner.__file__).resolve().parent / "_bin"
|
|
38
|
+
bin_path = bin_dir / runner._BIN_NAME
|
|
39
|
+
bin_dir.mkdir(parents=True, exist_ok=True)
|
|
40
|
+
try:
|
|
41
|
+
bin_path.write_text("#!/bin/sh\n")
|
|
42
|
+
bin_path.chmod(0o755)
|
|
43
|
+
assert runner._bundled_bin() == str(bin_path)
|
|
44
|
+
assert runner._resolve_bin(None) == str(bin_path)
|
|
45
|
+
finally:
|
|
46
|
+
bin_path.unlink(missing_ok=True)
|
|
47
|
+
if bin_dir.is_dir() and not any(bin_dir.iterdir()):
|
|
48
|
+
bin_dir.rmdir()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|