Bump version to 0.10.11 and update installation instructions; enhance progress reporting and estimated time features

This commit is contained in:
2026-08-09 10:23:14 +08:00
parent 9c896213a5
commit 97a720051f
12 changed files with 382 additions and 150 deletions

View File

@@ -611,7 +611,7 @@ class AddonTest(unittest.TestCase):
},
"fallback",
),
"3/8 pytorch: 2 downloaded",
"3/8 pytorch | 2 downloaded",
)
self.assertEqual(
operator._format_status(
@@ -636,6 +636,33 @@ class AddonTest(unittest.TestCase):
),
"Estimating depth",
)
self.assertEqual(
operator._format_status(
{
"progress": 0.25,
"message": "Downloading packages",
"stage": 2,
"stages": 3,
"stage_label": "PyTorch",
"eta_seconds": 125,
},
"fallback",
),
"2/3 PyTorch | Downloading packages | About 3 min left",
)
self.assertEqual(
operator._format_status(
{
"progress": 0.5,
"message": "",
"stage": 2,
"stages": 3,
"stage_label": "PyTorch",
},
"2/3 PyTorch | Downloading packages",
),
"2/3 PyTorch",
)
def test_model_options_mark_only_models_that_are_not_downloaded(self):
items_callback = self.addon.SnapFormSettings.__annotations__["model"][

View File

@@ -153,7 +153,13 @@ class BootstrapRuntimeTest(unittest.TestCase):
)
)
]
self.assertEqual(len(default_downloads), 3)
self.assertEqual(len(default_downloads), 2)
self.assertFalse(
any(
"depth-anything/DA3-BASE" in map(str, command)
for command in default_downloads
)
)
ben2_download = next(
command
for command in default_downloads
@@ -165,9 +171,12 @@ class BootstrapRuntimeTest(unittest.TestCase):
if value == "--allow-pattern"
]
self.assertEqual(patterns, ["config.json", "model.safetensors"])
self.assertIn("--stage", ben2_download)
self.assertIn("--stage-label", ben2_download)
self.assertEqual(ben2_download[-1], "ben2 base")
self.assertNotIn("--stage", ben2_download)
self.assertNotIn("--stage-label", ben2_download)
self.assertEqual(
ben2_download[ben2_download.index("--label") + 1],
"BEN2 Base",
)
def test_darwin_install_skips_cuda_index_and_xformers(self):
bootstrap = load_bootstrap_runtime()
@@ -400,7 +409,15 @@ class BootstrapRuntimeTest(unittest.TestCase):
all(starts[i] < starts[i + 1] for i in range(len(starts) - 1))
)
self.assertEqual(ends[-1], 1.0)
self.assertEqual(starts[0], bootstrap.RUNTIME_SLICE_START)
self.assertEqual(starts[0], bootstrap.ENVIRONMENT_SLICE[0])
runtime_ranges = stage_ranges[:4]
self.assertEqual(
[item[0] for item in runtime_ranges],
[1, 2, 3, 3],
)
self.assertTrue(
all(item[0] is None for item in stage_ranges[4:])
)
status = json.loads(
(root / "status.json").read_text(encoding="utf-8")
)
@@ -443,9 +460,9 @@ class BootstrapRuntimeTest(unittest.TestCase):
self.assertEqual(
messages,
[
"3 packages",
"3/3 packages",
"3/3 packages",
"Resolving 3 packages",
"Downloading packages 3/3",
"Installing packages 3/3",
],
)
self.assertEqual(mocked_write.call_args_list[0].args[1], 0.02)
@@ -508,8 +525,8 @@ class BootstrapRuntimeTest(unittest.TestCase):
)
)
messages = [call.args[2] for call in mocked_write.call_args_list]
self.assertIn("3 packages", messages)
self.assertIn("3/3 packages", messages)
self.assertIn("Resolving 3 packages", messages)
self.assertIn("Downloading packages 3/3", messages)
def test_run_install_maps_progress_into_stage_range(self):
bootstrap = load_bootstrap_runtime()
@@ -585,6 +602,22 @@ class BootstrapRuntimeTest(unittest.TestCase):
self.assertEqual(bootstrap.format_elapsed(12), "12s")
self.assertEqual(bootstrap.format_elapsed(83), "1m 23s")
def test_ai_component_estimate_continues_across_internal_steps(self):
bootstrap = load_bootstrap_runtime()
first = bootstrap.estimated_remaining(
"AI components",
bootstrap.XFORMERS_SLICE[0],
)
second = bootstrap.estimated_remaining(
"AI components",
bootstrap.AI_COMPONENTS_SLICE[0],
)
self.assertEqual(first, bootstrap.INSTALL_ESTIMATES["AI components"])
self.assertGreater(second, 0)
self.assertLess(second, first)
def test_resolve_indexes_uses_configured_pypi_index(self):
bootstrap = load_bootstrap_runtime()
bootstrap.resolve_indexes(
@@ -739,8 +772,10 @@ class BootstrapRuntimeTest(unittest.TestCase):
def test_model_download_failure_keeps_runtime_ready(self):
bootstrap = load_bootstrap_runtime()
commands = []
def fake_run(command, environment=None):
commands.append(command)
if any("DA3-SMALL" in str(value) for value in command):
raise subprocess.CalledProcessError(1, command)
@@ -775,6 +810,12 @@ class BootstrapRuntimeTest(unittest.TestCase):
)
self.assertEqual(status["progress"], 1.0)
self.assertIn("Runtime is ready", status["message"])
self.assertTrue(
any(
"PramaLLC/BEN2" in map(str, command)
for command in commands
)
)
def test_install_fails_when_bundled_uv_missing(self):
bootstrap = load_bootstrap_runtime()
@@ -806,6 +847,10 @@ class BootstrapRuntimeTest(unittest.TestCase):
), patch.object(
bootstrap.shutil,
"rmtree",
), patch.object(
bootstrap,
"bundled_uv_archive",
return_value=None,
):
with self.assertRaisesRegex(
RuntimeError,
@@ -1011,7 +1056,7 @@ class BootstrapRuntimeTest(unittest.TestCase):
self.assertIn(f"depth-anything-3 @ {da3_local}", ai_command)
self.assertIn(f"ben2 @ {ben2_local}", ai_command)
def test_install_stages_download_models_into_prefixed_directories(self):
def test_install_stages_download_only_default_models(self):
bootstrap = load_bootstrap_runtime()
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
@@ -1045,9 +1090,42 @@ class BootstrapRuntimeTest(unittest.TestCase):
for label, command, _environment, kind in stages
if kind == "download"
}
self.assertIn("da3_small", " ".join(map(str, downloads["da3 small"])))
self.assertIn("da3_base", " ".join(map(str, downloads["da3 base"])))
self.assertIn("ben2_base", " ".join(map(str, downloads["ben2 base"])))
self.assertIn("da3_small", " ".join(map(str, downloads["DA3 Small"])))
self.assertIn("ben2_base", " ".join(map(str, downloads["BEN2 Base"])))
self.assertNotIn("DA3 Base", downloads)
def test_install_does_not_verify_runtime(self):
bootstrap = load_bootstrap_runtime()
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
runtime_dir = root / "runtime"
runtime_dir.mkdir()
self.create_bundled_sources(root)
uv_path = root / "uv"
uv_path.write_bytes(b"uv")
args = SimpleNamespace(
uv=str(uv_path),
runtime_dir=str(runtime_dir),
models_dir=str(root / "models"),
backend="cu128",
da3_commit="test-commit",
ben2_commit="test-ben2-commit",
status=str(root / "status.json"),
)
with patch.object(
bootstrap,
"resolve_indexes",
return_value=None,
), patch.object(bootstrap, "run"), patch.object(
bootstrap,
"run_install",
), patch.object(
bootstrap,
"run_verify",
) as mocked_verify:
bootstrap.install(args)
mocked_verify.assert_not_called()
def test_run_verify_returns_detected_devices(self):
bootstrap = load_bootstrap_runtime()
@@ -1111,7 +1189,7 @@ class BootstrapRuntimeTest(unittest.TestCase):
messages = [call.args[2] for call in mocked_write.call_args_list]
self.assertTrue(
any("verifying runtime" in message for message in messages)
any("Checking Runtime" in message for message in messages)
)
self.assertEqual(report, {})

View File

@@ -58,7 +58,7 @@ class DownloadProgressTest(unittest.TestCase):
status = json.loads(status_path.read_text(encoding="utf-8"))
self.assertAlmostEqual(status["progress"], 0.275)
self.assertEqual(status["message"], "downloading owner/model")
self.assertEqual(status["message"], "Downloading owner/model")
def test_reports_download_speed(self):
class FakeTqdmWithRate(FakeTqdm):
@@ -87,9 +87,31 @@ class DownloadProgressTest(unittest.TestCase):
status = json.loads(status_path.read_text(encoding="utf-8"))
self.assertEqual(
status["message"],
"downloading da3 small 13.4 MB/s",
"Downloading da3 small | 13.4 MB/s",
)
def test_reports_estimated_time_remaining(self):
times = iter((0.0, 10.0))
with tempfile.TemporaryDirectory() as directory:
status_path = Path(directory) / "status.json"
progress_class = self.worker.create_download_progress_class(
status_path,
"owner/model",
tqdm_type=FakeTqdm,
clock=lambda: next(times),
)
progress = progress_class(
desc="Downloading bytes",
total=100,
initial=0,
unit="B",
)
progress.update(50)
status = json.loads(status_path.read_text(encoding="utf-8"))
self.assertEqual(status["eta_seconds"], 10)
def test_staged_download_prefixes_speed_with_downloading(self):
class FakeTqdmWithRate(FakeTqdm):
@property
@@ -118,7 +140,10 @@ class DownloadProgressTest(unittest.TestCase):
progress.update(50)
status = json.loads(status_path.read_text(encoding="utf-8"))
self.assertEqual(status["message"], "downloading 13.4 MB/s")
self.assertEqual(
status["message"],
"Downloading packages | 13.4 MB/s",
)
self.assertEqual(status["stage"], 6)
self.assertEqual(status["stages"], 8)
self.assertEqual(status["stage_label"], "da3 small")