Renaming the combo's choices to core's spellings broke queueing for anyone who
had one of the old ones saved: ComfyUI validates combo VALUES at queue time,
before the node ever runs, so the alias map added with the rename never got a
chance. The three legacy names are back in the list (they resolve through the
alias map at runtime), and the Editor now displays a stored legacy value as the
name it maps to and writes that back, so the dropdown can't show one setting
while another runs.
And that rejection was unreadable. ComfyUI's top-level `error` for a validation
failure is a fixed blob ("Prompt outputs failed validation", details ""); the
part naming the node, the widget and the bad value is in `node_errors`, which
`error or node_errors` never reached because the blob is always truthy. Every
rejected prompt therefore reported the same nothing. Both are formatted now:
Prompt outputs failed validation; FunPackLTXAVSceneChainSampler: Value not in
list — context_window_schedule: 'static_standard' not in [...]
Also: the progress poll no longer imports anything. current_progress() reads
run_phase's label straight off `sys`. It fires every 700ms on ComfyUI's event
loop WHILE the worker thread samples, and that is not a path to do import work
on — the direct read is both cheaper and the correct one, since run_phase keeps
its state there anyway.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
65 lines
2.6 KiB
Python
65 lines
2.6 KiB
Python
"""run_phase — the live "what is the sampler doing" label behind the progress readout.
|
|
|
|
The one invariant that actually breaks in production: the WRITER (samplers.py) reaches this
|
|
module as a package-relative import and the READER (movie_editor's bridge) imports FunPack
|
|
modules top-level by name, so the two can hold different module objects. State in module
|
|
globals would be two separate copies and the label would never arrive. It lives on `sys`
|
|
for exactly that reason, and this pins it down.
|
|
"""
|
|
import importlib.util
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
import run_phase # noqa: E402
|
|
|
|
|
|
def _second_import():
|
|
"""A second, independent module object for the same file — what the other import path
|
|
produces at runtime."""
|
|
spec = importlib.util.spec_from_file_location("other_pkg.run_phase", ROOT / "run_phase.py")
|
|
mod = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(mod)
|
|
return mod
|
|
|
|
|
|
def test_both_import_paths_see_the_same_label():
|
|
other = _second_import()
|
|
run_phase.set_phase("scene 2/3 · pass 2 of 2")
|
|
assert other.current()["label"] == "scene 2/3 · pass 2 of 2"
|
|
other.clear()
|
|
assert run_phase.current()["label"] == ""
|
|
|
|
|
|
def test_seq_only_ever_increases():
|
|
"""A poller uses it to tell "still the same phase" from "this phase came round again",
|
|
so it must never reset — not even when the label is cleared."""
|
|
before = run_phase.current()["seq"]
|
|
run_phase.set_phase("a")
|
|
run_phase.set_phase("a")
|
|
run_phase.clear()
|
|
assert run_phase.current()["seq"] == before + 3
|
|
|
|
|
|
def test_a_bad_label_never_raises():
|
|
"""This is a readout on the sampling path: it must not be able to fail a render."""
|
|
run_phase.set_phase(None)
|
|
assert run_phase.current()["label"] == ""
|
|
run_phase.set_phase(12)
|
|
assert run_phase.current()["label"] == "12"
|
|
run_phase.clear()
|
|
|
|
|
|
def test_the_bridge_reads_the_same_sys_key_without_importing_this_module():
|
|
"""movie_editor's progress poll runs every 700ms on ComfyUI's event loop WHILE the
|
|
worker thread samples, so it reads the label straight off `sys` rather than importing
|
|
anything. That makes the key name a contract between two files — pin it."""
|
|
src = (ROOT / "movie_editor" / "backend" / "bridge.py").read_text()
|
|
assert run_phase._SYS_KEY == "_funpack_run_phase"
|
|
assert f'getattr(sys, "{run_phase._SYS_KEY}"' in src
|
|
# ...and the poll must not have grown an import back.
|
|
poll = src.split("def current_progress(")[1].split("\ndef ")[0]
|
|
assert "import_module" not in poll and "_funpack_attr" not in poll
|