comfyui-docker-helper 0.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- comfyui_docker_helper-0.5.1/LICENSE +21 -0
- comfyui_docker_helper-0.5.1/PKG-INFO +43 -0
- comfyui_docker_helper-0.5.1/pyproject.toml +105 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/__init__.py +1 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/cli.py +49 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/cli_settings.py +3 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/comfyui_requirements.py +335 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/__init__.py +168 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/build_plan.py +1681 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/canonical_lock.py +1336 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/canonical_request.py +598 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/canonical_resolver.py +522 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/custom_node_inventory.py +105 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/diagnostics.py +42 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/file_checksum.py +21 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/final_manifest.py +553 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/final_models.py +192 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/final_planning.py +72 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/final_validation.py +1152 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/hook_validation.py +68 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/merge.py +118 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/model_base.py +9 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/os_packages.py +23 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/registry_validation.py +22 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/runtime_config.py +695 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/runtime_file_validation.py +91 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/runtime_hooks.py +26 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/runtime_models.py +60 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/selector_validation.py +134 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/service.py +129 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/shutdown_timeout.py +19 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/ssh_keys.py +167 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/url_validation.py +155 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/config/value_validation.py +51 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/__init__.py +1 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/application_installer.py +564 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/attempt_coordinator.py +289 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/build_plan_input.py +159 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/cli.py +138 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/comfyui_installer.py +923 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/custom_node_installer.py +1364 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/download_files.py +1196 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/entrypoint.py +134 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/evidence_writer.py +245 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/final_manifest.py +690 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/process_control.py +311 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/readiness.py +182 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/runners.py +388 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/runtime_diagnostics.py +80 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/runtime_download_state.py +86 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/runtime_downloads.py +718 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/runtime_files.py +1085 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/runtime_hooks.py +741 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/runtime_lifecycle.py +1003 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/runtime_ssh_service.py +391 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/runtime_state.py +785 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/ssh.py +629 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/container/transfer_core.py +2291 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/errors.py +27 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/exact_ledger.py +16 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/file_admission.py +73 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/host/__init__.py +1 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/host/buildx.py +79 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/host/canonical_acquisition.py +803 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/host/cli.py +430 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/host/diagnostics.py +92 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/host/identity_providers.py +1057 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/host/planning_authority.py +286 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/host/release_wheel.py +111 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/host/render_service.py +472 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/host/runtime_hook_inputs.py +217 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/host/uv_docker_executor.py +526 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/pytorch_resolution.py +59 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/release_artifacts.py +49 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/rendering/__init__.py +15 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/rendering/final_materializer.py +313 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/rendering/final_renderer.py +294 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/resources/final-core-probe.py +126 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/resources/release-projection/LICENSE +21 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/resources/release-projection/pyproject.toml +70 -0
- comfyui_docker_helper-0.5.1/src/comfyui_docker_helper/version.py +29 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jim Lee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: comfyui-docker-helper
|
|
3
|
+
Version: 0.5.1
|
|
4
|
+
Summary: Unofficial command-line helper for using ComfyUI with Docker
|
|
5
|
+
Keywords: comfyui,docker,docker-buildx,buildkit,cli,developer-tools,container-images
|
|
6
|
+
Author: jimlee2048
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
18
|
+
Classifier: Topic :: System :: Installation/Setup
|
|
19
|
+
Classifier: Topic :: Utilities
|
|
20
|
+
Requires-Dist: aria2p
|
|
21
|
+
Requires-Dist: build>=1,<2
|
|
22
|
+
Requires-Dist: httpx
|
|
23
|
+
Requires-Dist: packaging>=26.2
|
|
24
|
+
Requires-Dist: pydantic>=2,<3
|
|
25
|
+
Requires-Dist: python-on-whales>=0.81.0
|
|
26
|
+
Requires-Dist: rich
|
|
27
|
+
Requires-Dist: tomli-w
|
|
28
|
+
Requires-Dist: typer
|
|
29
|
+
Maintainer: jimlee2048
|
|
30
|
+
Requires-Python: >=3.12, <3.15
|
|
31
|
+
Project-URL: Homepage, https://github.com/jimlee2048/comfyui-docker-helper
|
|
32
|
+
Project-URL: Repository, https://github.com/jimlee2048/comfyui-docker-helper
|
|
33
|
+
Project-URL: Issues, https://github.com/jimlee2048/comfyui-docker-helper/issues
|
|
34
|
+
Project-URL: Changelog, https://github.com/jimlee2048/comfyui-docker-helper/releases
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
`comfyui-docker-helper` (`cdh`) is an independent, unofficial command-line
|
|
38
|
+
helper for using ComfyUI with Docker. It is not affiliated with or endorsed by
|
|
39
|
+
the ComfyUI project.
|
|
40
|
+
|
|
41
|
+
See the [GitHub repository](https://github.com/jimlee2048/comfyui-docker-helper)
|
|
42
|
+
for current capabilities, requirements, documentation, examples, and issue
|
|
43
|
+
tracking.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "comfyui-docker-helper"
|
|
3
|
+
version = "0.5.1"
|
|
4
|
+
description = "Unofficial command-line helper for using ComfyUI with Docker"
|
|
5
|
+
readme = { text = """
|
|
6
|
+
`comfyui-docker-helper` (`cdh`) is an independent, unofficial command-line
|
|
7
|
+
helper for using ComfyUI with Docker. It is not affiliated with or endorsed by
|
|
8
|
+
the ComfyUI project.
|
|
9
|
+
|
|
10
|
+
See the [GitHub repository](https://github.com/jimlee2048/comfyui-docker-helper)
|
|
11
|
+
for current capabilities, requirements, documentation, examples, and issue
|
|
12
|
+
tracking.""", content-type = "text/markdown" }
|
|
13
|
+
license = "MIT"
|
|
14
|
+
license-files = ["LICENSE"]
|
|
15
|
+
requires-python = ">=3.12,<3.15"
|
|
16
|
+
authors = [
|
|
17
|
+
{ name = "jimlee2048" },
|
|
18
|
+
]
|
|
19
|
+
maintainers = [
|
|
20
|
+
{ name = "jimlee2048" },
|
|
21
|
+
]
|
|
22
|
+
keywords = [
|
|
23
|
+
"comfyui",
|
|
24
|
+
"docker",
|
|
25
|
+
"docker-buildx",
|
|
26
|
+
"buildkit",
|
|
27
|
+
"cli",
|
|
28
|
+
"developer-tools",
|
|
29
|
+
"container-images",
|
|
30
|
+
]
|
|
31
|
+
classifiers = [
|
|
32
|
+
"Development Status :: 3 - Alpha",
|
|
33
|
+
"Environment :: Console",
|
|
34
|
+
"Intended Audience :: Developers",
|
|
35
|
+
"Programming Language :: Python :: 3",
|
|
36
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
37
|
+
"Programming Language :: Python :: 3.12",
|
|
38
|
+
"Programming Language :: Python :: 3.13",
|
|
39
|
+
"Programming Language :: Python :: 3.14",
|
|
40
|
+
"Topic :: Software Development :: Build Tools",
|
|
41
|
+
"Topic :: System :: Installation/Setup",
|
|
42
|
+
"Topic :: Utilities",
|
|
43
|
+
]
|
|
44
|
+
dependencies = [
|
|
45
|
+
"aria2p",
|
|
46
|
+
"build>=1,<2",
|
|
47
|
+
"httpx",
|
|
48
|
+
"packaging>=26.2",
|
|
49
|
+
"pydantic>=2,<3",
|
|
50
|
+
"python-on-whales>=0.81.0",
|
|
51
|
+
"rich",
|
|
52
|
+
"tomli-w",
|
|
53
|
+
"typer",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[project.scripts]
|
|
57
|
+
cdh = "comfyui_docker_helper.cli:app"
|
|
58
|
+
|
|
59
|
+
[project.urls]
|
|
60
|
+
Homepage = "https://github.com/jimlee2048/comfyui-docker-helper"
|
|
61
|
+
Repository = "https://github.com/jimlee2048/comfyui-docker-helper"
|
|
62
|
+
Issues = "https://github.com/jimlee2048/comfyui-docker-helper/issues"
|
|
63
|
+
Changelog = "https://github.com/jimlee2048/comfyui-docker-helper/releases"
|
|
64
|
+
|
|
65
|
+
[dependency-groups]
|
|
66
|
+
dev = [
|
|
67
|
+
"pytest",
|
|
68
|
+
"ruff",
|
|
69
|
+
"twine",
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
[build-system]
|
|
73
|
+
requires = ["uv_build==0.11.28"]
|
|
74
|
+
build-backend = "uv_build"
|
|
75
|
+
|
|
76
|
+
[tool.uv.build-backend]
|
|
77
|
+
module-name = "comfyui_docker_helper"
|
|
78
|
+
|
|
79
|
+
[tool.pytest.ini_options]
|
|
80
|
+
addopts = [
|
|
81
|
+
"--strict-config",
|
|
82
|
+
"--strict-markers",
|
|
83
|
+
]
|
|
84
|
+
markers = [
|
|
85
|
+
"unit: fast tests for isolated units",
|
|
86
|
+
"smoke: lightweight smoke checks or smoke fixture validation",
|
|
87
|
+
"acceptance: tests parameterized by the typed release acceptance catalog",
|
|
88
|
+
"docker: tests that require Docker and --run-docker authorization",
|
|
89
|
+
"network: tests that require network access and --run-network authorization",
|
|
90
|
+
"gpu: tests that require GPU access and --run-gpu authorization",
|
|
91
|
+
"slow: tests that require --run-slow authorization",
|
|
92
|
+
]
|
|
93
|
+
testpaths = ["tests"]
|
|
94
|
+
|
|
95
|
+
[tool.ruff]
|
|
96
|
+
target-version = "py312"
|
|
97
|
+
src = ["src", "tests"]
|
|
98
|
+
|
|
99
|
+
[tool.ruff.lint]
|
|
100
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
|
|
101
|
+
|
|
102
|
+
[tool.ruff.format]
|
|
103
|
+
indent-style = "space"
|
|
104
|
+
line-ending = "lf"
|
|
105
|
+
quote-style = "double"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""ComfyUI Docker Helper package."""
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Top-level command-line interface."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from comfyui_docker_helper.cli_settings import HELP_CONTEXT_SETTINGS
|
|
8
|
+
from comfyui_docker_helper.container.cli import app as container_app
|
|
9
|
+
from comfyui_docker_helper.errors import ApplicationGroup
|
|
10
|
+
from comfyui_docker_helper.host.cli import app as host_app
|
|
11
|
+
from comfyui_docker_helper.version import package_version
|
|
12
|
+
|
|
13
|
+
app = typer.Typer(
|
|
14
|
+
cls=ApplicationGroup,
|
|
15
|
+
name="cdh",
|
|
16
|
+
help="Build ComfyUI images and run their image-internal helpers.",
|
|
17
|
+
no_args_is_help=True,
|
|
18
|
+
add_completion=False,
|
|
19
|
+
context_settings=HELP_CONTEXT_SETTINGS,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _version_callback(show_version: bool) -> None:
|
|
24
|
+
if show_version:
|
|
25
|
+
typer.echo(f"cdh {package_version()}")
|
|
26
|
+
raise typer.Exit()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@app.callback()
|
|
30
|
+
def main(
|
|
31
|
+
version: Annotated[
|
|
32
|
+
bool,
|
|
33
|
+
typer.Option(
|
|
34
|
+
"--version",
|
|
35
|
+
callback=_version_callback,
|
|
36
|
+
help="Show the version and exit.",
|
|
37
|
+
is_eager=True,
|
|
38
|
+
),
|
|
39
|
+
] = False,
|
|
40
|
+
) -> None:
|
|
41
|
+
"""Route host and image-internal commands."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
app.add_typer(host_app, name="host", help="commands executed on the host machine")
|
|
45
|
+
app.add_typer(
|
|
46
|
+
container_app,
|
|
47
|
+
name="container",
|
|
48
|
+
help="helpers executed inside ComfyUI images",
|
|
49
|
+
)
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"""Strict ComfyUI requirements projection and PyTorch-group merge rules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import re
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
from packaging.requirements import InvalidRequirement, Requirement
|
|
10
|
+
from packaging.specifiers import InvalidSpecifier, SpecifierSet
|
|
11
|
+
from packaging.utils import canonicalize_name
|
|
12
|
+
from packaging.version import InvalidVersion, Version
|
|
13
|
+
|
|
14
|
+
from comfyui_docker_helper.config.canonical_lock import DirectPythonRequestMember
|
|
15
|
+
from comfyui_docker_helper.exact_ledger import (
|
|
16
|
+
CUDA_PROTECTED_REQUIREMENTS as CUDA_PROTECTED_REQUIREMENTS,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
COMFYUI_REQUIREMENTS_PATH = "requirements.txt"
|
|
20
|
+
_SOURCE_OPTION = re.compile(
|
|
21
|
+
r"(?:-e(?:ditable)?|-i|--index-url|--extra-index-url|--find-links|"
|
|
22
|
+
r"--trusted-host|--no-index|--pre|--prefer-binary|--only-binary|"
|
|
23
|
+
r"--no-binary|--require-hashes|--constraint|-c)"
|
|
24
|
+
r"(?:\s|=|$)"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ComfyUIRequirementsError(ValueError):
|
|
29
|
+
"""One contextual strict-requirements or merge failure."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True, slots=True)
|
|
33
|
+
class ParsedComfyUIRequirements:
|
|
34
|
+
"""Canonical target projection plus ordinary install remainder."""
|
|
35
|
+
|
|
36
|
+
digest: str
|
|
37
|
+
protected: tuple[DirectPythonRequestMember, ...]
|
|
38
|
+
ordinary: tuple[str, ...]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True, slots=True)
|
|
42
|
+
class DeclaredManagerRequirement:
|
|
43
|
+
"""One target-active checkout-declared Manager requirement."""
|
|
44
|
+
|
|
45
|
+
package: str
|
|
46
|
+
requirement: str
|
|
47
|
+
specifier: str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True, slots=True)
|
|
51
|
+
class ParsedManagerRequirements:
|
|
52
|
+
"""Strict install rows plus the exact checkout-owned Manager identity."""
|
|
53
|
+
|
|
54
|
+
digest: str
|
|
55
|
+
rows: tuple[str, ...]
|
|
56
|
+
active: tuple[DeclaredManagerRequirement, ...]
|
|
57
|
+
manager_version: str
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def parse_comfyui_requirements(
|
|
61
|
+
content: bytes,
|
|
62
|
+
*,
|
|
63
|
+
python_version: str,
|
|
64
|
+
platform: str,
|
|
65
|
+
machine: str,
|
|
66
|
+
protected_names: tuple[str, ...],
|
|
67
|
+
) -> ParsedComfyUIRequirements:
|
|
68
|
+
"""Parse PEP 508 rows and optionally project target-active protected members."""
|
|
69
|
+
names = (
|
|
70
|
+
set(_normalized_protected_names(protected_names)) if protected_names else set()
|
|
71
|
+
)
|
|
72
|
+
environment = target_marker_environment(python_version, platform, machine)
|
|
73
|
+
protected: list[DirectPythonRequestMember] = []
|
|
74
|
+
ordinary: list[str] = []
|
|
75
|
+
try:
|
|
76
|
+
document = content.decode("utf-8")
|
|
77
|
+
except UnicodeDecodeError as error:
|
|
78
|
+
raise ComfyUIRequirementsError("ComfyUI requirements must be UTF-8") from error
|
|
79
|
+
for line_number, raw in enumerate(document.splitlines(), start=1):
|
|
80
|
+
line = raw.strip()
|
|
81
|
+
if not line or line.startswith("#"):
|
|
82
|
+
continue
|
|
83
|
+
if line.startswith("-") or _SOURCE_OPTION.match(line):
|
|
84
|
+
raise ComfyUIRequirementsError(
|
|
85
|
+
f"ComfyUI requirements line {line_number} changes package sources"
|
|
86
|
+
)
|
|
87
|
+
try:
|
|
88
|
+
requirement = Requirement(line)
|
|
89
|
+
except InvalidRequirement as error:
|
|
90
|
+
raise ComfyUIRequirementsError(
|
|
91
|
+
f"ComfyUI requirements line {line_number} is not valid PEP 508"
|
|
92
|
+
) from error
|
|
93
|
+
if requirement.url is not None:
|
|
94
|
+
raise ComfyUIRequirementsError(
|
|
95
|
+
f"ComfyUI requirements line {line_number} uses a direct source"
|
|
96
|
+
)
|
|
97
|
+
name = canonicalize_name(requirement.name)
|
|
98
|
+
if name in names:
|
|
99
|
+
if requirement.marker is None or requirement.marker.evaluate(environment):
|
|
100
|
+
protected.append(_member(requirement))
|
|
101
|
+
continue
|
|
102
|
+
ordinary.append(str(requirement))
|
|
103
|
+
merged = merge_requirement_members(tuple(protected))
|
|
104
|
+
return ParsedComfyUIRequirements(
|
|
105
|
+
digest=f"sha256:{hashlib.sha256(content).hexdigest()}",
|
|
106
|
+
protected=merged,
|
|
107
|
+
ordinary=tuple(ordinary),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def parse_ordinary_requirements(
|
|
112
|
+
content: bytes,
|
|
113
|
+
*,
|
|
114
|
+
python_version: str,
|
|
115
|
+
platform: str,
|
|
116
|
+
machine: str,
|
|
117
|
+
) -> tuple[str, ...]:
|
|
118
|
+
"""Return strict ordinary rows for a cdh-owned requirements operation."""
|
|
119
|
+
return parse_comfyui_requirements(
|
|
120
|
+
content,
|
|
121
|
+
python_version=python_version,
|
|
122
|
+
platform=platform,
|
|
123
|
+
machine=machine,
|
|
124
|
+
protected_names=(),
|
|
125
|
+
).ordinary
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def parse_manager_requirements(
|
|
129
|
+
content: bytes,
|
|
130
|
+
*,
|
|
131
|
+
python_version: str,
|
|
132
|
+
platform: str,
|
|
133
|
+
machine: str,
|
|
134
|
+
) -> ParsedManagerRequirements:
|
|
135
|
+
"""Parse checkout-owned Manager requirements without accepting source control."""
|
|
136
|
+
environment = target_marker_environment(python_version, platform, machine)
|
|
137
|
+
rows: list[str] = []
|
|
138
|
+
active: list[DeclaredManagerRequirement] = []
|
|
139
|
+
seen_active: set[str] = set()
|
|
140
|
+
try:
|
|
141
|
+
document = content.decode("utf-8")
|
|
142
|
+
except UnicodeDecodeError as error:
|
|
143
|
+
raise ComfyUIRequirementsError("Manager requirements must be UTF-8") from error
|
|
144
|
+
for line_number, raw in enumerate(document.splitlines(), start=1):
|
|
145
|
+
line = raw.strip()
|
|
146
|
+
if not line or line.startswith("#"):
|
|
147
|
+
continue
|
|
148
|
+
if line.startswith("-") or _SOURCE_OPTION.match(line):
|
|
149
|
+
raise ComfyUIRequirementsError(
|
|
150
|
+
f"Manager requirements line {line_number} changes package sources"
|
|
151
|
+
)
|
|
152
|
+
try:
|
|
153
|
+
requirement = Requirement(line)
|
|
154
|
+
except InvalidRequirement as error:
|
|
155
|
+
raise ComfyUIRequirementsError(
|
|
156
|
+
f"Manager requirements line {line_number} is not valid PEP 508"
|
|
157
|
+
) from error
|
|
158
|
+
if requirement.url is not None:
|
|
159
|
+
raise ComfyUIRequirementsError(
|
|
160
|
+
f"Manager requirements line {line_number} uses a direct source"
|
|
161
|
+
)
|
|
162
|
+
canonical = str(requirement)
|
|
163
|
+
rows.append(canonical)
|
|
164
|
+
if requirement.marker is not None and not requirement.marker.evaluate(
|
|
165
|
+
environment
|
|
166
|
+
):
|
|
167
|
+
continue
|
|
168
|
+
package = canonicalize_name(requirement.name)
|
|
169
|
+
if package in seen_active:
|
|
170
|
+
raise ComfyUIRequirementsError(
|
|
171
|
+
f"Manager requirements duplicate target package {package}"
|
|
172
|
+
)
|
|
173
|
+
seen_active.add(package)
|
|
174
|
+
active.append(
|
|
175
|
+
DeclaredManagerRequirement(
|
|
176
|
+
package=package,
|
|
177
|
+
requirement=canonical,
|
|
178
|
+
specifier=str(requirement.specifier),
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
manager = [item for item in active if item.package == "comfyui-manager"]
|
|
182
|
+
if len(manager) != 1:
|
|
183
|
+
raise ComfyUIRequirementsError(
|
|
184
|
+
"Manager requirements must declare exactly one active comfyui-manager"
|
|
185
|
+
)
|
|
186
|
+
manager_version = _exact_version(
|
|
187
|
+
manager[0].specifier,
|
|
188
|
+
subject="comfyui-manager",
|
|
189
|
+
)
|
|
190
|
+
return ParsedManagerRequirements(
|
|
191
|
+
digest=f"sha256:{hashlib.sha256(content).hexdigest()}",
|
|
192
|
+
rows=tuple(rows),
|
|
193
|
+
active=tuple(active),
|
|
194
|
+
manager_version=manager_version,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def merge_pytorch_requirements(
|
|
199
|
+
mandatory_torch: DirectPythonRequestMember,
|
|
200
|
+
upstream: tuple[DirectPythonRequestMember, ...],
|
|
201
|
+
configured_extras: tuple[DirectPythonRequestMember, ...],
|
|
202
|
+
) -> tuple[DirectPythonRequestMember, ...]:
|
|
203
|
+
"""Merge all direct PyTorch ownership into one deterministic request."""
|
|
204
|
+
if mandatory_torch.package != "torch":
|
|
205
|
+
raise ComfyUIRequirementsError("mandatory PyTorch member must be torch")
|
|
206
|
+
return merge_requirement_members((mandatory_torch, *upstream, *configured_extras))
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def merge_requirement_members(
|
|
210
|
+
members: tuple[DirectPythonRequestMember, ...],
|
|
211
|
+
) -> tuple[DirectPythonRequestMember, ...]:
|
|
212
|
+
"""Merge normalized PEP 503 identities, extras, and compatible selectors."""
|
|
213
|
+
grouped: dict[str, list[DirectPythonRequestMember]] = {}
|
|
214
|
+
for member in members:
|
|
215
|
+
canonical = DirectPythonRequestMember.model_validate(
|
|
216
|
+
member.model_dump(mode="python")
|
|
217
|
+
)
|
|
218
|
+
grouped.setdefault(canonical.package, []).append(canonical)
|
|
219
|
+
result: list[DirectPythonRequestMember] = []
|
|
220
|
+
for package in sorted(grouped):
|
|
221
|
+
values = grouped[package]
|
|
222
|
+
extras = sorted({extra for item in values for extra in item.extras})
|
|
223
|
+
selector = _merge_selectors(package, tuple(item.selector for item in values))
|
|
224
|
+
result.append(
|
|
225
|
+
DirectPythonRequestMember(
|
|
226
|
+
package=package,
|
|
227
|
+
extras=extras,
|
|
228
|
+
selector=selector,
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
return tuple(result)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _member(requirement: Requirement) -> DirectPythonRequestMember:
|
|
235
|
+
try:
|
|
236
|
+
selector = str(SpecifierSet(str(requirement.specifier)))
|
|
237
|
+
except InvalidSpecifier as error: # pragma: no cover - Requirement owns syntax.
|
|
238
|
+
raise ComfyUIRequirementsError("requirement specifier is invalid") from error
|
|
239
|
+
return DirectPythonRequestMember(
|
|
240
|
+
package=canonicalize_name(requirement.name),
|
|
241
|
+
extras=sorted(canonicalize_name(extra) for extra in requirement.extras),
|
|
242
|
+
selector=selector,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _exact_version(specifier: str, *, subject: str) -> str:
|
|
247
|
+
clauses = tuple(SpecifierSet(specifier))
|
|
248
|
+
if len(clauses) != 1 or clauses[0].operator != "==" or "*" in clauses[0].version:
|
|
249
|
+
raise ComfyUIRequirementsError(
|
|
250
|
+
f"{subject} must have one exact checkout-owned version"
|
|
251
|
+
)
|
|
252
|
+
try:
|
|
253
|
+
version = Version(clauses[0].version)
|
|
254
|
+
except InvalidVersion as error:
|
|
255
|
+
raise ComfyUIRequirementsError(
|
|
256
|
+
f"{subject} has an invalid exact version"
|
|
257
|
+
) from error
|
|
258
|
+
value = str(version)
|
|
259
|
+
if value != clauses[0].version:
|
|
260
|
+
raise ComfyUIRequirementsError(
|
|
261
|
+
f"{subject} must have one canonical exact checkout-owned version"
|
|
262
|
+
)
|
|
263
|
+
return value
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _merge_selectors(package: str, selectors: tuple[str, ...]) -> str:
|
|
267
|
+
clauses = sorted(
|
|
268
|
+
{
|
|
269
|
+
clause
|
|
270
|
+
for selector in selectors
|
|
271
|
+
if selector
|
|
272
|
+
for clause in selector.split(",")
|
|
273
|
+
if clause
|
|
274
|
+
}
|
|
275
|
+
)
|
|
276
|
+
combined = str(SpecifierSet(",".join(clauses))) if clauses else ""
|
|
277
|
+
exact: set[Version] = set()
|
|
278
|
+
for clause in clauses:
|
|
279
|
+
if (
|
|
280
|
+
clause.startswith("==")
|
|
281
|
+
and not clause.startswith("===")
|
|
282
|
+
and "*" not in clause
|
|
283
|
+
):
|
|
284
|
+
try:
|
|
285
|
+
exact.add(Version(clause[2:]))
|
|
286
|
+
except InvalidVersion as error:
|
|
287
|
+
raise ComfyUIRequirementsError(
|
|
288
|
+
f"protected requirement {package} has an invalid exact selector"
|
|
289
|
+
) from error
|
|
290
|
+
if len(exact) > 1:
|
|
291
|
+
raise ComfyUIRequirementsError(
|
|
292
|
+
f"protected requirement {package} has conflicting exact selectors"
|
|
293
|
+
)
|
|
294
|
+
if exact and not SpecifierSet(combined).contains(
|
|
295
|
+
next(iter(exact)), prereleases=True
|
|
296
|
+
):
|
|
297
|
+
raise ComfyUIRequirementsError(
|
|
298
|
+
f"protected requirement {package} has incompatible selectors"
|
|
299
|
+
)
|
|
300
|
+
return combined
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _normalized_protected_names(names: tuple[str, ...]) -> tuple[str, ...]:
|
|
304
|
+
normalized = tuple(sorted(canonicalize_name(name) for name in names))
|
|
305
|
+
if not normalized or len(normalized) != len(set(normalized)):
|
|
306
|
+
raise ComfyUIRequirementsError(
|
|
307
|
+
"protected requirement names must be non-empty and unique"
|
|
308
|
+
)
|
|
309
|
+
return normalized
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def target_marker_environment(
|
|
313
|
+
python_version: str, platform: str, machine: str
|
|
314
|
+
) -> dict[str, str]:
|
|
315
|
+
"""Return the exact target environment used for PEP 508 marker evaluation."""
|
|
316
|
+
try:
|
|
317
|
+
parsed = Version(python_version)
|
|
318
|
+
except InvalidVersion as error:
|
|
319
|
+
raise ComfyUIRequirementsError("target Python version is invalid") from error
|
|
320
|
+
if platform != "linux/amd64" or machine != "x86_64" or len(parsed.release) < 2:
|
|
321
|
+
raise ComfyUIRequirementsError("requirements target is unsupported")
|
|
322
|
+
return {
|
|
323
|
+
"implementation_name": "cpython",
|
|
324
|
+
"implementation_version": python_version,
|
|
325
|
+
"os_name": "posix",
|
|
326
|
+
"platform_machine": machine,
|
|
327
|
+
"platform_python_implementation": "CPython",
|
|
328
|
+
"platform_release": "",
|
|
329
|
+
"platform_system": "Linux",
|
|
330
|
+
"platform_version": "",
|
|
331
|
+
"python_full_version": python_version,
|
|
332
|
+
"python_version": ".".join(str(value) for value in parsed.release[:2]),
|
|
333
|
+
"sys_platform": "linux",
|
|
334
|
+
"extra": "",
|
|
335
|
+
}
|