Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cdcc235a0 | ||
|
|
9a17000417 | ||
|
|
fbc06463f7 | ||
|
|
d6c5b110ab | ||
|
|
5d70efdc88 |
@@ -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
@@ -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
@@ -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
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user