comfy-env 0.1.14__py3-none-any.whl → 0.1.16__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 +115 -62
- comfy_env/cli.py +89 -319
- comfy_env/config/__init__.py +18 -8
- comfy_env/config/parser.py +21 -122
- comfy_env/config/types.py +37 -70
- comfy_env/detection/__init__.py +77 -0
- comfy_env/detection/cuda.py +61 -0
- comfy_env/detection/gpu.py +230 -0
- comfy_env/detection/platform.py +70 -0
- comfy_env/detection/runtime.py +103 -0
- comfy_env/environment/__init__.py +53 -0
- comfy_env/environment/cache.py +141 -0
- comfy_env/environment/libomp.py +41 -0
- comfy_env/environment/paths.py +38 -0
- comfy_env/environment/setup.py +88 -0
- comfy_env/install.py +163 -249
- comfy_env/isolation/__init__.py +33 -2
- comfy_env/isolation/tensor_utils.py +83 -0
- comfy_env/isolation/workers/__init__.py +16 -0
- comfy_env/{workers → isolation/workers}/mp.py +1 -1
- comfy_env/{workers → isolation/workers}/subprocess.py +2 -2
- comfy_env/isolation/wrap.py +149 -409
- comfy_env/packages/__init__.py +60 -0
- comfy_env/packages/apt.py +36 -0
- comfy_env/packages/cuda_wheels.py +97 -0
- comfy_env/packages/node_dependencies.py +77 -0
- comfy_env/packages/pixi.py +85 -0
- comfy_env/packages/toml_generator.py +88 -0
- comfy_env-0.1.16.dist-info/METADATA +279 -0
- comfy_env-0.1.16.dist-info/RECORD +36 -0
- comfy_env/cache.py +0 -331
- comfy_env/errors.py +0 -293
- comfy_env/nodes.py +0 -187
- comfy_env/pixi/__init__.py +0 -48
- comfy_env/pixi/core.py +0 -588
- comfy_env/pixi/cuda_detection.py +0 -303
- comfy_env/pixi/platform/__init__.py +0 -21
- comfy_env/pixi/platform/base.py +0 -96
- comfy_env/pixi/platform/darwin.py +0 -53
- comfy_env/pixi/platform/linux.py +0 -68
- comfy_env/pixi/platform/windows.py +0 -284
- comfy_env/pixi/resolver.py +0 -198
- comfy_env/prestartup.py +0 -192
- comfy_env/workers/__init__.py +0 -38
- comfy_env/workers/tensor_utils.py +0 -188
- comfy_env-0.1.14.dist-info/METADATA +0 -291
- comfy_env-0.1.14.dist-info/RECORD +0 -33
- /comfy_env/{workers → isolation/workers}/base.py +0 -0
- {comfy_env-0.1.14.dist-info → comfy_env-0.1.16.dist-info}/WHEEL +0 -0
- {comfy_env-0.1.14.dist-info → comfy_env-0.1.16.dist-info}/entry_points.txt +0 -0
- {comfy_env-0.1.14.dist-info → comfy_env-0.1.16.dist-info}/licenses/LICENSE +0 -0
comfy_env/errors.py
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Rich error messages for comfy-env.
|
|
3
|
-
|
|
4
|
-
This module provides error classes with actionable, user-friendly messages.
|
|
5
|
-
Instead of cryptic pip errors, users see exactly what went wrong and what
|
|
6
|
-
they can do to fix it.
|
|
7
|
-
|
|
8
|
-
Example output:
|
|
9
|
-
|
|
10
|
-
+------------------------------------------------------------------+
|
|
11
|
-
| CUDA Wheel Not Found |
|
|
12
|
-
+------------------------------------------------------------------+
|
|
13
|
-
| Package: nvdiffrast==0.4.0 |
|
|
14
|
-
| Requested: cu130-torch291-cp312-linux_x86_64 |
|
|
15
|
-
| |
|
|
16
|
-
| Suggestions: |
|
|
17
|
-
| 1. Use Python 3.10 instead of 3.12 |
|
|
18
|
-
| 2. Use CUDA 12.8 (set cuda = "12.8" in config) |
|
|
19
|
-
| 3. Build wheel locally: comfy-env build nvdiffrast |
|
|
20
|
-
+------------------------------------------------------------------+
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
from dataclasses import dataclass, field
|
|
24
|
-
from typing import List, Optional, TYPE_CHECKING
|
|
25
|
-
|
|
26
|
-
if TYPE_CHECKING:
|
|
27
|
-
from .resolver import RuntimeEnv
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class EnvManagerError(Exception):
|
|
31
|
-
"""Base exception for comfy-env errors."""
|
|
32
|
-
|
|
33
|
-
def __init__(self, message: str, details: Optional[str] = None):
|
|
34
|
-
self.message = message
|
|
35
|
-
self.details = details
|
|
36
|
-
super().__init__(self.format())
|
|
37
|
-
|
|
38
|
-
def format(self) -> str:
|
|
39
|
-
"""Format error message for display."""
|
|
40
|
-
if self.details:
|
|
41
|
-
return f"{self.message}\n\n{self.details}"
|
|
42
|
-
return self.message
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class ConfigError(EnvManagerError):
|
|
46
|
-
"""Error in configuration file."""
|
|
47
|
-
|
|
48
|
-
def __init__(self, message: str, file_path: Optional[str] = None, line: Optional[int] = None):
|
|
49
|
-
self.file_path = file_path
|
|
50
|
-
self.line = line
|
|
51
|
-
|
|
52
|
-
details = None
|
|
53
|
-
if file_path:
|
|
54
|
-
location = f"in {file_path}"
|
|
55
|
-
if line:
|
|
56
|
-
location += f" at line {line}"
|
|
57
|
-
details = location
|
|
58
|
-
|
|
59
|
-
super().__init__(message, details)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
@dataclass
|
|
63
|
-
class WheelNotFoundError(EnvManagerError):
|
|
64
|
-
"""
|
|
65
|
-
Raised when a CUDA wheel cannot be found or resolved.
|
|
66
|
-
|
|
67
|
-
Provides actionable suggestions based on the environment and what
|
|
68
|
-
wheels are typically available.
|
|
69
|
-
"""
|
|
70
|
-
package: str
|
|
71
|
-
version: Optional[str] = None
|
|
72
|
-
env: Optional["RuntimeEnv"] = None
|
|
73
|
-
tried_urls: List[str] = field(default_factory=list)
|
|
74
|
-
reason: Optional[str] = None
|
|
75
|
-
available_combos: List[str] = field(default_factory=list)
|
|
76
|
-
|
|
77
|
-
def __post_init__(self):
|
|
78
|
-
# Build the formatted message
|
|
79
|
-
self.message = self._build_message()
|
|
80
|
-
self.details = self._build_details()
|
|
81
|
-
Exception.__init__(self, self.format())
|
|
82
|
-
|
|
83
|
-
def _build_message(self) -> str:
|
|
84
|
-
"""Build the main error message."""
|
|
85
|
-
pkg = self.package
|
|
86
|
-
if self.version:
|
|
87
|
-
pkg = f"{self.package}=={self.version}"
|
|
88
|
-
return f"CUDA wheel not found: {pkg}"
|
|
89
|
-
|
|
90
|
-
def _build_details(self) -> str:
|
|
91
|
-
"""Build detailed error with suggestions."""
|
|
92
|
-
lines = []
|
|
93
|
-
|
|
94
|
-
# Box header
|
|
95
|
-
lines.append("+" + "-" * 66 + "+")
|
|
96
|
-
lines.append("| CUDA Wheel Not Found" + " " * 44 + "|")
|
|
97
|
-
lines.append("+" + "-" * 66 + "+")
|
|
98
|
-
|
|
99
|
-
# Package info
|
|
100
|
-
pkg_line = f" Package: {self.package}"
|
|
101
|
-
if self.version:
|
|
102
|
-
pkg_line += f"=={self.version}"
|
|
103
|
-
lines.append(f"|{pkg_line:<66}|")
|
|
104
|
-
|
|
105
|
-
# Requested configuration
|
|
106
|
-
if self.env:
|
|
107
|
-
requested = self._format_requested()
|
|
108
|
-
lines.append(f"| Requested: {requested:<54}|")
|
|
109
|
-
|
|
110
|
-
lines.append("|" + " " * 66 + "|")
|
|
111
|
-
|
|
112
|
-
# Reason if provided
|
|
113
|
-
if self.reason:
|
|
114
|
-
lines.append(f"| Reason: {self.reason:<57}|")
|
|
115
|
-
lines.append("|" + " " * 66 + "|")
|
|
116
|
-
|
|
117
|
-
# Tried URLs
|
|
118
|
-
if self.tried_urls:
|
|
119
|
-
lines.append("| Tried URLs:" + " " * 53 + "|")
|
|
120
|
-
for url in self.tried_urls[:3]: # Limit to first 3
|
|
121
|
-
# Truncate long URLs
|
|
122
|
-
if len(url) > 60:
|
|
123
|
-
url = "..." + url[-57:]
|
|
124
|
-
lines.append(f"| {url:<62}|")
|
|
125
|
-
lines.append("|" + " " * 66 + "|")
|
|
126
|
-
|
|
127
|
-
# Suggestions
|
|
128
|
-
suggestions = self._generate_suggestions()
|
|
129
|
-
if suggestions:
|
|
130
|
-
lines.append("| Suggestions:" + " " * 52 + "|")
|
|
131
|
-
for i, suggestion in enumerate(suggestions, 1):
|
|
132
|
-
lines.append(f"| {i}. {suggestion:<60}|")
|
|
133
|
-
lines.append("|" + " " * 66 + "|")
|
|
134
|
-
|
|
135
|
-
# Footer
|
|
136
|
-
lines.append("+" + "-" * 66 + "+")
|
|
137
|
-
|
|
138
|
-
return "\n".join(lines)
|
|
139
|
-
|
|
140
|
-
def _format_requested(self) -> str:
|
|
141
|
-
"""Format the requested configuration."""
|
|
142
|
-
if not self.env:
|
|
143
|
-
return "unknown"
|
|
144
|
-
|
|
145
|
-
parts = []
|
|
146
|
-
if self.env.cuda_short:
|
|
147
|
-
parts.append(f"cu{self.env.cuda_short}")
|
|
148
|
-
else:
|
|
149
|
-
parts.append("cpu")
|
|
150
|
-
|
|
151
|
-
if self.env.torch_mm:
|
|
152
|
-
parts.append(f"torch{self.env.torch_mm}")
|
|
153
|
-
|
|
154
|
-
parts.append(f"cp{self.env.python_short}")
|
|
155
|
-
parts.append(self.env.platform_tag)
|
|
156
|
-
|
|
157
|
-
return "-".join(parts)
|
|
158
|
-
|
|
159
|
-
def _generate_suggestions(self) -> List[str]:
|
|
160
|
-
"""Generate actionable suggestions based on the error context."""
|
|
161
|
-
suggestions = []
|
|
162
|
-
|
|
163
|
-
if not self.env:
|
|
164
|
-
suggestions.append("Run 'comfy-env info' to see your environment")
|
|
165
|
-
return suggestions
|
|
166
|
-
|
|
167
|
-
# Python version suggestion
|
|
168
|
-
if self.env.python_short not in ("310", "311"):
|
|
169
|
-
suggestions.append(
|
|
170
|
-
f"Use Python 3.10 or 3.11 (you have {self.env.python_version})"
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
# CUDA version suggestion
|
|
174
|
-
if self.env.cuda_version and self.env.cuda_version not in ("12.4", "12.8"):
|
|
175
|
-
suggestions.append(
|
|
176
|
-
f"Use CUDA 12.4 or 12.8 (you have {self.env.cuda_version})"
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
# PyTorch version suggestion
|
|
180
|
-
if self.env.torch_version:
|
|
181
|
-
torch_major_minor = ".".join(self.env.torch_version.split(".")[:2])
|
|
182
|
-
if torch_major_minor not in ("2.5", "2.8"):
|
|
183
|
-
suggestions.append(
|
|
184
|
-
f"Use PyTorch 2.5 or 2.8 (you have {self.env.torch_version})"
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
# General suggestions
|
|
188
|
-
suggestions.append(
|
|
189
|
-
f"Check if wheel exists: comfy-env resolve {self.package}"
|
|
190
|
-
)
|
|
191
|
-
suggestions.append(
|
|
192
|
-
f"Build wheel locally: comfy-env build {self.package}"
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
return suggestions[:4] # Limit to 4 suggestions
|
|
196
|
-
|
|
197
|
-
def format(self) -> str:
|
|
198
|
-
"""Format the complete error message."""
|
|
199
|
-
return f"{self.message}\n\n{self.details}"
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
class DependencyError(EnvManagerError):
|
|
203
|
-
"""Error resolving or installing dependencies."""
|
|
204
|
-
|
|
205
|
-
def __init__(
|
|
206
|
-
self,
|
|
207
|
-
message: str,
|
|
208
|
-
package: Optional[str] = None,
|
|
209
|
-
pip_error: Optional[str] = None,
|
|
210
|
-
):
|
|
211
|
-
self.package = package
|
|
212
|
-
self.pip_error = pip_error
|
|
213
|
-
|
|
214
|
-
details = None
|
|
215
|
-
if pip_error:
|
|
216
|
-
# Extract relevant lines from pip error
|
|
217
|
-
relevant_lines = self._extract_pip_error(pip_error)
|
|
218
|
-
if relevant_lines:
|
|
219
|
-
details = "pip error:\n" + "\n".join(relevant_lines)
|
|
220
|
-
|
|
221
|
-
super().__init__(message, details)
|
|
222
|
-
|
|
223
|
-
def _extract_pip_error(self, pip_error: str) -> List[str]:
|
|
224
|
-
"""Extract the most relevant lines from pip error output."""
|
|
225
|
-
lines = pip_error.strip().split("\n")
|
|
226
|
-
relevant = []
|
|
227
|
-
|
|
228
|
-
for line in lines:
|
|
229
|
-
# Skip empty lines and progress bars
|
|
230
|
-
if not line.strip():
|
|
231
|
-
continue
|
|
232
|
-
if line.startswith(" ") and "%" in line:
|
|
233
|
-
continue
|
|
234
|
-
|
|
235
|
-
# Keep error lines and important info
|
|
236
|
-
lower = line.lower()
|
|
237
|
-
if any(keyword in lower for keyword in [
|
|
238
|
-
"error", "failed", "could not", "no matching",
|
|
239
|
-
"requirement", "conflict", "incompatible"
|
|
240
|
-
]):
|
|
241
|
-
relevant.append(line)
|
|
242
|
-
|
|
243
|
-
return relevant[:10] # Limit to 10 lines
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
class CUDANotFoundError(EnvManagerError):
|
|
247
|
-
"""Raised when CUDA is required but not available."""
|
|
248
|
-
|
|
249
|
-
def __init__(self, package: Optional[str] = None):
|
|
250
|
-
message = "CUDA is required but not detected"
|
|
251
|
-
details_lines = [
|
|
252
|
-
"This package requires a CUDA-capable GPU.",
|
|
253
|
-
"",
|
|
254
|
-
"To fix this:",
|
|
255
|
-
" 1. Ensure you have an NVIDIA GPU",
|
|
256
|
-
" 2. Install NVIDIA drivers",
|
|
257
|
-
" 3. Install CUDA Toolkit",
|
|
258
|
-
"",
|
|
259
|
-
"Or if you want to run on CPU:",
|
|
260
|
-
" Set 'fallback_to_cpu = true' in your config",
|
|
261
|
-
]
|
|
262
|
-
|
|
263
|
-
if package:
|
|
264
|
-
message = f"CUDA is required for {package} but not detected"
|
|
265
|
-
|
|
266
|
-
super().__init__(message, "\n".join(details_lines))
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
class InstallError(EnvManagerError):
|
|
270
|
-
"""Error during package installation."""
|
|
271
|
-
|
|
272
|
-
def __init__(
|
|
273
|
-
self,
|
|
274
|
-
message: str,
|
|
275
|
-
package: Optional[str] = None,
|
|
276
|
-
exit_code: Optional[int] = None,
|
|
277
|
-
stderr: Optional[str] = None,
|
|
278
|
-
):
|
|
279
|
-
self.package = package
|
|
280
|
-
self.exit_code = exit_code
|
|
281
|
-
self.stderr = stderr
|
|
282
|
-
|
|
283
|
-
details_parts = []
|
|
284
|
-
if exit_code is not None:
|
|
285
|
-
details_parts.append(f"Exit code: {exit_code}")
|
|
286
|
-
if stderr:
|
|
287
|
-
# Truncate long stderr
|
|
288
|
-
if len(stderr) > 500:
|
|
289
|
-
stderr = stderr[:500] + "\n... (truncated)"
|
|
290
|
-
details_parts.append(f"Output:\n{stderr}")
|
|
291
|
-
|
|
292
|
-
details = "\n".join(details_parts) if details_parts else None
|
|
293
|
-
super().__init__(message, details)
|
comfy_env/nodes.py
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Node dependency installation for comfy-env.
|
|
3
|
-
|
|
4
|
-
This module handles installation of dependent ComfyUI custom nodes
|
|
5
|
-
specified in the [node_reqs] section of comfy-env.toml.
|
|
6
|
-
|
|
7
|
-
Example configuration:
|
|
8
|
-
[node_reqs]
|
|
9
|
-
ComfyUI_essentials = "cubiq/ComfyUI_essentials"
|
|
10
|
-
ComfyUI-DepthAnythingV2 = "kijai/ComfyUI-DepthAnythingV2"
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
import shutil
|
|
14
|
-
import subprocess
|
|
15
|
-
import sys
|
|
16
|
-
from pathlib import Path
|
|
17
|
-
from typing import TYPE_CHECKING, Callable, List, Set
|
|
18
|
-
|
|
19
|
-
if TYPE_CHECKING:
|
|
20
|
-
from .config.types import NodeReq
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def normalize_repo_url(repo: str) -> str:
|
|
24
|
-
"""
|
|
25
|
-
Convert GitHub shorthand to full URL.
|
|
26
|
-
|
|
27
|
-
Args:
|
|
28
|
-
repo: Either 'owner/repo' or full URL like 'https://github.com/owner/repo'
|
|
29
|
-
|
|
30
|
-
Returns:
|
|
31
|
-
Full GitHub URL
|
|
32
|
-
"""
|
|
33
|
-
if repo.startswith("http://") or repo.startswith("https://"):
|
|
34
|
-
return repo
|
|
35
|
-
return f"https://github.com/{repo}"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def clone_node(
|
|
39
|
-
repo: str,
|
|
40
|
-
name: str,
|
|
41
|
-
target_dir: Path,
|
|
42
|
-
log: Callable[[str], None],
|
|
43
|
-
) -> Path:
|
|
44
|
-
"""
|
|
45
|
-
Clone a node repository to target_dir/name.
|
|
46
|
-
|
|
47
|
-
Args:
|
|
48
|
-
repo: GitHub repo path (e.g., 'owner/repo') or full URL
|
|
49
|
-
name: Directory name for the cloned repo
|
|
50
|
-
target_dir: Parent directory (usually custom_nodes/)
|
|
51
|
-
log: Logging callback
|
|
52
|
-
|
|
53
|
-
Returns:
|
|
54
|
-
Path to the cloned node directory
|
|
55
|
-
|
|
56
|
-
Raises:
|
|
57
|
-
RuntimeError: If git clone fails
|
|
58
|
-
"""
|
|
59
|
-
node_path = target_dir / name
|
|
60
|
-
url = normalize_repo_url(repo)
|
|
61
|
-
|
|
62
|
-
log(f" Cloning {name} from {url}...")
|
|
63
|
-
result = subprocess.run(
|
|
64
|
-
["git", "clone", "--depth", "1", url, str(node_path)],
|
|
65
|
-
capture_output=True,
|
|
66
|
-
text=True,
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
if result.returncode != 0:
|
|
70
|
-
raise RuntimeError(f"Failed to clone {url}: {result.stderr.strip()}")
|
|
71
|
-
|
|
72
|
-
return node_path
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def install_requirements(
|
|
76
|
-
node_dir: Path,
|
|
77
|
-
log: Callable[[str], None],
|
|
78
|
-
) -> None:
|
|
79
|
-
"""
|
|
80
|
-
Install requirements.txt in a node directory if it exists.
|
|
81
|
-
|
|
82
|
-
Args:
|
|
83
|
-
node_dir: Path to the node directory
|
|
84
|
-
log: Logging callback
|
|
85
|
-
"""
|
|
86
|
-
requirements_file = node_dir / "requirements.txt"
|
|
87
|
-
|
|
88
|
-
if not requirements_file.exists():
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
log(f" Installing requirements for {node_dir.name}...")
|
|
92
|
-
|
|
93
|
-
# Try uv first, fall back to pip if uv not in PATH
|
|
94
|
-
if shutil.which("uv"):
|
|
95
|
-
cmd = ["uv", "pip", "install", "-r", str(requirements_file), "--python", sys.executable]
|
|
96
|
-
else:
|
|
97
|
-
cmd = [sys.executable, "-m", "pip", "install", "-r", str(requirements_file)]
|
|
98
|
-
|
|
99
|
-
result = subprocess.run(cmd, cwd=node_dir, capture_output=True, text=True)
|
|
100
|
-
if result.returncode != 0:
|
|
101
|
-
log(f" Warning: requirements.txt install failed for {node_dir.name}: {result.stderr.strip()[:200]}")
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def run_install_script(
|
|
105
|
-
node_dir: Path,
|
|
106
|
-
log: Callable[[str], None],
|
|
107
|
-
) -> None:
|
|
108
|
-
"""
|
|
109
|
-
Run install.py in a node directory if it exists.
|
|
110
|
-
|
|
111
|
-
Args:
|
|
112
|
-
node_dir: Path to the node directory
|
|
113
|
-
log: Logging callback
|
|
114
|
-
"""
|
|
115
|
-
install_script = node_dir / "install.py"
|
|
116
|
-
|
|
117
|
-
if install_script.exists():
|
|
118
|
-
log(f" Running install.py for {node_dir.name}...")
|
|
119
|
-
result = subprocess.run(
|
|
120
|
-
[sys.executable, str(install_script)],
|
|
121
|
-
cwd=node_dir,
|
|
122
|
-
capture_output=True,
|
|
123
|
-
text=True,
|
|
124
|
-
)
|
|
125
|
-
if result.returncode != 0:
|
|
126
|
-
log(f" Warning: install.py failed for {node_dir.name}: {result.stderr.strip()[:200]}")
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def install_node_deps(
|
|
130
|
-
node_reqs: "List[NodeReq]",
|
|
131
|
-
custom_nodes_dir: Path,
|
|
132
|
-
log: Callable[[str], None],
|
|
133
|
-
visited: Set[str],
|
|
134
|
-
) -> None:
|
|
135
|
-
"""
|
|
136
|
-
Install node dependencies recursively.
|
|
137
|
-
|
|
138
|
-
Args:
|
|
139
|
-
node_reqs: List of NodeReq objects to install
|
|
140
|
-
custom_nodes_dir: Path to custom_nodes directory
|
|
141
|
-
log: Logging callback
|
|
142
|
-
visited: Set of already-processed node names (for cycle detection)
|
|
143
|
-
"""
|
|
144
|
-
from .config.parser import discover_config
|
|
145
|
-
|
|
146
|
-
for req in node_reqs:
|
|
147
|
-
# Skip if already visited (cycle detection)
|
|
148
|
-
if req.name in visited:
|
|
149
|
-
log(f" {req.name}: already in dependency chain, skipping")
|
|
150
|
-
continue
|
|
151
|
-
|
|
152
|
-
visited.add(req.name)
|
|
153
|
-
|
|
154
|
-
node_path = custom_nodes_dir / req.name
|
|
155
|
-
|
|
156
|
-
# Skip if already installed (directory exists)
|
|
157
|
-
if node_path.exists():
|
|
158
|
-
log(f" {req.name}: already installed, skipping")
|
|
159
|
-
continue
|
|
160
|
-
|
|
161
|
-
try:
|
|
162
|
-
# Clone the repository
|
|
163
|
-
clone_node(req.repo, req.name, custom_nodes_dir, log)
|
|
164
|
-
|
|
165
|
-
# Install requirements.txt if present
|
|
166
|
-
install_requirements(node_path, log)
|
|
167
|
-
|
|
168
|
-
# Run install.py if present
|
|
169
|
-
run_install_script(node_path, log)
|
|
170
|
-
|
|
171
|
-
# Recursively process nested node_reqs
|
|
172
|
-
try:
|
|
173
|
-
nested_config = discover_config(node_path)
|
|
174
|
-
if nested_config and nested_config.node_reqs:
|
|
175
|
-
log(f" {req.name}: found {len(nested_config.node_reqs)} nested dependencies")
|
|
176
|
-
install_node_deps(
|
|
177
|
-
nested_config.node_reqs,
|
|
178
|
-
custom_nodes_dir,
|
|
179
|
-
log,
|
|
180
|
-
visited,
|
|
181
|
-
)
|
|
182
|
-
except Exception as e:
|
|
183
|
-
# Don't fail if we can't parse nested config
|
|
184
|
-
log(f" {req.name}: could not check for nested deps: {e}")
|
|
185
|
-
|
|
186
|
-
except Exception as e:
|
|
187
|
-
log(f" Warning: Failed to install {req.name}: {e}")
|
comfy_env/pixi/__init__.py
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Pixi integration for comfy-env.
|
|
3
|
-
|
|
4
|
-
All dependencies go through pixi for unified management.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from .core import (
|
|
8
|
-
ensure_pixi,
|
|
9
|
-
get_pixi_path,
|
|
10
|
-
get_pixi_python,
|
|
11
|
-
pixi_run,
|
|
12
|
-
pixi_install,
|
|
13
|
-
clean_pixi_artifacts,
|
|
14
|
-
CUDA_WHEELS_INDEX,
|
|
15
|
-
)
|
|
16
|
-
from .cuda_detection import (
|
|
17
|
-
detect_cuda_version,
|
|
18
|
-
detect_cuda_environment,
|
|
19
|
-
detect_gpu_info,
|
|
20
|
-
detect_gpus,
|
|
21
|
-
get_gpu_summary,
|
|
22
|
-
get_recommended_cuda_version,
|
|
23
|
-
GPUInfo,
|
|
24
|
-
CUDAEnvironment,
|
|
25
|
-
)
|
|
26
|
-
from .resolver import RuntimeEnv
|
|
27
|
-
|
|
28
|
-
__all__ = [
|
|
29
|
-
# Core pixi functions
|
|
30
|
-
"ensure_pixi",
|
|
31
|
-
"get_pixi_path",
|
|
32
|
-
"get_pixi_python",
|
|
33
|
-
"pixi_run",
|
|
34
|
-
"pixi_install",
|
|
35
|
-
"clean_pixi_artifacts",
|
|
36
|
-
"CUDA_WHEELS_INDEX",
|
|
37
|
-
# CUDA detection
|
|
38
|
-
"detect_cuda_version",
|
|
39
|
-
"detect_cuda_environment",
|
|
40
|
-
"detect_gpu_info",
|
|
41
|
-
"detect_gpus",
|
|
42
|
-
"get_gpu_summary",
|
|
43
|
-
"get_recommended_cuda_version",
|
|
44
|
-
"GPUInfo",
|
|
45
|
-
"CUDAEnvironment",
|
|
46
|
-
# Resolver
|
|
47
|
-
"RuntimeEnv",
|
|
48
|
-
]
|