commit 147d6481927014bace1413c97278e675effa40c1 Author: Drustan Hawk Date: Thu Oct 19 07:03:46 2023 +0200 Initial commit diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..6deafc2 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 120 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..de14c60 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +venv/ +__pycache__/ + +.vscode/* diff --git a/.mypy.ini b/.mypy.ini new file mode 100644 index 0000000..d6debb8 --- /dev/null +++ b/.mypy.ini @@ -0,0 +1,4 @@ +[mypy] +strict = True +mypy_path = src +packages = primitivetypes diff --git a/README.md b/README.md new file mode 100644 index 0000000..4136050 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# primitive-types +This repository contains typed primitives for ComfyUI in the form of the following nodes: +- `int` +- `float` +- `string` +- `string` (multiline) + +The motivation for these primitives is that the standard primitive node cannot be routed. As a result, if you have a configuration node, e.g., `CFG`, you must connect it directly to the sampler node. However, with an `int` node, you can route it (several times if you wish) \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..8f6b913 --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +from .src.primitivetypes.export import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS # noqa: F401 diff --git a/src/primitivetypes/__init__.py b/src/primitivetypes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/primitivetypes/base.py b/src/primitivetypes/base.py new file mode 100644 index 0000000..0222947 --- /dev/null +++ b/src/primitivetypes/base.py @@ -0,0 +1,69 @@ +from abc import ABC, abstractmethod +from typing import Any, Generic, Literal, NotRequired, TypedDict, TypeVar + + +Number = TypeVar("Number", int, float) +TypeString = Literal["INT", "FLOAT", "STRING"] + + +class NumberInputDict(TypedDict, Generic[Number]): + default: Number + min: NotRequired[Number] + max: NotRequired[Number] + step: Number + display: Literal["number", "slider"] + + +class StringInputDict(TypedDict): + default: str + multiline: bool + + +class InputDict(TypedDict): + required: dict[ + str, + ( + tuple[TypeString | list[TypeString]] | + tuple[TypeString, NumberInputDict[int] | NumberInputDict[float] | StringInputDict] + ) + ] + + hidden: NotRequired[dict[ + str, + ( + tuple[TypeString | list[TypeString]] | + tuple[TypeString, NumberInputDict[int] | NumberInputDict[float] | StringInputDict] + ) + ]] + + optional: NotRequired[dict[ + str, + ( + tuple[TypeString | list[TypeString]] | + tuple[TypeString, NumberInputDict[int] | NumberInputDict[float] | StringInputDict] + ) + ]] + + +class Node(ABC): + FUNCTION = "execute" + OUTPUT_NODE = False + + @property + @abstractmethod + def RETURN_TYPES(self) -> tuple[TypeString, ...]: + pass + + @property + @abstractmethod + def CATEGORY(self) -> str: + pass + + @classmethod + @abstractmethod + def INPUT_TYPES(cls) -> InputDict: + pass + + @abstractmethod + def execute(self) -> tuple[Any, ...] | None: + pass diff --git a/src/primitivetypes/export.py b/src/primitivetypes/export.py new file mode 100644 index 0000000..ca821bc --- /dev/null +++ b/src/primitivetypes/export.py @@ -0,0 +1,17 @@ +from .nodes import Int, Float, String, StringMultiline + + +NODE_CLASS_MAPPINGS = { + "int": Int, + "float": Float, + "string": String, + "string_multiline": StringMultiline +} + + +NODE_DISPLAY_NAME_MAPPINGS = { + "int": "int", + "float": "float", + "string": "string", + "string_multiline": "string (multiline)" +} diff --git a/src/primitivetypes/nodes.py b/src/primitivetypes/nodes.py new file mode 100644 index 0000000..3d1efd8 --- /dev/null +++ b/src/primitivetypes/nodes.py @@ -0,0 +1,66 @@ +from typing import TypeVar +from .base import Node, InputDict + + +T = TypeVar("T") + + +class CategoryTyped: + CATEGORY = "utils/Primitive Types" + + +class PassthroughExecute: + FUNCTION = "execute" + + def execute(self, *values: T) -> tuple[T, ...] | None: + if values: + return tuple(values) + return None + + +class Int(PassthroughExecute, CategoryTyped, Node): + @classmethod + def INPUT_TYPES(cls) -> InputDict: + return dict( + required=dict( + value=("INT", dict(default=0, step=1, display="number")) + ) + ) + + RETURN_TYPES = ("INT",) + + +class Float(PassthroughExecute, CategoryTyped, Node): + @classmethod + def INPUT_TYPES(cls) -> InputDict: + return dict( + required=dict( + value=("FLOAT", dict(default=0, min=0, max=4096, step=1, display="number")) + ) + ) + + RETURN_TYPES = ("FLOAT",) + + +class String(PassthroughExecute, CategoryTyped, Node): + @classmethod + def INPUT_TYPES(cls) -> InputDict: + return dict( + required=dict( + text=("STRING", dict(default="", multiline=False)) + ) + ) + + RETURN_TYPES = ("STRING",) + + +class StringMultiline(PassthroughExecute, CategoryTyped, Node): + @classmethod + def INPUT_TYPES(cls) -> InputDict: + return dict( + required=dict( + text=("STRING", dict(default="", multiline=True)) + ) + ) + + RETURN_TYPES = ("STRING",)