Files
Alistair Buxton 96b856a2d5 Remove executor hack, convert job to list instead
This makes the node pack work on the latest ComfyUI. Swap the JobIterator
node for a JobToList node. Nothing else needs to change.

This does mean that the workflow will be evaluated breadth-first instead
of depth-first, which means intermediate results will be cached in memory
and you won't get any output until the whole job is finished.
2024-09-14 16:11:30 +01:00

181 lines
4.3 KiB
Python

import collections
import itertools
from execution import PromptExecutor
from . import register_node
@register_node
class MakeJob:
"""Turns a sequence into a job with one attribute."""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"sequence": ("SEQUENCE", ),
"name": ("STRING", {"default": ''}),
},
}
RETURN_TYPES = ("JOB", "INT")
RETURN_NAMES = ("job", "count")
FUNCTION = "go"
CATEGORY = "ali1234/job"
def merge_dicts(self, *dicts):
#return collections.ChainMap(*reversed(dicts))
return dict(itertools.chain.from_iterable(d.items() for d in dicts))
def go(self, sequence, name):
result = [{name: value} for value in sequence]
return (result, len(result))
@register_node
class CombineJobs(MakeJob):
"""Combines multiple jobs."""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"a": ("JOB", ),
"method": (("zip", "product"), {"default": "zip"}),
},
"optional": {
x: ("JOB", ) for x in ('b', 'c', 'd', 'e')
}
}
def go(self, method, **kwargs):
method = {'product': itertools.product, 'zip': zip}[method]
result = [self.merge_dicts(*steps) for steps in method(*kwargs.values())]
return (result, len(result))
@register_node
class EnumerateJob(MakeJob):
"""Combines multiple jobs."""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"job": ("JOB", ),
"name": ("STRING", {"default": ''}),
},
}
def go(self, job, name):
result = [self.merge_dicts(step, {name: n}) for n, step in enumerate(job)]
return (result, len(result))
@register_node
class GetJobStep:
"""Gets the job step by number."""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"job": ("JOB", ),
"step": ("INT", {"default": 0}),
"wrap": (("repeat", "clamp"), {"default": "repeat"}),
},
}
RETURN_TYPES = ("ATTRIBUTES", )
RETURN_NAMES = ("attributes", )
FUNCTION = "go"
CATEGORY = "ali1234/job"
def go(self, job, step, wrap):
if wrap == 'repeat':
while step < 0:
step += len(job)
step = step % len(job)
elif wrap == 'clamp':
step = max(min(step, len(job)), 0)
return (job[step], )
@register_node
class FormatAttributes:
"""Applies attributes to a format string."""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"attributes": ("ATTRIBUTES",),
"format": ("STRING", {'default': '', 'multiline': True, "dynamicPrompts": False})
},
}
RETURN_TYPES = ("STRING", )
RETURN_NAMES = ("string", )
FUNCTION = "go"
CATEGORY = "ali1234/job"
def go(self, attributes, format):
return (format.format(**attributes), )
class AnyType(str):
def __ne__(self, __value: object) -> bool:
return False
@register_node
class GetAttribute:
"""Gets a named attribute from a step."""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"attributes": ("ATTRIBUTES", ),
"name": ("STRING", {"default": ''}),
},
}
RETURN_TYPES = (AnyType("*"), )
RETURN_NAMES = ("value", )
FUNCTION = "go"
CATEGORY = "ali1234/job"
def go(self, attributes, name):
return (attributes[name], )
# Dynamically register typed attribute getters to avoid wildcard bug
# https://github.com/comfyanonymous/ComfyUI/pull/770
for t in ('INT', 'FLOAT', 'STRING'):
register_node(type(
'GetAttribute'+t.title(),
(GetAttribute, ),
{
'RETURN_TYPES': (t, ),
}
))
@register_node
class JobToList:
"""Converts a job into a list."""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"job": ("JOB",),
},
}
RETURN_TYPES = ("ATTRIBUTES",)
RETURN_NAMES = ("attributes",)
OUTPUT_IS_LIST = (True,)
FUNCTION = "go"
CATEGORY = "ali1234/job"
def go(self, job):
return (job,)