Files
olivv-cs-ComfyUI-FunPack/core/tests/test_traits.py
T
digital-garbage 65b35e9cf3 log: one function for "it loaded and then broke", said in six places
The wording fix a commit ago was written by hand in two files and the same
mislabel was still sitting in four others -- traits, chain, modifier install and
node schema all reported a thing that loaded, recognised its work and then
raised as one that "did not load", sending a reader after an import error that
never happened. Three hand-written strings would have drifted, so log.broke()
now says it once and every site calls it.

Two of the new tests were vacuous when first written -- "nothing said did not
load" is also true where nothing ran at all, and picking the first provider
found lands on one whose traits exclude the test's model. Both assert the line
is present before asserting the wrong line is not.

One finding, confirmed, plus four sites it did not name.
2026-08-29 23:21:23 +03:00

225 lines
7.4 KiB
Python

"""Traits: what is read off a model, and what a model's own module contributes.
The split under test is the one the architecture depends on -- core reads only
facts that need no knowledge of any particular model, and everything else is
announced. If core ever learned a model's name, supporting the next one would
mean editing core, which is the whole thing this avoids.
"""
import pytest
from core.contract import ModuleSpec
from core.traits import contributed, has_block, missing_for, split, traits_of, universal
class _LatentFormat:
def __init__(self, dims, tdr=1):
self.latent_dimensions = dims
self.temporal_downscale_ratio = tdr
class _Kind:
def __init__(self, name):
self.name = name
class _Model:
"""The shape traits_of() reads: patcher -> model -> model_config."""
def __init__(self, dims=3, tdr=4, kind="FLOW"):
outer = self
class Inner:
model_type = _Kind(kind)
class model_config:
latent_format = _LatentFormat(dims, tdr)
self.model = Inner()
del outer
def test_latent_rank_is_read_not_guessed():
assert "temporal_latent" in universal(_Model(dims=3))
assert "spatial_latent" in universal(_Model(dims=2))
assert "waveform_latent" in universal(_Model(dims=1))
def test_the_prediction_type_is_named():
assert "predict_flow" in universal(_Model(kind="FLOW"))
assert "predict_eps" in universal(_Model(kind="EPS"))
def test_an_unreadable_model_yields_nothing_rather_than_raising():
class Bare:
model = None
assert traits_of(Bare()) == []
def test_a_module_contributes_traits_core_could_not_read():
spec = ModuleSpec(id="m", title="M", mount="", traits=lambda model: ["audio_stream"])
assert "audio_stream" in traits_of(_Model(), [spec])
def test_a_provider_that_raises_does_not_silence_the_others():
def explodes(model):
raise RuntimeError("boom")
bad = ModuleSpec(id="bad", title="B", mount="", traits=explodes)
good = ModuleSpec(id="good", title="G", mount="", traits=lambda m: ["audio_stream"])
found = traits_of(_Model(), [bad, good])
assert "audio_stream" in found, "one bad provider disabled every other module"
assert "temporal_latent" in found
def test_a_provider_that_raises_is_not_reported_as_one_that_never_loaded():
"""The log line, not only the surviving traits.
A traits provider checks whether the model is its own FIRST, so reaching the
exception means it loaded, recognised the model and then broke. Saying "did
not load" sends a reader after an import error that never happened -- and
losing a model's traits quietly narrows which modifiers are considered
compatible, so the log is the only thing pointing at the cause.
"""
from core import log
def explodes(model):
raise RuntimeError("adaln lookup exploded")
log._reset()
traits_of(_Model(), [ModuleSpec(id="bad", title="B", mount="", traits=explodes)])
said = log.history()
assert said, "a provider broke and nothing was said"
assert not any("did not load" in r["message"] for r in said), \
[r["message"] for r in said]
assert any("failed while" in r["message"] for r in said), [r["message"] for r in said]
assert said[-1]["level"] == log.WARNING
def test_declaring_no_traits_means_compatible_with_everything():
# Narrowing, not opting in -- the property that lets a modifier reach models
# FunPack knows nothing about.
plain = ModuleSpec(id="plain", title="P", mount="")
compatible, incompatible = split([plain], [])
assert compatible == [plain] and incompatible == []
def test_declaring_a_trait_excludes_the_models_that_lack_it():
picky = ModuleSpec(id="picky", title="P", mount="", requires=["audio_stream"])
compatible, incompatible = split([picky], ["temporal_latent"])
assert incompatible == [picky]
assert missing_for(picky, ["temporal_latent"]) == ["audio_stream"]
def test_a_structural_probe_finds_a_block_by_class_name():
class Target:
pass
class Root:
def modules(self):
return [self, Target()]
class Model:
class model:
diffusion_model = Root()
# The question a modifier actually has: does this model have the thing I
# touch -- not "is this the model I was written for".
assert has_block(Model(), "Target")
assert not has_block(Model(), "SomethingElse")
def test_a_probe_on_a_model_with_no_module_tree_is_false_not_fatal():
class Model:
model = None
assert has_block(Model(), "Anything") is False
# --- against ComfyUI's real model configs ---------------------------------
@pytest.fixture(scope="module")
def real_configs(comfyui):
import comfy.supported_models as sm
return sm.models
def test_every_real_model_config_can_be_read(real_configs):
# The point is coverage, not a particular answer: whatever ships upstream,
# reading it must not raise and must not invent anything.
known = {"temporal_latent", "spatial_latent", "waveform_latent",
"temporal_compression"}
for cfg in real_configs:
model = _Model.__new__(_Model)
class Inner:
model_type = None
model_config = cfg
model.model = Inner()
found = universal(model)
assert found <= known, f"{cfg.__name__} produced an unexpected trait: {found - known}"
def test_traits_describe_the_tensor_not_an_assumed_modality(real_configs):
"""QwenImage is an IMAGE model with a 3-D latent. LTXV is a video model with
a 3-D latent. They must read the same, because the latent really is the same
shape -- claiming otherwise would be FunPack guessing at modality, which
ComfyUI itself does not classify."""
by_name = {cfg.__name__: cfg for cfg in real_configs}
qwen, ltxv = by_name.get("QwenImage"), by_name.get("LTXV")
if qwen is None or ltxv is None:
pytest.skip("upstream renamed these configs")
def read(cfg):
model = _Model.__new__(_Model)
class Inner:
model_type = None
model_config = cfg
model.model = Inner()
return universal(model)
assert "temporal_latent" in read(qwen)
assert "temporal_latent" in read(ltxv)
def test_a_bare_probe_name_collides_across_architectures():
"""`Attention`, `MLP`, `Block` are not distinctive. A bare name matches any
of them, which is why a probe that needs to be sure must qualify."""
class Root:
def modules(self):
# A class named Attention, but from an unrelated architecture.
other = type("Attention", (), {})
other.__module__ = "somewhere.else.entirely"
return [self, other()]
class Model:
class model:
diffusion_model = Root()
assert has_block(Model(), "Attention") # the collision, unqualified
assert not has_block(Model(), "comfy.ldm.minimax.model.Attention")
def test_a_qualified_probe_matches_its_own_architecture():
class Root:
def modules(self):
target = type("AdalnProj", (), {})
target.__module__ = "comfy.ldm.minimax.model"
return [self, target()]
class Model:
class model:
diffusion_model = Root()
assert has_block(Model(), "comfy.ldm.minimax.model.AdalnProj")
assert has_block(Model(), "minimax.model.AdalnProj") # tail match
assert has_block(Model(), "AdalnProj") # distinctive enough
assert not has_block(Model(), "other.pack.AdalnProj")