comfy-env 0.0.64__py3-none-any.whl → 0.0.66__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 +70 -122
- comfy_env/cli.py +78 -7
- comfy_env/config/__init__.py +19 -0
- comfy_env/config/parser.py +151 -0
- comfy_env/config/types.py +64 -0
- comfy_env/install.py +83 -361
- comfy_env/isolation/__init__.py +9 -0
- comfy_env/isolation/wrap.py +351 -0
- comfy_env/nodes.py +2 -2
- comfy_env/pixi/__init__.py +48 -0
- comfy_env/pixi/core.py +356 -0
- comfy_env/{resolver.py → pixi/resolver.py} +1 -14
- comfy_env/prestartup.py +60 -0
- comfy_env/templates/comfy-env-instructions.txt +30 -87
- comfy_env/templates/comfy-env.toml +68 -136
- comfy_env/workers/__init__.py +21 -32
- comfy_env/workers/base.py +1 -1
- comfy_env/workers/{torch_mp.py → mp.py} +47 -14
- comfy_env/workers/{venv.py → subprocess.py} +405 -441
- {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/METADATA +2 -1
- comfy_env-0.0.66.dist-info/RECORD +34 -0
- comfy_env/decorator.py +0 -700
- comfy_env/env/__init__.py +0 -47
- comfy_env/env/config.py +0 -201
- comfy_env/env/config_file.py +0 -740
- comfy_env/env/manager.py +0 -636
- comfy_env/env/security.py +0 -267
- comfy_env/ipc/__init__.py +0 -55
- comfy_env/ipc/bridge.py +0 -476
- comfy_env/ipc/protocol.py +0 -265
- comfy_env/ipc/tensor.py +0 -371
- comfy_env/ipc/torch_bridge.py +0 -401
- comfy_env/ipc/transport.py +0 -318
- comfy_env/ipc/worker.py +0 -221
- comfy_env/isolation.py +0 -310
- comfy_env/pixi.py +0 -760
- comfy_env/stub_imports.py +0 -270
- comfy_env/stubs/__init__.py +0 -1
- comfy_env/stubs/comfy/__init__.py +0 -6
- comfy_env/stubs/comfy/model_management.py +0 -58
- comfy_env/stubs/comfy/utils.py +0 -29
- comfy_env/stubs/folder_paths.py +0 -71
- comfy_env/workers/pool.py +0 -241
- comfy_env-0.0.64.dist-info/RECORD +0 -48
- /comfy_env/{env/cuda_gpu_detection.py → pixi/cuda_detection.py} +0 -0
- /comfy_env/{env → pixi}/platform/__init__.py +0 -0
- /comfy_env/{env → pixi}/platform/base.py +0 -0
- /comfy_env/{env → pixi}/platform/darwin.py +0 -0
- /comfy_env/{env → pixi}/platform/linux.py +0 -0
- /comfy_env/{env → pixi}/platform/windows.py +0 -0
- /comfy_env/{registry.py → pixi/registry.py} +0 -0
- /comfy_env/{wheel_sources.yml → pixi/wheel_sources.yml} +0 -0
- {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/WHEEL +0 -0
- {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/entry_points.txt +0 -0
- {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/licenses/LICENSE +0 -0
comfy_env/__init__.py
CHANGED
|
@@ -1,55 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
comfy-env: Environment management for ComfyUI custom nodes.
|
|
3
|
+
|
|
4
|
+
All dependencies go through pixi for unified management.
|
|
5
|
+
|
|
6
|
+
Main APIs:
|
|
7
|
+
- install(): Install dependencies from comfy-env.toml
|
|
8
|
+
- wrap_isolated_nodes(): Wrap nodes for subprocess isolation
|
|
9
|
+
"""
|
|
10
|
+
|
|
1
11
|
from importlib.metadata import version, PackageNotFoundError
|
|
2
12
|
|
|
3
13
|
try:
|
|
4
14
|
__version__ = version("comfy-env")
|
|
5
15
|
except PackageNotFoundError:
|
|
6
|
-
__version__ = "0.0.0-dev"
|
|
16
|
+
__version__ = "0.0.0-dev"
|
|
7
17
|
|
|
8
|
-
|
|
9
|
-
from .
|
|
10
|
-
|
|
11
|
-
|
|
18
|
+
# Config types and parsing
|
|
19
|
+
from .config import (
|
|
20
|
+
ComfyEnvConfig,
|
|
21
|
+
NodeReq,
|
|
12
22
|
load_config,
|
|
13
23
|
discover_config,
|
|
14
|
-
|
|
24
|
+
CONFIG_FILE_NAME,
|
|
15
25
|
)
|
|
16
|
-
from .env.manager import IsolatedEnvManager
|
|
17
|
-
from .env.cuda_gpu_detection import (
|
|
18
|
-
GPUInfo,
|
|
19
|
-
CUDAEnvironment,
|
|
20
|
-
detect_cuda_environment,
|
|
21
|
-
detect_cuda_version,
|
|
22
|
-
detect_gpu_info,
|
|
23
|
-
detect_gpus,
|
|
24
|
-
get_gpu_summary,
|
|
25
|
-
get_recommended_cuda_version,
|
|
26
|
-
)
|
|
27
|
-
from .env.security import (
|
|
28
|
-
normalize_env_name,
|
|
29
|
-
validate_dependency,
|
|
30
|
-
validate_dependencies,
|
|
31
|
-
validate_path_within_root,
|
|
32
|
-
validate_wheel_url,
|
|
33
|
-
)
|
|
34
|
-
from .ipc.bridge import WorkerBridge
|
|
35
|
-
from .ipc.worker import BaseWorker, register
|
|
36
|
-
from .decorator import isolated, shutdown_all_processes
|
|
37
|
-
from .isolation import enable_isolation
|
|
38
|
-
from .stub_imports import setup_isolated_imports, cleanup_stubs
|
|
39
|
-
|
|
40
|
-
# New in-place installation API
|
|
41
|
-
from .install import install, verify_installation
|
|
42
|
-
from .resolver import RuntimeEnv
|
|
43
26
|
|
|
44
|
-
# Pixi integration
|
|
27
|
+
# Pixi integration
|
|
45
28
|
from .pixi import (
|
|
46
29
|
ensure_pixi,
|
|
47
30
|
get_pixi_path,
|
|
48
|
-
pixi_install,
|
|
49
|
-
create_pixi_toml,
|
|
50
31
|
get_pixi_python,
|
|
51
32
|
pixi_run,
|
|
33
|
+
pixi_install,
|
|
34
|
+
CUDA_WHEELS_INDEX,
|
|
35
|
+
PACKAGE_REGISTRY,
|
|
36
|
+
detect_cuda_version,
|
|
37
|
+
detect_cuda_environment,
|
|
38
|
+
get_recommended_cuda_version,
|
|
39
|
+
GPUInfo,
|
|
40
|
+
CUDAEnvironment,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Workers
|
|
44
|
+
from .workers import (
|
|
45
|
+
Worker,
|
|
46
|
+
WorkerError,
|
|
47
|
+
MPWorker,
|
|
48
|
+
SubprocessWorker,
|
|
52
49
|
)
|
|
50
|
+
|
|
51
|
+
# Isolation
|
|
52
|
+
from .isolation import wrap_isolated_nodes
|
|
53
|
+
|
|
54
|
+
# Install API
|
|
55
|
+
from .install import install, verify_installation
|
|
56
|
+
|
|
57
|
+
# Prestartup helpers
|
|
58
|
+
from .prestartup import setup_env
|
|
59
|
+
|
|
60
|
+
# Errors
|
|
53
61
|
from .errors import (
|
|
54
62
|
EnvManagerError,
|
|
55
63
|
ConfigError,
|
|
@@ -59,43 +67,39 @@ from .errors import (
|
|
|
59
67
|
InstallError,
|
|
60
68
|
)
|
|
61
69
|
|
|
62
|
-
# New workers module (recommended API)
|
|
63
|
-
from .workers import (
|
|
64
|
-
Worker,
|
|
65
|
-
TorchMPWorker,
|
|
66
|
-
VenvWorker,
|
|
67
|
-
WorkerPool,
|
|
68
|
-
get_worker,
|
|
69
|
-
register_worker,
|
|
70
|
-
shutdown_workers,
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
# TorchBridge is optional (requires PyTorch)
|
|
74
|
-
try:
|
|
75
|
-
from .ipc.torch_bridge import TorchBridge, TorchWorker
|
|
76
|
-
_TORCH_AVAILABLE = True
|
|
77
|
-
except ImportError:
|
|
78
|
-
_TORCH_AVAILABLE = False
|
|
79
|
-
|
|
80
|
-
# PersistentVenvWorker requires the ipc.transport module
|
|
81
|
-
try:
|
|
82
|
-
from .workers.venv import PersistentVenvWorker
|
|
83
|
-
_PERSISTENT_AVAILABLE = True
|
|
84
|
-
except ImportError:
|
|
85
|
-
_PERSISTENT_AVAILABLE = False
|
|
86
|
-
|
|
87
70
|
__all__ = [
|
|
88
|
-
#
|
|
71
|
+
# Install API
|
|
89
72
|
"install",
|
|
90
73
|
"verify_installation",
|
|
91
|
-
|
|
92
|
-
|
|
74
|
+
# Prestartup
|
|
75
|
+
"setup_env",
|
|
76
|
+
# Isolation
|
|
77
|
+
"wrap_isolated_nodes",
|
|
78
|
+
# Config
|
|
79
|
+
"ComfyEnvConfig",
|
|
80
|
+
"NodeReq",
|
|
81
|
+
"load_config",
|
|
82
|
+
"discover_config",
|
|
83
|
+
"CONFIG_FILE_NAME",
|
|
84
|
+
# Pixi
|
|
93
85
|
"ensure_pixi",
|
|
94
86
|
"get_pixi_path",
|
|
95
|
-
"pixi_install",
|
|
96
|
-
"create_pixi_toml",
|
|
97
87
|
"get_pixi_python",
|
|
98
88
|
"pixi_run",
|
|
89
|
+
"pixi_install",
|
|
90
|
+
"CUDA_WHEELS_INDEX",
|
|
91
|
+
"PACKAGE_REGISTRY",
|
|
92
|
+
# CUDA detection
|
|
93
|
+
"detect_cuda_version",
|
|
94
|
+
"detect_cuda_environment",
|
|
95
|
+
"get_recommended_cuda_version",
|
|
96
|
+
"GPUInfo",
|
|
97
|
+
"CUDAEnvironment",
|
|
98
|
+
# Workers
|
|
99
|
+
"Worker",
|
|
100
|
+
"WorkerError",
|
|
101
|
+
"MPWorker",
|
|
102
|
+
"SubprocessWorker",
|
|
99
103
|
# Errors
|
|
100
104
|
"EnvManagerError",
|
|
101
105
|
"ConfigError",
|
|
@@ -103,60 +107,4 @@ __all__ = [
|
|
|
103
107
|
"DependencyError",
|
|
104
108
|
"CUDANotFoundError",
|
|
105
109
|
"InstallError",
|
|
106
|
-
# Workers API (recommended for isolation)
|
|
107
|
-
"Worker",
|
|
108
|
-
"TorchMPWorker",
|
|
109
|
-
"VenvWorker",
|
|
110
|
-
"WorkerPool",
|
|
111
|
-
"get_worker",
|
|
112
|
-
"register_worker",
|
|
113
|
-
"shutdown_workers",
|
|
114
|
-
# Environment & Config
|
|
115
|
-
"IsolatedEnv",
|
|
116
|
-
"EnvManagerConfig",
|
|
117
|
-
"LocalConfig",
|
|
118
|
-
"NodeReq",
|
|
119
|
-
"CondaConfig",
|
|
120
|
-
"IsolatedEnvManager",
|
|
121
|
-
# Config file loading
|
|
122
|
-
"load_env_from_file",
|
|
123
|
-
"discover_env_config",
|
|
124
|
-
"load_config",
|
|
125
|
-
"discover_config",
|
|
126
|
-
"CONFIG_FILE_NAMES",
|
|
127
|
-
# Detection
|
|
128
|
-
"GPUInfo",
|
|
129
|
-
"CUDAEnvironment",
|
|
130
|
-
"detect_cuda_environment",
|
|
131
|
-
"detect_cuda_version",
|
|
132
|
-
"detect_gpu_info",
|
|
133
|
-
"detect_gpus",
|
|
134
|
-
"get_gpu_summary",
|
|
135
|
-
"get_recommended_cuda_version",
|
|
136
|
-
# Security validation
|
|
137
|
-
"normalize_env_name",
|
|
138
|
-
"validate_dependency",
|
|
139
|
-
"validate_dependencies",
|
|
140
|
-
"validate_path_within_root",
|
|
141
|
-
"validate_wheel_url",
|
|
142
|
-
# Legacy IPC (subprocess-based)
|
|
143
|
-
"WorkerBridge",
|
|
144
|
-
"BaseWorker",
|
|
145
|
-
"register",
|
|
146
|
-
# Legacy Decorator API
|
|
147
|
-
"isolated",
|
|
148
|
-
"shutdown_all_processes",
|
|
149
|
-
# New: Enable isolation for entire node pack
|
|
150
|
-
"enable_isolation",
|
|
151
|
-
# Import stubbing for isolated packages
|
|
152
|
-
"setup_isolated_imports",
|
|
153
|
-
"cleanup_stubs",
|
|
154
110
|
]
|
|
155
|
-
|
|
156
|
-
# Add torch-based IPC if available
|
|
157
|
-
if _TORCH_AVAILABLE:
|
|
158
|
-
__all__ += ["TorchBridge", "TorchWorker"]
|
|
159
|
-
|
|
160
|
-
# Add PersistentVenvWorker if available
|
|
161
|
-
if _PERSISTENT_AVAILABLE:
|
|
162
|
-
__all__ += ["PersistentVenvWorker"]
|
comfy_env/cli.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
CLI for comfy-env.
|
|
3
3
|
|
|
4
4
|
Provides the `comfy-env` command with subcommands:
|
|
5
|
+
- init: Create a default comfy-env.toml
|
|
6
|
+
- generate: Generate pixi.toml from comfy-env.toml
|
|
5
7
|
- install: Install dependencies from config
|
|
6
8
|
- info: Show runtime environment information
|
|
7
9
|
- resolve: Show resolved wheel URLs
|
|
@@ -9,7 +11,9 @@ Provides the `comfy-env` command with subcommands:
|
|
|
9
11
|
- list-packages: Show all packages in the built-in registry
|
|
10
12
|
|
|
11
13
|
Usage:
|
|
12
|
-
comfy-env
|
|
14
|
+
comfy-env init ---> creates template comfy-env.toml
|
|
15
|
+
comfy-env generate nodes/cgal/comfy-env.toml ---> nodes/cgal/pixi.toml
|
|
16
|
+
comfy-env install ---> installs from comfy
|
|
13
17
|
comfy-env install --dry-run
|
|
14
18
|
|
|
15
19
|
comfy-env info
|
|
@@ -54,6 +58,23 @@ def main(args: Optional[List[str]] = None) -> int:
|
|
|
54
58
|
help="Overwrite existing config file",
|
|
55
59
|
)
|
|
56
60
|
|
|
61
|
+
# generate command
|
|
62
|
+
generate_parser = subparsers.add_parser(
|
|
63
|
+
"generate",
|
|
64
|
+
help="Generate pixi.toml from comfy-env.toml",
|
|
65
|
+
description="Parse comfy-env.toml and generate a pixi.toml in the same directory",
|
|
66
|
+
)
|
|
67
|
+
generate_parser.add_argument(
|
|
68
|
+
"config",
|
|
69
|
+
type=str,
|
|
70
|
+
help="Path to comfy-env.toml",
|
|
71
|
+
)
|
|
72
|
+
generate_parser.add_argument(
|
|
73
|
+
"--force", "-f",
|
|
74
|
+
action="store_true",
|
|
75
|
+
help="Overwrite existing pixi.toml",
|
|
76
|
+
)
|
|
77
|
+
|
|
57
78
|
# install command
|
|
58
79
|
install_parser = subparsers.add_parser(
|
|
59
80
|
"install",
|
|
@@ -148,6 +169,8 @@ def main(args: Optional[List[str]] = None) -> int:
|
|
|
148
169
|
try:
|
|
149
170
|
if parsed.command == "init":
|
|
150
171
|
return cmd_init(parsed)
|
|
172
|
+
elif parsed.command == "generate":
|
|
173
|
+
return cmd_generate(parsed)
|
|
151
174
|
elif parsed.command == "install":
|
|
152
175
|
return cmd_install(parsed)
|
|
153
176
|
elif parsed.command == "info":
|
|
@@ -205,6 +228,53 @@ def cmd_init(args) -> int:
|
|
|
205
228
|
return 0
|
|
206
229
|
|
|
207
230
|
|
|
231
|
+
def cmd_generate(args) -> int:
|
|
232
|
+
"""Handle generate command - create pixi.toml from comfy-env.toml."""
|
|
233
|
+
from .config.parser import load_config
|
|
234
|
+
from .pixi import create_pixi_toml
|
|
235
|
+
|
|
236
|
+
config_path = Path(args.config).resolve()
|
|
237
|
+
|
|
238
|
+
if not config_path.exists():
|
|
239
|
+
print(f"Config file not found: {config_path}", file=sys.stderr)
|
|
240
|
+
return 1
|
|
241
|
+
|
|
242
|
+
if config_path.name != "comfy-env.toml":
|
|
243
|
+
print(f"Warning: Expected comfy-env.toml, got {config_path.name}", file=sys.stderr)
|
|
244
|
+
|
|
245
|
+
node_dir = config_path.parent
|
|
246
|
+
pixi_path = node_dir / "pixi.toml"
|
|
247
|
+
|
|
248
|
+
if pixi_path.exists() and not args.force:
|
|
249
|
+
print(f"pixi.toml already exists: {pixi_path}", file=sys.stderr)
|
|
250
|
+
print("Use --force to overwrite", file=sys.stderr)
|
|
251
|
+
return 1
|
|
252
|
+
|
|
253
|
+
# Load the config
|
|
254
|
+
config = load_config(config_path)
|
|
255
|
+
if not config or not config.envs:
|
|
256
|
+
print(f"No environments found in {config_path}", file=sys.stderr)
|
|
257
|
+
return 1
|
|
258
|
+
|
|
259
|
+
# Use the first environment
|
|
260
|
+
env_name = next(iter(config.envs.keys()))
|
|
261
|
+
env_config = config.envs[env_name]
|
|
262
|
+
|
|
263
|
+
print(f"Generating pixi.toml from {config_path}")
|
|
264
|
+
print(f" Environment: {env_name}")
|
|
265
|
+
print(f" Python: {env_config.python}")
|
|
266
|
+
|
|
267
|
+
# Generate pixi.toml
|
|
268
|
+
result_path = create_pixi_toml(env_config, node_dir)
|
|
269
|
+
|
|
270
|
+
print(f"Created {result_path}")
|
|
271
|
+
print()
|
|
272
|
+
print("Next steps:")
|
|
273
|
+
print(f" cd {node_dir}")
|
|
274
|
+
print(" pixi install")
|
|
275
|
+
return 0
|
|
276
|
+
|
|
277
|
+
|
|
208
278
|
def cmd_install(args) -> int:
|
|
209
279
|
"""Handle install command."""
|
|
210
280
|
from .install import install
|
|
@@ -228,7 +298,7 @@ def cmd_install(args) -> int:
|
|
|
228
298
|
|
|
229
299
|
def cmd_info(args) -> int:
|
|
230
300
|
"""Handle info command."""
|
|
231
|
-
from .
|
|
301
|
+
from .pixi import RuntimeEnv
|
|
232
302
|
|
|
233
303
|
env = RuntimeEnv.detect()
|
|
234
304
|
|
|
@@ -264,9 +334,10 @@ def cmd_info(args) -> int:
|
|
|
264
334
|
|
|
265
335
|
def cmd_resolve(args) -> int:
|
|
266
336
|
"""Handle resolve command."""
|
|
267
|
-
from .
|
|
268
|
-
from .
|
|
269
|
-
from .
|
|
337
|
+
from .pixi import RuntimeEnv, parse_wheel_requirement
|
|
338
|
+
from .pixi import PACKAGE_REGISTRY
|
|
339
|
+
from .pixi.registry import get_cuda_short2
|
|
340
|
+
from .config.parser import discover_env_config, load_env_from_file
|
|
270
341
|
|
|
271
342
|
env = RuntimeEnv.detect()
|
|
272
343
|
packages = []
|
|
@@ -353,7 +424,7 @@ def _substitute_template(template: str, vars_dict: dict) -> str:
|
|
|
353
424
|
def cmd_doctor(args) -> int:
|
|
354
425
|
"""Handle doctor command."""
|
|
355
426
|
from .install import verify_installation
|
|
356
|
-
from .
|
|
427
|
+
from .config.parser import discover_env_config, load_env_from_file
|
|
357
428
|
|
|
358
429
|
print("Running diagnostics...")
|
|
359
430
|
print("=" * 40)
|
|
@@ -397,7 +468,7 @@ def cmd_doctor(args) -> int:
|
|
|
397
468
|
|
|
398
469
|
def cmd_list_packages(args) -> int:
|
|
399
470
|
"""Handle list-packages command."""
|
|
400
|
-
from .
|
|
471
|
+
from .pixi import PACKAGE_REGISTRY
|
|
401
472
|
|
|
402
473
|
if args.json:
|
|
403
474
|
import json
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Config parsing for comfy-env.
|
|
3
|
+
|
|
4
|
+
This module handles parsing comfy-env.toml files and provides
|
|
5
|
+
typed configuration objects.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .types import ComfyEnvConfig, NodeReq
|
|
9
|
+
from .parser import load_config, discover_config, CONFIG_FILE_NAME
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
# Types
|
|
13
|
+
"ComfyEnvConfig",
|
|
14
|
+
"NodeReq",
|
|
15
|
+
# Parser
|
|
16
|
+
"load_config",
|
|
17
|
+
"discover_config",
|
|
18
|
+
"CONFIG_FILE_NAME",
|
|
19
|
+
]
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Load configuration from comfy-env.toml.
|
|
2
|
+
|
|
3
|
+
comfy-env.toml is a superset of pixi.toml. Custom sections we handle:
|
|
4
|
+
- python = "3.11" - Python version for isolated envs
|
|
5
|
+
- [cuda] packages = [...] - CUDA packages (triggers find-links + PyTorch detection)
|
|
6
|
+
- [node_reqs] - Other ComfyUI nodes to clone
|
|
7
|
+
|
|
8
|
+
Everything else passes through to pixi.toml directly.
|
|
9
|
+
|
|
10
|
+
Example config:
|
|
11
|
+
|
|
12
|
+
python = "3.11"
|
|
13
|
+
|
|
14
|
+
[cuda]
|
|
15
|
+
packages = ["cumesh"]
|
|
16
|
+
|
|
17
|
+
[dependencies]
|
|
18
|
+
mesalib = "*"
|
|
19
|
+
cgal = "*"
|
|
20
|
+
|
|
21
|
+
[pypi-dependencies]
|
|
22
|
+
numpy = ">=1.21.0,<2"
|
|
23
|
+
trimesh = { version = ">=4.0.0", extras = ["easy"] }
|
|
24
|
+
|
|
25
|
+
[target.linux-64.pypi-dependencies]
|
|
26
|
+
embreex = "*"
|
|
27
|
+
|
|
28
|
+
[node_reqs]
|
|
29
|
+
SomeNode = "owner/repo"
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
import copy
|
|
33
|
+
import sys
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
from typing import Optional, Dict, Any, List
|
|
36
|
+
|
|
37
|
+
# Use built-in tomllib (Python 3.11+) or tomli fallback
|
|
38
|
+
if sys.version_info >= (3, 11):
|
|
39
|
+
import tomllib
|
|
40
|
+
else:
|
|
41
|
+
try:
|
|
42
|
+
import tomli as tomllib
|
|
43
|
+
except ImportError:
|
|
44
|
+
tomllib = None # type: ignore
|
|
45
|
+
|
|
46
|
+
from .types import ComfyEnvConfig, NodeReq
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
CONFIG_FILE_NAME = "comfy-env.toml"
|
|
50
|
+
|
|
51
|
+
# Sections we handle specially (not passed through to pixi.toml)
|
|
52
|
+
CUSTOM_SECTIONS = {"python", "cuda", "node_reqs"}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def load_config(path: Path) -> ComfyEnvConfig:
|
|
56
|
+
"""
|
|
57
|
+
Load configuration from a TOML file.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
path: Path to comfy-env.toml
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
ComfyEnvConfig instance
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
FileNotFoundError: If config file doesn't exist
|
|
67
|
+
ImportError: If tomli not installed (Python < 3.11)
|
|
68
|
+
"""
|
|
69
|
+
if tomllib is None:
|
|
70
|
+
raise ImportError(
|
|
71
|
+
"TOML parsing requires tomli for Python < 3.11. "
|
|
72
|
+
"Install with: pip install tomli"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
path = Path(path)
|
|
76
|
+
if not path.exists():
|
|
77
|
+
raise FileNotFoundError(f"Config file not found: {path}")
|
|
78
|
+
|
|
79
|
+
with open(path, "rb") as f:
|
|
80
|
+
data = tomllib.load(f)
|
|
81
|
+
|
|
82
|
+
return _parse_config(data)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def discover_config(node_dir: Path) -> Optional[ComfyEnvConfig]:
|
|
86
|
+
"""
|
|
87
|
+
Find and load comfy-env.toml from a directory.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
node_dir: Directory to search
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
ComfyEnvConfig if found, None otherwise
|
|
94
|
+
"""
|
|
95
|
+
if tomllib is None:
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
config_path = Path(node_dir) / CONFIG_FILE_NAME
|
|
99
|
+
if config_path.exists():
|
|
100
|
+
return load_config(config_path)
|
|
101
|
+
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _parse_config(data: Dict[str, Any]) -> ComfyEnvConfig:
|
|
106
|
+
"""Parse TOML data into ComfyEnvConfig."""
|
|
107
|
+
# Make a copy so we can pop our custom sections
|
|
108
|
+
data = copy.deepcopy(data)
|
|
109
|
+
|
|
110
|
+
# Extract python version (top-level key)
|
|
111
|
+
python_version = data.pop("python", None)
|
|
112
|
+
if python_version is not None:
|
|
113
|
+
python_version = str(python_version)
|
|
114
|
+
|
|
115
|
+
# Extract [cuda] section
|
|
116
|
+
cuda_data = data.pop("cuda", {})
|
|
117
|
+
cuda_packages = _ensure_list(cuda_data.get("packages", []))
|
|
118
|
+
|
|
119
|
+
# Extract [node_reqs] section
|
|
120
|
+
node_reqs_data = data.pop("node_reqs", {})
|
|
121
|
+
node_reqs = _parse_node_reqs(node_reqs_data)
|
|
122
|
+
|
|
123
|
+
# Everything else passes through to pixi.toml
|
|
124
|
+
pixi_passthrough = data
|
|
125
|
+
|
|
126
|
+
return ComfyEnvConfig(
|
|
127
|
+
python=python_version,
|
|
128
|
+
cuda_packages=cuda_packages,
|
|
129
|
+
node_reqs=node_reqs,
|
|
130
|
+
pixi_passthrough=pixi_passthrough,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _parse_node_reqs(data: Dict[str, Any]) -> List[NodeReq]:
|
|
135
|
+
"""Parse [node_reqs] section."""
|
|
136
|
+
node_reqs = []
|
|
137
|
+
for name, value in data.items():
|
|
138
|
+
if isinstance(value, str):
|
|
139
|
+
node_reqs.append(NodeReq(name=name, repo=value))
|
|
140
|
+
elif isinstance(value, dict):
|
|
141
|
+
node_reqs.append(NodeReq(name=name, repo=value.get("repo", "")))
|
|
142
|
+
return node_reqs
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _ensure_list(value) -> List:
|
|
146
|
+
"""Ensure value is a list."""
|
|
147
|
+
if isinstance(value, list):
|
|
148
|
+
return value
|
|
149
|
+
if value:
|
|
150
|
+
return [value]
|
|
151
|
+
return []
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Configuration types for comfy-env."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class NodeReq:
|
|
9
|
+
"""A node dependency (another ComfyUI custom node)."""
|
|
10
|
+
name: str
|
|
11
|
+
repo: str # GitHub repo, e.g., "owner/repo"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class ComfyEnvConfig:
|
|
16
|
+
"""
|
|
17
|
+
Configuration from comfy-env.toml.
|
|
18
|
+
|
|
19
|
+
comfy-env.toml is a superset of pixi.toml. Custom sections we handle:
|
|
20
|
+
- python = "3.11" - Python version for isolated envs
|
|
21
|
+
- [cuda] packages = [...] - CUDA packages (triggers find-links + PyTorch detection)
|
|
22
|
+
- [node_reqs] - Other ComfyUI nodes to clone
|
|
23
|
+
|
|
24
|
+
Everything else passes through to pixi.toml directly:
|
|
25
|
+
- [dependencies] - conda packages
|
|
26
|
+
- [pypi-dependencies] - pip packages
|
|
27
|
+
- [target.linux-64.pypi-dependencies] - platform-specific deps
|
|
28
|
+
- Any other pixi.toml syntax
|
|
29
|
+
|
|
30
|
+
Example config:
|
|
31
|
+
python = "3.11"
|
|
32
|
+
|
|
33
|
+
[cuda]
|
|
34
|
+
packages = ["cumesh"]
|
|
35
|
+
|
|
36
|
+
[dependencies]
|
|
37
|
+
mesalib = "*"
|
|
38
|
+
cgal = "*"
|
|
39
|
+
|
|
40
|
+
[pypi-dependencies]
|
|
41
|
+
numpy = ">=1.21.0,<2"
|
|
42
|
+
trimesh = { version = ">=4.0.0", extras = ["easy"] }
|
|
43
|
+
|
|
44
|
+
[target.linux-64.pypi-dependencies]
|
|
45
|
+
embreex = "*"
|
|
46
|
+
|
|
47
|
+
[node_reqs]
|
|
48
|
+
SomeNode = "owner/repo"
|
|
49
|
+
"""
|
|
50
|
+
# python = "3.11" - Python version (for isolated envs)
|
|
51
|
+
python: Optional[str] = None
|
|
52
|
+
|
|
53
|
+
# [cuda] - CUDA packages (installed via find-links index)
|
|
54
|
+
cuda_packages: List[str] = field(default_factory=list)
|
|
55
|
+
|
|
56
|
+
# [node_reqs] - other ComfyUI nodes to clone
|
|
57
|
+
node_reqs: List[NodeReq] = field(default_factory=list)
|
|
58
|
+
|
|
59
|
+
# Everything else from comfy-env.toml passes through to pixi.toml
|
|
60
|
+
pixi_passthrough: Dict[str, Any] = field(default_factory=dict)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def has_cuda(self) -> bool:
|
|
64
|
+
return bool(self.cuda_packages)
|