Fix 4 bugs from third code review
- Fix delete_proj not persisting cleared current_project to config: page reload after deleting active project restored deleted name, silently breaking all DB sync - Fix sync_to_db crash on non-dict batch_data items: add isinstance guard matching import_json_file - Fix output_types ignored in load_dynamic: parse declared types and use to_int()/to_float() to coerce values, so downstream ComfyUI nodes receive correct types even when API returns strings - Fix backward-compat comma-split for types not trimming whitespace: legacy workflows with "STRING, INT" got types " INT" breaking ComfyUI connection type-matching Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -142,10 +142,24 @@ class ProjectLoaderDynamic:
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
keys = [k.strip() for k in output_keys.split(",") if k.strip()]
|
||||
|
||||
# Parse types for coercion
|
||||
types = []
|
||||
if output_types:
|
||||
try:
|
||||
types = json.loads(output_types)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
types = [t.strip() for t in output_types.split(",")]
|
||||
|
||||
results = []
|
||||
for key in keys:
|
||||
for i, key in enumerate(keys):
|
||||
val = data.get(key, "")
|
||||
if isinstance(val, bool):
|
||||
declared_type = types[i] if i < len(types) else ""
|
||||
# Coerce based on declared output type when possible
|
||||
if declared_type == "INT":
|
||||
results.append(to_int(val))
|
||||
elif declared_type == "FLOAT":
|
||||
results.append(to_float(val))
|
||||
elif isinstance(val, bool):
|
||||
results.append(str(val).lower())
|
||||
elif isinstance(val, int):
|
||||
results.append(val)
|
||||
|
||||
@@ -119,6 +119,10 @@ def render_projects_tab(state: AppState):
|
||||
state.db.delete_project(name)
|
||||
if state.current_project == name:
|
||||
state.current_project = ''
|
||||
state.config['current_project'] = ''
|
||||
save_config(state.current_dir,
|
||||
state.config.get('favorites', []),
|
||||
state.config)
|
||||
ui.notify(f'Deleted project "{name}"', type='positive')
|
||||
render_project_list.refresh()
|
||||
|
||||
|
||||
@@ -100,6 +100,22 @@ class TestProjectLoaderDynamic:
|
||||
assert result[0] == "comma_val"
|
||||
assert result[1] == "ok"
|
||||
|
||||
def test_load_dynamic_type_coercion(self):
|
||||
"""output_types should coerce values to declared types."""
|
||||
import json as _json
|
||||
data = {"seed": "42", "cfg": "1.5", "prompt": "hello"}
|
||||
node = ProjectLoaderDynamic()
|
||||
keys_json = _json.dumps(["seed", "cfg", "prompt"])
|
||||
types_json = _json.dumps(["INT", "FLOAT", "STRING"])
|
||||
with patch("project_loader._fetch_data", return_value=data):
|
||||
result = node.load_dynamic(
|
||||
"http://localhost:8080", "proj1", "batch_i2v", 1,
|
||||
output_keys=keys_json, output_types=types_json
|
||||
)
|
||||
assert result[0] == 42 # string "42" coerced to int
|
||||
assert result[1] == 1.5 # string "1.5" coerced to float
|
||||
assert result[2] == "hello" # string stays string
|
||||
|
||||
def test_load_dynamic_empty_keys(self):
|
||||
node = ProjectLoaderDynamic()
|
||||
with patch("project_loader._fetch_data", return_value={"prompt": "hello"}):
|
||||
|
||||
2
utils.py
2
utils.py
@@ -202,6 +202,8 @@ def sync_to_db(db, project_name: str, file_path: Path, data: dict) -> None:
|
||||
if isinstance(batch_data, list):
|
||||
db.conn.execute("DELETE FROM sequences WHERE data_file_id = ?", (df_id,))
|
||||
for item in batch_data:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
seq_num = int(item.get(KEY_SEQUENCE_NUMBER, 0))
|
||||
now = __import__('time').time()
|
||||
db.conn.execute(
|
||||
|
||||
@@ -154,7 +154,7 @@ app.registerExtension({
|
||||
let types = [];
|
||||
if (otWidget?.value) {
|
||||
try { types = JSON.parse(otWidget.value); } catch (_) {
|
||||
types = otWidget.value.split(",");
|
||||
types = otWidget.value.split(",").map(t => t.trim()).filter(Boolean);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ app.registerExtension({
|
||||
// On load, LiteGraph already restored serialized outputs with links.
|
||||
// Rename and set types to match stored state (preserves links).
|
||||
for (let i = 0; i < this.outputs.length && i < keys.length; i++) {
|
||||
this.outputs[i].name = keys[i].trim();
|
||||
this.outputs[i].name = keys[i];
|
||||
if (types[i]) this.outputs[i].type = types[i];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user