comfy-env 0.1.13__py3-none-any.whl → 0.1.15__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 +30 -27
- comfy_env/cache.py +203 -0
- comfy_env/cli.py +9 -11
- comfy_env/config/__init__.py +8 -10
- comfy_env/config/parser.py +28 -75
- comfy_env/install.py +141 -25
- comfy_env/isolation/__init__.py +2 -1
- comfy_env/isolation/wrap.py +202 -25
- comfy_env/nodes.py +1 -1
- comfy_env/pixi/core.py +44 -10
- comfy_env/prestartup.py +63 -19
- comfy_env/workers/subprocess.py +1 -1
- {comfy_env-0.1.13.dist-info → comfy_env-0.1.15.dist-info}/METADATA +2 -2
- comfy_env-0.1.15.dist-info/RECORD +31 -0
- comfy_env/config/types.py +0 -70
- comfy_env/errors.py +0 -293
- comfy_env-0.1.13.dist-info/RECORD +0 -32
- {comfy_env-0.1.13.dist-info → comfy_env-0.1.15.dist-info}/WHEEL +0 -0
- {comfy_env-0.1.13.dist-info → comfy_env-0.1.15.dist-info}/entry_points.txt +0 -0
- {comfy_env-0.1.13.dist-info → comfy_env-0.1.15.dist-info}/licenses/LICENSE +0 -0
comfy_env/isolation/wrap.py
CHANGED
|
@@ -145,15 +145,19 @@ def _find_env_paths(node_dir: Path) -> tuple[Optional[Path], Optional[Path]]:
|
|
|
145
145
|
"""
|
|
146
146
|
Find site-packages and lib directories for the isolated environment.
|
|
147
147
|
|
|
148
|
+
Fallback order:
|
|
149
|
+
1. Marker file -> central cache
|
|
150
|
+
2. _env_<name> (local)
|
|
151
|
+
3. .pixi/envs/default (old pixi)
|
|
152
|
+
4. .venv
|
|
153
|
+
|
|
148
154
|
Returns:
|
|
149
155
|
(site_packages, lib_dir) - lib_dir is for LD_LIBRARY_PATH
|
|
150
156
|
"""
|
|
151
157
|
import glob
|
|
152
158
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
env_dir = node_dir / env_name
|
|
156
|
-
if env_dir.exists():
|
|
159
|
+
def _get_paths_from_env(env_dir: Path) -> tuple[Optional[Path], Optional[Path]]:
|
|
160
|
+
"""Extract site-packages and lib_dir from an env directory."""
|
|
157
161
|
if sys.platform == "win32":
|
|
158
162
|
site_packages = env_dir / "Lib" / "site-packages"
|
|
159
163
|
lib_dir = env_dir / "Library" / "bin"
|
|
@@ -163,23 +167,42 @@ def _find_env_paths(node_dir: Path) -> tuple[Optional[Path], Optional[Path]]:
|
|
|
163
167
|
site_packages = Path(matches[0]) if matches else None
|
|
164
168
|
lib_dir = env_dir / "lib"
|
|
165
169
|
if site_packages and site_packages.exists():
|
|
166
|
-
return site_packages, lib_dir if lib_dir.exists() else None
|
|
170
|
+
return site_packages, lib_dir if lib_dir and lib_dir.exists() else None
|
|
171
|
+
return None, None
|
|
167
172
|
|
|
168
|
-
#
|
|
173
|
+
# 1. Check marker file -> central cache
|
|
174
|
+
marker_path = node_dir / ".comfy-env-marker.toml"
|
|
175
|
+
if marker_path.exists():
|
|
176
|
+
try:
|
|
177
|
+
import tomli
|
|
178
|
+
with open(marker_path, "rb") as f:
|
|
179
|
+
marker = tomli.load(f)
|
|
180
|
+
env_path = marker.get("env", {}).get("path")
|
|
181
|
+
if env_path:
|
|
182
|
+
env_dir = Path(env_path)
|
|
183
|
+
if env_dir.exists():
|
|
184
|
+
result = _get_paths_from_env(env_dir)
|
|
185
|
+
if result[0]:
|
|
186
|
+
return result
|
|
187
|
+
except Exception:
|
|
188
|
+
pass # Fall through to other options
|
|
189
|
+
|
|
190
|
+
# 2. Check _env_<name> directory (local)
|
|
191
|
+
env_name = get_env_name(node_dir.name)
|
|
192
|
+
env_dir = node_dir / env_name
|
|
193
|
+
if env_dir.exists():
|
|
194
|
+
result = _get_paths_from_env(env_dir)
|
|
195
|
+
if result[0]:
|
|
196
|
+
return result
|
|
197
|
+
|
|
198
|
+
# 3. Fallback: Check old .pixi/envs/default (for backward compat)
|
|
169
199
|
pixi_env = node_dir / ".pixi" / "envs" / "default"
|
|
170
200
|
if pixi_env.exists():
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
else:
|
|
175
|
-
pattern = str(pixi_env / "lib" / "python*" / "site-packages")
|
|
176
|
-
matches = glob.glob(pattern)
|
|
177
|
-
site_packages = Path(matches[0]) if matches else None
|
|
178
|
-
lib_dir = pixi_env / "lib"
|
|
179
|
-
if site_packages and site_packages.exists():
|
|
180
|
-
return site_packages, lib_dir if lib_dir.exists() else None
|
|
201
|
+
result = _get_paths_from_env(pixi_env)
|
|
202
|
+
if result[0]:
|
|
203
|
+
return result
|
|
181
204
|
|
|
182
|
-
# Check .venv directory
|
|
205
|
+
# 4. Check .venv directory
|
|
183
206
|
venv_dir = node_dir / ".venv"
|
|
184
207
|
if venv_dir.exists():
|
|
185
208
|
if sys.platform == "win32":
|
|
@@ -195,19 +218,46 @@ def _find_env_paths(node_dir: Path) -> tuple[Optional[Path], Optional[Path]]:
|
|
|
195
218
|
|
|
196
219
|
|
|
197
220
|
def _find_env_dir(node_dir: Path) -> Optional[Path]:
|
|
198
|
-
"""
|
|
199
|
-
|
|
221
|
+
"""
|
|
222
|
+
Find the environment directory (for cache key).
|
|
223
|
+
|
|
224
|
+
Fallback order:
|
|
225
|
+
1. Marker file -> central cache
|
|
226
|
+
2. _env_<name> (local)
|
|
227
|
+
3. .pixi/envs/default (old pixi)
|
|
228
|
+
4. .venv
|
|
229
|
+
"""
|
|
230
|
+
# 1. Check marker file -> central cache
|
|
231
|
+
marker_path = node_dir / ".comfy-env-marker.toml"
|
|
232
|
+
if marker_path.exists():
|
|
233
|
+
try:
|
|
234
|
+
import tomli
|
|
235
|
+
with open(marker_path, "rb") as f:
|
|
236
|
+
marker = tomli.load(f)
|
|
237
|
+
env_path = marker.get("env", {}).get("path")
|
|
238
|
+
if env_path:
|
|
239
|
+
env_dir = Path(env_path)
|
|
240
|
+
if env_dir.exists():
|
|
241
|
+
return env_dir
|
|
242
|
+
except Exception:
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
# 2. Check _env_<name> first
|
|
200
246
|
env_name = get_env_name(node_dir.name)
|
|
201
247
|
env_dir = node_dir / env_name
|
|
202
248
|
if env_dir.exists():
|
|
203
249
|
return env_dir
|
|
204
|
-
|
|
250
|
+
|
|
251
|
+
# 3. Fallback to old .pixi path
|
|
205
252
|
pixi_env = node_dir / ".pixi" / "envs" / "default"
|
|
206
253
|
if pixi_env.exists():
|
|
207
254
|
return pixi_env
|
|
255
|
+
|
|
256
|
+
# 4. Check .venv
|
|
208
257
|
venv_dir = node_dir / ".venv"
|
|
209
258
|
if venv_dir.exists():
|
|
210
259
|
return venv_dir
|
|
260
|
+
|
|
211
261
|
return None
|
|
212
262
|
|
|
213
263
|
|
|
@@ -335,6 +385,131 @@ def _wrap_node_class(
|
|
|
335
385
|
return cls
|
|
336
386
|
|
|
337
387
|
|
|
388
|
+
def _is_comfy_env_enabled() -> bool:
|
|
389
|
+
"""Check if comfy-env isolation is enabled (default: True)."""
|
|
390
|
+
val = os.environ.get("USE_COMFY_ENV", "1").lower()
|
|
391
|
+
return val not in ("0", "false", "no", "off")
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def wrap_nodes() -> None:
|
|
395
|
+
"""
|
|
396
|
+
Auto-wrap nodes for isolation. Call from your __init__.py after defining NODE_CLASS_MAPPINGS.
|
|
397
|
+
|
|
398
|
+
Usage:
|
|
399
|
+
from comfy_env import wrap_nodes
|
|
400
|
+
wrap_nodes()
|
|
401
|
+
"""
|
|
402
|
+
# Skip if isolation is disabled
|
|
403
|
+
if not _is_comfy_env_enabled():
|
|
404
|
+
print(f"[comfy-env] Isolation disabled, nodes running in main process")
|
|
405
|
+
return
|
|
406
|
+
|
|
407
|
+
# Skip if running inside worker subprocess
|
|
408
|
+
if os.environ.get("COMFYUI_ISOLATION_WORKER") == "1":
|
|
409
|
+
return
|
|
410
|
+
|
|
411
|
+
# Get caller's frame and module
|
|
412
|
+
frame = inspect.stack()[1]
|
|
413
|
+
caller_module = inspect.getmodule(frame.frame)
|
|
414
|
+
if caller_module is None:
|
|
415
|
+
print("[comfy-env] Warning: Could not determine caller module")
|
|
416
|
+
return
|
|
417
|
+
|
|
418
|
+
# Get NODE_CLASS_MAPPINGS from caller's module
|
|
419
|
+
node_class_mappings = getattr(caller_module, "NODE_CLASS_MAPPINGS", None)
|
|
420
|
+
if not node_class_mappings:
|
|
421
|
+
print("[comfy-env] Warning: No NODE_CLASS_MAPPINGS found in caller module")
|
|
422
|
+
return
|
|
423
|
+
|
|
424
|
+
# Get package root directory
|
|
425
|
+
caller_file = Path(frame.filename).resolve()
|
|
426
|
+
package_dir = caller_file.parent
|
|
427
|
+
|
|
428
|
+
# Find all comfy-env.toml files
|
|
429
|
+
config_files = list(package_dir.rglob("comfy-env.toml"))
|
|
430
|
+
if not config_files:
|
|
431
|
+
return # No configs, nothing to wrap
|
|
432
|
+
|
|
433
|
+
# Get ComfyUI base path
|
|
434
|
+
try:
|
|
435
|
+
import folder_paths
|
|
436
|
+
comfyui_base = folder_paths.base_path
|
|
437
|
+
except ImportError:
|
|
438
|
+
comfyui_base = None
|
|
439
|
+
|
|
440
|
+
# Build a map of config_dir -> env info
|
|
441
|
+
config_envs = []
|
|
442
|
+
for config_file in config_files:
|
|
443
|
+
config_dir = config_file.parent
|
|
444
|
+
env_dir = _find_env_dir(config_dir)
|
|
445
|
+
site_packages, lib_dir = _find_env_paths(config_dir)
|
|
446
|
+
|
|
447
|
+
if not env_dir or not site_packages:
|
|
448
|
+
continue
|
|
449
|
+
|
|
450
|
+
# Read env_vars from config
|
|
451
|
+
env_vars = {}
|
|
452
|
+
try:
|
|
453
|
+
import tomli
|
|
454
|
+
with open(config_file, "rb") as f:
|
|
455
|
+
config = tomli.load(f)
|
|
456
|
+
env_vars_data = config.get("env_vars", {})
|
|
457
|
+
env_vars = {str(k): str(v) for k, v in env_vars_data.items()}
|
|
458
|
+
except Exception:
|
|
459
|
+
pass
|
|
460
|
+
|
|
461
|
+
if comfyui_base:
|
|
462
|
+
env_vars["COMFYUI_BASE"] = str(comfyui_base)
|
|
463
|
+
|
|
464
|
+
config_envs.append({
|
|
465
|
+
"config_dir": config_dir,
|
|
466
|
+
"env_dir": env_dir,
|
|
467
|
+
"site_packages": site_packages,
|
|
468
|
+
"lib_dir": lib_dir,
|
|
469
|
+
"env_vars": env_vars,
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
if not config_envs:
|
|
473
|
+
return
|
|
474
|
+
|
|
475
|
+
# Match nodes to configs by checking source file location
|
|
476
|
+
wrapped_count = 0
|
|
477
|
+
for node_name, node_cls in node_class_mappings.items():
|
|
478
|
+
if not hasattr(node_cls, "FUNCTION"):
|
|
479
|
+
continue
|
|
480
|
+
|
|
481
|
+
# Get node's source file
|
|
482
|
+
try:
|
|
483
|
+
source_file = Path(inspect.getfile(node_cls)).resolve()
|
|
484
|
+
except (TypeError, OSError):
|
|
485
|
+
continue
|
|
486
|
+
|
|
487
|
+
# Find which config this node belongs to
|
|
488
|
+
for env_info in config_envs:
|
|
489
|
+
config_dir = env_info["config_dir"]
|
|
490
|
+
try:
|
|
491
|
+
source_file.relative_to(config_dir)
|
|
492
|
+
# Node is under this config dir - wrap it
|
|
493
|
+
sys_path = [str(env_info["site_packages"]), str(config_dir)]
|
|
494
|
+
lib_path = str(env_info["lib_dir"]) if env_info["lib_dir"] else None
|
|
495
|
+
|
|
496
|
+
_wrap_node_class(
|
|
497
|
+
node_cls,
|
|
498
|
+
env_info["env_dir"],
|
|
499
|
+
config_dir,
|
|
500
|
+
sys_path,
|
|
501
|
+
lib_path,
|
|
502
|
+
env_info["env_vars"],
|
|
503
|
+
)
|
|
504
|
+
wrapped_count += 1
|
|
505
|
+
break
|
|
506
|
+
except ValueError:
|
|
507
|
+
continue # Node not under this config dir
|
|
508
|
+
|
|
509
|
+
if wrapped_count > 0:
|
|
510
|
+
print(f"[comfy-env] Wrapped {wrapped_count} nodes for isolation")
|
|
511
|
+
|
|
512
|
+
|
|
338
513
|
def wrap_isolated_nodes(
|
|
339
514
|
node_class_mappings: Dict[str, type],
|
|
340
515
|
nodes_dir: Path,
|
|
@@ -369,6 +544,11 @@ def wrap_isolated_nodes(
|
|
|
369
544
|
wrap_isolated_nodes(cgal_nodes, Path(__file__).parent / "nodes/cgal")
|
|
370
545
|
)
|
|
371
546
|
"""
|
|
547
|
+
# Skip if isolation is disabled
|
|
548
|
+
if not _is_comfy_env_enabled():
|
|
549
|
+
print(f"[comfy-env] Isolation disabled, nodes running in main process")
|
|
550
|
+
return node_class_mappings
|
|
551
|
+
|
|
372
552
|
# Skip if running inside worker subprocess
|
|
373
553
|
if os.environ.get("COMFYUI_ISOLATION_WORKER") == "1":
|
|
374
554
|
return node_class_mappings
|
|
@@ -391,12 +571,9 @@ def wrap_isolated_nodes(
|
|
|
391
571
|
# Read env_vars from comfy-env.toml
|
|
392
572
|
env_vars = {}
|
|
393
573
|
try:
|
|
394
|
-
|
|
395
|
-
import tomllib
|
|
396
|
-
else:
|
|
397
|
-
import tomli as tomllib
|
|
574
|
+
import tomli
|
|
398
575
|
with open(config_file, "rb") as f:
|
|
399
|
-
config =
|
|
576
|
+
config = tomli.load(f)
|
|
400
577
|
env_vars_data = config.get("env_vars", {})
|
|
401
578
|
env_vars = {str(k): str(v) for k, v in env_vars_data.items()}
|
|
402
579
|
except Exception:
|
comfy_env/nodes.py
CHANGED
comfy_env/pixi/core.py
CHANGED
|
@@ -18,7 +18,7 @@ import urllib.request
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from typing import Any, Callable, Dict, List, Optional
|
|
20
20
|
|
|
21
|
-
from ..config.
|
|
21
|
+
from ..config.parser import ComfyEnvConfig
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
# Pixi download URLs by platform
|
|
@@ -37,7 +37,6 @@ CUDA_WHEELS_INDEX = "https://pozzettiandrea.github.io/cuda-wheels/"
|
|
|
37
37
|
CUDA_TORCH_MAP = {
|
|
38
38
|
"12.8": "2.8",
|
|
39
39
|
"12.4": "2.4",
|
|
40
|
-
"12.1": "2.4",
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
def find_wheel_url(
|
|
@@ -534,20 +533,55 @@ def pixi_install(
|
|
|
534
533
|
|
|
535
534
|
log("CUDA packages installed")
|
|
536
535
|
|
|
537
|
-
# Move environment from .pixi/envs/default to
|
|
536
|
+
# Move environment from .pixi/envs/default to central cache
|
|
537
|
+
from ..cache import (
|
|
538
|
+
get_central_env_path, write_marker, write_env_metadata,
|
|
539
|
+
MARKER_FILE, get_cache_dir
|
|
540
|
+
)
|
|
541
|
+
|
|
538
542
|
old_env = node_dir / ".pixi" / "envs" / "default"
|
|
539
|
-
|
|
540
|
-
|
|
543
|
+
config_path = node_dir / "comfy-env.toml"
|
|
544
|
+
|
|
545
|
+
# Determine the main node directory (for naming)
|
|
546
|
+
# If node_dir is custom_nodes/NodeName/subdir, main_node_dir is custom_nodes/NodeName
|
|
547
|
+
# If node_dir is custom_nodes/NodeName, main_node_dir is custom_nodes/NodeName
|
|
548
|
+
if node_dir.parent.name == "custom_nodes":
|
|
549
|
+
main_node_dir = node_dir
|
|
550
|
+
else:
|
|
551
|
+
# Walk up to find custom_nodes parent
|
|
552
|
+
main_node_dir = node_dir
|
|
553
|
+
for parent in node_dir.parents:
|
|
554
|
+
if parent.parent.name == "custom_nodes":
|
|
555
|
+
main_node_dir = parent
|
|
556
|
+
break
|
|
557
|
+
|
|
558
|
+
# Get central env path
|
|
559
|
+
central_env = get_central_env_path(main_node_dir, config_path)
|
|
541
560
|
|
|
542
561
|
if old_env.exists():
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
#
|
|
562
|
+
# Ensure cache directory exists
|
|
563
|
+
get_cache_dir()
|
|
564
|
+
|
|
565
|
+
# Remove old central env if exists
|
|
566
|
+
if central_env.exists():
|
|
567
|
+
shutil.rmtree(central_env)
|
|
568
|
+
|
|
569
|
+
# Move to central cache
|
|
570
|
+
shutil.move(str(old_env), str(central_env))
|
|
571
|
+
|
|
572
|
+
# Write marker file in node directory
|
|
573
|
+
write_marker(config_path, central_env)
|
|
574
|
+
|
|
575
|
+
# Write metadata in env for orphan detection
|
|
576
|
+
marker_path = config_path.parent / MARKER_FILE
|
|
577
|
+
write_env_metadata(central_env, marker_path)
|
|
578
|
+
|
|
579
|
+
# Clean up .pixi directory
|
|
547
580
|
pixi_dir = node_dir / ".pixi"
|
|
548
581
|
if pixi_dir.exists():
|
|
549
582
|
shutil.rmtree(pixi_dir)
|
|
550
|
-
|
|
583
|
+
|
|
584
|
+
log(f"Environment created at: {central_env}")
|
|
551
585
|
|
|
552
586
|
log("Installation complete!")
|
|
553
587
|
return True
|
comfy_env/prestartup.py
CHANGED
|
@@ -20,24 +20,16 @@ def get_env_name(dir_name: str) -> str:
|
|
|
20
20
|
def _load_env_vars(config_path: str) -> Dict[str, str]:
|
|
21
21
|
"""
|
|
22
22
|
Load [env_vars] section from comfy-env.toml.
|
|
23
|
-
|
|
24
|
-
Uses tomllib (Python 3.11+) or tomli fallback.
|
|
25
23
|
Returns empty dict if file not found or parsing fails.
|
|
26
24
|
"""
|
|
27
25
|
if not os.path.exists(config_path):
|
|
28
26
|
return {}
|
|
29
27
|
|
|
30
28
|
try:
|
|
31
|
-
|
|
32
|
-
import tomllib
|
|
33
|
-
else:
|
|
34
|
-
try:
|
|
35
|
-
import tomli as tomllib
|
|
36
|
-
except ImportError:
|
|
37
|
-
return {}
|
|
29
|
+
import tomli
|
|
38
30
|
|
|
39
31
|
with open(config_path, "rb") as f:
|
|
40
|
-
data =
|
|
32
|
+
data = tomli.load(f)
|
|
41
33
|
|
|
42
34
|
env_vars_data = data.get("env_vars", {})
|
|
43
35
|
return {str(k): str(v) for k, v in env_vars_data.items()}
|
|
@@ -95,6 +87,12 @@ def _dedupe_libomp_macos():
|
|
|
95
87
|
pass # Permission denied, etc.
|
|
96
88
|
|
|
97
89
|
|
|
90
|
+
def _is_comfy_env_enabled() -> bool:
|
|
91
|
+
"""Check if comfy-env isolation is enabled (default: True)."""
|
|
92
|
+
val = os.environ.get("USE_COMFY_ENV", "1").lower()
|
|
93
|
+
return val not in ("0", "false", "no", "off")
|
|
94
|
+
|
|
95
|
+
|
|
98
96
|
def setup_env(node_dir: Optional[str] = None) -> None:
|
|
99
97
|
"""
|
|
100
98
|
Set up environment for pixi conda libraries.
|
|
@@ -112,6 +110,10 @@ def setup_env(node_dir: Optional[str] = None) -> None:
|
|
|
112
110
|
from comfy_env import setup_env
|
|
113
111
|
setup_env()
|
|
114
112
|
"""
|
|
113
|
+
# Skip if isolation is disabled
|
|
114
|
+
if not _is_comfy_env_enabled():
|
|
115
|
+
return
|
|
116
|
+
|
|
115
117
|
# macOS: Dedupe libomp to prevent OpenMP conflicts (torch vs pymeshlab, etc.)
|
|
116
118
|
_dedupe_libomp_macos()
|
|
117
119
|
|
|
@@ -127,15 +129,40 @@ def setup_env(node_dir: Optional[str] = None) -> None:
|
|
|
127
129
|
for key, value in env_vars.items():
|
|
128
130
|
os.environ[key] = value
|
|
129
131
|
|
|
130
|
-
#
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
# Resolve environment path with fallback chain:
|
|
133
|
+
# 1. Marker file -> central cache
|
|
134
|
+
# 2. _env_<name> (current local)
|
|
135
|
+
# 3. .pixi/envs/default (old pixi)
|
|
136
|
+
pixi_env = None
|
|
137
|
+
|
|
138
|
+
# 1. Check marker file -> central cache
|
|
139
|
+
marker_path = os.path.join(node_dir, ".comfy-env-marker.toml")
|
|
140
|
+
if os.path.exists(marker_path):
|
|
141
|
+
try:
|
|
142
|
+
import tomli
|
|
143
|
+
with open(marker_path, "rb") as f:
|
|
144
|
+
marker = tomli.load(f)
|
|
145
|
+
env_path = marker.get("env", {}).get("path")
|
|
146
|
+
if env_path and os.path.exists(env_path):
|
|
147
|
+
pixi_env = env_path
|
|
148
|
+
except Exception:
|
|
149
|
+
pass # Fall through to other options
|
|
150
|
+
|
|
151
|
+
# 2. Check _env_<name> (local)
|
|
152
|
+
if not pixi_env:
|
|
153
|
+
env_name = get_env_name(os.path.basename(node_dir))
|
|
154
|
+
local_env = os.path.join(node_dir, env_name)
|
|
155
|
+
if os.path.exists(local_env):
|
|
156
|
+
pixi_env = local_env
|
|
157
|
+
|
|
158
|
+
# 3. Fallback to old .pixi path
|
|
159
|
+
if not pixi_env:
|
|
160
|
+
old_pixi = os.path.join(node_dir, ".pixi", "envs", "default")
|
|
161
|
+
if os.path.exists(old_pixi):
|
|
162
|
+
pixi_env = old_pixi
|
|
163
|
+
|
|
164
|
+
if not pixi_env:
|
|
165
|
+
return # No environment found
|
|
139
166
|
|
|
140
167
|
if sys.platform == "win32":
|
|
141
168
|
# Windows: add to PATH for DLL loading
|
|
@@ -162,3 +189,20 @@ def setup_env(node_dir: Optional[str] = None) -> None:
|
|
|
162
189
|
|
|
163
190
|
if site_packages and os.path.exists(site_packages) and site_packages not in sys.path:
|
|
164
191
|
sys.path.insert(0, site_packages)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def copy_files(src, dst, pattern: str = "*") -> None:
|
|
195
|
+
"""Copy files matching pattern from src to dst (skip existing)."""
|
|
196
|
+
import shutil
|
|
197
|
+
|
|
198
|
+
src, dst = Path(src), Path(dst)
|
|
199
|
+
if not src.exists():
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
dst.mkdir(parents=True, exist_ok=True)
|
|
203
|
+
for f in src.glob(pattern):
|
|
204
|
+
if f.is_file():
|
|
205
|
+
target = dst / f.relative_to(src)
|
|
206
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
207
|
+
if not target.exists():
|
|
208
|
+
shutil.copy2(f, target)
|
comfy_env/workers/subprocess.py
CHANGED
|
@@ -440,7 +440,7 @@ def _watchdog():
|
|
|
440
440
|
f.flush()
|
|
441
441
|
|
|
442
442
|
# Also print
|
|
443
|
-
print(f"\\n=== WATCHDOG TICK {tick} ===", flush=True)
|
|
443
|
+
print(f"\\n=== WATCHDOG TICK {tick} (debug only, don't worry) ===", flush=True)
|
|
444
444
|
print(dump, flush=True)
|
|
445
445
|
print("=== END ===\\n", flush=True)
|
|
446
446
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: comfy-env
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.15
|
|
4
4
|
Summary: Environment management for ComfyUI custom nodes - CUDA wheel resolution and process isolation
|
|
5
5
|
Project-URL: Homepage, https://github.com/PozzettiAndrea/comfy-env
|
|
6
6
|
Project-URL: Repository, https://github.com/PozzettiAndrea/comfy-env
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
19
19
|
Requires-Python: >=3.10
|
|
20
20
|
Requires-Dist: pip>=21.0
|
|
21
21
|
Requires-Dist: tomli-w>=1.0.0
|
|
22
|
-
Requires-Dist: tomli>=2.0.0
|
|
22
|
+
Requires-Dist: tomli>=2.0.0
|
|
23
23
|
Requires-Dist: uv>=0.4.0
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: mypy; extra == 'dev'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
comfy_env/__init__.py,sha256=vtMEMhys8esoSXM3H5pFJGecE_ZoDwPS0foeEzlbmZA,2331
|
|
2
|
+
comfy_env/cache.py,sha256=LnOVGCheF0hM-JZcX_-HD46o_nA7U2SIptdOT1TQkBE,6293
|
|
3
|
+
comfy_env/cli.py,sha256=UJbPbCEKfAAoZ_J0JeUs4jVsWuJ8BeR6CV6XtCSuy1g,12707
|
|
4
|
+
comfy_env/install.py,sha256=nuG3z5V1PQ4IoXKknLfS9sUKAUq0RHcpZp7jvdOTgIY,14853
|
|
5
|
+
comfy_env/nodes.py,sha256=tqMBf3hTjtgbT9sdcUiNnMaiFNPsHWtw79_TgvsrU9Q,5392
|
|
6
|
+
comfy_env/prestartup.py,sha256=Aw8yvmkraP3bRXPTZKOFnhUHLNtGujAZ4aD76nKx6pA,7215
|
|
7
|
+
comfy_env/config/__init__.py,sha256=_udIO2AkntkFfktst1uKqRkLdne1cXf2b0BrErvNT_k,289
|
|
8
|
+
comfy_env/config/parser.py,sha256=B9iK3RQdErAHnmvD_jfZm87u5K5gTnEwFDMdOXil3YM,3458
|
|
9
|
+
comfy_env/isolation/__init__.py,sha256=Fy8A7_5plxcCMFOf00cJlEMbRVu8aKFkTo0Spj31YqI,206
|
|
10
|
+
comfy_env/isolation/wrap.py,sha256=vJz75TtS6NMyfxX_UlqNdZNQQYROHP9KbcghmNIPyFM,20046
|
|
11
|
+
comfy_env/pixi/__init__.py,sha256=BUrq7AQf3WDm0cHWh72B2xZbURNnDu2dCuELWiQCUiM,997
|
|
12
|
+
comfy_env/pixi/core.py,sha256=LY2wrvMNqO-cp9MOgixvpe1mJKtGK3U311-0rbURTBo,21770
|
|
13
|
+
comfy_env/pixi/cuda_detection.py,sha256=sqB3LjvGNdV4eFqiARQGfyecBM3ZiUmeh6nG0YCRYQw,9751
|
|
14
|
+
comfy_env/pixi/resolver.py,sha256=U_A8rBDxCj4gUlJt2YJQniP4cCKqxJEiVFgXOoH7vM8,6339
|
|
15
|
+
comfy_env/pixi/platform/__init__.py,sha256=Nb5MPZIEeanSMEWwqU4p4bnEKTJn1tWcwobnhq9x9IY,614
|
|
16
|
+
comfy_env/pixi/platform/base.py,sha256=iS0ptTTVjXRwPU4qWUdvHI7jteuzxGSjWr5BUQ7hGiU,2453
|
|
17
|
+
comfy_env/pixi/platform/darwin.py,sha256=HK3VkLT6DfesAnIXwx2IaUFHTBclF0xTQnC7azWY6Kc,1552
|
|
18
|
+
comfy_env/pixi/platform/linux.py,sha256=xLp8FEbFqZLQrzIZBI9z3C4g23Ab1ASTHLsXDzsdCoA,2062
|
|
19
|
+
comfy_env/pixi/platform/windows.py,sha256=FCOCgpzGzorY9-HueMlJUR8DxM2eH-cj9iZk6K026Is,10891
|
|
20
|
+
comfy_env/templates/comfy-env-instructions.txt,sha256=ve1RAthW7ouumU9h6DM7mIRX1MS8_Tyonq2U4tcrFu8,1031
|
|
21
|
+
comfy_env/templates/comfy-env.toml,sha256=ROIqi4BlPL1MEdL1VgebfTHpdwPNYGHwWeigI9Kw-1I,4831
|
|
22
|
+
comfy_env/workers/__init__.py,sha256=TMVG55d2XLP1mJ3x1d16H0SBDJZtk2kMC5P4HLk9TrA,1073
|
|
23
|
+
comfy_env/workers/base.py,sha256=4ZYTaQ4J0kBHCoO_OfZnsowm4rJCoqinZUaOtgkOPbw,2307
|
|
24
|
+
comfy_env/workers/mp.py,sha256=R0XWsiHv8gswxa_-iNHU14o_9Og0RFG0QnY9DRZzn2c,34060
|
|
25
|
+
comfy_env/workers/subprocess.py,sha256=FvhEWilFWkjYNVDm7mmbFmXiSYB7eT5Tl2uvJ_23qnc,57154
|
|
26
|
+
comfy_env/workers/tensor_utils.py,sha256=TCuOAjJymrSbkgfyvcKtQ_KbVWTqSwP9VH_bCaFLLq8,6409
|
|
27
|
+
comfy_env-0.1.15.dist-info/METADATA,sha256=HMJQ4qs9ObD6YpTGffqmUACR67_kzcWv392WpdNASzc,6946
|
|
28
|
+
comfy_env-0.1.15.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
29
|
+
comfy_env-0.1.15.dist-info/entry_points.txt,sha256=J4fXeqgxU_YenuW_Zxn_pEL7J-3R0--b6MS5t0QmAr0,49
|
|
30
|
+
comfy_env-0.1.15.dist-info/licenses/LICENSE,sha256=E68QZMMpW4P2YKstTZ3QU54HRQO8ecew09XZ4_Vn870,1093
|
|
31
|
+
comfy_env-0.1.15.dist-info/RECORD,,
|
comfy_env/config/types.py
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
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
|
-
# [apt] - System packages to install via apt (Linux only)
|
|
57
|
-
apt_packages: List[str] = field(default_factory=list)
|
|
58
|
-
|
|
59
|
-
# [env_vars] - Environment variables to set early (in prestartup)
|
|
60
|
-
env_vars: Dict[str, str] = field(default_factory=dict)
|
|
61
|
-
|
|
62
|
-
# [node_reqs] - other ComfyUI nodes to clone
|
|
63
|
-
node_reqs: List[NodeReq] = field(default_factory=list)
|
|
64
|
-
|
|
65
|
-
# Everything else from comfy-env.toml passes through to pixi.toml
|
|
66
|
-
pixi_passthrough: Dict[str, Any] = field(default_factory=dict)
|
|
67
|
-
|
|
68
|
-
@property
|
|
69
|
-
def has_cuda(self) -> bool:
|
|
70
|
-
return bool(self.cuda_packages)
|