mirror of
https://github.com/Comfy-Org/ComfyUI.git
synced 2026-08-25 10:22:12 +08:00
Do not stamp past references the reset could not classify
A reference whose file is on disk but whose path is under no configured root was skipped so that a missing extra_model_paths.yaml could not strip tags off assets that were merely out of view. But the step still finished successfully and the version was still stamped, so restoring the root repaired nothing: the step never ran again, and no other path re-derives an existing reference's loader_path -- the scan only builds specs for paths it has not seen, and the enricher computes a loader path without writing it back. The guard turned destroying the data into never repairing it. A step now reports whether it finished its work, and an unfinished one is not stamped. A file that is simply gone still does not count as unfinished; that is the scan's business under its own missing semantics. Two ordering fixes in the scan around the same call: The walk is handed the seeder's pause-aware checkpoint rather than a cancel-only check. The seeder is paused while a prompt runs, so a check that ignores pause left the reset statting the whole asset table during generation while the status reported PAUSED. Cancellation is checked immediately after the reset instead of after the prune. Cancelling during a long reprojection otherwise fell through into the prune and temp reconciliation, doing state-mutating work on a scan that had been told to stop.
This commit is contained in:
@@ -563,6 +563,93 @@ class TestSeederSemanticsReset:
|
||||
assert captured["check"]() is True
|
||||
|
||||
|
||||
def test_semantics_reset_suspends_on_pause(self, fresh_seeder: _AssetSeeder):
|
||||
"""A pause must stop the walk between batches, not just a cancel.
|
||||
|
||||
The seeder is paused while a prompt runs, so a check that ignores pause
|
||||
keeps statting the whole asset table during generation.
|
||||
"""
|
||||
captured = {}
|
||||
|
||||
with (
|
||||
patch("app.assets.seeder.dependencies_available", return_value=True),
|
||||
patch(
|
||||
"app.assets.seeder.run_pending_semantics_steps",
|
||||
side_effect=lambda interrupt_check=None: captured.update(
|
||||
check=interrupt_check
|
||||
),
|
||||
),
|
||||
patch("app.assets.seeder.sync_root_safely", return_value=set()),
|
||||
patch("app.assets.seeder.collect_paths_for_roots", return_value=[]),
|
||||
patch("app.assets.seeder.build_asset_specs", return_value=([], set(), 0)),
|
||||
patch("app.assets.seeder.insert_asset_specs", return_value=0),
|
||||
patch("app.assets.seeder.get_unenriched_assets_for_roots", return_value=[]),
|
||||
patch("app.assets.seeder.enrich_assets_batch", return_value=(0, 0)),
|
||||
):
|
||||
fresh_seeder.start(roots=("models",))
|
||||
fresh_seeder.wait(timeout=5.0)
|
||||
|
||||
check = captured.get("check")
|
||||
assert check is not None
|
||||
|
||||
fresh_seeder._run_gate.clear()
|
||||
returned = threading.Event()
|
||||
|
||||
def _call_check():
|
||||
check()
|
||||
returned.set()
|
||||
|
||||
threading.Thread(target=_call_check, daemon=True).start()
|
||||
assert not returned.wait(timeout=0.5), (
|
||||
"the interrupt check must block while paused, not report 'keep going'"
|
||||
)
|
||||
|
||||
fresh_seeder._run_gate.set()
|
||||
assert returned.wait(timeout=5.0)
|
||||
|
||||
def test_cancel_during_semantics_reset_stops_before_pruning(
|
||||
self, fresh_seeder: _AssetSeeder
|
||||
):
|
||||
"""A cancelled reset must not fall through into the prune's writes."""
|
||||
calls = []
|
||||
|
||||
def _cancel_during_reset(interrupt_check=None):
|
||||
calls.append("reset")
|
||||
fresh_seeder._cancel_event.set()
|
||||
|
||||
with (
|
||||
patch("app.assets.seeder.dependencies_available", return_value=True),
|
||||
patch(
|
||||
"app.assets.seeder.run_pending_semantics_steps",
|
||||
side_effect=_cancel_during_reset,
|
||||
),
|
||||
patch("app.assets.seeder.get_owned_prefixes", return_value=["/models"]),
|
||||
patch(
|
||||
"app.assets.seeder.mark_missing_outside_prefixes_safely",
|
||||
side_effect=lambda prefixes: calls.append("prune") or 0,
|
||||
),
|
||||
patch(
|
||||
"app.assets.seeder.sync_temp_references_safely",
|
||||
side_effect=lambda: calls.append("sync_temp"),
|
||||
),
|
||||
patch(
|
||||
"app.assets.seeder.sync_root_safely",
|
||||
side_effect=lambda root: calls.append("sync") or set(),
|
||||
),
|
||||
patch("app.assets.seeder.collect_paths_for_roots", return_value=[]),
|
||||
patch("app.assets.seeder.build_asset_specs", return_value=([], set(), 0)),
|
||||
patch("app.assets.seeder.insert_asset_specs", return_value=0),
|
||||
patch("app.assets.seeder.get_unenriched_assets_for_roots", return_value=[]),
|
||||
patch("app.assets.seeder.enrich_assets_batch", return_value=(0, 0)),
|
||||
):
|
||||
fresh_seeder.start(roots=("models",), prune_first=True)
|
||||
fresh_seeder.wait(timeout=5.0)
|
||||
|
||||
assert calls == ["reset"], (
|
||||
f"cancel must stop the scan before the prune's writes, got {calls}"
|
||||
)
|
||||
|
||||
|
||||
class TestSeederPhases:
|
||||
"""Test phased scanning behavior."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user