comfy-env 0.0.24__py3-none-any.whl → 0.0.25__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}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comfy-env
3
- Version: 0.0.24
3
+ Version: 0.0.25
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
@@ -33,8 +34,8 @@ comfy_env/workers/tensor_utils.py,sha256=TCuOAjJymrSbkgfyvcKtQ_KbVWTqSwP9VH_bCaF
33
34
  comfy_env/workers/torch_mp.py,sha256=4YSNPn7hALrvMVbkO4RkTeFTcc0lhfLMk5QTWjY4PHw,22134
34
35
  comfy_env/workers/venv.py,sha256=_ekHfZPqBIPY08DjqiXm6cTBQH4DrbxRWR3AAv3mit8,31589
35
36
  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,,
37
+ comfy_env-0.0.25.dist-info/METADATA,sha256=IcYAcYC42bhuCU4ijkeY1CfUwX4b3AVRhnFZ3mR1BXw,5400
38
+ comfy_env-0.0.25.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
39
+ comfy_env-0.0.25.dist-info/entry_points.txt,sha256=J4fXeqgxU_YenuW_Zxn_pEL7J-3R0--b6MS5t0QmAr0,49
40
+ comfy_env-0.0.25.dist-info/licenses/LICENSE,sha256=E68QZMMpW4P2YKstTZ3QU54HRQO8ecew09XZ4_Vn870,1093
41
+ comfy_env-0.0.25.dist-info/RECORD,,