Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QNQWU3ukHBLpBb9driYFi8
174 lines
8.0 KiB
Python
174 lines
8.0 KiB
Python
"""Module control: the project's switch and the persistent quarantine."""
|
|
|
|
import types
|
|
|
|
from core import control, patching
|
|
|
|
|
|
def spec(mid, provides=("modifier",), source="nowhere"):
|
|
return types.SimpleNamespace(id=mid, provides={p: (lambda *a, **k: None) for p in provides}, source=source)
|
|
|
|
|
|
def test_only_modules_that_modify_a_run_can_be_switched():
|
|
assert control.controllable(spec("a", ("modifier",)))
|
|
assert control.controllable(spec("a", ("sampler_modifier",)))
|
|
assert not control.controllable(spec("loader", ("routes",)))
|
|
|
|
|
|
def test_a_module_the_project_turned_off_is_left_out_and_said():
|
|
kept, notes = control.partition([spec("a"), spec("b")], {"_off": {"modules": ["a"]}})
|
|
assert [s.id for s in kept] == ["b"] and "a: turned off for this project" in notes[0]
|
|
|
|
|
|
def test_structure_cannot_be_turned_off():
|
|
kept, _ = control.partition([spec("loader", ("routes",))], {"_off": {"modules": ["loader"]}})
|
|
assert [s.id for s in kept] == ["loader"]
|
|
|
|
|
|
def test_a_fault_is_remembered_across_runs_until_released():
|
|
a = spec("a")
|
|
control.quarantine(a, "RuntimeError: boom")
|
|
kept, notes = control.partition([a], {})
|
|
assert kept == [] and "a: OFF" in notes[0] and "boom" in notes[0] and "Settings ▸ Modules" in notes[0]
|
|
assert control.fingerprint() == "a"
|
|
assert control.release("a") and not control.release("a")
|
|
assert [s.id for s in control.partition([a], {})[0]] == ["a"]
|
|
|
|
|
|
def test_a_module_whose_code_changed_since_it_failed_gets_another_try(monkeypatch):
|
|
a = spec("a")
|
|
monkeypatch.setattr(control, "signature", lambda s: "v1")
|
|
control.quarantine(a, "boom")
|
|
monkeypatch.setattr(control, "signature", lambda s: "v2")
|
|
assert [s.id for s in control.partition([a], {})[0]] == ["a"]
|
|
assert control.fingerprint() == ""
|
|
|
|
|
|
def test_an_interrupt_or_out_of_memory_is_not_the_modules_fault(monkeypatch):
|
|
from core import registry
|
|
a = spec("a")
|
|
monkeypatch.setattr(registry, "current", lambda: types.SimpleNamespace(specs={"a": a}))
|
|
|
|
class InterruptProcessingException(Exception): ...
|
|
class OutOfMemoryError(Exception): ...
|
|
|
|
control.fault("funui.a", InterruptProcessingException())
|
|
control.fault("funui.a", OutOfMemoryError())
|
|
assert control.fingerprint() == ""
|
|
control.fault("funui.a", ValueError("bad"))
|
|
assert control.fingerprint() == "a"
|
|
|
|
|
|
def test_a_hook_that_raised_in_a_run_reaches_the_quarantine(monkeypatch):
|
|
from core import registry
|
|
a = spec("a")
|
|
monkeypatch.setattr(registry, "current", lambda: types.SimpleNamespace(specs={"a": a}))
|
|
dropped = patching.Dropped()
|
|
assert dropped.record("funui.a", RuntimeError("step 3"))
|
|
assert "a" in control.quarantined([a]) and "step 3" in control.quarantined([a])["a"]["reason"]
|
|
|
|
|
|
def test_a_malformed_off_entry_is_named():
|
|
assert control.bad_off({"modules": ["a"]}) is None and control.bad_off(None) is None
|
|
assert "modules" in control.bad_off(["a"]) and "modules" in control.bad_off({"modules": [1]})
|
|
|
|
|
|
def test_an_off_or_quarantined_modules_values_are_not_checked(monkeypatch):
|
|
from core import registry
|
|
a, b = spec("a"), spec("b")
|
|
monkeypatch.setattr(registry, "current", lambda: types.SimpleNamespace(specs={"a": a, "b": b}))
|
|
control.quarantine(b, "boom")
|
|
assert control.skipped({"_off": {"modules": ["a"]}}) == {"a", "b"}
|
|
assert control.skipped({}) == {"b"}
|
|
|
|
|
|
def test_edited_code_that_this_process_has_not_loaded_stays_off_and_says_restart(monkeypatch):
|
|
a = spec("a")
|
|
sig = {"now": "v1"}
|
|
monkeypatch.setattr(control, "signature", lambda s: sig["now"])
|
|
control._loaded["a"] = "v1"
|
|
control.quarantine(a, "boom")
|
|
sig["now"] = "v2" # edited, not restarted
|
|
kept, notes = control.partition([a], {})
|
|
assert kept == [] and "restart ComfyUI" in notes[0]
|
|
control._loaded["a"] = "v2" # after a restart the new code is what ran
|
|
assert [s.id for s in control.partition([a], {})[0]] == ["a"]
|
|
|
|
|
|
def test_a_quarantine_that_cannot_be_written_still_holds_for_the_session(monkeypatch, tmp_path):
|
|
a = spec("a")
|
|
monkeypatch.setattr(control.config, "QUARANTINE_FILE", tmp_path / "no" / "such" / "dir" / "q.json")
|
|
control.quarantine(a, "boom")
|
|
assert control.partition([a], {})[0] == []
|
|
control.release("a")
|
|
assert [s.id for s in control.partition([a], {})[0]] == ["a"]
|
|
|
|
|
|
def test_running_out_of_memory_by_message_is_not_the_modules_fault(monkeypatch):
|
|
from core import registry
|
|
a = spec("a")
|
|
monkeypatch.setattr(registry, "current", lambda: types.SimpleNamespace(specs={"a": a}))
|
|
control.fault("funui.a", RuntimeError("MPS backend out of memory (MPS allocated: 9 GiB)"))
|
|
assert control.fingerprint() == ""
|
|
|
|
|
|
|
|
def test_a_module_that_cannot_run_here_is_said_but_not_quarantined():
|
|
control.start_failed(spec("a"), control.Unavailable("needs a newer ComfyUI"))
|
|
assert control.fingerprint() == ""
|
|
control.start_failed(spec("a"), ValueError("a real bug"))
|
|
assert control.fingerprint() == "a"
|
|
|
|
|
|
def test_disable_all_enhancements_sits_out_every_modifier_and_nothing_else():
|
|
kept, notes = control.partition([spec("a"), spec("loader", ("routes",))], {"_off": {"all": True}})
|
|
assert [s.id for s in kept] == ["loader"] and "all enhancements" in notes[0]
|
|
assert control.all_off({"_off": {"all": True}}) and not control.all_off({"_off": {"all": "yes"}}) and not control.all_off({})
|
|
assert control.bad_off({"modules": [], "all": "yes"}) and control.bad_off({"modules": [], "all": True}) is None
|
|
|
|
|
|
def test_all_alone_is_a_valid_payload():
|
|
assert control.bad_off({"all": True}) is None and control.bad_off({"all": False}) is None
|
|
assert control.bad_off({"all": "yes"}) and control.bad_off({"modules": "x"})
|
|
|
|
|
|
def test_a_saved_turn_off_for_a_module_that_is_gone_is_said_once():
|
|
from core import log
|
|
log._reset()
|
|
control.partition([spec("b")], {"_off": {"modules": ["gone"]}})
|
|
control.partition([spec("b")], {"_off": {"modules": ["gone"]}})
|
|
said = [r for r in log.history() if "'gone'" in r["message"]]
|
|
assert len(said) == 1 and "ignored" in said[0]["message"]
|
|
|
|
|
|
def test_a_turned_off_module_that_is_loaded_but_not_in_the_filtered_list_is_not_called_gone(monkeypatch):
|
|
"""Callers pass a filtered list (by capability, by the model's traits); "gone" means absent from the registry."""
|
|
from core import log, registry
|
|
log._reset()
|
|
reg = registry.Registry()
|
|
reg.specs["sharpen"] = spec("sharpen")
|
|
monkeypatch.setattr(registry, "current", lambda rescan=False: reg)
|
|
control.partition([spec("other")], {"_off": {"modules": ["sharpen"]}})
|
|
control.partition([spec("other")], {"_off": {"all": True}})
|
|
assert not [r for r in log.history() if "no module with that id" in r["message"]]
|
|
|
|
|
|
def test_camera_switch_is_its_own_flag_and_leaves_all_enhancements_alone():
|
|
assert control.camera_off({"_off": {"camera": True}}) and not control.camera_off({"_off": {"camera": "yes"}}) and not control.camera_off({})
|
|
assert not control.all_off({"_off": {"camera": True}})
|
|
assert control.bad_off({"camera": True}) is None and control.bad_off({"camera": "yes"})
|
|
kept, _ = control.partition([spec("a")], {"_off": {"camera": True}})
|
|
assert [s.id for s in kept] == ["a"]
|
|
|
|
|
|
def test_guidance_switch_turns_off_guidance_modifiers_only(monkeypatch):
|
|
from core import registry
|
|
specs = {"g": types.SimpleNamespace(id="g", provides={"modifier": 1}, category="guidance"),
|
|
"s": types.SimpleNamespace(id="s", provides={"modifier": 1}, category="sampling"),
|
|
"l": types.SimpleNamespace(id="l", provides={}, category="guidance")}
|
|
monkeypatch.setattr(registry, "current", lambda rescan=False: types.SimpleNamespace(specs=specs))
|
|
assert control.guidance_off({"_off": {"guidance": True}}) and not control.guidance_off({"_off": {"guidance": 1}})
|
|
assert control.bad_off({"guidance": True}) is None and control.bad_off({"guidance": "yes"})
|
|
assert control.off_by_project({"_off": {"guidance": True}}) == {"g"}
|
|
assert control.off_by_project({"_off": {"guidance": False, "modules": ["s"]}}) == {"s"}
|