Compare commits

...
Author SHA1 Message Date
Peyton DeNiro 6cdcc235a0 update README to add additional troubleshooting steps for installation 2026-04-11 19:53:04 -05:00
Peyton DeNiro 9a17000417 Update version. 2026-03-29 19:06:52 -05:00
Peyton DeNiro fbc06463f7 Merge pull request #153 from pydn/runtime-parity-cache-memory-lifecycle
Add generated runtime cleanup after workflow execution
2026-03-29 19:02:51 -05:00
Peyton DeNiro d6c5b110ab Merge pull request #152 from pydn/cluster-2-compatibility-78-99-137
Clarify Python support and harden export/install compatibility
2026-03-29 17:55:19 -05:00
Peyton DeNiro 5d70efdc88 Align Cluster 2 compatibility boundaries
Declare Python 3.12 support, replace the hidden pip bootstrap with explicit uv sync guidance, and remove prompt() from the Web UI save flow. Add focused contract coverage for the metadata and frontend defaults.
2026-03-29 17:34:30 -05:00
6 changed files with 85 additions and 32 deletions
+39 -8
View File
@@ -19,17 +19,14 @@ This project supports Python 3.12 and newer.
For ComfyUI to recognize this project as an extension, the repo must be discoverable through ComfyUI's `custom_nodes` search paths.
Use one of these setups:
Clone directly into `ComfyUI/custom_nodes`:
1. Clone directly into `ComfyUI/custom_nodes`
```bash
cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/pydn/ComfyUI-to-Python-Extension.git
cd ComfyUI-to-Python-Extension
uv sync
```
2. Keep the repo elsewhere, then either:
Or keep the repo elsewhere, then either:
- symlink it into `ComfyUI/custom_nodes`
- add its parent directory to ComfyUI's `custom_nodes` search paths via `extra_model_paths.yaml`
@@ -38,10 +35,29 @@ Example symlink setup:
git clone https://github.com/pydn/ComfyUI-to-Python-Extension.git
cd /path/to/ComfyUI/custom_nodes
ln -s /path/to/ComfyUI-to-Python-Extension ComfyUI-to-Python-Extension
cd /path/to/ComfyUI-to-Python-Extension
uv sync
```
Then install this extension into the same Python environment that launches ComfyUI.
The `pyproject.toml` file declares the package dependencies, but those dependencies still need to be installed into ComfyUI's runtime Python.
If you run ComfyUI from a source checkout with `uv`:
```bash
cd /path/to/ComfyUI
uv pip install -e ./custom_nodes/ComfyUI-to-Python-Extension
uv run python main.py
```
If you use the Windows portable build:
```
cd C:\path\to\ComfyUI_windows_portable\ComfyUI\custom_nodes\ComfyUI-to-Python-Extension
..\..\..\python_embeded\python.exe -m pip install -e .
```
Running `uv sync` inside `ComfyUI-to-Python-Extension` creates this extension's own `.venv`.
ComfyUI does not automatically import dependencies from that `.venv`; it imports custom nodes with the Python interpreter used to launch ComfyUI.
After installation, restart ComfyUI.
### CLI exporter / generated scripts
@@ -56,9 +72,18 @@ export COMFYUI_PATH=/path/to/ComfyUI
```
`COMFYUI_PATH` helps the exporter and generated scripts find the ComfyUI codebase. It does not, by itself, register this repo as a ComfyUI extension for the Web UI.
It also does not install ComfyUI runtime dependencies such as `torch` into the current Python environment.
`COMFYUI_PATH` is checked first. If it is not set, the exporter falls back to searching parent directories for a folder named `ComfyUI`.
If the CLI fails with `ModuleNotFoundError: No module named 'torch'`, run the command with the same Python environment that launches ComfyUI, or install ComfyUI's runtime dependencies into the environment you are using for the CLI.
For Windows portable builds, run the CLI with ComfyUI's embedded Python from the extension directory:
```
..\..\..\python_embeded\python.exe -m comfyui_to_python --input_file ".\workflow_api.json" --output_file ".\workflow_api.py"
```
## Web UI Export
In current ComfyUI builds, `Save As Script` is typically available under:
@@ -131,7 +156,13 @@ Lifecycle notes:
## Troubleshooting
- unsupported Python version:
use Python 3.12 or newer, then rerun `uv sync`
use Python 3.12 or newer in the environment that runs the extension, then reinstall the extension dependencies there
- Web UI import fails after `uv sync`:
`uv sync` in this repo installs dependencies into this repo's `.venv`, but ComfyUI loads custom nodes with its own Python environment. Install the extension into the Python interpreter that launches ComfyUI.
- Windows portable import fails after `uv sync`:
ComfyUI portable uses its bundled `python_embeded` interpreter. From the extension directory, run `..\..\..\python_embeded\python.exe -m pip install -e .`, then restart ComfyUI.
- CLI fails with `No module named 'torch'`:
the extension `.venv` may not have ComfyUI's runtime dependencies. Either run the CLI from the Python environment that launches ComfyUI, or make sure the target ComfyUI environment is installed and `COMFYUI_PATH` points to it.
- `Save As Script` not visible:
check your current ComfyUI menu/frontend version and look under `File`
- `Save As Script` not visible after restart:
+4 -12
View File
@@ -13,18 +13,10 @@ sys.path.append(ext_dir)
try:
import black
except ImportError:
print("Unable to import requirements for ComfyUI-SaveAsScript.")
print("Installing...")
import importlib
spec = importlib.util.spec_from_file_location(
"impact_install", os.path.join(os.path.dirname(__file__), "install.py")
)
impact_install = importlib.util.module_from_spec(spec)
spec.loader.exec_module(impact_install)
print("Successfully installed. Hopefully, at least.")
raise ImportError(
"ComfyUI-to-Python-Extension requires the project dependencies to be installed. "
f"Run 'uv sync' in {ext_dir} with Python 3.12+ before loading this extension."
) from None
# Prevent reimporting of custom nodes
os.environ["RUNNING_IN_COMFYUI"] = "TRUE"
+6 -10
View File
@@ -1,6 +1,9 @@
import { api } from "../../scripts/api.js";
import { app } from "../../scripts/app.js";
const DEFAULT_SCRIPT_FILENAME = "workflow_api.py";
const DEFAULT_WORKFLOW_NAME = "workflow_api.json";
function $el(tag, options = {}) {
const element = document.createElement(tag);
const { parent, style, ...props } = options;
@@ -39,15 +42,12 @@ const extension = {
});
},
savePythonScript() {
var filename = prompt("Save script as:");
if(filename === undefined || filename === null || filename === "") {
return
}
const filename = DEFAULT_SCRIPT_FILENAME;
app.graphToPrompt().then(async (p) => {
const frontendWorkflow = p.workflow ?? app.graph.serialize();
const json = JSON.stringify({
name: filename + ".json",
name: DEFAULT_WORKFLOW_NAME,
workflow: JSON.stringify(p.output, null, 2),
frontend_workflow: JSON.stringify(frontendWorkflow, null, 2),
}, null, 2); // convert the data to a JSON string
@@ -55,10 +55,6 @@ const extension = {
if(response.status == 200) {
const blob = new Blob([await response.text()], {type: "text/python;charset=utf-8"});
const url = URL.createObjectURL(blob);
if(!filename.endsWith(".py")) {
filename += ".py";
}
const a = $el("a", {
href: url,
download: filename,
+2 -1
View File
@@ -1,8 +1,9 @@
[project]
name = "comfyui-to-python-extension"
description = "This custom node allows you to generate pure python code from your ComfyUI workflow with the click of a button. Great for rapid experimentation or production deployment."
version = "2.0.0"
version = "2.1.0"
license = { text = "MIT License" }
requires-python = ">=3.12"
dependencies = ["black"]
[project.urls]
+33
View File
@@ -0,0 +1,33 @@
import tomllib
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
class ProjectContractsTest(unittest.TestCase):
def test_project_declares_supported_python_floor(self):
pyproject = tomllib.loads((REPO_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
self.assertEqual(pyproject["project"]["requires-python"], ">=3.12")
def test_readme_documents_python_support_and_default_save_filename(self):
readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8")
self.assertIn("This project supports Python 3.12 and newer.", readme)
self.assertIn("default filename `workflow_api.py`", readme)
def test_extension_import_path_requires_uv_sync_instead_of_running_install_py(self):
init_text = (REPO_ROOT / "__init__.py").read_text(encoding="utf-8")
self.assertIn("Run 'uv sync'", init_text)
self.assertNotIn("spec_from_file_location", init_text)
self.assertNotIn("Successfully installed. Hopefully, at least.", init_text)
def test_frontend_save_flow_uses_deterministic_filename_without_prompt(self):
save_as_script = (REPO_ROOT / "js" / "save-as-script.js").read_text(encoding="utf-8")
self.assertIn('const DEFAULT_SCRIPT_FILENAME = "workflow_api.py";', save_as_script)
self.assertNotIn("prompt(", save_as_script)
Generated
+1 -1
View File
@@ -57,7 +57,7 @@ wheels = [
[[package]]
name = "comfyui-to-python-extension"
version = "2.0.0"
version = "2.1.0"
source = { virtual = "." }
dependencies = [
{ name = "black" },