comfy-env 0.0.51__tar.gz → 0.0.53__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 (49) hide show
  1. {comfy_env-0.0.51 → comfy_env-0.0.53}/PKG-INFO +1 -1
  2. {comfy_env-0.0.51 → comfy_env-0.0.53}/pyproject.toml +1 -1
  3. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/pixi.py +38 -4
  4. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/stub_imports.py +18 -11
  5. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/workers/venv.py +77 -2
  6. {comfy_env-0.0.51 → comfy_env-0.0.53}/.github/workflows/publish.yml +0 -0
  7. {comfy_env-0.0.51 → comfy_env-0.0.53}/.gitignore +0 -0
  8. {comfy_env-0.0.51 → comfy_env-0.0.53}/LICENSE +0 -0
  9. {comfy_env-0.0.51 → comfy_env-0.0.53}/README.md +0 -0
  10. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/__init__.py +0 -0
  11. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/cli.py +0 -0
  12. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/decorator.py +0 -0
  13. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/env/__init__.py +0 -0
  14. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/env/config.py +0 -0
  15. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/env/config_file.py +0 -0
  16. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/env/cuda_gpu_detection.py +0 -0
  17. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/env/manager.py +0 -0
  18. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/env/platform/__init__.py +0 -0
  19. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/env/platform/base.py +0 -0
  20. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/env/platform/darwin.py +0 -0
  21. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/env/platform/linux.py +0 -0
  22. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/env/platform/windows.py +0 -0
  23. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/env/security.py +0 -0
  24. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/errors.py +0 -0
  25. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/install.py +0 -0
  26. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/ipc/__init__.py +0 -0
  27. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/ipc/bridge.py +0 -0
  28. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/ipc/protocol.py +0 -0
  29. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/ipc/tensor.py +0 -0
  30. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/ipc/torch_bridge.py +0 -0
  31. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/ipc/transport.py +0 -0
  32. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/ipc/worker.py +0 -0
  33. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/isolation.py +0 -0
  34. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/nodes.py +0 -0
  35. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/registry.py +0 -0
  36. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/resolver.py +0 -0
  37. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/stubs/__init__.py +0 -0
  38. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/stubs/comfy/__init__.py +0 -0
  39. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/stubs/comfy/model_management.py +0 -0
  40. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/stubs/comfy/utils.py +0 -0
  41. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/stubs/folder_paths.py +0 -0
  42. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/templates/comfy-env-instructions.txt +0 -0
  43. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/templates/comfy-env.toml +0 -0
  44. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/wheel_sources.yml +0 -0
  45. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/workers/__init__.py +0 -0
  46. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/workers/base.py +0 -0
  47. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/workers/pool.py +0 -0
  48. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/workers/tensor_utils.py +0 -0
  49. {comfy_env-0.0.51 → comfy_env-0.0.53}/src/comfy_env/workers/torch_mp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comfy-env
3
- Version: 0.0.51
3
+ Version: 0.0.53
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.51"
3
+ version = "0.0.53"
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"}
@@ -203,10 +203,12 @@ def create_pixi_toml(
203
203
  Returns:
204
204
  Path to the generated pixi.toml file.
205
205
  """
206
- if not env_config.conda:
207
- raise ValueError("Environment has no conda configuration")
208
-
209
- conda = env_config.conda
206
+ # Conda is optional - use defaults if not present
207
+ if env_config.conda:
208
+ conda = env_config.conda
209
+ else:
210
+ from comfy_env.env.config import CondaConfig
211
+ conda = CondaConfig(channels=["conda-forge"], packages=[])
210
212
  pixi_toml_path = node_dir / "pixi.toml"
211
213
 
212
214
  # Build pixi.toml content
@@ -246,6 +248,10 @@ def create_pixi_toml(
246
248
  lines.append("[dependencies]")
247
249
  lines.append(f'python = "{env_config.python}.*"')
248
250
 
251
+ # On Windows, use MKL BLAS to avoid OpenBLAS crashes (numpy blas_fpe_check issue)
252
+ if sys.platform == "win32":
253
+ lines.append('libblas = { version = "*", build = "*mkl" }')
254
+
249
255
  for pkg in conda.packages:
250
256
  # Parse package spec (name=version or name>=version or just name)
251
257
  if "=" in pkg and not pkg.startswith("="):
@@ -371,6 +377,34 @@ def create_pixi_toml(
371
377
  lines.append(f'{name} = {value}')
372
378
 
373
379
  for dep in pypi_deps:
380
+ # Handle git dependencies in two formats:
381
+ # 1. pkg @ git+https://github.com/user/repo.git@commit
382
+ # 2. git+https://github.com/user/repo.git@commit (extract name from URL)
383
+ if "git+" in dep:
384
+ if " @ git+" in dep:
385
+ # Format: pkg @ git+URL@commit
386
+ match = re.match(r'^([a-zA-Z0-9._-]+)\s*@\s*git\+(.+?)(?:@([a-f0-9]+))?$', dep)
387
+ if match:
388
+ pkg_name = match.group(1)
389
+ git_url = match.group(2)
390
+ rev = match.group(3)
391
+ else:
392
+ # Format: git+URL@commit (extract package name from repo name)
393
+ match = re.match(r'^git\+(.+?)(?:@([a-f0-9]+))?$', dep)
394
+ if match:
395
+ git_url = match.group(1)
396
+ rev = match.group(2)
397
+ # Extract package name from URL (repo name without .git)
398
+ repo_match = re.search(r'/([^/]+?)(?:\.git)?$', git_url)
399
+ pkg_name = repo_match.group(1) if repo_match else git_url.split('/')[-1].replace('.git', '')
400
+
401
+ if match:
402
+ if rev:
403
+ lines.append(f'{pkg_name} = {{ git = "{git_url}", rev = "{rev}" }}')
404
+ else:
405
+ lines.append(f'{pkg_name} = {{ git = "{git_url}" }}')
406
+ continue
407
+
374
408
  # Parse pip requirement format to pixi format
375
409
  # Handles extras like trimesh[easy]>=4.0.0
376
410
  name, version_spec, extras = _parse_pypi_requirement(dep)
@@ -156,19 +156,26 @@ def _get_import_names_from_pixi(node_dir: Path) -> Set[str]:
156
156
  """
157
157
  import_names = set()
158
158
 
159
- # Find the pixi site-packages
160
- pixi_lib = node_dir / ".pixi" / "envs" / "default" / "lib"
159
+ pixi_base = node_dir / ".pixi" / "envs" / "default"
161
160
 
162
- if not pixi_lib.exists():
163
- return import_names
164
-
165
- # Find the python version directory (e.g., python3.11)
166
- python_dirs = list(pixi_lib.glob("python3.*"))
167
- if not python_dirs:
168
- return import_names
161
+ # Find site-packages (different paths on Windows vs Linux)
162
+ # Linux: .pixi/envs/default/lib/python3.x/site-packages
163
+ # Windows: .pixi/envs/default/Lib/site-packages
164
+ site_packages = None
169
165
 
170
- site_packages = python_dirs[0] / "site-packages"
171
- if not site_packages.exists():
166
+ # Try Windows path first (Lib/site-packages)
167
+ win_site = pixi_base / "Lib" / "site-packages"
168
+ if win_site.exists():
169
+ site_packages = win_site
170
+ else:
171
+ # Try Linux path (lib/python3.x/site-packages)
172
+ pixi_lib = pixi_base / "lib"
173
+ if pixi_lib.exists():
174
+ python_dirs = list(pixi_lib.glob("python3.*"))
175
+ if python_dirs:
176
+ site_packages = python_dirs[0] / "site-packages"
177
+
178
+ if site_packages is None or not site_packages.exists():
172
179
  return import_names
173
180
 
174
181
  # Scan for importable modules
@@ -252,10 +252,29 @@ def _serialize_for_ipc(obj, visited=None):
252
252
  # Worker script template - minimal, runs in target venv
253
253
  _WORKER_SCRIPT = '''
254
254
  import sys
255
+ import os
255
256
  import json
256
257
  import traceback
257
258
  from types import SimpleNamespace
258
259
 
260
+ # On Windows, add DLL directories for proper library loading
261
+ if sys.platform == "win32" and hasattr(os, "add_dll_directory"):
262
+ _host_python_dir = os.environ.get("COMFYUI_HOST_PYTHON_DIR")
263
+ if _host_python_dir:
264
+ try:
265
+ os.add_dll_directory(_host_python_dir)
266
+ _dlls_dir = os.path.join(_host_python_dir, "DLLs")
267
+ if os.path.isdir(_dlls_dir):
268
+ os.add_dll_directory(_dlls_dir)
269
+ except Exception:
270
+ pass
271
+ _pixi_library_bin = os.environ.get("COMFYUI_PIXI_LIBRARY_BIN")
272
+ if _pixi_library_bin:
273
+ try:
274
+ os.add_dll_directory(_pixi_library_bin)
275
+ except Exception:
276
+ pass
277
+
259
278
  def _deserialize_isolated_objects(obj):
260
279
  """Reconstruct objects serialized with __isolated_object__ marker."""
261
280
  if isinstance(obj, dict):
@@ -475,12 +494,29 @@ class VenvWorker(Worker):
475
494
  env.update(self.extra_env)
476
495
  env["COMFYUI_ISOLATION_WORKER"] = "1"
477
496
 
478
- # For conda/pixi environments, add lib dir to LD_LIBRARY_PATH
497
+ # For conda/pixi environments, add lib dir to LD_LIBRARY_PATH (Linux)
479
498
  lib_dir = self.python.parent.parent / "lib"
480
499
  if lib_dir.is_dir():
481
500
  existing = env.get("LD_LIBRARY_PATH", "")
482
501
  env["LD_LIBRARY_PATH"] = f"{lib_dir}:{existing}" if existing else str(lib_dir)
483
502
 
503
+ # On Windows, pass host Python directory and pixi Library/bin for DLL loading
504
+ if sys.platform == "win32":
505
+ env["COMFYUI_HOST_PYTHON_DIR"] = str(Path(sys.executable).parent)
506
+
507
+ # For pixi environments with MKL, add Library/bin to PATH for DLL loading
508
+ # Pixi has python.exe directly in env dir, not in Scripts/
509
+ env_dir = self.python.parent
510
+ library_bin = env_dir / "Library" / "bin"
511
+ if library_bin.is_dir():
512
+ existing_path = env.get("PATH", "")
513
+ env["PATH"] = f"{env_dir};{library_bin};{existing_path}"
514
+ env["COMFYUI_PIXI_LIBRARY_BIN"] = str(library_bin)
515
+ # Allow duplicate OpenMP libraries (MKL's libiomp5md.dll + PyTorch's libomp.dll)
516
+ env["KMP_DUPLICATE_LIB_OK"] = "TRUE"
517
+ # Use UTF-8 encoding for stdout/stderr to handle Unicode symbols
518
+ env["PYTHONIOENCODING"] = "utf-8"
519
+
484
520
  # Run subprocess
485
521
  cmd = [
486
522
  str(self.python),
@@ -592,6 +628,15 @@ if sys.platform == "win32":
592
628
  except Exception:
593
629
  pass
594
630
 
631
+ # For pixi environments with MKL, add Library/bin for MKL DLLs
632
+ _pixi_library_bin = os.environ.get("COMFYUI_PIXI_LIBRARY_BIN")
633
+ if _pixi_library_bin and hasattr(os, "add_dll_directory"):
634
+ try:
635
+ os.add_dll_directory(_pixi_library_bin)
636
+ print(f"[worker] Added pixi Library/bin to DLL search: {_pixi_library_bin}", flush=True)
637
+ except Exception as e:
638
+ print(f"[worker] Failed to add pixi Library/bin: {e}", flush=True)
639
+
595
640
  # =============================================================================
596
641
  # Object Reference System - keep complex objects in worker, pass refs to host
597
642
  # =============================================================================
@@ -626,8 +671,12 @@ def _should_use_reference(obj):
626
671
  # Primitives - pass by value
627
672
  if isinstance(obj, (bool, int, float, str, bytes)):
628
673
  return False
629
- # NumPy arrays and torch tensors - pass by value (they serialize well)
674
+ # NumPy scalars - pass by value (convert to Python primitives)
630
675
  obj_type = type(obj).__name__
676
+ if obj_type in ('float16', 'float32', 'float64', 'int8', 'int16', 'int32', 'int64',
677
+ 'uint8', 'uint16', 'uint32', 'uint64', 'bool_'):
678
+ return False
679
+ # NumPy arrays and torch tensors - pass by value (they serialize well)
631
680
  if obj_type in ('ndarray', 'Tensor'):
632
681
  return False
633
682
  # Dicts, lists, tuples - recurse into contents (don't ref the container)
@@ -660,6 +709,16 @@ def _serialize_result(obj, visited=None):
660
709
  return [_serialize_result(v, visited) for v in obj]
661
710
  if isinstance(obj, tuple):
662
711
  return tuple(_serialize_result(v, visited) for v in obj)
712
+
713
+ # Convert numpy scalars to Python primitives for JSON serialization
714
+ obj_type = type(obj).__name__
715
+ if obj_type in ('float16', 'float32', 'float64'):
716
+ return float(obj)
717
+ if obj_type in ('int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64'):
718
+ return int(obj)
719
+ if obj_type == 'bool_':
720
+ return bool(obj)
721
+
663
722
  return obj
664
723
 
665
724
  def _deserialize_input(obj):
@@ -977,6 +1036,22 @@ class PersistentVenvWorker(Worker):
977
1036
  if sys.platform == "win32":
978
1037
  env["COMFYUI_HOST_PYTHON_DIR"] = str(Path(sys.executable).parent)
979
1038
 
1039
+ # For pixi environments with MKL, add Library/bin to PATH for DLL loading
1040
+ # MKL DLLs are in .pixi/envs/default/Library/bin/
1041
+ # Pixi has python.exe directly in env dir, not in Scripts/
1042
+ env_dir = self.python.parent
1043
+ library_bin = env_dir / "Library" / "bin"
1044
+ if library_bin.is_dir():
1045
+ existing_path = env.get("PATH", "")
1046
+ # Add env dir and Library/bin to PATH
1047
+ env["PATH"] = f"{env_dir};{library_bin};{existing_path}"
1048
+ # Also pass as env var so worker can use os.add_dll_directory()
1049
+ env["COMFYUI_PIXI_LIBRARY_BIN"] = str(library_bin)
1050
+ # Allow duplicate OpenMP libraries (MKL's libiomp5md.dll + PyTorch's libomp.dll)
1051
+ env["KMP_DUPLICATE_LIB_OK"] = "TRUE"
1052
+ # Use UTF-8 encoding for stdout/stderr to handle Unicode symbols
1053
+ env["PYTHONIOENCODING"] = "utf-8"
1054
+
980
1055
  # Find ComfyUI base and set env var for folder_paths stub
981
1056
  comfyui_base = self._find_comfyui_base()
982
1057
  if comfyui_base:
File without changes
File without changes
File without changes