comfy-env 0.0.8__py3-none-any.whl
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.
- comfy_env/__init__.py +161 -0
- comfy_env/cli.py +388 -0
- comfy_env/decorator.py +422 -0
- comfy_env/env/__init__.py +30 -0
- comfy_env/env/config.py +144 -0
- comfy_env/env/config_file.py +592 -0
- comfy_env/env/detection.py +176 -0
- comfy_env/env/manager.py +657 -0
- comfy_env/env/platform/__init__.py +21 -0
- comfy_env/env/platform/base.py +96 -0
- comfy_env/env/platform/darwin.py +53 -0
- comfy_env/env/platform/linux.py +68 -0
- comfy_env/env/platform/windows.py +377 -0
- comfy_env/env/security.py +267 -0
- comfy_env/errors.py +325 -0
- comfy_env/install.py +539 -0
- comfy_env/ipc/__init__.py +55 -0
- comfy_env/ipc/bridge.py +512 -0
- comfy_env/ipc/protocol.py +265 -0
- comfy_env/ipc/tensor.py +371 -0
- comfy_env/ipc/torch_bridge.py +401 -0
- comfy_env/ipc/transport.py +318 -0
- comfy_env/ipc/worker.py +221 -0
- comfy_env/registry.py +252 -0
- comfy_env/resolver.py +399 -0
- comfy_env/runner.py +273 -0
- comfy_env/stubs/__init__.py +1 -0
- comfy_env/stubs/folder_paths.py +57 -0
- comfy_env/workers/__init__.py +49 -0
- comfy_env/workers/base.py +82 -0
- comfy_env/workers/pool.py +241 -0
- comfy_env/workers/tensor_utils.py +188 -0
- comfy_env/workers/torch_mp.py +375 -0
- comfy_env/workers/venv.py +903 -0
- comfy_env-0.0.8.dist-info/METADATA +228 -0
- comfy_env-0.0.8.dist-info/RECORD +39 -0
- comfy_env-0.0.8.dist-info/WHEEL +4 -0
- comfy_env-0.0.8.dist-info/entry_points.txt +2 -0
- comfy_env-0.0.8.dist-info/licenses/LICENSE +21 -0
comfy_env/__init__.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""
|
|
2
|
+
comfy-env: Environment management for ComfyUI custom nodes.
|
|
3
|
+
|
|
4
|
+
This package provides:
|
|
5
|
+
- CUDA wheel resolution and in-place installation (Type 2 nodes)
|
|
6
|
+
- Process isolation with separate venvs (Type 1 nodes)
|
|
7
|
+
|
|
8
|
+
## Quick Start - In-Place Installation
|
|
9
|
+
|
|
10
|
+
from comfy_env import install
|
|
11
|
+
|
|
12
|
+
# Auto-discover config and install CUDA wheels
|
|
13
|
+
install()
|
|
14
|
+
|
|
15
|
+
# Or with explicit config
|
|
16
|
+
install(config="comfyui_env.toml")
|
|
17
|
+
|
|
18
|
+
## Quick Start - Process Isolation
|
|
19
|
+
|
|
20
|
+
from comfy_env.workers import get_worker, TorchMPWorker
|
|
21
|
+
|
|
22
|
+
# Same-venv isolation (zero-copy tensors)
|
|
23
|
+
worker = TorchMPWorker()
|
|
24
|
+
result = worker.call(my_gpu_function, image=tensor)
|
|
25
|
+
|
|
26
|
+
# Cross-venv isolation
|
|
27
|
+
from comfy_env.workers import PersistentVenvWorker
|
|
28
|
+
worker = PersistentVenvWorker(python="/path/to/venv/bin/python")
|
|
29
|
+
result = worker.call_module("my_module", "my_func", image=tensor)
|
|
30
|
+
|
|
31
|
+
## CLI
|
|
32
|
+
|
|
33
|
+
comfy-env install # Install from config
|
|
34
|
+
comfy-env info # Show environment info
|
|
35
|
+
comfy-env resolve pkg==1.0 # Show resolved wheel URL
|
|
36
|
+
comfy-env doctor # Verify installation
|
|
37
|
+
|
|
38
|
+
## Legacy APIs (still supported)
|
|
39
|
+
|
|
40
|
+
The @isolated decorator and WorkerBridge are still available.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
__version__ = "0.0.8"
|
|
44
|
+
|
|
45
|
+
from .env.config import IsolatedEnv, EnvManagerConfig, LocalConfig, NodeReq
|
|
46
|
+
from .env.config_file import (
|
|
47
|
+
load_env_from_file,
|
|
48
|
+
discover_env_config,
|
|
49
|
+
load_config,
|
|
50
|
+
discover_config,
|
|
51
|
+
CONFIG_FILE_NAMES,
|
|
52
|
+
)
|
|
53
|
+
from .env.manager import IsolatedEnvManager
|
|
54
|
+
from .env.detection import detect_cuda_version, detect_gpu_info, get_gpu_summary
|
|
55
|
+
from .env.security import (
|
|
56
|
+
normalize_env_name,
|
|
57
|
+
validate_dependency,
|
|
58
|
+
validate_dependencies,
|
|
59
|
+
validate_path_within_root,
|
|
60
|
+
validate_wheel_url,
|
|
61
|
+
)
|
|
62
|
+
from .ipc.bridge import WorkerBridge
|
|
63
|
+
from .ipc.worker import BaseWorker, register
|
|
64
|
+
from .decorator import isolated, shutdown_all_processes
|
|
65
|
+
|
|
66
|
+
# New in-place installation API
|
|
67
|
+
from .install import install, verify_installation
|
|
68
|
+
from .resolver import RuntimeEnv, WheelResolver
|
|
69
|
+
from .errors import (
|
|
70
|
+
EnvManagerError,
|
|
71
|
+
ConfigError,
|
|
72
|
+
WheelNotFoundError,
|
|
73
|
+
DependencyError,
|
|
74
|
+
CUDANotFoundError,
|
|
75
|
+
InstallError,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# New workers module (recommended API)
|
|
79
|
+
from .workers import (
|
|
80
|
+
Worker,
|
|
81
|
+
TorchMPWorker,
|
|
82
|
+
VenvWorker,
|
|
83
|
+
WorkerPool,
|
|
84
|
+
get_worker,
|
|
85
|
+
register_worker,
|
|
86
|
+
shutdown_workers,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# TorchBridge is optional (requires PyTorch)
|
|
90
|
+
try:
|
|
91
|
+
from .ipc.torch_bridge import TorchBridge, TorchWorker
|
|
92
|
+
_TORCH_AVAILABLE = True
|
|
93
|
+
except ImportError:
|
|
94
|
+
_TORCH_AVAILABLE = False
|
|
95
|
+
|
|
96
|
+
# PersistentVenvWorker requires the ipc.transport module
|
|
97
|
+
try:
|
|
98
|
+
from .workers.venv import PersistentVenvWorker
|
|
99
|
+
_PERSISTENT_AVAILABLE = True
|
|
100
|
+
except ImportError:
|
|
101
|
+
_PERSISTENT_AVAILABLE = False
|
|
102
|
+
|
|
103
|
+
__all__ = [
|
|
104
|
+
# NEW: In-place installation API
|
|
105
|
+
"install",
|
|
106
|
+
"verify_installation",
|
|
107
|
+
"RuntimeEnv",
|
|
108
|
+
"WheelResolver",
|
|
109
|
+
# Errors
|
|
110
|
+
"EnvManagerError",
|
|
111
|
+
"ConfigError",
|
|
112
|
+
"WheelNotFoundError",
|
|
113
|
+
"DependencyError",
|
|
114
|
+
"CUDANotFoundError",
|
|
115
|
+
"InstallError",
|
|
116
|
+
# Workers API (recommended for isolation)
|
|
117
|
+
"Worker",
|
|
118
|
+
"TorchMPWorker",
|
|
119
|
+
"VenvWorker",
|
|
120
|
+
"WorkerPool",
|
|
121
|
+
"get_worker",
|
|
122
|
+
"register_worker",
|
|
123
|
+
"shutdown_workers",
|
|
124
|
+
# Environment & Config
|
|
125
|
+
"IsolatedEnv",
|
|
126
|
+
"EnvManagerConfig",
|
|
127
|
+
"LocalConfig",
|
|
128
|
+
"NodeReq",
|
|
129
|
+
"IsolatedEnvManager",
|
|
130
|
+
# Config file loading
|
|
131
|
+
"load_env_from_file",
|
|
132
|
+
"discover_env_config",
|
|
133
|
+
"load_config",
|
|
134
|
+
"discover_config",
|
|
135
|
+
"CONFIG_FILE_NAMES",
|
|
136
|
+
# Detection
|
|
137
|
+
"detect_cuda_version",
|
|
138
|
+
"detect_gpu_info",
|
|
139
|
+
"get_gpu_summary",
|
|
140
|
+
# Security validation
|
|
141
|
+
"normalize_env_name",
|
|
142
|
+
"validate_dependency",
|
|
143
|
+
"validate_dependencies",
|
|
144
|
+
"validate_path_within_root",
|
|
145
|
+
"validate_wheel_url",
|
|
146
|
+
# Legacy IPC (subprocess-based)
|
|
147
|
+
"WorkerBridge",
|
|
148
|
+
"BaseWorker",
|
|
149
|
+
"register",
|
|
150
|
+
# Legacy Decorator API
|
|
151
|
+
"isolated",
|
|
152
|
+
"shutdown_all_processes",
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
# Add torch-based IPC if available
|
|
156
|
+
if _TORCH_AVAILABLE:
|
|
157
|
+
__all__ += ["TorchBridge", "TorchWorker"]
|
|
158
|
+
|
|
159
|
+
# Add PersistentVenvWorker if available
|
|
160
|
+
if _PERSISTENT_AVAILABLE:
|
|
161
|
+
__all__ += ["PersistentVenvWorker"]
|
comfy_env/cli.py
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI for comfy-env.
|
|
3
|
+
|
|
4
|
+
Provides the `comfy-env` command with subcommands:
|
|
5
|
+
- install: Install dependencies from config
|
|
6
|
+
- info: Show runtime environment information
|
|
7
|
+
- resolve: Show resolved wheel URLs
|
|
8
|
+
- doctor: Verify installation
|
|
9
|
+
- list-packages: Show all packages in the built-in registry
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
comfy-env install
|
|
13
|
+
comfy-env install --isolated
|
|
14
|
+
comfy-env install --dry-run
|
|
15
|
+
|
|
16
|
+
comfy-env info
|
|
17
|
+
|
|
18
|
+
comfy-env resolve nvdiffrast==0.4.0
|
|
19
|
+
comfy-env resolve --all
|
|
20
|
+
|
|
21
|
+
comfy-env doctor
|
|
22
|
+
|
|
23
|
+
comfy-env list-packages
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import argparse
|
|
27
|
+
import sys
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import List, Optional
|
|
30
|
+
|
|
31
|
+
from . import __version__
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main(args: Optional[List[str]] = None) -> int:
|
|
35
|
+
"""Main entry point for comfy-env CLI."""
|
|
36
|
+
parser = argparse.ArgumentParser(
|
|
37
|
+
prog="comfy-env",
|
|
38
|
+
description="Environment management for ComfyUI custom nodes",
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"--version", action="version", version=f"comfy-env {__version__}"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
45
|
+
|
|
46
|
+
# install command
|
|
47
|
+
install_parser = subparsers.add_parser(
|
|
48
|
+
"install",
|
|
49
|
+
help="Install dependencies from config",
|
|
50
|
+
description="Install CUDA wheels and dependencies from comfyui_env.toml",
|
|
51
|
+
)
|
|
52
|
+
install_parser.add_argument(
|
|
53
|
+
"--config", "-c",
|
|
54
|
+
type=str,
|
|
55
|
+
help="Path to config file (default: auto-discover)",
|
|
56
|
+
)
|
|
57
|
+
install_parser.add_argument(
|
|
58
|
+
"--isolated",
|
|
59
|
+
action="store_true",
|
|
60
|
+
help="Create isolated venv instead of installing in-place",
|
|
61
|
+
)
|
|
62
|
+
install_parser.add_argument(
|
|
63
|
+
"--dry-run",
|
|
64
|
+
action="store_true",
|
|
65
|
+
help="Show what would be installed without installing",
|
|
66
|
+
)
|
|
67
|
+
install_parser.add_argument(
|
|
68
|
+
"--verify",
|
|
69
|
+
action="store_true",
|
|
70
|
+
help="Verify wheel URLs exist before installing",
|
|
71
|
+
)
|
|
72
|
+
install_parser.add_argument(
|
|
73
|
+
"--dir", "-d",
|
|
74
|
+
type=str,
|
|
75
|
+
help="Directory containing the config (default: current directory)",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# info command
|
|
79
|
+
info_parser = subparsers.add_parser(
|
|
80
|
+
"info",
|
|
81
|
+
help="Show runtime environment information",
|
|
82
|
+
description="Display detected Python, CUDA, PyTorch, and GPU information",
|
|
83
|
+
)
|
|
84
|
+
info_parser.add_argument(
|
|
85
|
+
"--json",
|
|
86
|
+
action="store_true",
|
|
87
|
+
help="Output as JSON",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# resolve command
|
|
91
|
+
resolve_parser = subparsers.add_parser(
|
|
92
|
+
"resolve",
|
|
93
|
+
help="Resolve wheel URLs for packages",
|
|
94
|
+
description="Show resolved wheel URLs without installing",
|
|
95
|
+
)
|
|
96
|
+
resolve_parser.add_argument(
|
|
97
|
+
"packages",
|
|
98
|
+
nargs="*",
|
|
99
|
+
help="Package specs (e.g., nvdiffrast==0.4.0)",
|
|
100
|
+
)
|
|
101
|
+
resolve_parser.add_argument(
|
|
102
|
+
"--all", "-a",
|
|
103
|
+
action="store_true",
|
|
104
|
+
help="Resolve all packages from config",
|
|
105
|
+
)
|
|
106
|
+
resolve_parser.add_argument(
|
|
107
|
+
"--config", "-c",
|
|
108
|
+
type=str,
|
|
109
|
+
help="Path to config file",
|
|
110
|
+
)
|
|
111
|
+
resolve_parser.add_argument(
|
|
112
|
+
"--verify",
|
|
113
|
+
action="store_true",
|
|
114
|
+
help="Verify URLs exist (HTTP HEAD check)",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# doctor command
|
|
118
|
+
doctor_parser = subparsers.add_parser(
|
|
119
|
+
"doctor",
|
|
120
|
+
help="Verify installation and diagnose issues",
|
|
121
|
+
description="Check if packages are properly installed and importable",
|
|
122
|
+
)
|
|
123
|
+
doctor_parser.add_argument(
|
|
124
|
+
"--package", "-p",
|
|
125
|
+
type=str,
|
|
126
|
+
help="Check specific package",
|
|
127
|
+
)
|
|
128
|
+
doctor_parser.add_argument(
|
|
129
|
+
"--config", "-c",
|
|
130
|
+
type=str,
|
|
131
|
+
help="Path to config file",
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# list-packages command
|
|
135
|
+
list_parser = subparsers.add_parser(
|
|
136
|
+
"list-packages",
|
|
137
|
+
help="Show all packages in the built-in registry",
|
|
138
|
+
description="List CUDA packages that comfy-env knows how to install",
|
|
139
|
+
)
|
|
140
|
+
list_parser.add_argument(
|
|
141
|
+
"--json",
|
|
142
|
+
action="store_true",
|
|
143
|
+
help="Output as JSON",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
parsed = parser.parse_args(args)
|
|
147
|
+
|
|
148
|
+
if parsed.command is None:
|
|
149
|
+
parser.print_help()
|
|
150
|
+
return 0
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
if parsed.command == "install":
|
|
154
|
+
return cmd_install(parsed)
|
|
155
|
+
elif parsed.command == "info":
|
|
156
|
+
return cmd_info(parsed)
|
|
157
|
+
elif parsed.command == "resolve":
|
|
158
|
+
return cmd_resolve(parsed)
|
|
159
|
+
elif parsed.command == "doctor":
|
|
160
|
+
return cmd_doctor(parsed)
|
|
161
|
+
elif parsed.command == "list-packages":
|
|
162
|
+
return cmd_list_packages(parsed)
|
|
163
|
+
else:
|
|
164
|
+
parser.print_help()
|
|
165
|
+
return 1
|
|
166
|
+
except KeyboardInterrupt:
|
|
167
|
+
print("\nInterrupted")
|
|
168
|
+
return 130
|
|
169
|
+
except Exception as e:
|
|
170
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
171
|
+
return 1
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def cmd_install(args) -> int:
|
|
175
|
+
"""Handle install command."""
|
|
176
|
+
from .install import install
|
|
177
|
+
|
|
178
|
+
mode = "isolated" if args.isolated else "inplace"
|
|
179
|
+
node_dir = Path(args.dir) if args.dir else Path.cwd()
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
install(
|
|
183
|
+
config=args.config,
|
|
184
|
+
mode=mode,
|
|
185
|
+
node_dir=node_dir,
|
|
186
|
+
dry_run=args.dry_run,
|
|
187
|
+
verify_wheels=args.verify,
|
|
188
|
+
)
|
|
189
|
+
return 0
|
|
190
|
+
except FileNotFoundError as e:
|
|
191
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
192
|
+
return 1
|
|
193
|
+
except Exception as e:
|
|
194
|
+
print(f"Installation failed: {e}", file=sys.stderr)
|
|
195
|
+
return 1
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def cmd_info(args) -> int:
|
|
199
|
+
"""Handle info command."""
|
|
200
|
+
from .resolver import RuntimeEnv
|
|
201
|
+
|
|
202
|
+
env = RuntimeEnv.detect()
|
|
203
|
+
|
|
204
|
+
if args.json:
|
|
205
|
+
import json
|
|
206
|
+
print(json.dumps(env.as_dict(), indent=2))
|
|
207
|
+
return 0
|
|
208
|
+
|
|
209
|
+
print("Runtime Environment")
|
|
210
|
+
print("=" * 40)
|
|
211
|
+
print(f" OS: {env.os_name}")
|
|
212
|
+
print(f" Platform: {env.platform_tag}")
|
|
213
|
+
print(f" Python: {env.python_version}")
|
|
214
|
+
|
|
215
|
+
if env.cuda_version:
|
|
216
|
+
print(f" CUDA: {env.cuda_version}")
|
|
217
|
+
else:
|
|
218
|
+
print(" CUDA: Not detected")
|
|
219
|
+
|
|
220
|
+
if env.torch_version:
|
|
221
|
+
print(f" PyTorch: {env.torch_version}")
|
|
222
|
+
else:
|
|
223
|
+
print(" PyTorch: Not installed")
|
|
224
|
+
|
|
225
|
+
if env.gpu_name:
|
|
226
|
+
print(f" GPU: {env.gpu_name}")
|
|
227
|
+
if env.gpu_compute:
|
|
228
|
+
print(f" Compute: {env.gpu_compute}")
|
|
229
|
+
|
|
230
|
+
print()
|
|
231
|
+
return 0
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def cmd_resolve(args) -> int:
|
|
235
|
+
"""Handle resolve command."""
|
|
236
|
+
from .resolver import RuntimeEnv, WheelResolver, parse_wheel_requirement
|
|
237
|
+
from .env.config_file import discover_env_config, load_env_from_file
|
|
238
|
+
|
|
239
|
+
env = RuntimeEnv.detect()
|
|
240
|
+
resolver = WheelResolver()
|
|
241
|
+
|
|
242
|
+
packages = []
|
|
243
|
+
|
|
244
|
+
# Get packages from args or config
|
|
245
|
+
if args.all or (not args.packages and args.config):
|
|
246
|
+
# Load from config
|
|
247
|
+
if args.config:
|
|
248
|
+
config = load_env_from_file(Path(args.config))
|
|
249
|
+
else:
|
|
250
|
+
config = discover_env_config(Path.cwd())
|
|
251
|
+
|
|
252
|
+
if config and config.no_deps_requirements:
|
|
253
|
+
packages = config.no_deps_requirements
|
|
254
|
+
else:
|
|
255
|
+
print("No CUDA packages found in config", file=sys.stderr)
|
|
256
|
+
return 1
|
|
257
|
+
elif args.packages:
|
|
258
|
+
packages = args.packages
|
|
259
|
+
else:
|
|
260
|
+
print("Specify packages or use --all with a config file", file=sys.stderr)
|
|
261
|
+
return 1
|
|
262
|
+
|
|
263
|
+
print(f"Resolving wheels for: {env}")
|
|
264
|
+
print("=" * 60)
|
|
265
|
+
|
|
266
|
+
all_ok = True
|
|
267
|
+
for pkg_spec in packages:
|
|
268
|
+
package, version = parse_wheel_requirement(pkg_spec)
|
|
269
|
+
if version is None:
|
|
270
|
+
print(f" {package}: No version specified, skipping")
|
|
271
|
+
continue
|
|
272
|
+
|
|
273
|
+
try:
|
|
274
|
+
url = resolver.resolve(package, version, env, verify=args.verify)
|
|
275
|
+
status = "OK" if args.verify else "resolved"
|
|
276
|
+
print(f" {package}=={version}: {status}")
|
|
277
|
+
print(f" {url}")
|
|
278
|
+
except Exception as e:
|
|
279
|
+
print(f" {package}=={version}: FAILED")
|
|
280
|
+
print(f" {e}")
|
|
281
|
+
all_ok = False
|
|
282
|
+
|
|
283
|
+
return 0 if all_ok else 1
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def cmd_doctor(args) -> int:
|
|
287
|
+
"""Handle doctor command."""
|
|
288
|
+
from .install import verify_installation
|
|
289
|
+
from .env.config_file import discover_env_config, load_env_from_file
|
|
290
|
+
|
|
291
|
+
print("Running diagnostics...")
|
|
292
|
+
print("=" * 40)
|
|
293
|
+
|
|
294
|
+
# Check environment
|
|
295
|
+
print("\n1. Environment")
|
|
296
|
+
cmd_info(argparse.Namespace(json=False))
|
|
297
|
+
|
|
298
|
+
# Check packages
|
|
299
|
+
print("2. Package Verification")
|
|
300
|
+
|
|
301
|
+
packages = []
|
|
302
|
+
if args.package:
|
|
303
|
+
packages = [args.package]
|
|
304
|
+
elif args.config:
|
|
305
|
+
config = load_env_from_file(Path(args.config))
|
|
306
|
+
if config:
|
|
307
|
+
packages = (config.requirements or []) + (config.no_deps_requirements or [])
|
|
308
|
+
else:
|
|
309
|
+
config = discover_env_config(Path.cwd())
|
|
310
|
+
if config:
|
|
311
|
+
packages = (config.requirements or []) + (config.no_deps_requirements or [])
|
|
312
|
+
|
|
313
|
+
if packages:
|
|
314
|
+
# Extract package names from specs
|
|
315
|
+
pkg_names = []
|
|
316
|
+
for pkg in packages:
|
|
317
|
+
name = pkg.split("==")[0].split(">=")[0].split("[")[0]
|
|
318
|
+
pkg_names.append(name)
|
|
319
|
+
|
|
320
|
+
all_ok = verify_installation(pkg_names)
|
|
321
|
+
if all_ok:
|
|
322
|
+
print("\nAll packages verified!")
|
|
323
|
+
return 0
|
|
324
|
+
else:
|
|
325
|
+
print("\nSome packages failed verification.")
|
|
326
|
+
return 1
|
|
327
|
+
else:
|
|
328
|
+
print(" No packages to verify (no config found)")
|
|
329
|
+
return 0
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def cmd_list_packages(args) -> int:
|
|
333
|
+
"""Handle list-packages command."""
|
|
334
|
+
from .registry import PACKAGE_REGISTRY, list_packages
|
|
335
|
+
|
|
336
|
+
if args.json:
|
|
337
|
+
import json
|
|
338
|
+
result = {}
|
|
339
|
+
for name, config in PACKAGE_REGISTRY.items():
|
|
340
|
+
result[name] = {
|
|
341
|
+
"method": config["method"],
|
|
342
|
+
"description": config.get("description", ""),
|
|
343
|
+
}
|
|
344
|
+
if "index_url" in config:
|
|
345
|
+
result[name]["index_url"] = config["index_url"]
|
|
346
|
+
if "package_template" in config:
|
|
347
|
+
result[name]["package_template"] = config["package_template"]
|
|
348
|
+
print(json.dumps(result, indent=2))
|
|
349
|
+
return 0
|
|
350
|
+
|
|
351
|
+
print("Built-in CUDA Package Registry")
|
|
352
|
+
print("=" * 60)
|
|
353
|
+
print()
|
|
354
|
+
print("These packages can be installed without specifying wheel_sources.")
|
|
355
|
+
print("Just add them to your comfyui_env.toml:")
|
|
356
|
+
print()
|
|
357
|
+
print(" [cuda]")
|
|
358
|
+
print(" torch-scatter = \"2.1.2\"")
|
|
359
|
+
print(" torch-cluster = \"1.6.3\"")
|
|
360
|
+
print()
|
|
361
|
+
print("-" * 60)
|
|
362
|
+
|
|
363
|
+
# Group packages by method
|
|
364
|
+
by_method = {}
|
|
365
|
+
for name, config in PACKAGE_REGISTRY.items():
|
|
366
|
+
method = config["method"]
|
|
367
|
+
if method not in by_method:
|
|
368
|
+
by_method[method] = []
|
|
369
|
+
by_method[method].append((name, config))
|
|
370
|
+
|
|
371
|
+
method_labels = {
|
|
372
|
+
"index": "PEP 503 Index (pip --extra-index-url)",
|
|
373
|
+
"github_index": "GitHub Pages (pip --find-links)",
|
|
374
|
+
"pypi_variant": "PyPI with CUDA variant names",
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
for method, packages in by_method.items():
|
|
378
|
+
print(f"\n{method_labels.get(method, method)}:")
|
|
379
|
+
for name, config in sorted(packages):
|
|
380
|
+
desc = config.get("description", "")
|
|
381
|
+
print(f" {name:20} - {desc}")
|
|
382
|
+
|
|
383
|
+
print()
|
|
384
|
+
return 0
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
if __name__ == "__main__":
|
|
388
|
+
sys.exit(main())
|