comfy-env 0.1.3__tar.gz → 0.1.5__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 (34) hide show
  1. {comfy_env-0.1.3 → comfy_env-0.1.5}/PKG-INFO +1 -1
  2. {comfy_env-0.1.3 → comfy_env-0.1.5}/pyproject.toml +1 -1
  3. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/pixi/core.py +86 -19
  4. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/workers/mp.py +44 -2
  5. {comfy_env-0.1.3 → comfy_env-0.1.5}/.github/workflows/ci.yml +0 -0
  6. {comfy_env-0.1.3 → comfy_env-0.1.5}/.github/workflows/publish.yml +0 -0
  7. {comfy_env-0.1.3 → comfy_env-0.1.5}/.gitignore +0 -0
  8. {comfy_env-0.1.3 → comfy_env-0.1.5}/LICENSE +0 -0
  9. {comfy_env-0.1.3 → comfy_env-0.1.5}/README.md +0 -0
  10. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/__init__.py +0 -0
  11. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/cli.py +0 -0
  12. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/config/__init__.py +0 -0
  13. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/config/parser.py +0 -0
  14. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/config/types.py +0 -0
  15. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/errors.py +0 -0
  16. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/install.py +0 -0
  17. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/isolation/__init__.py +0 -0
  18. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/isolation/wrap.py +0 -0
  19. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/nodes.py +0 -0
  20. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/pixi/__init__.py +0 -0
  21. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/pixi/cuda_detection.py +0 -0
  22. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/pixi/platform/__init__.py +0 -0
  23. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/pixi/platform/base.py +0 -0
  24. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/pixi/platform/darwin.py +0 -0
  25. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/pixi/platform/linux.py +0 -0
  26. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/pixi/platform/windows.py +0 -0
  27. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/pixi/resolver.py +0 -0
  28. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/prestartup.py +0 -0
  29. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/templates/comfy-env-instructions.txt +0 -0
  30. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/templates/comfy-env.toml +0 -0
  31. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/workers/__init__.py +0 -0
  32. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/workers/base.py +0 -0
  33. {comfy_env-0.1.3 → comfy_env-0.1.5}/src/comfy_env/workers/subprocess.py +0 -0
  34. {comfy_env-0.1.3 → comfy_env-0.1.5}/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.1.3
3
+ Version: 0.1.5
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.1.3"
3
+ version = "0.1.5"
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"}
@@ -9,10 +9,12 @@ See: https://pixi.sh/
9
9
 
10
10
  import copy
11
11
  import platform
12
+ import re
12
13
  import shutil
13
14
  import stat
14
15
  import subprocess
15
16
  import sys
17
+ import urllib.request
16
18
  from pathlib import Path
17
19
  from typing import Any, Callable, Dict, List, Optional
18
20
 
@@ -28,15 +30,9 @@ PIXI_URLS = {
28
30
  ("Windows", "AMD64"): "https://github.com/prefix-dev/pixi/releases/latest/download/pixi-x86_64-pc-windows-msvc.exe",
29
31
  }
30
32
 
31
- # CUDA wheels index
33
+ # CUDA wheels index (includes flash-attn, PyG packages, and custom wheels)
32
34
  CUDA_WHEELS_INDEX = "https://pozzettiandrea.github.io/cuda-wheels/"
33
35
 
34
- # Flash attention pre-built wheels (mjun0812)
35
- FLASH_ATTN_INDEX = "https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/expanded_assets/v0.7.16"
36
-
37
- # PyTorch Geometric wheels index
38
- PYG_WHEELS_INDEX = "https://data.pyg.org/whl/"
39
-
40
36
  # CUDA version -> PyTorch version mapping
41
37
  CUDA_TORCH_MAP = {
42
38
  "12.8": "2.8",
@@ -44,19 +40,87 @@ CUDA_TORCH_MAP = {
44
40
  "12.1": "2.4",
45
41
  }
46
42
 
43
+ def find_matching_wheel(package: str, torch_version: str, cuda_version: str) -> Optional[str]:
44
+ """
45
+ Query cuda-wheels index to find a wheel matching the CUDA/torch version.
46
+ Returns the full version spec (e.g., "flash-attn===2.8.3+cu128torch2.8") or None.
47
+ """
48
+ cuda_short = cuda_version.replace(".", "")[:3] # "12.8" -> "128"
49
+ torch_short = torch_version.replace(".", "")[:2] # "2.8" -> "28"
50
+
51
+ # Try different directory name variants
52
+ pkg_variants = [package, package.replace("-", "_"), package.replace("_", "-")]
53
+
54
+ for pkg_dir in pkg_variants:
55
+ url = f"{CUDA_WHEELS_INDEX}{pkg_dir}/"
56
+ try:
57
+ with urllib.request.urlopen(url, timeout=10) as resp:
58
+ html = resp.read().decode("utf-8")
59
+ except Exception:
60
+ continue
61
+
62
+ # Parse wheel filenames from href attributes
63
+ # Pattern: package_name-version+localversion-cpXX-cpXX-platform.whl
64
+ wheel_pattern = re.compile(
65
+ r'href="[^"]*?([^"/]+\.whl)"',
66
+ re.IGNORECASE
67
+ )
68
+
69
+ # Local version patterns to match:
70
+ # flash-attn style: +cu128torch2.8
71
+ # PyG style: +pt28cu128
72
+ local_patterns = [
73
+ f"+cu{cuda_short}torch{torch_version}", # flash-attn style
74
+ f"+pt{torch_short}cu{cuda_short}", # PyG style
75
+ ]
76
+
77
+ best_match = None
78
+ best_version = None
79
+
80
+ for match in wheel_pattern.finditer(html):
81
+ wheel_name = match.group(1)
82
+ # URL decode
83
+ wheel_name = wheel_name.replace("%2B", "+")
84
+
85
+ # Check if wheel matches our CUDA/torch version
86
+ for local_pattern in local_patterns:
87
+ if local_pattern in wheel_name:
88
+ # Extract version from wheel name
89
+ # Format: name-version+local-cpXX-cpXX-platform.whl
90
+ parts = wheel_name.split("-")
91
+ if len(parts) >= 2:
92
+ version_part = parts[1] # e.g., "2.8.3+cu128torch2.8"
93
+ if best_version is None or version_part > best_version:
94
+ best_version = version_part
95
+ best_match = f"{package}==={version_part}"
96
+ break
97
+
98
+ if best_match:
99
+ return best_match
100
+
101
+ return None
102
+
103
+
104
+ def get_package_spec(package: str, torch_version: str, cuda_version: str) -> str:
105
+ """
106
+ Get package spec with local version for CUDA/torch compatibility.
107
+ Queries the index to find matching wheels dynamically.
108
+ """
109
+ spec = find_matching_wheel(package, torch_version, cuda_version)
110
+ return spec if spec else package
111
+
47
112
 
48
113
  def get_all_find_links(package: str, torch_version: str, cuda_version: str) -> list:
49
114
  """Get all find-links URLs for a CUDA package."""
50
- cuda_short = cuda_version.replace(".", "")[:3]
51
-
52
- return [
53
- # cuda-wheels index (package-specific page)
54
- f"{CUDA_WHEELS_INDEX}{package}/",
55
- # flash-attn GitHub releases
56
- FLASH_ATTN_INDEX,
57
- # PyG index (torch version specific)
58
- f"{PYG_WHEELS_INDEX}torch-{torch_version}.0+cu{cuda_short}.html",
59
- ]
115
+ # Try both underscore and hyphen variants since directory naming is inconsistent
116
+ pkg_underscore = package.replace("-", "_")
117
+ pkg_hyphen = package.replace("_", "-")
118
+ urls = [f"{CUDA_WHEELS_INDEX}{package}/"]
119
+ if pkg_underscore != package:
120
+ urls.append(f"{CUDA_WHEELS_INDEX}{pkg_underscore}/")
121
+ if pkg_hyphen != package:
122
+ urls.append(f"{CUDA_WHEELS_INDEX}{pkg_hyphen}/")
123
+ return urls
60
124
 
61
125
 
62
126
  def get_current_platform() -> str:
@@ -340,6 +404,7 @@ def pixi_install(
340
404
  for package in cfg.cuda_packages:
341
405
  pip_cmd = [
342
406
  str(python_path), "-m", "pip", "install",
407
+ "--no-index", # Only use --find-links, don't query PyPI
343
408
  "--no-deps",
344
409
  "--no-cache-dir",
345
410
  ]
@@ -348,8 +413,10 @@ def pixi_install(
348
413
  for url in get_all_find_links(package, torch_version, cuda_version):
349
414
  pip_cmd.extend(["--find-links", url])
350
415
 
351
- log(f" Installing {package}")
352
- pip_cmd.append(package)
416
+ # Get package spec with local version for CUDA/torch compatibility
417
+ pkg_spec = get_package_spec(package, torch_version, cuda_version)
418
+ log(f" Installing {pkg_spec}")
419
+ pip_cmd.append(pkg_spec)
353
420
  result = subprocess.run(pip_cmd, capture_output=True, text=True)
354
421
  if result.returncode != 0:
355
422
  log(f"CUDA package install failed for {package}:\n{result.stderr}")
@@ -40,7 +40,44 @@ _SHUTDOWN = object()
40
40
  _CALL_METHOD = "call_method"
41
41
 
42
42
 
43
- def _worker_loop(queue_in, queue_out, sys_path_additions=None, lib_path=None, env_vars=None):
43
+ def _dump_worker_env(worker_name: str = "unknown"):
44
+ """Dump worker environment to .comfy-env/logs/ for debugging."""
45
+ import json
46
+ import os
47
+ import platform
48
+ import sys
49
+ from datetime import datetime
50
+ from pathlib import Path
51
+
52
+ log_dir = Path.cwd() / ".comfy-env" / "logs"
53
+ log_dir.mkdir(parents=True, exist_ok=True)
54
+
55
+ debug_info = {
56
+ "timestamp": datetime.now().isoformat(),
57
+ "worker_name": worker_name,
58
+ "pid": os.getpid(),
59
+ "cwd": os.getcwd(),
60
+ "python": {
61
+ "executable": sys.executable,
62
+ "version": sys.version,
63
+ "prefix": sys.prefix,
64
+ },
65
+ "platform": {
66
+ "system": platform.system(),
67
+ "machine": platform.machine(),
68
+ "release": platform.release(),
69
+ },
70
+ "env_vars": dict(os.environ),
71
+ "sys_path": sys.path,
72
+ "modules_loaded": sorted(sys.modules.keys()),
73
+ }
74
+
75
+ log_file = log_dir / f"worker_{worker_name}_{os.getpid()}.json"
76
+ log_file.write_text(json.dumps(debug_info, indent=2, default=str))
77
+ print(f"[comfy-env] Worker env dumped to: {log_file}")
78
+
79
+
80
+ def _worker_loop(queue_in, queue_out, sys_path_additions=None, lib_path=None, env_vars=None, worker_name=None):
44
81
  """
45
82
  Worker process main loop.
46
83
 
@@ -57,6 +94,7 @@ def _worker_loop(queue_in, queue_out, sys_path_additions=None, lib_path=None, en
57
94
  sys_path_additions: Paths to add to sys.path
58
95
  lib_path: Path to add to LD_LIBRARY_PATH (for conda libraries)
59
96
  env_vars: Environment variables to set (from comfy-env.toml)
97
+ worker_name: Name of the worker (for logging)
60
98
  """
61
99
  import os
62
100
  import sys
@@ -69,6 +107,10 @@ def _worker_loop(queue_in, queue_out, sys_path_additions=None, lib_path=None, en
69
107
  # Set worker mode env var
70
108
  os.environ["COMFYUI_ISOLATION_WORKER"] = "1"
71
109
 
110
+ # Debug logging if enabled
111
+ if os.environ.get("COMFY_ENV_DEBUG", "").lower() in ("1", "true", "yes"):
112
+ _dump_worker_env(worker_name or "unknown")
113
+
72
114
  # DLL/library isolation - match SubprocessWorker's isolation level
73
115
  # Filter out conflicting paths from conda/mamba/etc and use proper DLL registration
74
116
  path_sep = ";" if sys.platform == "win32" else ":"
@@ -511,7 +553,7 @@ class MPWorker(Worker):
511
553
  self._queue_out = ctx.Queue()
512
554
  self._process = ctx.Process(
513
555
  target=_worker_loop,
514
- args=(self._queue_in, self._queue_out, self._sys_path, self._lib_path, self._env_vars),
556
+ args=(self._queue_in, self._queue_out, self._sys_path, self._lib_path, self._env_vars, self.name),
515
557
  daemon=True,
516
558
  )
517
559
  self._process.start()
File without changes
File without changes
File without changes