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.
Files changed (50) hide show
  1. {comfy_env-0.0.17 → comfy_env-0.0.19}/PKG-INFO +1 -1
  2. {comfy_env-0.0.17 → comfy_env-0.0.19}/pyproject.toml +1 -1
  3. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/cli.py +2 -2
  4. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/decorator.py +6 -1
  5. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/config.py +20 -1
  6. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/config_file.py +36 -3
  7. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/install.py +109 -17
  8. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/workers/torch_mp.py +17 -3
  9. comfy_env-0.0.17/CLAUDE.md +0 -131
  10. comfy_env-0.0.17/CRITICISM.md +0 -316
  11. comfy_env-0.0.17/examples/basic_node/__init__.py +0 -5
  12. comfy_env-0.0.17/examples/basic_node/comfy-env.toml +0 -65
  13. comfy_env-0.0.17/examples/basic_node/nodes.py +0 -157
  14. comfy_env-0.0.17/examples/basic_node/worker.py +0 -79
  15. comfy_env-0.0.17/examples/decorator_node/__init__.py +0 -9
  16. comfy_env-0.0.17/examples/decorator_node/nodes.py +0 -182
  17. comfy_env-0.0.17/src/comfy_env/tools.py +0 -221
  18. {comfy_env-0.0.17 → comfy_env-0.0.19}/.github/workflows/publish.yml +0 -0
  19. {comfy_env-0.0.17 → comfy_env-0.0.19}/.gitignore +0 -0
  20. {comfy_env-0.0.17 → comfy_env-0.0.19}/LICENSE +0 -0
  21. {comfy_env-0.0.17 → comfy_env-0.0.19}/README.md +0 -0
  22. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/__init__.py +0 -0
  23. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/__init__.py +0 -0
  24. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/cuda_gpu_detection.py +0 -0
  25. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/manager.py +0 -0
  26. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/platform/__init__.py +0 -0
  27. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/platform/base.py +0 -0
  28. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/platform/darwin.py +0 -0
  29. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/platform/linux.py +0 -0
  30. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/platform/windows.py +0 -0
  31. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/env/security.py +0 -0
  32. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/errors.py +0 -0
  33. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/__init__.py +0 -0
  34. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/bridge.py +0 -0
  35. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/protocol.py +0 -0
  36. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/tensor.py +0 -0
  37. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/torch_bridge.py +0 -0
  38. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/transport.py +0 -0
  39. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/ipc/worker.py +0 -0
  40. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/registry.py +0 -0
  41. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/resolver.py +0 -0
  42. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/stubs/__init__.py +0 -0
  43. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/stubs/folder_paths.py +0 -0
  44. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/wheel_sources.yml +0 -0
  45. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/workers/__init__.py +0 -0
  46. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/workers/base.py +0 -0
  47. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/workers/pool.py +0 -0
  48. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/workers/tensor_utils.py +0 -0
  49. {comfy_env-0.0.17 → comfy_env-0.0.19}/src/comfy_env/workers/venv.py +0 -0
  50. {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.17
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "comfy-env"
3
- version = "0.0.17"
3
+ version = "0.0.19"
4
4
  description = "Environment management for ComfyUI custom nodes - CUDA wheel resolution and process isolation"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -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 comfyui_env.toml",
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 comfyui_env.toml:")
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 comfyui_env.toml.
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 section (shared between simple and full format)
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="comfyui_env.toml", mode="inplace")
15
+ install(config="comfy-env.toml", mode="inplace")
16
16
 
17
17
  # Isolated environment
18
- install(config="comfyui_env.toml", mode="isolated")
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, ToolConfig
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
- from .tools import install_tool
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 comfyui_env.toml configuration.
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="comfyui_env.toml")
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 comfyui_env.toml or specify path explicitly."
184
+ "Create comfy-env.toml or specify path explicitly."
87
185
  )
88
186
 
89
- # Install tools first (e.g., Blender)
90
- # Tools are installed to ComfyUI root (shared across all nodes)
91
- if full_config.tools:
92
- log(f"Installing {len(full_config.tools)} tool(s)...")
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()
@@ -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