Files
DumiFlex-ComfyUI-Wildcard-P…/engine/converters.py
T

88 lines
2.8 KiB
Python

"""Pure-Python parsers for the WP_VarTo* converter nodes.
Each function:
- Never raises. Unparseable input or out-of-range index returns `default`.
- Has a TS mirror in `src/components/var-picker/parser.ts`. The two copies
share a fixture (`tests/fixtures/converter_cases.json`) consumed by a
parity test (`tests/test_parser_parity.py` + `parser.test.ts`).
"""
import re
from typing import Protocol
from engine.syntax.types import deref_var_value, split_var_accessor
class _CtxLike(Protocol):
context: dict[str, object]
_INT_RE = re.compile(r"-?\d+")
_FLOAT_RE = re.compile(r"-?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?")
_BOOL_SPLIT_RE = re.compile(r"[\s,;|/]+")
_TRUTHY: frozenset[str] = frozenset({"true", "yes", "on", "1"})
_FALSY: frozenset[str] = frozenset({"false", "no", "off", "0"})
def parse_int(text: str, index: int, default: int) -> int:
"""Return the Nth signed-integer match from `text`, or `default`."""
matches = _INT_RE.findall(text or "")
if index < 0 or index >= len(matches):
return default
try:
return int(matches[index])
except ValueError:
return default
def parse_float(text: str, index: int, default: float) -> float:
"""Return the Nth signed-float match from `text`, or `default`.
Matches integers too (no required decimal) so a var of `"1920"` still
yields `1920.0`. Scientific notation supported (`3.2e-4`).
"""
matches = _FLOAT_RE.findall(text or "")
if index < 0 or index >= len(matches):
return default
try:
return float(matches[index])
except ValueError:
return default
def parse_bool(text: str, index: int, default: bool) -> bool:
"""Return the Nth bool token from `text`, or `default`.
Tokens are produced by splitting on `[\\s,;|/]+`. Each token is
matched case-insensitively against the truthy/falsy sets. Tokens
that match neither (e.g. `"1.5"`, `"enabled"`) are skipped — they
don't consume an index slot.
"""
if not text:
return default
tokens = _BOOL_SPLIT_RE.split(text)
bools: list[bool] = []
for tok in tokens:
low = tok.lower()
if low in _TRUTHY:
bools.append(True)
elif low in _FALSY:
bools.append(False)
if index < 0 or index >= len(bools):
return default
return bools[index]
def lookup_var(context: _CtxLike, name: str) -> str:
"""Read `name` from `context.context`. Strips leading `$`. Never raises.
Missing key, `None`, or empty name all collapse to `""` — every parser
then falls back to its `default`.
"""
bare = (name or "").lstrip("$").strip()
if not bare:
return ""
base, index = split_var_accessor(bare)
raw = context.context.get(base) if context and getattr(context, "context", None) else None
return deref_var_value(raw, index)