comfy-env 0.0.64__py3-none-any.whl → 0.0.66__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. comfy_env/__init__.py +70 -122
  2. comfy_env/cli.py +78 -7
  3. comfy_env/config/__init__.py +19 -0
  4. comfy_env/config/parser.py +151 -0
  5. comfy_env/config/types.py +64 -0
  6. comfy_env/install.py +83 -361
  7. comfy_env/isolation/__init__.py +9 -0
  8. comfy_env/isolation/wrap.py +351 -0
  9. comfy_env/nodes.py +2 -2
  10. comfy_env/pixi/__init__.py +48 -0
  11. comfy_env/pixi/core.py +356 -0
  12. comfy_env/{resolver.py → pixi/resolver.py} +1 -14
  13. comfy_env/prestartup.py +60 -0
  14. comfy_env/templates/comfy-env-instructions.txt +30 -87
  15. comfy_env/templates/comfy-env.toml +68 -136
  16. comfy_env/workers/__init__.py +21 -32
  17. comfy_env/workers/base.py +1 -1
  18. comfy_env/workers/{torch_mp.py → mp.py} +47 -14
  19. comfy_env/workers/{venv.py → subprocess.py} +405 -441
  20. {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/METADATA +2 -1
  21. comfy_env-0.0.66.dist-info/RECORD +34 -0
  22. comfy_env/decorator.py +0 -700
  23. comfy_env/env/__init__.py +0 -47
  24. comfy_env/env/config.py +0 -201
  25. comfy_env/env/config_file.py +0 -740
  26. comfy_env/env/manager.py +0 -636
  27. comfy_env/env/security.py +0 -267
  28. comfy_env/ipc/__init__.py +0 -55
  29. comfy_env/ipc/bridge.py +0 -476
  30. comfy_env/ipc/protocol.py +0 -265
  31. comfy_env/ipc/tensor.py +0 -371
  32. comfy_env/ipc/torch_bridge.py +0 -401
  33. comfy_env/ipc/transport.py +0 -318
  34. comfy_env/ipc/worker.py +0 -221
  35. comfy_env/isolation.py +0 -310
  36. comfy_env/pixi.py +0 -760
  37. comfy_env/stub_imports.py +0 -270
  38. comfy_env/stubs/__init__.py +0 -1
  39. comfy_env/stubs/comfy/__init__.py +0 -6
  40. comfy_env/stubs/comfy/model_management.py +0 -58
  41. comfy_env/stubs/comfy/utils.py +0 -29
  42. comfy_env/stubs/folder_paths.py +0 -71
  43. comfy_env/workers/pool.py +0 -241
  44. comfy_env-0.0.64.dist-info/RECORD +0 -48
  45. /comfy_env/{env/cuda_gpu_detection.py → pixi/cuda_detection.py} +0 -0
  46. /comfy_env/{env → pixi}/platform/__init__.py +0 -0
  47. /comfy_env/{env → pixi}/platform/base.py +0 -0
  48. /comfy_env/{env → pixi}/platform/darwin.py +0 -0
  49. /comfy_env/{env → pixi}/platform/linux.py +0 -0
  50. /comfy_env/{env → pixi}/platform/windows.py +0 -0
  51. /comfy_env/{registry.py → pixi/registry.py} +0 -0
  52. /comfy_env/{wheel_sources.yml → pixi/wheel_sources.yml} +0 -0
  53. {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/WHEEL +0 -0
  54. {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/entry_points.txt +0 -0
  55. {comfy_env-0.0.64.dist-info → comfy_env-0.0.66.dist-info}/licenses/LICENSE +0 -0
comfy_env/env/manager.py DELETED
@@ -1,636 +0,0 @@
1
- """
2
- IsolatedEnvManager - Creates and manages isolated Python environments.
3
-
4
- Uses uv for fast environment creation and package installation.
5
- """
6
-
7
- import os
8
- import subprocess
9
- import shutil
10
- import sys
11
- from pathlib import Path
12
- from typing import Optional, Callable
13
-
14
- from .config import IsolatedEnv
15
- from .platform import get_platform, PlatformProvider
16
- from .cuda_gpu_detection import detect_cuda_version
17
- from .security import (
18
- normalize_env_name,
19
- validate_dependency,
20
- validate_dependencies,
21
- validate_path_within_root,
22
- validate_wheel_url,
23
- )
24
- from ..registry import PACKAGE_REGISTRY, is_registered, get_cuda_short2
25
- from ..resolver import RuntimeEnv, parse_wheel_requirement
26
-
27
-
28
- class IsolatedEnvManager:
29
- """
30
- Manages isolated Python environments for ComfyUI nodes.
31
-
32
- This class handles:
33
- - Creating Python virtual environments using uv
34
- - Installing PyTorch with correct CUDA version
35
- - Installing packages from requirements or wheel sources
36
- - Caching environments (same config = reuse existing)
37
- - Platform-specific handling (Windows DLLs, etc.)
38
-
39
- Example:
40
- manager = IsolatedEnvManager(base_dir=Path("./"))
41
-
42
- env = IsolatedEnv(
43
- name="my-node",
44
- python="3.10",
45
- cuda="12.8",
46
- requirements=["torch==2.8.0", "nvdiffrast"],
47
- )
48
-
49
- # Create environment and install dependencies
50
- env_path = manager.setup(env)
51
-
52
- # Get Python executable path
53
- python_exe = manager.get_python(env)
54
- """
55
-
56
- def __init__(
57
- self,
58
- base_dir: Path,
59
- log_callback: Optional[Callable[[str], None]] = None,
60
- ):
61
- """
62
- Initialize environment manager.
63
-
64
- Args:
65
- base_dir: Directory where environments will be created
66
- log_callback: Optional callback for logging (default: print)
67
- """
68
- self.base_dir = Path(base_dir)
69
- self.platform: PlatformProvider = get_platform()
70
- self.log = log_callback or print
71
-
72
- # Check platform compatibility
73
- is_compatible, error = self.platform.check_prerequisites()
74
- if not is_compatible:
75
- raise RuntimeError(f"Platform incompatible: {error}")
76
-
77
- def _find_uv(self) -> Optional[Path]:
78
- """Find uv executable in PATH or current Python environment."""
79
- # First check system PATH
80
- uv_path = shutil.which("uv")
81
- if uv_path:
82
- return Path(uv_path)
83
-
84
- # Check current Python environment's Scripts/bin folder
85
- import sys
86
- if sys.platform == "win32":
87
- candidates = [
88
- Path(sys.prefix) / "Scripts" / "uv.exe",
89
- Path(sys.base_prefix) / "Scripts" / "uv.exe",
90
- ]
91
- else:
92
- candidates = [
93
- Path(sys.prefix) / "bin" / "uv",
94
- Path(sys.base_prefix) / "bin" / "uv",
95
- ]
96
-
97
- for candidate in candidates:
98
- if candidate.exists():
99
- return candidate
100
-
101
- return None
102
-
103
- def _run_uv(self, args: list, env_dir: Optional[Path] = None, **kwargs) -> subprocess.CompletedProcess:
104
- """Run uv command."""
105
- uv = self._find_uv()
106
- if not uv:
107
- raise RuntimeError(
108
- "uv not found. Please install it:\n"
109
- " curl -LsSf https://astral.sh/uv/install.sh | sh\n"
110
- " # or on Windows: powershell -c \"irm https://astral.sh/uv/install.ps1 | iex\""
111
- )
112
-
113
- cmd = [str(uv)] + args
114
- return subprocess.run(cmd, capture_output=True, text=True, **kwargs)
115
-
116
- def get_env_dir(self, env: IsolatedEnv) -> Path:
117
- """Get the environment directory path for a config."""
118
- # Validate environment name to prevent directory traversal
119
- safe_name = normalize_env_name(env.name)
120
- env_dir = env.get_default_env_dir(self.base_dir)
121
-
122
- # Ensure the resulting path is within base_dir
123
- validate_path_within_root(env_dir, self.base_dir)
124
-
125
- return env_dir
126
-
127
- def get_python(self, env: IsolatedEnv) -> Path:
128
- """Get the Python executable path for an environment."""
129
- env_dir = self.get_env_dir(env)
130
- return self.platform.get_env_paths(env_dir, env.python).python
131
-
132
- def get_pip(self, env: IsolatedEnv) -> Path:
133
- """Get the pip executable path for an environment."""
134
- env_dir = self.get_env_dir(env)
135
- return self.platform.get_env_paths(env_dir, env.python).pip
136
-
137
- def exists(self, env: IsolatedEnv) -> bool:
138
- """Check if environment already exists."""
139
- python_exe = self.get_python(env)
140
- return python_exe.exists()
141
-
142
- def is_ready(self, env: IsolatedEnv, verify_packages: Optional[list] = None) -> bool:
143
- """
144
- Check if environment is ready to use.
145
-
146
- Args:
147
- env: Environment configuration
148
- verify_packages: Optional list of packages to verify (e.g., ["torch", "numpy"])
149
-
150
- Returns:
151
- True if environment exists and packages are importable
152
- """
153
- python_exe = self.get_python(env)
154
- if not python_exe.exists():
155
- return False
156
-
157
- if verify_packages:
158
- imports = ", ".join(verify_packages)
159
- try:
160
- result = subprocess.run(
161
- [str(python_exe), "-c", f"import {imports}"],
162
- capture_output=True,
163
- text=True,
164
- timeout=30
165
- )
166
- return result.returncode == 0
167
- except (subprocess.SubprocessError, OSError):
168
- return False
169
-
170
- return True
171
-
172
- def create_venv(self, env: IsolatedEnv) -> Path:
173
- """
174
- Create a virtual environment using uv.
175
-
176
- Args:
177
- env: Environment configuration
178
-
179
- Returns:
180
- Path to the environment directory
181
- """
182
- env_dir = self.get_env_dir(env)
183
-
184
- if env_dir.exists():
185
- self.log(f"Environment already exists: {env_dir}")
186
- return env_dir
187
-
188
- self.log(f"Creating environment: {env_dir}")
189
- self.log(f" Python: {env.python}")
190
-
191
- result = self._run_uv([
192
- "venv",
193
- str(env_dir),
194
- "--python", env.python,
195
- ])
196
-
197
- if result.returncode != 0:
198
- raise RuntimeError(f"Failed to create venv: {result.stderr}")
199
-
200
- self.log(f"Environment created successfully")
201
- return env_dir
202
-
203
- def install_pytorch(self, env: IsolatedEnv) -> None:
204
- """
205
- Install PyTorch with appropriate CUDA version.
206
-
207
- Args:
208
- env: Environment configuration
209
- """
210
- cuda_version = env.cuda
211
- if cuda_version is None:
212
- # Auto-detect CUDA version
213
- cuda_version = detect_cuda_version()
214
- if cuda_version:
215
- self.log(f"Auto-detected CUDA version: {cuda_version}")
216
- else:
217
- self.log("No GPU detected, installing CPU-only PyTorch")
218
-
219
- python_exe = self.get_python(env)
220
-
221
- # Determine PyTorch index URL
222
- if cuda_version:
223
- cuda_short = cuda_version.replace(".", "")
224
- index_url = f"https://download.pytorch.org/whl/cu{cuda_short}"
225
- self.log(f"Installing PyTorch with CUDA {cuda_version}")
226
- else:
227
- index_url = "https://download.pytorch.org/whl/cpu"
228
- self.log("Installing PyTorch (CPU)")
229
-
230
- # Build uv pip command
231
- uv = self._find_uv()
232
- pip_args = [
233
- str(uv), "pip", "install",
234
- "--python", str(python_exe),
235
- "--index-url", index_url,
236
- ]
237
-
238
- # Install torch + torchvision together from same index to ensure ABI compatibility
239
- if env.pytorch_version:
240
- pip_args.append(f"torch=={env.pytorch_version}")
241
- else:
242
- pip_args.append("torch")
243
-
244
- # Always install torchvision from the same index - uv will resolve matching version
245
- pip_args.append("torchvision")
246
-
247
- result = subprocess.run(pip_args, capture_output=True, text=True)
248
-
249
- if result.returncode != 0:
250
- raise RuntimeError(f"Failed to install PyTorch: {result.stderr}")
251
-
252
- self.log("PyTorch + torchvision installed successfully")
253
-
254
- def install_requirements(
255
- self,
256
- env: IsolatedEnv,
257
- extra_args: Optional[list] = None,
258
- ) -> None:
259
- """
260
- Install requirements for an environment.
261
-
262
- Args:
263
- env: Environment configuration
264
- extra_args: Extra pip arguments
265
- """
266
- python_exe = self.get_python(env)
267
-
268
- # Merge platform-specific requirements
269
- if sys.platform == 'win32':
270
- platform_reqs = env.windows_requirements
271
- platform_name = 'Windows'
272
- elif sys.platform == 'darwin':
273
- platform_reqs = env.darwin_requirements
274
- platform_name = 'macOS'
275
- else:
276
- platform_reqs = env.linux_requirements
277
- platform_name = 'Linux'
278
-
279
- all_requirements = list(env.requirements) + list(platform_reqs)
280
-
281
- if platform_reqs:
282
- self.log(f"Including {len(platform_reqs)} {platform_name}-specific packages")
283
-
284
- if not all_requirements and not env.requirements_file:
285
- self.log("No requirements to install")
286
- return
287
-
288
- # Validate requirements for security
289
- if all_requirements:
290
- validate_dependencies(all_requirements)
291
-
292
- # Validate wheel sources
293
- for wheel_source in env.wheel_sources:
294
- validate_wheel_url(wheel_source)
295
-
296
- # Validate index URLs
297
- for index_url in env.index_urls:
298
- validate_wheel_url(index_url)
299
-
300
- uv = self._find_uv()
301
- pip_args = [str(uv), "pip", "install", "--python", str(python_exe)]
302
-
303
- # Add wheel sources as --find-links
304
- for wheel_source in env.wheel_sources:
305
- pip_args.extend(["--find-links", wheel_source])
306
-
307
- # Add extra index URLs
308
- for index_url in env.index_urls:
309
- pip_args.extend(["--extra-index-url", index_url])
310
-
311
- # Add extra args
312
- if extra_args:
313
- pip_args.extend(extra_args)
314
-
315
- # Install from requirements file
316
- if env.requirements_file and env.requirements_file.exists():
317
- self.log(f"Installing from {env.requirements_file}")
318
- result = subprocess.run(
319
- pip_args + ["-r", str(env.requirements_file)],
320
- capture_output=True,
321
- text=True,
322
- )
323
- if result.returncode != 0:
324
- raise RuntimeError(f"Failed to install requirements: {result.stderr}")
325
-
326
- # Install no-deps requirements first (e.g., CUDA extensions with conflicting metadata)
327
- # For packages in registry, resolve proper wheel URLs
328
- if env.no_deps_requirements:
329
- self.log(f"Installing {len(env.no_deps_requirements)} CUDA packages")
330
- self._install_cuda_packages(env, pip_args)
331
-
332
- # Install individual requirements (including platform-specific)
333
- if all_requirements:
334
- self.log(f"Installing {len(all_requirements)} packages")
335
- result = subprocess.run(
336
- pip_args + all_requirements,
337
- capture_output=True,
338
- text=True,
339
- )
340
- if result.returncode != 0:
341
- raise RuntimeError(f"Failed to install packages: {result.stderr}")
342
-
343
- self.log("Requirements installed successfully")
344
-
345
- def _install_cuda_packages(self, env: IsolatedEnv, pip_args: list) -> None:
346
- """Install CUDA packages using registry-based wheel resolution."""
347
- import platform as plat
348
-
349
- # Build RuntimeEnv from IsolatedEnv config
350
- os_name = plat.system().lower()
351
- if os_name == 'darwin':
352
- platform_tag = f"macosx_{plat.mac_ver()[0].replace('.', '_')}_{plat.machine()}"
353
- elif os_name == 'windows':
354
- platform_tag = f"win_{plat.machine().lower()}"
355
- else:
356
- platform_tag = f"linux_{plat.machine()}"
357
-
358
- cuda_short = env.cuda.replace(".", "") if env.cuda else None
359
- torch_short = env.pytorch_version.replace(".", "") if env.pytorch_version else None
360
- torch_mm = "".join(env.pytorch_version.split(".")[:2]) if env.pytorch_version else None
361
-
362
- runtime_env = RuntimeEnv(
363
- os_name=os_name,
364
- platform_tag=platform_tag,
365
- python_version=env.python,
366
- python_short=env.python.replace(".", ""),
367
- cuda_version=env.cuda,
368
- cuda_short=cuda_short,
369
- torch_version=env.pytorch_version,
370
- torch_short=torch_short,
371
- torch_mm=torch_mm,
372
- )
373
- vars_dict = runtime_env.as_dict()
374
- if env.cuda:
375
- vars_dict["cuda_short2"] = get_cuda_short2(env.cuda)
376
- vars_dict["py_tag"] = f"cp{env.python.replace('.', '')}"
377
-
378
- for req in env.no_deps_requirements:
379
- package, version = parse_wheel_requirement(req)
380
- pkg_lower = package.lower()
381
-
382
- if pkg_lower in PACKAGE_REGISTRY:
383
- config = PACKAGE_REGISTRY[pkg_lower]
384
-
385
- if "wheel_template" in config:
386
- # Direct wheel URL from template
387
- template = config["wheel_template"]
388
-
389
- # Only require version if template uses {version}
390
- effective_version = None
391
- if "{version}" in template:
392
- effective_version = version or config.get("default_version")
393
- if not effective_version:
394
- raise RuntimeError(f"Package {package} requires version (no default in registry)")
395
- vars_dict["version"] = effective_version
396
-
397
- wheel_url = self._substitute_template(template, vars_dict)
398
- version_str = f"=={effective_version}" if effective_version else ""
399
- self.log(f" Installing {package}{version_str}...")
400
- self.log(f" URL: {wheel_url}")
401
- result = subprocess.run(
402
- pip_args + ["--no-deps", wheel_url],
403
- capture_output=True, text=True,
404
- )
405
- if result.returncode != 0:
406
- raise RuntimeError(f"Failed to install {package}: {result.stderr}")
407
-
408
- elif "find_links" in config:
409
- # find_links: pip resolves wheel from index URL
410
- find_links_url = config["find_links"]
411
- effective_version = version or config.get("default_version")
412
- pkg_spec = f"{package}=={effective_version}" if effective_version else package
413
- self.log(f" Installing {pkg_spec} from {find_links_url}...")
414
- result = subprocess.run(
415
- pip_args + ["--no-deps", "--find-links", find_links_url, pkg_spec],
416
- capture_output=True, text=True,
417
- )
418
- if result.returncode != 0:
419
- raise RuntimeError(f"Failed to install {package}: {result.stderr}")
420
-
421
- elif "package_name" in config:
422
- # PyPI variant (e.g., spconv-cu124)
423
- pkg_name = self._substitute_template(config["package_name"], vars_dict)
424
- pkg_spec = f"{pkg_name}=={version}" if version else pkg_name
425
- self.log(f" Installing {package} as {pkg_spec}...")
426
- result = subprocess.run(
427
- pip_args + [pkg_spec],
428
- capture_output=True, text=True,
429
- )
430
- if result.returncode != 0:
431
- raise RuntimeError(f"Failed to install {package}: {result.stderr}")
432
-
433
- else:
434
- raise RuntimeError(f"Package {package} in registry but missing wheel_template, find_links, or package_name")
435
-
436
- else:
437
- # Not in registry - error
438
- raise RuntimeError(
439
- f"Package {package} not found in registry. "
440
- f"Add wheel_template to [wheel_sources] in comfy-env.toml"
441
- )
442
-
443
- def _substitute_template(self, template: str, vars_dict: dict) -> str:
444
- """Substitute template variables with environment values."""
445
- result = template
446
- for key, value in vars_dict.items():
447
- if value is not None:
448
- result = result.replace(f"{{{key}}}", str(value))
449
- return result
450
-
451
- def _install_comfy_env(self, env: IsolatedEnv) -> None:
452
- """Install comfy-env package (needed for BaseWorker)."""
453
- python_exe = self.get_python(env)
454
- uv = self._find_uv()
455
-
456
- self.log("Installing comfy-env (for worker support)...")
457
-
458
- # Check for local wheel in COMFY_LOCAL_WHEELS directory (set by comfy-test --local)
459
- install_target = "comfy-env @ git+https://github.com/PozzettiAndrea/comfy-env"
460
- wheels_dir = os.environ.get("COMFY_LOCAL_WHEELS")
461
- self.log(f" COMFY_LOCAL_WHEELS={wheels_dir}")
462
- if wheels_dir:
463
- wheels_path = Path(wheels_dir)
464
- self.log(f" Wheels dir exists: {wheels_path.exists()}")
465
- if wheels_path.exists():
466
- local_wheels = list(wheels_path.glob("comfy_env-*.whl"))
467
- self.log(f" Found wheels: {[w.name for w in local_wheels]}")
468
- if local_wheels:
469
- install_target = str(local_wheels[0])
470
- self.log(f" Using local wheel: {local_wheels[0].name}")
471
-
472
- result = subprocess.run(
473
- [str(uv), "pip", "install", "--python", str(python_exe),
474
- "--upgrade", "--no-cache", install_target],
475
- capture_output=True,
476
- text=True,
477
- )
478
- if result.returncode != 0:
479
- # Non-fatal - might already be installed or network issues
480
- self.log(f"Warning: Failed to install comfy-env: {result.stderr}")
481
- else:
482
- self.log("comfy-env installed")
483
-
484
- def _setup_windows_deps(self, env: IsolatedEnv, env_dir: Path) -> None:
485
- """
486
- Set up Windows-specific dependencies (VC++ runtime, DLL paths).
487
-
488
- This:
489
- 1. Installs msvc-runtime package (provides VC++ DLLs)
490
- 2. Sets up the Lib/x64/vc17/bin directory structure for opencv
491
- """
492
- python_exe = self.get_python(env)
493
- uv = self._find_uv()
494
-
495
- # Install msvc-runtime package to get VC++ DLLs
496
- self.log("Installing VC++ runtime (msvc-runtime)...")
497
- result = subprocess.run(
498
- [str(uv), "pip", "install", "--python", str(python_exe), "msvc-runtime"],
499
- capture_output=True,
500
- text=True,
501
- )
502
- if result.returncode != 0:
503
- self.log(f"Warning: Failed to install msvc-runtime: {result.stderr}")
504
- else:
505
- self.log("msvc-runtime installed")
506
-
507
- # Set up opencv DLL paths (copies DLLs to Lib/x64/vc17/bin)
508
- self.log("Setting up opencv DLL paths...")
509
- success, msg = self.platform.setup_opencv_dll_paths(env_dir)
510
- if success:
511
- if msg:
512
- self.log(f" {msg}")
513
- else:
514
- self.log(f"Warning: {msg}")
515
-
516
- def ensure_system_deps(self) -> bool:
517
- """
518
- Ensure system-level dependencies are installed (Windows only).
519
-
520
- On Windows, this checks for Media Foundation and installs if missing.
521
- Returns True if all deps are available, False otherwise.
522
- """
523
- if self.platform.name != 'windows':
524
- return True
525
-
526
- # Check and install Media Foundation if needed
527
- success, error = self.platform.ensure_media_foundation(self.log)
528
- if not success:
529
- self.log(f"WARNING: {error}")
530
- self.log("Some packages (like opencv) may not work correctly.")
531
- return False
532
-
533
- return True
534
-
535
- def setup(
536
- self,
537
- env: IsolatedEnv,
538
- install_pytorch: bool = True,
539
- verify_packages: Optional[list] = None,
540
- ) -> Path:
541
- """
542
- Create environment and install all dependencies.
543
-
544
- This is the main entry point for setting up an isolated environment.
545
-
546
- Args:
547
- env: Environment configuration
548
- install_pytorch: Whether to install PyTorch
549
- verify_packages: Packages to verify after installation
550
-
551
- Returns:
552
- Path to the environment directory
553
- """
554
- self.log("=" * 50)
555
- self.log(f"Setting up isolated environment: {env.name}")
556
- self.log("=" * 50)
557
-
558
- # Ensure system-level deps (Media Foundation on Windows)
559
- self.ensure_system_deps()
560
-
561
- # Check if already ready
562
- if self.is_ready(env, verify_packages):
563
- self.log("Environment already ready, skipping setup")
564
- return self.get_env_dir(env)
565
-
566
- # Create virtual environment
567
- env_dir = self.create_venv(env)
568
-
569
- # Install PyTorch
570
- if install_pytorch and (env.cuda is not None or detect_cuda_version()):
571
- self.install_pytorch(env)
572
-
573
- # Install comfy-env (needed for BaseWorker in workers)
574
- self._install_comfy_env(env)
575
-
576
- # Install other requirements
577
- self.install_requirements(env)
578
-
579
- # Windows: Install VC++ runtime and set up DLL paths
580
- if self.platform.name == 'windows':
581
- self._setup_windows_deps(env, env_dir)
582
-
583
- # Verify installation
584
- if verify_packages:
585
- if self.is_ready(env, verify_packages):
586
- self.log("Verification passed!")
587
- else:
588
- raise RuntimeError(f"Verification failed: could not import {verify_packages}")
589
-
590
- self.log("=" * 50)
591
- self.log("Setup complete!")
592
- self.log(f"Python: {self.get_python(env)}")
593
- self.log("=" * 50)
594
-
595
- return env_dir
596
-
597
- def delete(self, env: IsolatedEnv) -> bool:
598
- """
599
- Delete an environment.
600
-
601
- Args:
602
- env: Environment configuration
603
-
604
- Returns:
605
- True if deleted, False if didn't exist
606
- """
607
- env_dir = self.get_env_dir(env)
608
-
609
- if not env_dir.exists():
610
- return False
611
-
612
- self.log(f"Deleting environment: {env_dir}")
613
- self.platform.rmtree_robust(env_dir)
614
- self.log("Environment deleted")
615
- return True
616
-
617
- def repair(
618
- self,
619
- env: IsolatedEnv,
620
- install_pytorch: bool = True,
621
- verify_packages: Optional[list] = None,
622
- ) -> Path:
623
- """
624
- Delete and recreate an environment.
625
-
626
- Args:
627
- env: Environment configuration
628
- install_pytorch: Whether to install PyTorch
629
- verify_packages: Packages to verify after installation
630
-
631
- Returns:
632
- Path to the new environment directory
633
- """
634
- self.log("Repairing environment (delete + recreate)")
635
- self.delete(env)
636
- return self.setup(env, install_pytorch, verify_packages)