comfy-env 0.0.67__tar.gz → 0.0.69__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 (33) hide show
  1. {comfy_env-0.0.67 → comfy_env-0.0.69}/PKG-INFO +1 -1
  2. {comfy_env-0.0.67 → comfy_env-0.0.69}/pyproject.toml +1 -1
  3. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/install.py +1 -1
  4. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/pixi/core.py +43 -51
  5. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/workers/mp.py +60 -12
  6. {comfy_env-0.0.67 → comfy_env-0.0.69}/.github/workflows/publish.yml +0 -0
  7. {comfy_env-0.0.67 → comfy_env-0.0.69}/.gitignore +0 -0
  8. {comfy_env-0.0.67 → comfy_env-0.0.69}/LICENSE +0 -0
  9. {comfy_env-0.0.67 → comfy_env-0.0.69}/README.md +0 -0
  10. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/__init__.py +0 -0
  11. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/cli.py +0 -0
  12. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/config/__init__.py +0 -0
  13. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/config/parser.py +0 -0
  14. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/config/types.py +0 -0
  15. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/errors.py +0 -0
  16. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/isolation/__init__.py +0 -0
  17. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/isolation/wrap.py +0 -0
  18. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/nodes.py +0 -0
  19. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/pixi/__init__.py +0 -0
  20. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/pixi/cuda_detection.py +0 -0
  21. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/pixi/platform/__init__.py +0 -0
  22. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/pixi/platform/base.py +0 -0
  23. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/pixi/platform/darwin.py +0 -0
  24. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/pixi/platform/linux.py +0 -0
  25. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/pixi/platform/windows.py +0 -0
  26. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/pixi/resolver.py +0 -0
  27. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/prestartup.py +0 -0
  28. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/templates/comfy-env-instructions.txt +0 -0
  29. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/templates/comfy-env.toml +0 -0
  30. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/workers/__init__.py +0 -0
  31. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/workers/base.py +0 -0
  32. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/workers/subprocess.py +0 -0
  33. {comfy_env-0.0.67 → comfy_env-0.0.69}/src/comfy_env/workers/tensor_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comfy-env
3
- Version: 0.0.67
3
+ Version: 0.0.69
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.67"
3
+ version = "0.0.69"
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"}
@@ -151,7 +151,7 @@ def _install_isolated_subdirs(
151
151
  log(f" (dry run)")
152
152
  continue
153
153
 
154
- pixi_install(sub_cfg, sub_dir, log, create_env_link=True)
154
+ pixi_install(sub_cfg, sub_dir, log)
155
155
 
156
156
 
157
157
  def verify_installation(
@@ -172,7 +172,6 @@ def pixi_install(
172
172
  cfg: ComfyEnvConfig,
173
173
  node_dir: Path,
174
174
  log: Callable[[str], None] = print,
175
- create_env_link: bool = False,
176
175
  ) -> bool:
177
176
  """
178
177
  Install all packages via pixi.
@@ -188,7 +187,6 @@ def pixi_install(
188
187
  cfg: ComfyEnvConfig with packages to install.
189
188
  node_dir: Directory to install in.
190
189
  log: Logging callback.
191
- create_env_link: If True, create _env_<name> symlink for isolation.
192
190
 
193
191
  Returns:
194
192
  True if installation succeeded.
@@ -217,9 +215,31 @@ def pixi_install(
217
215
  else:
218
216
  log("Warning: CUDA packages requested but no GPU detected")
219
217
 
218
+ # Install system dependencies on Linux (OpenGL, build tools)
219
+ if sys.platform == "linux":
220
+ log("Installing system dependencies...")
221
+ subprocess.run(
222
+ ["sudo", "apt-get", "update"],
223
+ capture_output=True,
224
+ )
225
+ subprocess.run(
226
+ ["sudo", "apt-get", "install", "-y",
227
+ "python3-dev", # Build deps for C extensions
228
+ "libgl1-mesa-glx", # OpenGL
229
+ "libglu1-mesa", # GLU
230
+ ],
231
+ capture_output=True,
232
+ )
233
+
220
234
  # Clean previous artifacts
221
235
  clean_pixi_artifacts(node_dir, log)
222
236
 
237
+ # Create .pixi/config.toml to ensure inline (non-detached) environments
238
+ pixi_config_dir = node_dir / ".pixi"
239
+ pixi_config_dir.mkdir(parents=True, exist_ok=True)
240
+ pixi_config_file = pixi_config_dir / "config.toml"
241
+ pixi_config_file.write_text("detached-environments = false\n")
242
+
223
243
  # Ensure pixi is installed
224
244
  pixi_path = ensure_pixi(log=log)
225
245
 
@@ -252,24 +272,10 @@ def pixi_install(
252
272
  dependencies.setdefault("pip", "*") # Always include pip
253
273
  pixi_data["dependencies"] = dependencies
254
274
 
255
- # Add pypi-options for CUDA wheels
275
+ # Add pypi-options for PyTorch index (CUDA packages installed separately via pip)
256
276
  if cfg.has_cuda and cuda_version:
257
277
  pypi_options = pixi_data.get("pypi-options", {})
258
- # Merge find-links (pixi expects [{url: "..."}, ...] format)
259
- find_links = pypi_options.get("find-links", [])
260
- existing_urls = {
261
- entry.get("url") if isinstance(entry, dict) else entry
262
- for entry in find_links
263
- }
264
- if CUDA_WHEELS_INDEX not in existing_urls:
265
- find_links.append({"url": CUDA_WHEELS_INDEX})
266
- # Normalize any plain strings to {url: ...} format
267
- find_links = [
268
- {"url": entry} if isinstance(entry, str) else entry
269
- for entry in find_links
270
- ]
271
- pypi_options["find-links"] = find_links
272
- # Merge extra-index-urls
278
+ # Add PyTorch CUDA index for torch installation
273
279
  cuda_short = cuda_version.replace(".", "")[:3]
274
280
  pytorch_index = f"https://download.pytorch.org/whl/cu{cuda_short}"
275
281
  extra_urls = pypi_options.get("extra-index-urls", [])
@@ -311,46 +317,32 @@ def pixi_install(
311
317
  log(f"pixi install failed:\n{result.stderr}")
312
318
  raise RuntimeError(f"pixi install failed: {result.stderr}")
313
319
 
314
- # Install CUDA packages with --no-deps (avoids PyPI version conflicts)
320
+ # Install CUDA packages with --no-index --find-links (bypasses PyPI completely)
315
321
  if cfg.cuda_packages and cuda_version:
316
- log(f"Installing CUDA packages with --no-deps: {cfg.cuda_packages}")
322
+ log(f"Installing CUDA packages: {cfg.cuda_packages}")
317
323
  python_path = get_pixi_python(node_dir)
318
324
  if not python_path:
319
325
  raise RuntimeError("Could not find Python in pixi environment")
320
326
 
321
- pip_cmd = [
322
- str(python_path), "-m", "pip", "install",
323
- "--no-deps",
324
- "--index-url", CUDA_WHEELS_INDEX,
325
- ] + cfg.cuda_packages
327
+ for package in cfg.cuda_packages:
328
+ # Each package has its own find-links page at CUDA_WHEELS_INDEX/<package>/
329
+ find_links_url = f"{CUDA_WHEELS_INDEX}{package}/"
330
+ log(f" Installing {package} from {find_links_url}")
331
+
332
+ pip_cmd = [
333
+ str(python_path), "-m", "pip", "install",
334
+ "--no-index",
335
+ "--no-deps",
336
+ "--no-cache-dir",
337
+ "--find-links", find_links_url,
338
+ package,
339
+ ]
340
+ result = subprocess.run(pip_cmd, capture_output=True, text=True)
341
+ if result.returncode != 0:
342
+ log(f"CUDA package install failed for {package}:\n{result.stderr}")
343
+ raise RuntimeError(f"CUDA package install failed: {result.stderr}")
326
344
 
327
- result = subprocess.run(pip_cmd, capture_output=True, text=True)
328
- if result.returncode != 0:
329
- log(f"CUDA package install failed:\n{result.stderr}")
330
- raise RuntimeError(f"CUDA package install failed: {result.stderr}")
331
345
  log("CUDA packages installed")
332
346
 
333
- # Create symlink/junction to _env_<name> for discovery (only for isolated subdirs)
334
- if create_env_link:
335
- env_dir = node_dir / ".pixi" / "envs" / "default"
336
- env_link = node_dir / f"_env_{node_dir.name}"
337
- if env_dir.exists():
338
- # Remove existing link/dir if present
339
- if env_link.is_symlink() or env_link.exists():
340
- if env_link.is_symlink():
341
- env_link.unlink()
342
- else:
343
- shutil.rmtree(env_link)
344
- # Create symlink (Linux/Mac) or junction (Windows)
345
- if sys.platform == "win32":
346
- # Use junction on Windows
347
- subprocess.run(
348
- ["cmd", "/c", "mklink", "/J", str(env_link), str(env_dir)],
349
- capture_output=True,
350
- )
351
- else:
352
- env_link.symlink_to(env_dir)
353
- log(f"Linked: {env_link.name} -> .pixi/envs/default")
354
-
355
347
  log("Installation complete!")
356
348
  return True
@@ -430,20 +430,68 @@ class MPWorker(Worker):
430
430
  return
431
431
 
432
432
  # Import torch here to avoid import at module level
433
+ import os
434
+ import sys
435
+
436
+ # Clear conda/pixi environment variables FIRST, before importing multiprocessing
437
+ # These can cause the child process to pick up the wrong Python interpreter
438
+ # or stdlib, leading to sys.version mismatch errors in platform module
439
+ conda_env_vars = [
440
+ 'CONDA_PREFIX',
441
+ 'CONDA_DEFAULT_ENV',
442
+ 'CONDA_PYTHON_EXE',
443
+ 'CONDA_EXE',
444
+ 'CONDA_SHLVL',
445
+ 'PYTHONHOME',
446
+ 'PYTHONPATH', # Also clear PYTHONPATH to prevent pixi paths
447
+ '_CE_CONDA',
448
+ '_CE_M',
449
+ ]
450
+ saved_env = {}
451
+ for var in conda_env_vars:
452
+ if var in os.environ:
453
+ saved_env[var] = os.environ.pop(var)
454
+
455
+ # Also remove pixi paths from LD_LIBRARY_PATH
456
+ ld_lib = os.environ.get('LD_LIBRARY_PATH', '')
457
+ if '.pixi' in ld_lib:
458
+ saved_env['LD_LIBRARY_PATH'] = ld_lib
459
+ # Filter out pixi paths
460
+ new_ld_lib = ':'.join(p for p in ld_lib.split(':') if '.pixi' not in p)
461
+ if new_ld_lib:
462
+ os.environ['LD_LIBRARY_PATH'] = new_ld_lib
463
+ else:
464
+ os.environ.pop('LD_LIBRARY_PATH', None)
465
+
433
466
  import torch.multiprocessing as mp
434
467
 
435
- # Use spawn to get clean subprocess (no inherited CUDA context)
436
- ctx = mp.get_context('spawn')
437
-
438
- self._queue_in = ctx.Queue()
439
- self._queue_out = ctx.Queue()
440
- self._process = ctx.Process(
441
- target=_worker_loop,
442
- args=(self._queue_in, self._queue_out, self._sys_path, self._lib_path),
443
- daemon=True,
444
- )
445
- self._process.start()
446
- self._started = True
468
+ try:
469
+ # Use spawn to get clean subprocess (no inherited CUDA context)
470
+ ctx = mp.get_context('spawn')
471
+
472
+ # Explicitly set the spawn executable to the current Python
473
+ # This prevents pixi/conda from hijacking the spawn process
474
+ import multiprocessing.spawn as mp_spawn
475
+ original_exe = mp_spawn.get_executable()
476
+ if original_exe != sys.executable.encode() and original_exe != sys.executable:
477
+ print(f"[comfy-env] Warning: spawn executable was {original_exe}, forcing to {sys.executable}")
478
+ mp_spawn.set_executable(sys.executable)
479
+
480
+ self._queue_in = ctx.Queue()
481
+ self._queue_out = ctx.Queue()
482
+ self._process = ctx.Process(
483
+ target=_worker_loop,
484
+ args=(self._queue_in, self._queue_out, self._sys_path, self._lib_path),
485
+ daemon=True,
486
+ )
487
+ self._process.start()
488
+ self._started = True
489
+
490
+ # Restore original executable setting
491
+ mp_spawn.set_executable(original_exe)
492
+ finally:
493
+ # Restore env vars in parent process
494
+ os.environ.update(saved_env)
447
495
 
448
496
  def call(
449
497
  self,
File without changes
File without changes
File without changes