comfy-env 0.0.17__tar.gz → 0.0.19__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.
- {comfy_env-0.0.17 → comfy_env-0.0.19}/PKG-INFO +1 -1
- {comfy_env-0.0.17 → comfy_env-0.0.19}/pyproject.toml +1 -1
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/cli.py +2 -2
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/decorator.py +6 -1
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/config.py +20 -1
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/config_file.py +36 -3
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/install.py +109 -17
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/workers/torch_mp.py +17 -3
- comfy_env-0.0.17/CLAUDE.md +0 -131
- comfy_env-0.0.17/CRITICISM.md +0 -316
- comfy_env-0.0.17/examples/basic_node/__init__.py +0 -5
- comfy_env-0.0.17/examples/basic_node/comfy-env.toml +0 -65
- comfy_env-0.0.17/examples/basic_node/nodes.py +0 -157
- comfy_env-0.0.17/examples/basic_node/worker.py +0 -79
- comfy_env-0.0.17/examples/decorator_node/__init__.py +0 -9
- comfy_env-0.0.17/examples/decorator_node/nodes.py +0 -182
- comfy_env-0.0.17/src/comfy_env/tools.py +0 -221
- {comfy_env-0.0.17 → comfy_env-0.0.19}/.github/workflows/publish.yml +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/.gitignore +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/LICENSE +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/README.md +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/__init__.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/__init__.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/cuda_gpu_detection.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/manager.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/platform/__init__.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/platform/base.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/platform/darwin.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/platform/linux.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/platform/windows.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/security.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/errors.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/__init__.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/bridge.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/protocol.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/tensor.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/torch_bridge.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/transport.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/worker.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/registry.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/resolver.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/stubs/__init__.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/stubs/folder_paths.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/wheel_sources.yml +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/workers/__init__.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/workers/base.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/workers/pool.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/workers/tensor_utils.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/workers/venv.py +0 -0
- {comfy_env-0.0.17 → comfy_env-0.0.19}/untitled.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: comfy-env
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.19
|
|
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
|
|
@@ -47,7 +47,7 @@ def main(args: Optional[List[str]] = None) -> int:
|
|
|
47
47
|
install_parser = subparsers.add_parser(
|
|
48
48
|
"install",
|
|
49
49
|
help="Install dependencies from config",
|
|
50
|
-
description="Install CUDA wheels and dependencies from
|
|
50
|
+
description="Install CUDA wheels and dependencies from comfy-env.toml",
|
|
51
51
|
)
|
|
52
52
|
install_parser.add_argument(
|
|
53
53
|
"--config", "-c",
|
|
@@ -450,7 +450,7 @@ def cmd_list_packages(args) -> int:
|
|
|
450
450
|
print("=" * 60)
|
|
451
451
|
print()
|
|
452
452
|
print("These packages can be installed without specifying wheel_sources.")
|
|
453
|
-
print("Just add them to your
|
|
453
|
+
print("Just add them to your comfy-env.toml:")
|
|
454
454
|
print()
|
|
455
455
|
print(" [cuda]")
|
|
456
456
|
print(" torch-scatter = \"2.1.2\"")
|
|
@@ -153,7 +153,7 @@ def _get_or_create_worker(config: WorkerConfig, log_fn: Callable):
|
|
|
153
153
|
# Same Python - use TorchMPWorker (fast, zero-copy)
|
|
154
154
|
from .workers import TorchMPWorker
|
|
155
155
|
log_fn(f"Creating TorchMPWorker (same Python, zero-copy tensors)")
|
|
156
|
-
worker = TorchMPWorker(name=config.env_name)
|
|
156
|
+
worker = TorchMPWorker(name=config.env_name, sys_path=config.sys_path)
|
|
157
157
|
else:
|
|
158
158
|
# Different Python - use PersistentVenvWorker
|
|
159
159
|
from .workers.venv import PersistentVenvWorker
|
|
@@ -369,6 +369,11 @@ def isolated(
|
|
|
369
369
|
# Get module name for import in worker
|
|
370
370
|
module_name = cls.__module__
|
|
371
371
|
|
|
372
|
+
# Handle ComfyUI's dynamic import which can set __module__ to a path
|
|
373
|
+
if module_name.startswith('/') or module_name.startswith('\\'):
|
|
374
|
+
# Module name is a filesystem path - use the source file stem instead
|
|
375
|
+
module_name = source_file.stem
|
|
376
|
+
|
|
372
377
|
# Call worker using appropriate method
|
|
373
378
|
if worker_config.python is None:
|
|
374
379
|
# TorchMPWorker - use call_method protocol (avoids pickle issues)
|
|
@@ -5,6 +5,18 @@ from pathlib import Path
|
|
|
5
5
|
from typing import Dict, List, Optional
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
@dataclass
|
|
9
|
+
class SystemConfig:
|
|
10
|
+
"""Configuration for system-level packages.
|
|
11
|
+
|
|
12
|
+
These are OS-level packages (apt, brew, etc.) that need to be installed
|
|
13
|
+
before Python packages can work properly.
|
|
14
|
+
"""
|
|
15
|
+
linux: List[str] = field(default_factory=list) # apt packages
|
|
16
|
+
darwin: List[str] = field(default_factory=list) # brew packages (future)
|
|
17
|
+
windows: List[str] = field(default_factory=list) # winget packages (future)
|
|
18
|
+
|
|
19
|
+
|
|
8
20
|
@dataclass
|
|
9
21
|
class LocalConfig:
|
|
10
22
|
"""Configuration for local (host environment) installs.
|
|
@@ -34,9 +46,10 @@ class ToolConfig:
|
|
|
34
46
|
@dataclass
|
|
35
47
|
class EnvManagerConfig:
|
|
36
48
|
"""
|
|
37
|
-
Full configuration parsed from
|
|
49
|
+
Full configuration parsed from comfy-env.toml.
|
|
38
50
|
|
|
39
51
|
Supports the v2 schema:
|
|
52
|
+
[system] - System-level packages (apt, brew, etc.)
|
|
40
53
|
[local.cuda] - CUDA packages for host environment
|
|
41
54
|
[local.packages] - Regular packages for host environment
|
|
42
55
|
[envname] - Isolated env definition
|
|
@@ -45,11 +58,17 @@ class EnvManagerConfig:
|
|
|
45
58
|
[node_reqs] - Node dependencies
|
|
46
59
|
[tools] - External tools (e.g., blender = "4.2")
|
|
47
60
|
"""
|
|
61
|
+
system: SystemConfig = field(default_factory=SystemConfig)
|
|
48
62
|
local: LocalConfig = field(default_factory=LocalConfig)
|
|
49
63
|
envs: Dict[str, "IsolatedEnv"] = field(default_factory=dict)
|
|
50
64
|
node_reqs: List[NodeReq] = field(default_factory=list)
|
|
51
65
|
tools: Dict[str, ToolConfig] = field(default_factory=dict)
|
|
52
66
|
|
|
67
|
+
@property
|
|
68
|
+
def has_system(self) -> bool:
|
|
69
|
+
"""Check if there are system packages to install."""
|
|
70
|
+
return bool(self.system.linux or self.system.darwin or self.system.windows)
|
|
71
|
+
|
|
53
72
|
@property
|
|
54
73
|
def has_local(self) -> bool:
|
|
55
74
|
"""Check if there are local packages to install."""
|
|
@@ -63,7 +63,7 @@ else:
|
|
|
63
63
|
except ImportError:
|
|
64
64
|
tomllib = None # type: ignore
|
|
65
65
|
|
|
66
|
-
from .config import IsolatedEnv, EnvManagerConfig, LocalConfig, NodeReq, ToolConfig
|
|
66
|
+
from .config import IsolatedEnv, EnvManagerConfig, LocalConfig, NodeReq, SystemConfig, ToolConfig
|
|
67
67
|
from .cuda_gpu_detection import detect_cuda_version
|
|
68
68
|
|
|
69
69
|
|
|
@@ -332,7 +332,7 @@ def _substitute_vars(s: str, variables: Dict[str, str]) -> str:
|
|
|
332
332
|
# =============================================================================
|
|
333
333
|
|
|
334
334
|
# Reserved table names that are NOT isolated environments
|
|
335
|
-
RESERVED_TABLES = {"local", "node_reqs", "env", "packages", "sources", "cuda", "variables", "worker", "tools"}
|
|
335
|
+
RESERVED_TABLES = {"local", "node_reqs", "env", "packages", "sources", "cuda", "variables", "worker", "tools", "system"}
|
|
336
336
|
|
|
337
337
|
|
|
338
338
|
def load_config(
|
|
@@ -426,12 +426,14 @@ def _parse_full_config(data: Dict[str, Any], base_dir: Path) -> EnvManagerConfig
|
|
|
426
426
|
return _convert_simple_to_full(data, base_dir)
|
|
427
427
|
|
|
428
428
|
# Parse full schema
|
|
429
|
+
system = _parse_system_section(data.get("system", {}))
|
|
429
430
|
local = _parse_local_section(data.get("local", {}))
|
|
430
431
|
envs = _parse_env_sections(data, base_dir)
|
|
431
432
|
node_reqs = _parse_node_reqs(data.get("node_reqs", {}))
|
|
432
433
|
tools = _parse_tools_section(data.get("tools", {}))
|
|
433
434
|
|
|
434
435
|
return EnvManagerConfig(
|
|
436
|
+
system=system,
|
|
435
437
|
local=local,
|
|
436
438
|
envs=envs,
|
|
437
439
|
node_reqs=node_reqs,
|
|
@@ -613,6 +615,34 @@ def _parse_tools_section(tools_data: Dict[str, Any]) -> Dict[str, ToolConfig]:
|
|
|
613
615
|
return tools
|
|
614
616
|
|
|
615
617
|
|
|
618
|
+
def _parse_system_section(system_data: Dict[str, Any]) -> SystemConfig:
|
|
619
|
+
"""Parse [system] section.
|
|
620
|
+
|
|
621
|
+
Supports:
|
|
622
|
+
[system]
|
|
623
|
+
linux = ["libgl1", "libopengl0"]
|
|
624
|
+
darwin = ["mesa"] # future
|
|
625
|
+
windows = ["vcredist"] # future
|
|
626
|
+
"""
|
|
627
|
+
linux = system_data.get("linux", [])
|
|
628
|
+
darwin = system_data.get("darwin", [])
|
|
629
|
+
windows = system_data.get("windows", [])
|
|
630
|
+
|
|
631
|
+
# Ensure all are lists
|
|
632
|
+
if not isinstance(linux, list):
|
|
633
|
+
linux = [linux] if linux else []
|
|
634
|
+
if not isinstance(darwin, list):
|
|
635
|
+
darwin = [darwin] if darwin else []
|
|
636
|
+
if not isinstance(windows, list):
|
|
637
|
+
windows = [windows] if windows else []
|
|
638
|
+
|
|
639
|
+
return SystemConfig(
|
|
640
|
+
linux=linux,
|
|
641
|
+
darwin=darwin,
|
|
642
|
+
windows=windows,
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
|
|
616
646
|
def _convert_simple_to_full(data: Dict[str, Any], base_dir: Path) -> EnvManagerConfig:
|
|
617
647
|
"""Convert simple config format to full EnvManagerConfig.
|
|
618
648
|
|
|
@@ -622,8 +652,9 @@ def _convert_simple_to_full(data: Dict[str, Any], base_dir: Path) -> EnvManagerC
|
|
|
622
652
|
# Parse using simple parser to get IsolatedEnv
|
|
623
653
|
simple_env = _parse_config(data, base_dir)
|
|
624
654
|
|
|
625
|
-
# Parse tools
|
|
655
|
+
# Parse tools and system sections (shared between simple and full format)
|
|
626
656
|
tools = _parse_tools_section(data.get("tools", {}))
|
|
657
|
+
system = _parse_system_section(data.get("system", {}))
|
|
627
658
|
|
|
628
659
|
# Check if this has explicit env settings (isolated venv) vs just CUDA packages (local install)
|
|
629
660
|
env_section = data.get("env", {})
|
|
@@ -632,6 +663,7 @@ def _convert_simple_to_full(data: Dict[str, Any], base_dir: Path) -> EnvManagerC
|
|
|
632
663
|
if has_explicit_env:
|
|
633
664
|
# Isolated venv config
|
|
634
665
|
return EnvManagerConfig(
|
|
666
|
+
system=system,
|
|
635
667
|
local=LocalConfig(),
|
|
636
668
|
envs={simple_env.name: simple_env},
|
|
637
669
|
node_reqs=[],
|
|
@@ -648,6 +680,7 @@ def _convert_simple_to_full(data: Dict[str, Any], base_dir: Path) -> EnvManagerC
|
|
|
648
680
|
cuda_packages[req] = ""
|
|
649
681
|
|
|
650
682
|
return EnvManagerConfig(
|
|
683
|
+
system=system,
|
|
651
684
|
local=LocalConfig(
|
|
652
685
|
cuda_packages=cuda_packages,
|
|
653
686
|
requirements=simple_env.requirements,
|
|
@@ -12,10 +12,10 @@ Example:
|
|
|
12
12
|
install()
|
|
13
13
|
|
|
14
14
|
# In-place with explicit config
|
|
15
|
-
install(config="
|
|
15
|
+
install(config="comfy-env.toml", mode="inplace")
|
|
16
16
|
|
|
17
17
|
# Isolated environment
|
|
18
|
-
install(config="
|
|
18
|
+
install(config="comfy-env.toml", mode="isolated")
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
import shutil
|
|
@@ -24,13 +24,111 @@ import sys
|
|
|
24
24
|
from pathlib import Path
|
|
25
25
|
from typing import Any, Callable, Dict, List, Optional, Union
|
|
26
26
|
|
|
27
|
-
from .env.config import IsolatedEnv,
|
|
27
|
+
from .env.config import IsolatedEnv, SystemConfig
|
|
28
28
|
from .env.config_file import discover_env_config, load_env_from_file, load_config, discover_config
|
|
29
29
|
from .env.manager import IsolatedEnvManager
|
|
30
30
|
from .errors import CUDANotFoundError, DependencyError, InstallError, WheelNotFoundError
|
|
31
31
|
from .registry import PACKAGE_REGISTRY, get_cuda_short2, is_registered
|
|
32
32
|
from .resolver import RuntimeEnv, WheelResolver, parse_wheel_requirement
|
|
33
|
-
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _install_system_packages(
|
|
36
|
+
system_config: SystemConfig,
|
|
37
|
+
log: Callable[[str], None],
|
|
38
|
+
dry_run: bool = False,
|
|
39
|
+
) -> bool:
|
|
40
|
+
"""
|
|
41
|
+
Install system-level packages (apt, brew, etc.).
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
system_config: SystemConfig with package lists per OS.
|
|
45
|
+
log: Logging callback.
|
|
46
|
+
dry_run: If True, show what would be installed without installing.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
True if installation succeeded or no packages needed.
|
|
50
|
+
"""
|
|
51
|
+
platform = sys.platform
|
|
52
|
+
|
|
53
|
+
if platform.startswith("linux"):
|
|
54
|
+
packages = system_config.linux
|
|
55
|
+
if not packages:
|
|
56
|
+
return True
|
|
57
|
+
|
|
58
|
+
log(f"Installing {len(packages)} system package(s) via apt...")
|
|
59
|
+
|
|
60
|
+
if dry_run:
|
|
61
|
+
log(f" Would install: {', '.join(packages)}")
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
# Check if apt-get is available
|
|
65
|
+
if not shutil.which("apt-get"):
|
|
66
|
+
log(" Warning: apt-get not found. Cannot install system packages.")
|
|
67
|
+
log(f" Please install manually: {', '.join(packages)}")
|
|
68
|
+
return True # Don't fail - just warn
|
|
69
|
+
|
|
70
|
+
# Check if we can use sudo
|
|
71
|
+
sudo_available = shutil.which("sudo") is not None
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
if sudo_available:
|
|
75
|
+
# Try with sudo
|
|
76
|
+
log(" Running apt-get update...")
|
|
77
|
+
update_result = subprocess.run(
|
|
78
|
+
["sudo", "apt-get", "update"],
|
|
79
|
+
capture_output=True,
|
|
80
|
+
text=True,
|
|
81
|
+
)
|
|
82
|
+
if update_result.returncode != 0:
|
|
83
|
+
log(f" Warning: apt-get update failed: {update_result.stderr.strip()}")
|
|
84
|
+
# Continue anyway - packages might already be cached
|
|
85
|
+
|
|
86
|
+
log(f" Installing: {', '.join(packages)}")
|
|
87
|
+
install_cmd = ["sudo", "apt-get", "install", "-y"] + packages
|
|
88
|
+
install_result = subprocess.run(
|
|
89
|
+
install_cmd,
|
|
90
|
+
capture_output=True,
|
|
91
|
+
text=True,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if install_result.returncode != 0:
|
|
95
|
+
log(f" Warning: apt-get install failed: {install_result.stderr.strip()}")
|
|
96
|
+
log(f" Please install manually: sudo apt-get install {' '.join(packages)}")
|
|
97
|
+
return True # Don't fail - just warn
|
|
98
|
+
else:
|
|
99
|
+
log(" System packages installed successfully.")
|
|
100
|
+
return True
|
|
101
|
+
else:
|
|
102
|
+
log(" Warning: sudo not available. Cannot install system packages automatically.")
|
|
103
|
+
log(f" Please install manually: sudo apt-get install {' '.join(packages)}")
|
|
104
|
+
return True # Don't fail - just warn
|
|
105
|
+
|
|
106
|
+
except Exception as e:
|
|
107
|
+
log(f" Warning: Failed to install system packages: {e}")
|
|
108
|
+
log(f" Please install manually: sudo apt-get install {' '.join(packages)}")
|
|
109
|
+
return True # Don't fail - just warn
|
|
110
|
+
|
|
111
|
+
elif platform == "darwin":
|
|
112
|
+
packages = system_config.darwin
|
|
113
|
+
if not packages:
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
log(f"System packages for macOS: {', '.join(packages)}")
|
|
117
|
+
log(" Note: macOS system package installation not yet implemented.")
|
|
118
|
+
log(f" Please install manually with Homebrew: brew install {' '.join(packages)}")
|
|
119
|
+
return True
|
|
120
|
+
|
|
121
|
+
elif platform == "win32":
|
|
122
|
+
packages = system_config.windows
|
|
123
|
+
if not packages:
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
log(f"System packages for Windows: {', '.join(packages)}")
|
|
127
|
+
log(" Note: Windows system package installation not yet implemented.")
|
|
128
|
+
log(f" Please install manually.")
|
|
129
|
+
return True
|
|
130
|
+
|
|
131
|
+
return True
|
|
34
132
|
|
|
35
133
|
|
|
36
134
|
def install(
|
|
@@ -42,7 +140,7 @@ def install(
|
|
|
42
140
|
verify_wheels: bool = False,
|
|
43
141
|
) -> bool:
|
|
44
142
|
"""
|
|
45
|
-
Install dependencies from a
|
|
143
|
+
Install dependencies from a comfy-env.toml configuration.
|
|
46
144
|
|
|
47
145
|
This is the main entry point for installing CUDA dependencies.
|
|
48
146
|
|
|
@@ -67,7 +165,7 @@ def install(
|
|
|
67
165
|
install()
|
|
68
166
|
|
|
69
167
|
# Explicit config file
|
|
70
|
-
install(config="
|
|
168
|
+
install(config="comfy-env.toml")
|
|
71
169
|
|
|
72
170
|
# Isolated mode
|
|
73
171
|
install(mode="isolated")
|
|
@@ -83,19 +181,13 @@ def install(
|
|
|
83
181
|
if full_config is None:
|
|
84
182
|
raise FileNotFoundError(
|
|
85
183
|
"No configuration file found. "
|
|
86
|
-
"Create
|
|
184
|
+
"Create comfy-env.toml or specify path explicitly."
|
|
87
185
|
)
|
|
88
186
|
|
|
89
|
-
# Install
|
|
90
|
-
#
|
|
91
|
-
if full_config.
|
|
92
|
-
|
|
93
|
-
comfyui_root = node_dir.parent.parent # custom_nodes/../.. = ComfyUI/
|
|
94
|
-
for name, tool_config in full_config.tools.items():
|
|
95
|
-
if dry_run:
|
|
96
|
-
log(f" Would install {name} {tool_config.version}")
|
|
97
|
-
else:
|
|
98
|
-
install_tool(tool_config, log, comfyui_root)
|
|
187
|
+
# Install system packages first (apt, brew, etc.)
|
|
188
|
+
# These need to be installed before Python packages that depend on them
|
|
189
|
+
if full_config.has_system:
|
|
190
|
+
_install_system_packages(full_config.system, log, dry_run)
|
|
99
191
|
|
|
100
192
|
# Get environment config
|
|
101
193
|
env_config = full_config.default_env
|
|
@@ -40,7 +40,7 @@ _SHUTDOWN = object()
|
|
|
40
40
|
_CALL_METHOD = "call_method"
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
def _worker_loop(queue_in, queue_out):
|
|
43
|
+
def _worker_loop(queue_in, queue_out, sys_path_additions=None):
|
|
44
44
|
"""
|
|
45
45
|
Worker process main loop.
|
|
46
46
|
|
|
@@ -54,10 +54,22 @@ def _worker_loop(queue_in, queue_out):
|
|
|
54
54
|
import importlib
|
|
55
55
|
import os
|
|
56
56
|
import sys
|
|
57
|
+
from pathlib import Path
|
|
57
58
|
|
|
58
59
|
# Set worker mode env var
|
|
59
60
|
os.environ["COMFYUI_ISOLATION_WORKER"] = "1"
|
|
60
61
|
|
|
62
|
+
# Add stubs directory for folder_paths etc. (same as PersistentVenvWorker)
|
|
63
|
+
stubs_dir = Path(__file__).parent.parent / "stubs"
|
|
64
|
+
if str(stubs_dir) not in sys.path:
|
|
65
|
+
sys.path.insert(0, str(stubs_dir))
|
|
66
|
+
|
|
67
|
+
# Add custom paths to sys.path for module discovery
|
|
68
|
+
if sys_path_additions:
|
|
69
|
+
for path in sys_path_additions:
|
|
70
|
+
if path not in sys.path:
|
|
71
|
+
sys.path.insert(0, path)
|
|
72
|
+
|
|
61
73
|
while True:
|
|
62
74
|
try:
|
|
63
75
|
item = queue_in.get()
|
|
@@ -151,14 +163,16 @@ class TorchMPWorker(Worker):
|
|
|
151
163
|
interpreter without inherited state from the parent.
|
|
152
164
|
"""
|
|
153
165
|
|
|
154
|
-
def __init__(self, name: Optional[str] = None):
|
|
166
|
+
def __init__(self, name: Optional[str] = None, sys_path: Optional[list] = None):
|
|
155
167
|
"""
|
|
156
168
|
Initialize the worker.
|
|
157
169
|
|
|
158
170
|
Args:
|
|
159
171
|
name: Optional name for logging/debugging.
|
|
172
|
+
sys_path: Optional list of paths to add to sys.path in worker process.
|
|
160
173
|
"""
|
|
161
174
|
self.name = name or "TorchMPWorker"
|
|
175
|
+
self._sys_path = sys_path or []
|
|
162
176
|
self._process = None
|
|
163
177
|
self._queue_in = None
|
|
164
178
|
self._queue_out = None
|
|
@@ -185,7 +199,7 @@ class TorchMPWorker(Worker):
|
|
|
185
199
|
self._queue_out = ctx.Queue()
|
|
186
200
|
self._process = ctx.Process(
|
|
187
201
|
target=_worker_loop,
|
|
188
|
-
args=(self._queue_in, self._queue_out),
|
|
202
|
+
args=(self._queue_in, self._queue_out, self._sys_path),
|
|
189
203
|
daemon=True,
|
|
190
204
|
)
|
|
191
205
|
self._process.start()
|
comfy_env-0.0.17/CLAUDE.md
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code when working with this repository.
|
|
4
|
-
|
|
5
|
-
## Project Overview
|
|
6
|
-
|
|
7
|
-
**comfy-env** is a Python library for ComfyUI custom nodes that provides:
|
|
8
|
-
|
|
9
|
-
1. **CUDA Wheel Resolution** - Deterministic wheel URL construction for CUDA packages (nvdiffrast, pytorch3d, etc.)
|
|
10
|
-
2. **In-Place Installation** - Install CUDA wheels into current environment without compile
|
|
11
|
-
3. **Process Isolation** - Run nodes in separate venvs with different dependencies
|
|
12
|
-
|
|
13
|
-
## Architecture
|
|
14
|
-
|
|
15
|
-
### Type 1 Nodes (Isolated Venv)
|
|
16
|
-
Nodes that need separate venv due to conflicting dependencies:
|
|
17
|
-
```
|
|
18
|
-
ComfyUI Main Process Isolated Subprocess
|
|
19
|
-
┌─────────────────────────┐ ┌──────────────────────────┐
|
|
20
|
-
│ @isolated decorator │ │ runner.py entrypoint │
|
|
21
|
-
│ intercepts FUNCTION │ UDS/ │ │
|
|
22
|
-
│ method calls │ stdin ──► │ Imports node module │
|
|
23
|
-
│ │ │ (decorator is no-op) │
|
|
24
|
-
│ Tensor IPC via shared │ │ │
|
|
25
|
-
│ memory / CUDA IPC │ ◄──────────│ Returns result │
|
|
26
|
-
└─────────────────────────┘ └──────────────────────────┘
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### Type 2 Nodes (In-Place)
|
|
30
|
-
Nodes that just need CUDA wheels resolved:
|
|
31
|
-
```
|
|
32
|
-
comfy-env.toml
|
|
33
|
-
│
|
|
34
|
-
▼
|
|
35
|
-
┌──────────────────────────────────────────────┐
|
|
36
|
-
│ WheelResolver │
|
|
37
|
-
│ - Detects CUDA/PyTorch/Python versions │
|
|
38
|
-
│ - Constructs exact wheel URLs │
|
|
39
|
-
│ - pip install --no-deps │
|
|
40
|
-
└──────────────────────────────────────────────┘
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Key Components
|
|
44
|
-
|
|
45
|
-
| File | Purpose |
|
|
46
|
-
|------|---------|
|
|
47
|
-
| `src/comfy_env/install.py` | `install()` function for both modes |
|
|
48
|
-
| `src/comfy_env/resolver.py` | Wheel URL resolution with template expansion |
|
|
49
|
-
| `src/comfy_env/cli.py` | `comfy-env` CLI commands |
|
|
50
|
-
| `src/comfy_env/decorator.py` | `@isolated` decorator for process isolation |
|
|
51
|
-
| `src/comfy_env/workers/` | Worker classes (TorchMPWorker, VenvWorker) |
|
|
52
|
-
| `src/comfy_env/env/manager.py` | venv creation with `uv` |
|
|
53
|
-
| `src/comfy_env/env/config_file.py` | TOML config parsing |
|
|
54
|
-
|
|
55
|
-
## Development Commands
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
# Install in development mode
|
|
59
|
-
cd /home/shadeform/comfy-env
|
|
60
|
-
pip install -e .
|
|
61
|
-
|
|
62
|
-
# Run CLI
|
|
63
|
-
comfy-env info
|
|
64
|
-
comfy-env install --dry-run
|
|
65
|
-
comfy-env resolve nvdiffrast==0.4.0
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Usage Patterns
|
|
69
|
-
|
|
70
|
-
### In-Place Installation (Type 2)
|
|
71
|
-
```python
|
|
72
|
-
from comfy_env import install
|
|
73
|
-
|
|
74
|
-
# Auto-discover config and install
|
|
75
|
-
install()
|
|
76
|
-
|
|
77
|
-
# Dry run
|
|
78
|
-
install(dry_run=True)
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Process Isolation (Type 1)
|
|
82
|
-
```python
|
|
83
|
-
from comfy_env import isolated
|
|
84
|
-
|
|
85
|
-
@isolated(env="myenv")
|
|
86
|
-
class MyGPUNode:
|
|
87
|
-
FUNCTION = "process"
|
|
88
|
-
RETURN_TYPES = ("IMAGE",)
|
|
89
|
-
|
|
90
|
-
def process(self, image):
|
|
91
|
-
# Runs in isolated subprocess
|
|
92
|
-
import heavy_dependency
|
|
93
|
-
return (result,)
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Direct Worker Usage
|
|
97
|
-
```python
|
|
98
|
-
from comfy_env import TorchMPWorker
|
|
99
|
-
|
|
100
|
-
worker = TorchMPWorker()
|
|
101
|
-
result = worker.call(my_function, image=tensor)
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Config File Format
|
|
105
|
-
|
|
106
|
-
```toml
|
|
107
|
-
[env]
|
|
108
|
-
name = "my-node"
|
|
109
|
-
python = "3.10"
|
|
110
|
-
cuda = "auto"
|
|
111
|
-
|
|
112
|
-
[packages]
|
|
113
|
-
requirements = ["transformers>=4.56"]
|
|
114
|
-
no_deps = ["nvdiffrast==0.4.0"]
|
|
115
|
-
|
|
116
|
-
[sources]
|
|
117
|
-
wheel_sources = ["https://github.com/.../releases/download/"]
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Key Design Decisions
|
|
121
|
-
|
|
122
|
-
1. **Deterministic Resolution**: Wheel URLs are constructed, not solved. If URL 404s, fail fast with clear message.
|
|
123
|
-
|
|
124
|
-
2. **No Compilation on User Machines**: If wheel doesn't exist, fail with actionable error showing what combos are available.
|
|
125
|
-
|
|
126
|
-
3. **Template Variables**: `{cuda_short}`, `{torch_mm}`, `{py_short}`, `{platform}` for URL construction.
|
|
127
|
-
|
|
128
|
-
## Related Projects
|
|
129
|
-
|
|
130
|
-
- **pyisolate** - ComfyUI's official security-focused isolation
|
|
131
|
-
- **comfy-cli** - High-level ComfyUI management
|