StringConstructorDictFromText
This commit is contained in:
@@ -1 +1,4 @@
|
||||
# v0.0.1
|
||||
|
||||
MVP nodes:
|
||||
- `Format-Dict from Text`
|
||||
|
||||
+9
-3
@@ -4,7 +4,13 @@
|
||||
|
||||
import typing as _t
|
||||
|
||||
NODE_CLASS_MAPPINGS: _t.Dict[str, type] = dict()
|
||||
NODE_DISPLAY_NAME_MAPPINGS: _t.Dict[str, str] = dict()
|
||||
from .node_dict_from_text import StringConstructorDictFromText
|
||||
|
||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||
NODE_CLASS_MAPPINGS: _t.Dict[str, type] = {
|
||||
'StringConstructorDictFromText': StringConstructorDictFromText,
|
||||
}
|
||||
NODE_DISPLAY_NAME_MAPPINGS: _t.Dict[str, str] = {
|
||||
'StringConstructorDictFromText': "Format-Dict from Text"
|
||||
}
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# encoding: utf-8
|
||||
"""
|
||||
The actual code (MVC pattern) for `StringConstructorDictFromText` node.
|
||||
"""
|
||||
|
||||
import typing as _t
|
||||
|
||||
from frozendict import frozendict as _frozendict, deepfreeze as _deepfreeze
|
||||
|
||||
from .enums import T as _T
|
||||
from .funcs_common import _show_text_on_node, _raise_from_errors_dict, _validate_key, _verify_input_dict
|
||||
|
||||
|
||||
def _return_line_raw(line_raw: str, line_stripped:str) -> str:
|
||||
return line_raw
|
||||
|
||||
|
||||
def _return_line_stripped(line_raw: str, line_stripped:str) -> str:
|
||||
return line_stripped
|
||||
|
||||
|
||||
def _parsed_kv_pairs_gen(multiline_str: str, strip_lines=True):
|
||||
"""
|
||||
Given a multiline string, extract keywords (first non-empty line in each chunk)
|
||||
and their substrings (all the following non-empty lines).
|
||||
"""
|
||||
if not multiline_str:
|
||||
return
|
||||
multiline_str = str(multiline_str)
|
||||
if not multiline_str.strip():
|
||||
return
|
||||
|
||||
appended_line_f = _return_line_stripped if strip_lines else _return_line_raw
|
||||
cur_chunk: _t.List[str] = list()
|
||||
|
||||
def dump_chunk():
|
||||
cur_chunk_iter = iter(cur_chunk) # It's more backwards-compatible than `a, *b = x`
|
||||
chunk_key = next(cur_chunk_iter)
|
||||
cur_chunk_lines = list(cur_chunk_iter)
|
||||
# If there are no actual lines, join() would return an empty string:
|
||||
return chunk_key.strip(), '\n'.join(cur_chunk_lines)
|
||||
|
||||
for line in multiline_str.splitlines():
|
||||
line_stripped: str = line.strip()
|
||||
if line_stripped:
|
||||
cur_chunk.append(appended_line_f(line, line_stripped))
|
||||
continue
|
||||
|
||||
assert not line_stripped
|
||||
if cur_chunk:
|
||||
yield dump_chunk()
|
||||
cur_chunk = list()
|
||||
|
||||
# We simply skip all the empty lines entirely
|
||||
|
||||
if cur_chunk:
|
||||
yield dump_chunk()
|
||||
|
||||
|
||||
def parse_dict_from_text(
|
||||
multiline_str: str, strip_lines=True,
|
||||
show: bool = True, in_dict: _t.Dict[str, _T] = None, unique_id: str = None
|
||||
) -> _t.Tuple[_t.Dict[str, _T]]:
|
||||
out_dict: _t.Dict[str, _T] = _verify_input_dict(in_dict)
|
||||
|
||||
errors_dict: _t.Dict[_t.Any, str] = dict()
|
||||
new_dict: _t.Dict[str, str] = {
|
||||
_k: _v for _k, _v in (
|
||||
(_validate_key(k, errors_dict), v)
|
||||
for k, v in _parsed_kv_pairs_gen(multiline_str, strip_lines=strip_lines)
|
||||
) if _k is not None
|
||||
}
|
||||
_raise_from_errors_dict(errors_dict)
|
||||
|
||||
out_dict.update(new_dict)
|
||||
|
||||
if show and unique_id:
|
||||
status_text = ','.join(new_dict.keys()) if new_dict else ''
|
||||
_show_text_on_node(status_text, unique_id)
|
||||
|
||||
return (_deepfreeze(out_dict), )
|
||||
@@ -0,0 +1,69 @@
|
||||
# encoding: utf-8
|
||||
"""
|
||||
"""
|
||||
|
||||
import typing as _t
|
||||
|
||||
from inspect import cleandoc as _cleandoc
|
||||
|
||||
from frozendict import frozendict as _frozendict, 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_from_text import parse_dict_from_text as _parse_dict_from_text
|
||||
|
||||
|
||||
# A tiny optimization by reusing the same immutable dict:
|
||||
_input_types = _deepfreeze({
|
||||
'required': {
|
||||
'cleanup': (_IO.BOOLEAN, {'default': True, 'label_on': 'extra spaces', 'label_off': 'no', 'tooltip': (
|
||||
"When enabled, each line in each sub-string is stripped from any spaces at its start and end."
|
||||
)}),
|
||||
'strings': (_IO.STRING, {'multiline': True, 'tooltip': (
|
||||
"Sub-string names followed by their text. Different sub-string chunks are separated by empty lines. Example:\n\n"
|
||||
"char1_short\n1boy, blond, short hair\n\n"
|
||||
"char1_long\n1boy, smiling, blue eyes, blond, short hair,\nsitting on a motorcycle, wearing a leather jacket"
|
||||
)}),
|
||||
'show_status': (_IO.BOOLEAN, {'default': False, 'label_on': 'found names', 'label_off': 'no', 'tooltip': (
|
||||
"Show detected string names on the node itself?"
|
||||
)}),
|
||||
},
|
||||
'optional': {
|
||||
'str_dict': _DataTypes.input_str_dict(tooltip=(
|
||||
"An optional input dictionary. If it's connected, it will be expanded/updated with new strings."
|
||||
)),
|
||||
},
|
||||
'hidden': {
|
||||
'unique_id': 'UNIQUE_ID', # used for text display at the bottom of the node
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
class StringConstructorDictFromText:
|
||||
"""
|
||||
Build a dict of named sub-strings to be used later in string formatting (text construction).
|
||||
"""
|
||||
NODE_NAME = 'StringConstructorDictFromText'
|
||||
CATEGORY = _meta.category
|
||||
DESCRIPTION = _format_docstring(_cleandoc(__doc__))
|
||||
|
||||
OUTPUT_NODE = True # Just to show the status message even if not connected to anything
|
||||
|
||||
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, cleanup: bool, strings: str, show_status: bool,
|
||||
str_dict: _t.Dict[str, _t.Any] = None,
|
||||
unique_id: str = None,
|
||||
):
|
||||
return _parse_dict_from_text(strings, strip_lines=cleanup, show=show_status, in_dict=str_dict, unique_id=unique_id)
|
||||
Reference in New Issue
Block a user