From 5d70efdc88eb53e3aace0acdd8794f6d03ca97bb Mon Sep 17 00:00:00 2001 From: Peyton DeNiro <25550995+pydn@users.noreply.github.com> Date: Sun, 29 Mar 2026 17:34:30 -0500 Subject: [PATCH] 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. --- __init__.py | 16 ++++------------ js/save-as-script.js | 16 ++++++---------- pyproject.toml | 1 + tests/test_project_contracts.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 22 deletions(-) create mode 100644 tests/test_project_contracts.py diff --git a/__init__.py b/__init__.py index 10cdf1c..8283e84 100644 --- a/__init__.py +++ b/__init__.py @@ -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" diff --git a/js/save-as-script.js b/js/save-as-script.js index 9de431f..19175fc 100644 --- a/js/save-as-script.js +++ b/js/save-as-script.js @@ -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, diff --git a/pyproject.toml b/pyproject.toml index b100249..1e07500 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,6 +3,7 @@ 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" license = { text = "MIT License" } +requires-python = ">=3.12" dependencies = ["black"] [project.urls] diff --git a/tests/test_project_contracts.py b/tests/test_project_contracts.py new file mode 100644 index 0000000..5632583 --- /dev/null +++ b/tests/test_project_contracts.py @@ -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) +