diff --git a/__init__.py b/__init__.py index ee14aed..ed2ba9b 100644 --- a/__init__.py +++ b/__init__.py @@ -6,6 +6,7 @@ from comfy_api.latest import ComfyExtension as _ComfyExtension, io as _io from .node_dict_from_text import * from .nodes_add import * +from .nodes_extract import * class DictToolsExtension(_ComfyExtension): async def get_node_list(self) -> list[type[_io.ComfyNode]]: diff --git a/__meta.py b/__meta.py index 5b8b6c2..0944892 100644 --- a/__meta.py +++ b/__meta.py @@ -4,5 +4,9 @@ Metadata-related module. """ category: str = "🗂️ Dict Tools" +category_add: str = f"{category}/Add" +category_extract: str = f"{category}/Extract" + category_old: str = f"{category}/_old" + pack_id_suffix: str = " [Dict Tools]" diff --git a/_io_custom.py b/_io_custom.py index 9dd9acd..373ea36 100644 --- a/_io_custom.py +++ b/_io_custom.py @@ -20,6 +20,10 @@ _DICT_OUTPUT = _DICT.Output( 'DICT', display_name='dict' ) +_KEY_INPUT_ADD = _io.String.Input( + 'name', + tooltip="Name (key) of the item inserted into the dict.", +) _T_Input = _t.TypeVar('T_Input', bound=_io.Input) diff --git a/nodes_add.py b/nodes_add.py index 769ab63..71881e8 100644 --- a/nodes_add.py +++ b/nodes_add.py @@ -1,5 +1,5 @@ # encoding: utf-8 -"""The main module for simple dict nodes.""" +"""Nodes to add/update individual items to a dict (``Add`` category).""" from inspect import cleandoc as _cleandoc @@ -8,37 +8,29 @@ from frozendict import frozendict as _frozendict from comfy_api.latest import io as _io, ui as _ui from .__meta import ( - category as _category, + category_add as _category_add, pack_id_suffix as _pack_id ) from .__typing import _A, _U, _O, _t, T as _T, DictMap as _DictMap from ._dict_funcs import _new_updated_dict from ._io_custom import ( _BaseNode, - _DICT_INPUT_OPTIONAL, _DICT_OUTPUT, - _input_override, _schema_old_name + _DICT_INPUT_OPTIONAL, _DICT_OUTPUT, _KEY_INPUT_ADD, + _schema_old_name ) from .docstring_formatter import format_docstring as _format_docstring -_dict = dict - # ---------------------------------------------------------- class DictAddAny(_BaseNode): """Add/update any-type item to a Format-Dict.""" _schema = _io.Schema( node_id=f'DictAddAny{_pack_id}', - display_name='Add ANY to Dict', - category=_category, + display_name='ANY to Dict', + category=_category_add, description=_format_docstring(_cleandoc(__doc__)), inputs=[ - _io.String.Input( - 'name', - tooltip=( - "Name (key) of the item inserted into the dict. " - "It must comprise only of latin letters, digits and underscores + it can't start with a digit." - ), - ), + _KEY_INPUT_ADD, _DICT_INPUT_OPTIONAL, _io.AnyType.Input( @@ -70,17 +62,11 @@ class DictAddString(_BaseNode): """Add/update a string to a Format-Dict.""" _schema = _io.Schema( node_id=f'DictAddString{_pack_id}', - display_name='Add String to Dict', - category=_category, + display_name='STRING to Dict', + category=_category_add, description=_format_docstring(_cleandoc(__doc__)), inputs=[ - _io.String.Input( - 'name', - tooltip=( - "Name (key) of the string inserted into the dict. " - "It must comprise only of latin letters, digits and underscores + it can't start with a digit." - ), - ), + _KEY_INPUT_ADD, _io.Boolean.Input( 'cleanup', tooltip=( @@ -116,80 +102,6 @@ class DictAddString(_BaseNode): result= _new_updated_dict(dict, {name: string}) return _io.NodeOutput(result) -# ---------------------------------------------------------- - -class DictExtractString(_BaseNode): - """Extract a single string from a Format-Dict.""" - _schema = _io.Schema( - node_id=f'DictExtractString{_pack_id}', - display_name='Extract String from Dict', - category=_category, - description=_format_docstring(_cleandoc(__doc__)), - inputs=[ - _io.Boolean.Input( - 'cleanup_key', display_name='cleanup', - tooltip=( - "Automatically remove leading/trailing spaces and newlines from the key." - ), - default=True, - label_on='leading/trailing spaces', - label_off='no', - ), - _io.String.Input( - 'key', - tooltip=( - "Key (unique name) of a string to extract from dictionary.\n" - "If the element with such key isn't a string, it will be turned to one.\n" - "If no such key exists in the dict, an empty string returned." - ), - ), - _io.Boolean.Input( - 'show_status', - tooltip="Show the extracted string on the node itself?", - default=False, - label_on='value', - label_off='no', - ), - - _DICT_INPUT_OPTIONAL, - ], - outputs=[_io.String.Output('string')], - # hidden=[_io.Hidden.unique_id], - ) - - @classmethod - def execute(cls, - cleanup_key: bool, key: str, show_status: bool = False, - dict: _O[_DictMap] = None - ) -> _io.NodeOutput: - """Extract a single string from the Format-Dict.""" - if cleanup_key: - for line in key.split(): - line = line.strip() # just in case \r is left - if line: - key = line - break - - # The passed dict might not actually be a dict but ANY mapping, - # which doesn't have a `get()` method. - # So direct key access is better: - string: str = '' - # noinspection PyBroadException - try: - string = dict[key] - except Exception: - pass - - if string is None: - string = '' - elif not isinstance(string, str): - string = repr(string) - - return _io.NodeOutput( - string, - ui=_ui.PreviewText(string) if show_status else None - ) - # ========================================================== # Deprecated nodes with old IDs (for backwards compatibility) @@ -207,25 +119,3 @@ class DictAddStringOld1(DictAddString): DictAddString._schema, 'StringConstructorDictAddString' ) - - -class DictExtractStringOld1(_BaseNode): - # noinspection PyProtectedMember - _schema = _schema_old_name( - DictExtractString._schema, - 'StringConstructorDictExtractString', - inputs=[ - _input_override(x, id='name') if x.id == 'key' else x - for x in DictExtractString._schema.inputs - if x.id != 'cleanup_key' - ], - ) - - @classmethod - def execute(cls, - name: str, show_status: bool = False, - dict: _O[_DictMap] = None - ) -> _io.NodeOutput: - return DictExtractString.execute( - cleanup_key=False, key=name, show_status=show_status, dict=dict - ) diff --git a/nodes_extract.py b/nodes_extract.py new file mode 100644 index 0000000..0910b13 --- /dev/null +++ b/nodes_extract.py @@ -0,0 +1,118 @@ +# encoding: utf-8 +"""Nodes to extract individual items from a dict (``Extract`` category).""" + +from inspect import cleandoc as _cleandoc + +from comfy_api.latest import io as _io, ui as _ui + +from .__meta import ( + category_extract as _category_extract, + pack_id_suffix as _pack_id +) +from .__typing import _A, _U, _O, _t, T as _T, DictMap as _DictMap +from ._io_custom import ( + _BaseNode, + _DICT_INPUT_OPTIONAL, + _input_override, _schema_old_name +) +from .docstring_formatter import format_docstring as _format_docstring + +# ---------------------------------------------------------- + +class DictExtractString(_BaseNode): + """Extract a single string from a Format-Dict.""" + _schema = _io.Schema( + node_id=f'DictExtractString{_pack_id}', + display_name='STRING from Dict', + category=_category_extract, + description=_format_docstring(_cleandoc(__doc__)), + inputs=[ + _io.Boolean.Input( + 'cleanup_key', display_name='cleanup', + tooltip=( + "Automatically remove leading/trailing spaces and newlines from the key." + ), + default=True, + label_on='leading/trailing spaces', + label_off='no', + ), + _io.String.Input( + 'key', + tooltip=( + "Key (unique name) of a string to extract from dictionary.\n" + "If the element with such key isn't a string, it will be turned to one.\n" + "If no such key exists in the dict, an empty string returned." + ), + ), + _io.Boolean.Input( + 'show_status', + tooltip="Show the extracted string on the node itself?", + default=False, + label_on='value', + label_off='no', + ), + + _DICT_INPUT_OPTIONAL, + ], + outputs=[_io.String.Output('string')], + # hidden=[_io.Hidden.unique_id], + ) + + @classmethod + def execute(cls, + cleanup_key: bool, key: str, show_status: bool = False, + dict: _O[_DictMap] = None + ) -> _io.NodeOutput: + """Extract a single string from the Format-Dict.""" + if cleanup_key: + for line in key.split(): + line = line.strip() # just in case \r is left + if line: + key = line + break + + # The passed dict might not actually be a dict but ANY mapping, + # which doesn't have a `get()` method. + # So direct key access is better: + string: str = '' + # noinspection PyBroadException + try: + string = dict[key] + except Exception: + pass + + if string is None: + string = '' + elif not isinstance(string, str): + string = repr(string) + + return _io.NodeOutput( + string, + ui=_ui.PreviewText(string) if show_status else None + ) + + +# ========================================================== +# Deprecated nodes with old IDs (for backwards compatibility) + + +class DictExtractStringOld1(_BaseNode): + # noinspection PyProtectedMember + _schema = _schema_old_name( + DictExtractString._schema, + 'StringConstructorDictExtractString', + inputs=[ + _input_override(x, id='name') if x.id == 'key' else x + for x in DictExtractString._schema.inputs + if x.id != 'cleanup_key' + ], + ) + + @classmethod + def execute(cls, + name: str, show_status: bool = False, + dict: _O[_DictMap] = None + ) -> _io.NodeOutput: + return DictExtractString.execute( + cleanup_key=False, key=name, show_status=show_status, dict=dict + )