comfy-env 0.1.8__py3-none-any.whl → 0.1.9__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/isolation/wrap.py +11 -0
- comfy_env/nodes.py +28 -0
- comfy_env/pixi/core.py +96 -13
- comfy_env/workers/subprocess.py +9 -3
- {comfy_env-0.1.8.dist-info → comfy_env-0.1.9.dist-info}/METADATA +2 -1
- {comfy_env-0.1.8.dist-info → comfy_env-0.1.9.dist-info}/RECORD +9 -9
- {comfy_env-0.1.8.dist-info → comfy_env-0.1.9.dist-info}/WHEEL +0 -0
- {comfy_env-0.1.8.dist-info → comfy_env-0.1.9.dist-info}/entry_points.txt +0 -0
- {comfy_env-0.1.8.dist-info → comfy_env-0.1.9.dist-info}/licenses/LICENSE +0 -0
comfy_env/isolation/wrap.py
CHANGED
|
@@ -319,6 +319,13 @@ def wrap_isolated_nodes(
|
|
|
319
319
|
if os.environ.get("COMFYUI_ISOLATION_WORKER") == "1":
|
|
320
320
|
return node_class_mappings
|
|
321
321
|
|
|
322
|
+
# Get ComfyUI base path from folder_paths (canonical source)
|
|
323
|
+
try:
|
|
324
|
+
import folder_paths
|
|
325
|
+
comfyui_base = folder_paths.base_path
|
|
326
|
+
except ImportError:
|
|
327
|
+
comfyui_base = None
|
|
328
|
+
|
|
322
329
|
nodes_dir = Path(nodes_dir).resolve()
|
|
323
330
|
|
|
324
331
|
# Check for comfy-env.toml
|
|
@@ -341,6 +348,10 @@ def wrap_isolated_nodes(
|
|
|
341
348
|
except Exception:
|
|
342
349
|
pass # Ignore errors reading config
|
|
343
350
|
|
|
351
|
+
# Set COMFYUI_BASE for worker to find ComfyUI modules
|
|
352
|
+
if comfyui_base:
|
|
353
|
+
env_vars["COMFYUI_BASE"] = str(comfyui_base)
|
|
354
|
+
|
|
344
355
|
# Find environment directory and paths
|
|
345
356
|
env_dir = _find_env_dir(nodes_dir)
|
|
346
357
|
site_packages, lib_dir = _find_env_paths(nodes_dir)
|
comfy_env/nodes.py
CHANGED
|
@@ -71,6 +71,31 @@ def clone_node(
|
|
|
71
71
|
return node_path
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
def install_requirements(
|
|
75
|
+
node_dir: Path,
|
|
76
|
+
log: Callable[[str], None],
|
|
77
|
+
) -> None:
|
|
78
|
+
"""
|
|
79
|
+
Install requirements.txt in a node directory if it exists.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
node_dir: Path to the node directory
|
|
83
|
+
log: Logging callback
|
|
84
|
+
"""
|
|
85
|
+
requirements_file = node_dir / "requirements.txt"
|
|
86
|
+
|
|
87
|
+
if requirements_file.exists():
|
|
88
|
+
log(f" Installing requirements for {node_dir.name}...")
|
|
89
|
+
result = subprocess.run(
|
|
90
|
+
["uv", "pip", "install", "-r", str(requirements_file), "--python", sys.executable],
|
|
91
|
+
cwd=node_dir,
|
|
92
|
+
capture_output=True,
|
|
93
|
+
text=True,
|
|
94
|
+
)
|
|
95
|
+
if result.returncode != 0:
|
|
96
|
+
log(f" Warning: requirements.txt install failed for {node_dir.name}: {result.stderr.strip()[:200]}")
|
|
97
|
+
|
|
98
|
+
|
|
74
99
|
def run_install_script(
|
|
75
100
|
node_dir: Path,
|
|
76
101
|
log: Callable[[str], None],
|
|
@@ -132,6 +157,9 @@ def install_node_deps(
|
|
|
132
157
|
# Clone the repository
|
|
133
158
|
clone_node(req.repo, req.name, custom_nodes_dir, log)
|
|
134
159
|
|
|
160
|
+
# Install requirements.txt if present
|
|
161
|
+
install_requirements(node_path, log)
|
|
162
|
+
|
|
135
163
|
# Run install.py if present
|
|
136
164
|
run_install_script(node_path, log)
|
|
137
165
|
|
comfy_env/pixi/core.py
CHANGED
|
@@ -40,10 +40,87 @@ CUDA_TORCH_MAP = {
|
|
|
40
40
|
"12.1": "2.4",
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
def find_wheel_url(
|
|
44
|
+
package: str,
|
|
45
|
+
torch_version: str,
|
|
46
|
+
cuda_version: str,
|
|
47
|
+
python_version: str,
|
|
48
|
+
) -> Optional[str]:
|
|
49
|
+
"""
|
|
50
|
+
Query cuda-wheels index and return the direct URL for the matching wheel.
|
|
51
|
+
|
|
52
|
+
This bypasses pip's version validation by providing a direct URL,
|
|
53
|
+
which is necessary for wheels where the filename has a local version
|
|
54
|
+
but the internal METADATA doesn't (e.g., flash-attn from mjun0812).
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
package: Package name (e.g., "flash-attn")
|
|
58
|
+
torch_version: PyTorch version (e.g., "2.8")
|
|
59
|
+
cuda_version: CUDA version (e.g., "12.8")
|
|
60
|
+
python_version: Python version (e.g., "3.10")
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Direct URL to the wheel file, or None if no match found.
|
|
64
|
+
"""
|
|
65
|
+
cuda_short = cuda_version.replace(".", "")[:3] # "12.8" -> "128"
|
|
66
|
+
torch_short = torch_version.replace(".", "")[:2] # "2.8" -> "28"
|
|
67
|
+
py_tag = f"cp{python_version.replace('.', '')}" # "3.10" -> "cp310"
|
|
68
|
+
|
|
69
|
+
# Platform tag for current system
|
|
70
|
+
if sys.platform == "linux":
|
|
71
|
+
platform_tag = "linux_x86_64"
|
|
72
|
+
elif sys.platform == "win32":
|
|
73
|
+
platform_tag = "win_amd64"
|
|
74
|
+
else:
|
|
75
|
+
platform_tag = None # macOS doesn't typically have CUDA wheels
|
|
76
|
+
|
|
77
|
+
# Local version patterns to match:
|
|
78
|
+
# cuda-wheels style: +cu128torch28
|
|
79
|
+
# PyG style: +pt28cu128
|
|
80
|
+
local_patterns = [
|
|
81
|
+
f"+cu{cuda_short}torch{torch_short}", # cuda-wheels style
|
|
82
|
+
f"+pt{torch_short}cu{cuda_short}", # PyG style
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
pkg_variants = [package, package.replace("-", "_"), package.replace("_", "-")]
|
|
86
|
+
|
|
87
|
+
for pkg_dir in pkg_variants:
|
|
88
|
+
index_url = f"{CUDA_WHEELS_INDEX}{pkg_dir}/"
|
|
89
|
+
try:
|
|
90
|
+
with urllib.request.urlopen(index_url, timeout=10) as resp:
|
|
91
|
+
html = resp.read().decode("utf-8")
|
|
92
|
+
except Exception:
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
# Parse href and display name from HTML: <a href="URL">DISPLAY_NAME</a>
|
|
96
|
+
link_pattern = re.compile(r'href="([^"]+\.whl)"[^>]*>([^<]+)</a>', re.IGNORECASE)
|
|
97
|
+
|
|
98
|
+
for match in link_pattern.finditer(html):
|
|
99
|
+
wheel_url = match.group(1)
|
|
100
|
+
display_name = match.group(2)
|
|
101
|
+
|
|
102
|
+
# Match on display name (has normalized torch28 format)
|
|
103
|
+
matches_cuda_torch = any(p in display_name for p in local_patterns)
|
|
104
|
+
matches_python = py_tag in display_name
|
|
105
|
+
matches_platform = platform_tag is None or platform_tag in display_name
|
|
106
|
+
|
|
107
|
+
if matches_cuda_torch and matches_python and matches_platform:
|
|
108
|
+
# Return absolute URL
|
|
109
|
+
if wheel_url.startswith("http"):
|
|
110
|
+
return wheel_url
|
|
111
|
+
# Relative URL - construct absolute
|
|
112
|
+
return f"{CUDA_WHEELS_INDEX}{pkg_dir}/{wheel_url}"
|
|
113
|
+
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
|
|
43
117
|
def find_matching_wheel(package: str, torch_version: str, cuda_version: str) -> Optional[str]:
|
|
44
118
|
"""
|
|
45
119
|
Query cuda-wheels index to find a wheel matching the CUDA/torch version.
|
|
46
120
|
Returns the full version spec (e.g., "flash-attn===2.8.3+cu128torch2.8") or None.
|
|
121
|
+
|
|
122
|
+
Note: This is used as a fallback for packages with correct wheel metadata.
|
|
123
|
+
For packages with mismatched metadata (like flash-attn), use find_wheel_url() instead.
|
|
47
124
|
"""
|
|
48
125
|
cuda_short = cuda_version.replace(".", "")[:3] # "12.8" -> "128"
|
|
49
126
|
torch_short = torch_version.replace(".", "")[:2] # "2.8" -> "28"
|
|
@@ -67,11 +144,11 @@ def find_matching_wheel(package: str, torch_version: str, cuda_version: str) ->
|
|
|
67
144
|
)
|
|
68
145
|
|
|
69
146
|
# Local version patterns to match:
|
|
70
|
-
#
|
|
147
|
+
# cuda-wheels style: +cu128torch28
|
|
71
148
|
# PyG style: +pt28cu128
|
|
72
149
|
local_patterns = [
|
|
73
|
-
f"+cu{cuda_short}torch{
|
|
74
|
-
f"+pt{torch_short}cu{cuda_short}",
|
|
150
|
+
f"+cu{cuda_short}torch{torch_short}", # cuda-wheels style
|
|
151
|
+
f"+pt{torch_short}cu{cuda_short}", # PyG style
|
|
75
152
|
]
|
|
76
153
|
|
|
77
154
|
best_match = None
|
|
@@ -394,29 +471,35 @@ def pixi_install(
|
|
|
394
471
|
log(f"pixi install failed:\n{result.stderr}")
|
|
395
472
|
raise RuntimeError(f"pixi install failed: {result.stderr}")
|
|
396
473
|
|
|
397
|
-
# Install CUDA packages
|
|
474
|
+
# Install CUDA packages via direct URL or find-links fallback
|
|
398
475
|
if cfg.cuda_packages and cuda_version:
|
|
399
476
|
log(f"Installing CUDA packages: {cfg.cuda_packages}")
|
|
400
477
|
python_path = get_pixi_python(node_dir)
|
|
401
478
|
if not python_path:
|
|
402
479
|
raise RuntimeError("Could not find Python in pixi environment")
|
|
403
480
|
|
|
481
|
+
# Get Python version from the pixi environment
|
|
482
|
+
py_version = f"{sys.version_info.major}.{sys.version_info.minor}"
|
|
483
|
+
|
|
404
484
|
for package in cfg.cuda_packages:
|
|
485
|
+
# Find direct wheel URL (bypasses metadata validation)
|
|
486
|
+
wheel_url = find_wheel_url(package, torch_version, cuda_version, py_version)
|
|
487
|
+
|
|
488
|
+
if not wheel_url:
|
|
489
|
+
raise RuntimeError(
|
|
490
|
+
f"No wheel found for {package} with CUDA {cuda_version}, "
|
|
491
|
+
f"torch {torch_version}, Python {py_version}. "
|
|
492
|
+
f"Check cuda-wheels index."
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
log(f" Installing {package} from {wheel_url}")
|
|
405
496
|
pip_cmd = [
|
|
406
497
|
str(python_path), "-m", "pip", "install",
|
|
407
|
-
"--no-index", # Only use --find-links, don't query PyPI
|
|
408
498
|
"--no-deps",
|
|
409
499
|
"--no-cache-dir",
|
|
500
|
+
wheel_url,
|
|
410
501
|
]
|
|
411
502
|
|
|
412
|
-
# Add all find-links sources
|
|
413
|
-
for url in get_all_find_links(package, torch_version, cuda_version):
|
|
414
|
-
pip_cmd.extend(["--find-links", url])
|
|
415
|
-
|
|
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)
|
|
420
503
|
result = subprocess.run(pip_cmd, capture_output=True, text=True)
|
|
421
504
|
if result.returncode != 0:
|
|
422
505
|
log(f"CUDA package install failed for {package}:\n{result.stderr}")
|
comfy_env/workers/subprocess.py
CHANGED
|
@@ -1044,15 +1044,21 @@ class SubprocessWorker(Worker):
|
|
|
1044
1044
|
|
|
1045
1045
|
def _find_comfyui_base(self) -> Optional[Path]:
|
|
1046
1046
|
"""Find ComfyUI base directory."""
|
|
1047
|
-
#
|
|
1048
|
-
|
|
1047
|
+
# Use folder_paths.base_path (canonical source) if available
|
|
1048
|
+
try:
|
|
1049
|
+
import folder_paths
|
|
1050
|
+
return Path(folder_paths.base_path)
|
|
1051
|
+
except ImportError:
|
|
1052
|
+
pass
|
|
1053
|
+
|
|
1054
|
+
# Fallback: Check common child directories (for test environments)
|
|
1049
1055
|
for base in [self.working_dir, self.working_dir.parent]:
|
|
1050
1056
|
for child in [".comfy-test-env/ComfyUI", "ComfyUI"]:
|
|
1051
1057
|
candidate = base / child
|
|
1052
1058
|
if (candidate / "main.py").exists() and (candidate / "comfy").exists():
|
|
1053
1059
|
return candidate
|
|
1054
1060
|
|
|
1055
|
-
# Walk up from working_dir (standard ComfyUI custom_nodes layout)
|
|
1061
|
+
# Fallback: Walk up from working_dir (standard ComfyUI custom_nodes layout)
|
|
1056
1062
|
current = self.working_dir.resolve()
|
|
1057
1063
|
for _ in range(10):
|
|
1058
1064
|
if (current / "main.py").exists() and (current / "comfy").exists():
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: comfy-env
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
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
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
19
|
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: pip>=21.0
|
|
20
21
|
Requires-Dist: tomli-w>=1.0.0
|
|
21
22
|
Requires-Dist: tomli>=2.0.0; python_version < '3.11'
|
|
22
23
|
Requires-Dist: uv>=0.4.0
|
|
@@ -2,15 +2,15 @@ comfy_env/__init__.py,sha256=s0RkyKsBlDiSI4ZSwivtWLvYhc8DS0CUEWgFLVJbtLA,2176
|
|
|
2
2
|
comfy_env/cli.py,sha256=ty4HYlzollCUCS0o6Sha6eczPAsW_gHRVgvck3IfA2w,12723
|
|
3
3
|
comfy_env/errors.py,sha256=q-C3vyrPa_kk_Ao8l17mIGfJiG2IR0hCFV0GFcNLmcI,9924
|
|
4
4
|
comfy_env/install.py,sha256=N7eBj8wB2DrGepVYk-Hks2mSf6UuGzj34pfVLNYJgQ4,10357
|
|
5
|
-
comfy_env/nodes.py,sha256=
|
|
5
|
+
comfy_env/nodes.py,sha256=nR-LctovXYTqk-seHovq9W_Ll0hlLeGMw-SQsa6IkBM,5235
|
|
6
6
|
comfy_env/prestartup.py,sha256=NML_nzg4h37YdEg7gHxA7ke_uXWZsldz-X3HldHEUsE,5338
|
|
7
7
|
comfy_env/config/__init__.py,sha256=4Guylkb-FV8QxhFwschzpzbr2eu8y-KNgNT3_JOm9jc,403
|
|
8
8
|
comfy_env/config/parser.py,sha256=dA1lX5ExBEfCqUJwe4V5i_jn2NJ69bMq3c3ji3lMSV8,4295
|
|
9
9
|
comfy_env/config/types.py,sha256=Sb8HO34xsSZu5YAc2K4M7Gb3QNevJlngf12hHiwuU0w,2140
|
|
10
10
|
comfy_env/isolation/__init__.py,sha256=vw9a4mpJ2CFjy-PLe_A3zQ6umBQklgqWNxwn9beNw3g,175
|
|
11
|
-
comfy_env/isolation/wrap.py,sha256=
|
|
11
|
+
comfy_env/isolation/wrap.py,sha256=fYsf3tWpzNPoZT7mvWHbD-LmgBMhMCIfzlfcOFgwhlU,12641
|
|
12
12
|
comfy_env/pixi/__init__.py,sha256=BUrq7AQf3WDm0cHWh72B2xZbURNnDu2dCuELWiQCUiM,997
|
|
13
|
-
comfy_env/pixi/core.py,sha256=
|
|
13
|
+
comfy_env/pixi/core.py,sha256=zPEtuF8D2gnIaljmGEGT8Ttw0lTy_cV-hIIn1fvlbBU,18788
|
|
14
14
|
comfy_env/pixi/cuda_detection.py,sha256=sqB3LjvGNdV4eFqiARQGfyecBM3ZiUmeh6nG0YCRYQw,9751
|
|
15
15
|
comfy_env/pixi/resolver.py,sha256=U_A8rBDxCj4gUlJt2YJQniP4cCKqxJEiVFgXOoH7vM8,6339
|
|
16
16
|
comfy_env/pixi/platform/__init__.py,sha256=Nb5MPZIEeanSMEWwqU4p4bnEKTJn1tWcwobnhq9x9IY,614
|
|
@@ -23,10 +23,10 @@ comfy_env/templates/comfy-env.toml,sha256=ROIqi4BlPL1MEdL1VgebfTHpdwPNYGHwWeigI9
|
|
|
23
23
|
comfy_env/workers/__init__.py,sha256=TMVG55d2XLP1mJ3x1d16H0SBDJZtk2kMC5P4HLk9TrA,1073
|
|
24
24
|
comfy_env/workers/base.py,sha256=4ZYTaQ4J0kBHCoO_OfZnsowm4rJCoqinZUaOtgkOPbw,2307
|
|
25
25
|
comfy_env/workers/mp.py,sha256=8Xqcr4R4KlHtP1P_UDAtSTPHA_5i-ySTlxxLpqyOk-w,29757
|
|
26
|
-
comfy_env/workers/subprocess.py,sha256=
|
|
26
|
+
comfy_env/workers/subprocess.py,sha256=a9gpaTppbL7ZRgv4grK__QioGUiN0Ci9RIZGTVTsQ78,57993
|
|
27
27
|
comfy_env/workers/tensor_utils.py,sha256=TCuOAjJymrSbkgfyvcKtQ_KbVWTqSwP9VH_bCaFLLq8,6409
|
|
28
|
-
comfy_env-0.1.
|
|
29
|
-
comfy_env-0.1.
|
|
30
|
-
comfy_env-0.1.
|
|
31
|
-
comfy_env-0.1.
|
|
32
|
-
comfy_env-0.1.
|
|
28
|
+
comfy_env-0.1.9.dist-info/METADATA,sha256=qplbcTtKJj8y2m8rGUpIMcZyZymKdqA7wQNQf4t5HsI,6970
|
|
29
|
+
comfy_env-0.1.9.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
30
|
+
comfy_env-0.1.9.dist-info/entry_points.txt,sha256=J4fXeqgxU_YenuW_Zxn_pEL7J-3R0--b6MS5t0QmAr0,49
|
|
31
|
+
comfy_env-0.1.9.dist-info/licenses/LICENSE,sha256=E68QZMMpW4P2YKstTZ3QU54HRQO8ecew09XZ4_Vn870,1093
|
|
32
|
+
comfy_env-0.1.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|