comfy-env 0.0.24__py3-none-any.whl → 0.0.26__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/install.py CHANGED
@@ -22,9 +22,9 @@ import shutil
22
22
  import subprocess
23
23
  import sys
24
24
  from pathlib import Path
25
- from typing import Any, Callable, Dict, List, Optional, Union
25
+ from typing import Any, Callable, Dict, List, Optional, Set, Union
26
26
 
27
- from .env.config import IsolatedEnv, SystemConfig
27
+ from .env.config import IsolatedEnv, NodeReq, SystemConfig
28
28
  from .env.config_file import load_config, discover_config
29
29
  from .env.manager import IsolatedEnvManager
30
30
  from .errors import CUDANotFoundError, DependencyError, InstallError, WheelNotFoundError
@@ -132,6 +132,47 @@ def _install_system_packages(
132
132
  return True
133
133
 
134
134
 
135
+ def _install_node_dependencies(
136
+ node_reqs: List[NodeReq],
137
+ node_dir: Path,
138
+ log: Callable[[str], None],
139
+ dry_run: bool = False,
140
+ ) -> bool:
141
+ """
142
+ Install node dependencies (other ComfyUI custom nodes).
143
+
144
+ Args:
145
+ node_reqs: List of NodeReq objects from [node_reqs] config section.
146
+ node_dir: Directory of the current node (used to find custom_nodes/).
147
+ log: Logging callback.
148
+ dry_run: If True, show what would be installed without installing.
149
+
150
+ Returns:
151
+ True if installation succeeded or no dependencies needed.
152
+ """
153
+ from .nodes import install_node_deps
154
+
155
+ # Detect custom_nodes directory (parent of current node)
156
+ custom_nodes_dir = node_dir.parent
157
+
158
+ log(f"\nInstalling {len(node_reqs)} node dependencies...")
159
+
160
+ if dry_run:
161
+ for req in node_reqs:
162
+ node_path = custom_nodes_dir / req.name
163
+ status = "exists" if node_path.exists() else "would clone"
164
+ log(f" {req.name}: {status}")
165
+ return True
166
+
167
+ # Track visited nodes to prevent cycles
168
+ # Start with current node's directory name
169
+ visited: Set[str] = {node_dir.name}
170
+
171
+ install_node_deps(node_reqs, custom_nodes_dir, log, visited)
172
+
173
+ return True
174
+
175
+
135
176
  def install(
136
177
  config: Optional[Union[str, Path]] = None,
137
178
  mode: str = "inplace",
@@ -185,7 +226,12 @@ def install(
185
226
  "Create comfy-env.toml or specify path explicitly."
186
227
  )
187
228
 
188
- # Install system packages first (apt, brew, etc.)
229
+ # Install node dependencies first (other ComfyUI custom nodes)
230
+ # These may have their own system packages and Python packages
231
+ if full_config.node_reqs:
232
+ _install_node_dependencies(full_config.node_reqs, node_dir, log, dry_run)
233
+
234
+ # Install system packages (apt, brew, etc.)
189
235
  # These need to be installed before Python packages that depend on them
190
236
  if full_config.has_system:
191
237
  _install_system_packages(full_config.system, log, dry_run)
comfy_env/nodes.py ADDED
@@ -0,0 +1,154 @@
1
+ """
2
+ Node dependency installation for comfy-env.
3
+
4
+ This module handles installation of dependent ComfyUI custom nodes
5
+ specified in the [node_reqs] section of comfy-env.toml.
6
+
7
+ Example configuration:
8
+ [node_reqs]
9
+ ComfyUI_essentials = "cubiq/ComfyUI_essentials"
10
+ ComfyUI-DepthAnythingV2 = "kijai/ComfyUI-DepthAnythingV2"
11
+ """
12
+
13
+ import subprocess
14
+ import sys
15
+ from pathlib import Path
16
+ from typing import TYPE_CHECKING, Callable, List, Set
17
+
18
+ if TYPE_CHECKING:
19
+ from .env.config import NodeReq
20
+
21
+
22
+ def normalize_repo_url(repo: str) -> str:
23
+ """
24
+ Convert GitHub shorthand to full URL.
25
+
26
+ Args:
27
+ repo: Either 'owner/repo' or full URL like 'https://github.com/owner/repo'
28
+
29
+ Returns:
30
+ Full GitHub URL
31
+ """
32
+ if repo.startswith("http://") or repo.startswith("https://"):
33
+ return repo
34
+ return f"https://github.com/{repo}"
35
+
36
+
37
+ def clone_node(
38
+ repo: str,
39
+ name: str,
40
+ target_dir: Path,
41
+ log: Callable[[str], None],
42
+ ) -> Path:
43
+ """
44
+ Clone a node repository to target_dir/name.
45
+
46
+ Args:
47
+ repo: GitHub repo path (e.g., 'owner/repo') or full URL
48
+ name: Directory name for the cloned repo
49
+ target_dir: Parent directory (usually custom_nodes/)
50
+ log: Logging callback
51
+
52
+ Returns:
53
+ Path to the cloned node directory
54
+
55
+ Raises:
56
+ RuntimeError: If git clone fails
57
+ """
58
+ node_path = target_dir / name
59
+ url = normalize_repo_url(repo)
60
+
61
+ log(f" Cloning {name} from {url}...")
62
+ result = subprocess.run(
63
+ ["git", "clone", "--depth", "1", url, str(node_path)],
64
+ capture_output=True,
65
+ text=True,
66
+ )
67
+
68
+ if result.returncode != 0:
69
+ raise RuntimeError(f"Failed to clone {url}: {result.stderr.strip()}")
70
+
71
+ return node_path
72
+
73
+
74
+ def run_install_script(
75
+ node_dir: Path,
76
+ log: Callable[[str], None],
77
+ ) -> None:
78
+ """
79
+ Run install.py in a node directory if it exists.
80
+
81
+ Args:
82
+ node_dir: Path to the node directory
83
+ log: Logging callback
84
+ """
85
+ install_script = node_dir / "install.py"
86
+
87
+ if install_script.exists():
88
+ log(f" Running install.py for {node_dir.name}...")
89
+ result = subprocess.run(
90
+ [sys.executable, str(install_script)],
91
+ cwd=node_dir,
92
+ capture_output=True,
93
+ text=True,
94
+ )
95
+ if result.returncode != 0:
96
+ log(f" Warning: install.py failed for {node_dir.name}: {result.stderr.strip()[:200]}")
97
+
98
+
99
+ def install_node_deps(
100
+ node_reqs: "List[NodeReq]",
101
+ custom_nodes_dir: Path,
102
+ log: Callable[[str], None],
103
+ visited: Set[str],
104
+ ) -> None:
105
+ """
106
+ Install node dependencies recursively.
107
+
108
+ Args:
109
+ node_reqs: List of NodeReq objects to install
110
+ custom_nodes_dir: Path to custom_nodes directory
111
+ log: Logging callback
112
+ visited: Set of already-processed node names (for cycle detection)
113
+ """
114
+ from .env.config_file import discover_config
115
+
116
+ for req in node_reqs:
117
+ # Skip if already visited (cycle detection)
118
+ if req.name in visited:
119
+ log(f" {req.name}: already in dependency chain, skipping")
120
+ continue
121
+
122
+ visited.add(req.name)
123
+
124
+ node_path = custom_nodes_dir / req.name
125
+
126
+ # Skip if already installed (directory exists)
127
+ if node_path.exists():
128
+ log(f" {req.name}: already installed, skipping")
129
+ continue
130
+
131
+ try:
132
+ # Clone the repository
133
+ clone_node(req.repo, req.name, custom_nodes_dir, log)
134
+
135
+ # Run install.py if present
136
+ run_install_script(node_path, log)
137
+
138
+ # Recursively process nested node_reqs
139
+ try:
140
+ nested_config = discover_config(node_path)
141
+ if nested_config and nested_config.node_reqs:
142
+ log(f" {req.name}: found {len(nested_config.node_reqs)} nested dependencies")
143
+ install_node_deps(
144
+ nested_config.node_reqs,
145
+ custom_nodes_dir,
146
+ log,
147
+ visited,
148
+ )
149
+ except Exception as e:
150
+ # Don't fail if we can't parse nested config
151
+ log(f" {req.name}: could not check for nested deps: {e}")
152
+
153
+ except Exception as e:
154
+ log(f" Warning: Failed to install {req.name}: {e}")
@@ -58,38 +58,48 @@ packages:
58
58
  description: PyTorch3D - 3D deep learning library
59
59
 
60
60
  # ===========================================================================
61
- # PozzettiAndrea wheel repos (GitHub Pages indexes)
61
+ # PozzettiAndrea cuda-wheels (unified PEP 503 index)
62
+ # https://pozzettiandrea.github.io/cuda-wheels
62
63
  # ===========================================================================
63
64
  nvdiffrast:
64
- method: github_index
65
- index_url: "https://pozzettiandrea.github.io/nvdiffrast-full-wheels/cu{cuda_short}-torch{torch_short}/"
65
+ method: index
66
+ index_url: "https://pozzettiandrea.github.io/cuda-wheels"
66
67
  description: NVIDIA differentiable rasterizer
67
68
 
68
69
  cumesh:
69
- method: github_index
70
- index_url: "https://pozzettiandrea.github.io/cumesh-wheels/cu{cuda_short}-torch{torch_short}/"
70
+ method: index
71
+ index_url: "https://pozzettiandrea.github.io/cuda-wheels"
71
72
  description: CUDA-accelerated mesh utilities
72
73
 
73
74
  o_voxel:
74
- method: github_index
75
- index_url: "https://pozzettiandrea.github.io/ovoxel-wheels/cu{cuda_short}-torch{torch_short}/"
75
+ method: index
76
+ index_url: "https://pozzettiandrea.github.io/cuda-wheels"
76
77
  description: O-Voxel CUDA extension for TRELLIS
77
78
 
78
79
  flex_gemm:
79
- method: github_index
80
- index_url: "https://pozzettiandrea.github.io/flexgemm-wheels/cu{cuda_short}-torch{torch_short}/"
80
+ method: index
81
+ index_url: "https://pozzettiandrea.github.io/cuda-wheels"
81
82
  description: Flexible GEMM operations
82
83
 
83
84
  nvdiffrec_render:
84
- method: github_release
85
+ method: index
86
+ index_url: "https://pozzettiandrea.github.io/cuda-wheels"
85
87
  description: NVDiffRec rendering utilities
86
- sources:
87
- - name: PozzettiAndrea
88
- url_template: "https://github.com/PozzettiAndrea/nvdiffrec_render-wheels/releases/download/cu{cuda_short}-torch{torch_short}/nvdiffrec_render-{version}%2Bcu{cuda_short}torch{torch_mm}-{py_tag}-{py_tag}-linux_x86_64.whl"
89
- platforms: [linux_x86_64]
90
- - name: PozzettiAndrea-windows
91
- url_template: "https://github.com/PozzettiAndrea/nvdiffrec_render-wheels/releases/download/cu{cuda_short}-torch{torch_short}/nvdiffrec_render-{version}%2Bcu{cuda_short}torch{torch_mm}-{py_tag}-{py_tag}-win_amd64.whl"
92
- platforms: [win_amd64]
88
+
89
+ gsplat:
90
+ method: index
91
+ index_url: "https://pozzettiandrea.github.io/cuda-wheels"
92
+ description: Gaussian splatting rasterization
93
+
94
+ cc_torch:
95
+ method: index
96
+ index_url: "https://pozzettiandrea.github.io/cuda-wheels"
97
+ description: GPU-accelerated connected components
98
+
99
+ torch_generic_nms:
100
+ method: index
101
+ index_url: "https://pozzettiandrea.github.io/cuda-wheels"
102
+ description: GPU-accelerated Non-Maximum Suppression
93
103
 
94
104
  # ===========================================================================
95
105
  # spconv - PyPI with CUDA-versioned package names
@@ -103,15 +113,9 @@ packages:
103
113
  # sageattention - Fast quantized attention
104
114
  # ===========================================================================
105
115
  sageattention:
106
- method: github_release
116
+ method: index
117
+ index_url: "https://pozzettiandrea.github.io/cuda-wheels"
107
118
  description: SageAttention - 2-5x faster than FlashAttention with quantized kernels
108
- sources:
109
- - name: kijai-hf
110
- url_template: "https://huggingface.co/Kijai/PrecompiledWheels/resolve/main/sageattention-{version}-cp312-cp312-linux_x86_64.whl"
111
- platforms: [linux_x86_64]
112
- - name: woct0rdho
113
- url_template: "https://github.com/woct0rdho/SageAttention/releases/download/v2.2.0-windows.post3/sageattention-2.2.0%2Bcu{cuda_short}torch{torch_version}.post3-cp39-abi3-win_amd64.whl"
114
- platforms: [win_amd64]
115
119
 
116
120
  # ===========================================================================
117
121
  # triton - Required for sageattention on Linux
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comfy-env
3
- Version: 0.0.24
3
+ Version: 0.0.26
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
@@ -2,7 +2,8 @@ comfy_env/__init__.py,sha256=u2uTyoysPQyNMcRp5U4VTMJF11FBW6Goqu0DN-BdUuY,3678
2
2
  comfy_env/cli.py,sha256=X-GCQMP0mtMcE3ZgkT-VLQ4Gq3UUvcb_Ux_NClEFhgI,15975
3
3
  comfy_env/decorator.py,sha256=6JCKwLHaZtOLVDexs_gh_-NtS2ZK0V7nGCPqkyeYEAA,16688
4
4
  comfy_env/errors.py,sha256=8hN8NDlo8oBUdapc-eT3ZluigI5VBzfqsSBvQdfWlz4,9943
5
- comfy_env/install.py,sha256=CA5O0-ghkTdV67fVyKJy6wt1vL35inBeqWtA52udjHI,24301
5
+ comfy_env/install.py,sha256=pyyaYbCFZn6T5_sgjumd1TeFaTEXUTcdFbex31m_JEc,25837
6
+ comfy_env/nodes.py,sha256=CWUe35jU5SKk4ur-SddZePdqWgxJDlxGhpcJiu5pAK4,4354
6
7
  comfy_env/pixi.py,sha256=Oj9Z9bBg8IpsPF3usAx9v4w6OhpNk4LQjUuAP5ohnQY,13324
7
8
  comfy_env/registry.py,sha256=uFCtGmWYvwGCqObXgzmArX7o5JsFNsHXxayofk3m6no,2569
8
9
  comfy_env/resolver.py,sha256=l-AnmCE1puG6CvdpDB-KrsfG_cn_3uO2DryYizUnG_4,12474
@@ -32,9 +33,9 @@ comfy_env/workers/pool.py,sha256=MtjeOWfvHSCockq8j1gfnxIl-t01GSB79T5N4YB82Lg,695
32
33
  comfy_env/workers/tensor_utils.py,sha256=TCuOAjJymrSbkgfyvcKtQ_KbVWTqSwP9VH_bCaFLLq8,6409
33
34
  comfy_env/workers/torch_mp.py,sha256=4YSNPn7hALrvMVbkO4RkTeFTcc0lhfLMk5QTWjY4PHw,22134
34
35
  comfy_env/workers/venv.py,sha256=_ekHfZPqBIPY08DjqiXm6cTBQH4DrbxRWR3AAv3mit8,31589
35
- comfy_env/wheel_sources.yml,sha256=nSZ8XB_I5JXQGB7AgC6lHs_IXMd9Kcno10artNL8BKw,7775
36
- comfy_env-0.0.24.dist-info/METADATA,sha256=acYtmccSaZ0IrwiSKLZAekSHGur8G0vsmqYc0Uw0D-U,5400
37
- comfy_env-0.0.24.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
38
- comfy_env-0.0.24.dist-info/entry_points.txt,sha256=J4fXeqgxU_YenuW_Zxn_pEL7J-3R0--b6MS5t0QmAr0,49
39
- comfy_env-0.0.24.dist-info/licenses/LICENSE,sha256=E68QZMMpW4P2YKstTZ3QU54HRQO8ecew09XZ4_Vn870,1093
40
- comfy_env-0.0.24.dist-info/RECORD,,
36
+ comfy_env/wheel_sources.yml,sha256=O9DJhhZnaTbV9A3Kidq8T4VntxyGAod0epT2dK-43N8,7139
37
+ comfy_env-0.0.26.dist-info/METADATA,sha256=0w8ajF9E7Qu5aUsFhrO4NEmCHaIdKwn1OCN9PqrS_nI,5400
38
+ comfy_env-0.0.26.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
39
+ comfy_env-0.0.26.dist-info/entry_points.txt,sha256=J4fXeqgxU_YenuW_Zxn_pEL7J-3R0--b6MS5t0QmAr0,49
40
+ comfy_env-0.0.26.dist-info/licenses/LICENSE,sha256=E68QZMMpW4P2YKstTZ3QU54HRQO8ecew09XZ4_Vn870,1093
41
+ comfy_env-0.0.26.dist-info/RECORD,,