Buckets:
| """Math expression node using simpleeval for safe evaluation. | |
| Provides a ComfyMathExpression node that evaluates math expressions | |
| against dynamically-grown numeric inputs. | |
| """ | |
| import math | |
| import string | |
| from simpleeval import simple_eval | |
| from typing_extensions import override | |
| from comfy_api.latest import ComfyExtension, io | |
| MAX_EXPONENT = 4000 | |
| def _variadic_sum(*args): | |
| """Support both sum(values) and sum(a, b, c).""" | |
| if len(args) == 1 and hasattr(args[0], "__iter__"): | |
| return sum(args[0]) | |
| return sum(args) | |
| def _safe_pow(base, exp): | |
| """Wrap pow() with an exponent cap to prevent DoS via huge exponents. | |
| The ** operator is already guarded by simpleeval's safe_power, but | |
| pow() as a callable bypasses that guard. | |
| """ | |
| if abs(exp) > MAX_EXPONENT: | |
| raise ValueError(f"Exponent {exp} exceeds maximum allowed ({MAX_EXPONENT})") | |
| return pow(base, exp) | |
| MATH_FUNCTIONS = { | |
| "sum": _variadic_sum, | |
| "min": min, | |
| "max": max, | |
| "abs": abs, | |
| "round": round, | |
| "pow": _safe_pow, | |
| "sqrt": math.sqrt, | |
| "ceil": math.ceil, | |
| "floor": math.floor, | |
| "log": math.log, | |
| "log2": math.log2, | |
| "log10": math.log10, | |
| "sin": math.sin, | |
| "cos": math.cos, | |
| "tan": math.tan, | |
| "int": int, | |
| "float": float, | |
| } | |
| class MathExpressionNode(io.ComfyNode): | |
| """Evaluates a math expression against dynamically-grown inputs.""" | |
| def define_schema(cls) -> io.Schema: | |
| autogrow = io.Autogrow.TemplateNames( | |
| input=io.MultiType.Input("value", [io.Float, io.Int, io.Boolean]), | |
| names=list(string.ascii_lowercase), | |
| min=1, | |
| ) | |
| return io.Schema( | |
| node_id="ComfyMathExpression", | |
| display_name="Math Expression", | |
| category="utilities", | |
| search_aliases=["math", "expression", "formula", "calculate", "calculator", "eval"], | |
| inputs=[ | |
| io.String.Input("expression", default="a + b", multiline=True), | |
| io.Autogrow.Input("values", template=autogrow), | |
| ], | |
| outputs=[ | |
| io.Float.Output(display_name="FLOAT"), | |
| io.Int.Output(display_name="INT"), | |
| io.Boolean.Output(display_name="BOOL"), | |
| ], | |
| ) | |
| def execute( | |
| cls, expression: str, values: io.Autogrow.Type | |
| ) -> io.NodeOutput: | |
| if not expression.strip(): | |
| raise ValueError("Expression cannot be empty.") | |
| context: dict = dict(values) | |
| context["values"] = list(values.values()) | |
| result = simple_eval(expression, names=context, functions=MATH_FUNCTIONS) | |
| # bool check must come first because bool is a subclass of int in Python | |
| if not isinstance(result, (int, float)): | |
| raise ValueError( | |
| f"Math Expression '{expression}' must evaluate to a numeric result, " | |
| f"got {type(result).__name__}: {result!r}" | |
| ) | |
| try: | |
| float_result = float(result) | |
| except OverflowError: | |
| raise ValueError( | |
| f"Math Expression '{expression}' produced a result too large to " | |
| f"represent as a float: {result}" | |
| ) from None | |
| if not math.isfinite(float_result): | |
| raise ValueError( | |
| f"Math Expression '{expression}' produced a non-finite result: {result}" | |
| ) | |
| return io.NodeOutput(float_result, int(result), bool(result)) | |
| class MathExtension(ComfyExtension): | |
| async def get_node_list(self) -> list[type[io.ComfyNode]]: | |
| return [MathExpressionNode] | |
| async def comfy_entrypoint() -> MathExtension: | |
| return MathExtension() | |
Xet Storage Details
- Size:
- 3.74 kB
- Xet hash:
- 4eec7d0ae09c1a95faf98d7cb0714d331b6c1fa63680b1927ec8c669d584e4bf
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.