From 7a9b1dd023ee5d7053846d4f306a72f97993bf92 Mon Sep 17 00:00:00 2001 From: "Lex Darlog (DRL)" <3897975+Lex-DRL@users.noreply.github.com> Date: Mon, 21 Jul 2025 19:54:53 +0500 Subject: [PATCH] `StringConstructorDictAddAny` --- CHANGELOG.md | 14 ++++++++--- README.md | 2 +- __init__.py | 3 +++ funcs_dict_add_any.py | 27 ++++++++++++++++++++ node_dict_add_any.py | 58 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 funcs_dict_add_any.py create mode 100644 node_dict_add_any.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d0b238..e3feb02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# `TODO` +- Recursive formatting. + +# v1.0.0 + +The minimally-complete version. + +New node: +- `Add ANY to Format-Dict (Advanced)` + # v0.0.1 MVP nodes: @@ -5,7 +15,3 @@ MVP nodes: - `Format-Dict from Text` - `Format-Dict Preview` - `String Formatter` - -⚠️ Active `TODO:` -- Recursive formatting. -- Append non-string to Format-Dict. diff --git a/README.md b/README.md index d1e5565..34ad443 100644 --- a/README.md +++ b/README.md @@ -24,4 +24,4 @@ For those who are unfamiliar with python's string formatting, the rules are very > [!NOTE] > For advanced users: > Internally, a built-in `str.format()` is called with keyword arguments from `str_dict`, which is literally just a dict... though, an immutable one. -> So any formatting patterns are available (like `{float_value:.3f}`). However, currently there are no nodes to append non-string values (planned, TODO). +> So any formatting patterns are available (like `{float_value:.3f}`) + `Add ANY to Format-Dict` node is there for exactly that. diff --git a/__init__.py b/__init__.py index 83000be..4c613d2 100644 --- a/__init__.py +++ b/__init__.py @@ -4,18 +4,21 @@ import typing as _t +from .node_dict_add_any import StringConstructorDictAddAny from .node_dict_add_string import StringConstructorDictAddString from .node_dict_from_text import StringConstructorDictFromText from .node_dict_preview import StringConstructorDictPreview from .node_formatter import StringConstructorFormatter NODE_CLASS_MAPPINGS: _t.Dict[str, type] = { + 'StringConstructorDictAddAny': StringConstructorDictAddAny, 'StringConstructorDictAddString': StringConstructorDictAddString, 'StringConstructorDictFromText': StringConstructorDictFromText, 'StringConstructorDictPreview': StringConstructorDictPreview, 'StringConstructorFormatter': StringConstructorFormatter, } NODE_DISPLAY_NAME_MAPPINGS: _t.Dict[str, str] = { + 'StringConstructorDictAddAny': "Add ANY to Format-Dict (Advanced)", 'StringConstructorDictAddString': "Add String to Format-Dict", 'StringConstructorDictFromText': "Format-Dict from Text", 'StringConstructorDictPreview': "Preview Format-Dict", diff --git a/funcs_dict_add_any.py b/funcs_dict_add_any.py new file mode 100644 index 0000000..67b4d63 --- /dev/null +++ b/funcs_dict_add_any.py @@ -0,0 +1,27 @@ +# encoding: utf-8 +""" +The actual code (MVC pattern) for `StringConstructorDictAddAny` node. +""" + +import typing as _t + +from frozendict import deepfreeze as _deepfreeze + +from .funcs_common import _raise_from_errors_dict, _validate_key, _verify_input_dict + + +def set_dict_any_type_item( + name: str, value: _t.Any = None, in_dict: _t.Dict[str, _t.Any] = None, + # unique_id: str = None, +) -> _t.Tuple[_t.Dict[str, _t.Any]]: + """Update/append an item of any type to the dict.""" + out_dict: _t.Dict[str, _t.Any] = _verify_input_dict(in_dict) + if value is None: + return (_deepfreeze(in_dict), ) # No need to create another dict instance if it's already frozen + + errors_dict: _t.Dict[_t.Any, str] = dict() + name = _validate_key(name, errors_dict) + _raise_from_errors_dict(errors_dict) + new_dict: _t.Dict[str, _t.Any] = {name: value} + out_dict.update(new_dict) + return (_deepfreeze(out_dict), ) diff --git a/node_dict_add_any.py b/node_dict_add_any.py new file mode 100644 index 0000000..93f912c --- /dev/null +++ b/node_dict_add_any.py @@ -0,0 +1,58 @@ +# encoding: utf-8 +""" +""" + +import typing as _t + +from inspect import cleandoc as _cleandoc + +from frozendict import deepfreeze as _deepfreeze + +from comfy.comfy_types.node_typing import IO as _IO + +from . import _meta +from .docstring_formatter import format_docstring as _format_docstring +from .enums import DataTypes as _DataTypes +from .funcs_dict_add_any import set_dict_any_type_item as _set_dict_any_type_item +from .node_dict_add_string import _input_types as _input_types_str + + +# A tiny optimization by reusing the same immutable dict: +_input_types = _deepfreeze({ + 'required': { + 'name': (_IO.STRING, {'tooltip': ( + "Name of the non-string item inserted into the dict. " + "It must comprise only of latin letters, digits and underscores + it can't start with a digit." + )}), + }, + 'optional': { + 'str_dict': _input_types_str['optional']['str_dict'], + 'value': (_IO.ANY, {"forceInput": True, 'tooltip': "The actual non-string item to add into the dict."}), + }, + # 'hidden': { + # 'unique_id': 'UNIQUE_ID', # used for text display at the bottom of the node + # }, +}) + + +class StringConstructorDictAddAny: + """Add/update a non-string item to the Format-Dict - to do some advanced formatting.""" + NODE_NAME = 'StringConstructorDictAddAny' + CATEGORY = _meta.category + DESCRIPTION = _format_docstring(_cleandoc(__doc__)) + + FUNCTION = 'main' + RETURN_TYPES = (_DataTypes.STR_DICT, ) + RETURN_NAMES = (_DataTypes.STR_DICT.lower(), ) + # OUTPUT_TOOLTIPS = tuple() + + @classmethod + def INPUT_TYPES(cls): + return _input_types + + def main( + self, name: str, value: _t.Any = None, + str_dict: _t.Dict[str, _t.Any] = None, + # unique_id: str = None, + ): + return _set_dict_any_type_item(name, value, in_dict=str_dict)