comfy-env 0.1.14__tar.gz → 0.1.15__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 (36) hide show
  1. {comfy_env-0.1.14 → comfy_env-0.1.15}/PKG-INFO +2 -2
  2. {comfy_env-0.1.14 → comfy_env-0.1.15}/pyproject.toml +5 -5
  3. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/__init__.py +8 -30
  4. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/cache.py +11 -139
  5. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/cli.py +9 -11
  6. comfy_env-0.1.15/src/comfy_env/config/__init__.py +17 -0
  7. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/config/parser.py +28 -75
  8. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/install.py +141 -25
  9. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/isolation/__init__.py +2 -1
  10. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/isolation/wrap.py +136 -15
  11. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/nodes.py +1 -1
  12. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/pixi/core.py +1 -2
  13. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/prestartup.py +31 -15
  14. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/workers/subprocess.py +1 -1
  15. comfy_env-0.1.14/src/comfy_env/config/__init__.py +0 -19
  16. comfy_env-0.1.14/src/comfy_env/config/types.py +0 -70
  17. comfy_env-0.1.14/src/comfy_env/errors.py +0 -293
  18. {comfy_env-0.1.14 → comfy_env-0.1.15}/.github/workflows/ci.yml +0 -0
  19. {comfy_env-0.1.14 → comfy_env-0.1.15}/.github/workflows/publish.yml +0 -0
  20. {comfy_env-0.1.14 → comfy_env-0.1.15}/.gitignore +0 -0
  21. {comfy_env-0.1.14 → comfy_env-0.1.15}/LICENSE +0 -0
  22. {comfy_env-0.1.14 → comfy_env-0.1.15}/README.md +0 -0
  23. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/pixi/__init__.py +0 -0
  24. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/pixi/cuda_detection.py +0 -0
  25. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/pixi/platform/__init__.py +0 -0
  26. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/pixi/platform/base.py +0 -0
  27. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/pixi/platform/darwin.py +0 -0
  28. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/pixi/platform/linux.py +0 -0
  29. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/pixi/platform/windows.py +0 -0
  30. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/pixi/resolver.py +0 -0
  31. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/templates/comfy-env-instructions.txt +0 -0
  32. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/templates/comfy-env.toml +0 -0
  33. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/workers/__init__.py +0 -0
  34. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/workers/base.py +0 -0
  35. {comfy_env-0.1.14 → comfy_env-0.1.15}/src/comfy_env/workers/mp.py +0 -0
  36. {comfy_env-0.1.14 → comfy_env-0.1.15}/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.14
3
+ Version: 0.1.15
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
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.13
19
19
  Requires-Python: >=3.10
20
20
  Requires-Dist: pip>=21.0
21
21
  Requires-Dist: tomli-w>=1.0.0
22
- Requires-Dist: tomli>=2.0.0; python_version < '3.11'
22
+ Requires-Dist: tomli>=2.0.0
23
23
  Requires-Dist: uv>=0.4.0
24
24
  Provides-Extra: dev
25
25
  Requires-Dist: mypy; extra == 'dev'
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "comfy-env"
3
- version = "0.1.14"
3
+ version = "0.1.15"
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"}
@@ -19,10 +19,10 @@ classifiers = [
19
19
  "Programming Language :: Python :: 3.13",
20
20
  ]
21
21
  dependencies = [
22
- "tomli>=2.0.0; python_version < '3.11'", # TOML parsing (built-in tomllib for 3.11+)
23
- "tomli-w>=1.0.0", # TOML writing (no stdlib equivalent)
24
- "uv>=0.4.0", # Fast Python package installer and venv creator
25
- "pip>=21.0", # Fallback package installer
22
+ "tomli>=2.0.0",
23
+ "tomli-w>=1.0.0",
24
+ "uv>=0.4.0",
25
+ "pip>=21.0",
26
26
  ]
27
27
 
28
28
  [project.optional-dependencies]
@@ -1,12 +1,4 @@
1
- """
2
- comfy-env: Environment management for ComfyUI custom nodes.
3
-
4
- All dependencies go through pixi for unified management.
5
-
6
- Main APIs:
7
- - install(): Install dependencies from comfy-env.toml
8
- - wrap_isolated_nodes(): Wrap nodes for subprocess isolation
9
- """
1
+ """Environment management for ComfyUI custom nodes."""
10
2
 
11
3
  from importlib.metadata import version, PackageNotFoundError
12
4
 
@@ -48,13 +40,13 @@ from .workers import (
48
40
  )
49
41
 
50
42
  # Isolation
51
- from .isolation import wrap_isolated_nodes
43
+ from .isolation import wrap_isolated_nodes, wrap_nodes
52
44
 
53
45
  # Install API
54
- from .install import install, verify_installation
46
+ from .install import install, verify_installation, USE_COMFY_ENV_VAR
55
47
 
56
48
  # Prestartup helpers
57
- from .prestartup import setup_env
49
+ from .prestartup import setup_env, copy_files
58
50
 
59
51
  # Cache management
60
52
  from .cache import (
@@ -65,24 +57,17 @@ from .cache import (
65
57
  MARKER_FILE,
66
58
  )
67
59
 
68
- # Errors
69
- from .errors import (
70
- EnvManagerError,
71
- ConfigError,
72
- WheelNotFoundError,
73
- DependencyError,
74
- CUDANotFoundError,
75
- InstallError,
76
- )
77
-
78
60
  __all__ = [
79
61
  # Install API
80
62
  "install",
81
63
  "verify_installation",
64
+ "USE_COMFY_ENV_VAR",
82
65
  # Prestartup
83
66
  "setup_env",
67
+ "copy_files",
84
68
  # Isolation
85
69
  "wrap_isolated_nodes",
70
+ "wrap_nodes",
86
71
  # Config
87
72
  "ComfyEnvConfig",
88
73
  "NodeReq",
@@ -107,13 +92,6 @@ __all__ = [
107
92
  "WorkerError",
108
93
  "MPWorker",
109
94
  "SubprocessWorker",
110
- # Errors
111
- "EnvManagerError",
112
- "ConfigError",
113
- "WheelNotFoundError",
114
- "DependencyError",
115
- "CUDANotFoundError",
116
- "InstallError",
117
95
  # Cache
118
96
  "get_cache_dir",
119
97
  "cleanup_orphaned_envs",
@@ -124,7 +102,7 @@ __all__ = [
124
102
 
125
103
  # Run orphan cleanup once on module load (silently)
126
104
  def _run_startup_cleanup():
127
- """Clean orphaned envs on startup. Runs silently, never fails startup."""
105
+ """Clean orphaned envs on startup."""
128
106
  try:
129
107
  cleanup_orphaned_envs(log=lambda x: None) # Silent
130
108
  except Exception:
@@ -1,11 +1,4 @@
1
- """
2
- Central environment cache management for comfy-env.
3
-
4
- Stores environments in ~/.comfy-env/envs/<nodename>_<subfolder>_<hash>/
5
- to avoid Windows MAX_PATH (260 char) issues.
6
-
7
- Marker files in node folders link to central envs and enable orphan cleanup.
8
- """
1
+ """Central environment cache management."""
9
2
 
10
3
  import hashlib
11
4
  import os
@@ -21,24 +14,8 @@ try:
21
14
  except ImportError:
22
15
  __version__ = "0.0.0-dev"
23
16
 
24
- # Lazy import tomli/tomllib
25
- def _get_tomli():
26
- if sys.version_info >= (3, 11):
27
- import tomllib
28
- return tomllib
29
- else:
30
- try:
31
- import tomli
32
- return tomli
33
- except ImportError:
34
- return None
35
-
36
- def _get_tomli_w():
37
- try:
38
- import tomli_w
39
- return tomli_w
40
- except ImportError:
41
- return None
17
+ import tomli
18
+ import tomli_w
42
19
 
43
20
 
44
21
  # Constants
@@ -70,17 +47,7 @@ def sanitize_name(name: str) -> str:
70
47
 
71
48
 
72
49
  def get_env_name(node_dir: Path, config_path: Path) -> str:
73
- """
74
- Generate env name: <nodename>_<subfolder>_<hash>
75
-
76
- Args:
77
- node_dir: The custom node directory (e.g., custom_nodes/ComfyUI-UniRig)
78
- config_path: Path to comfy-env.toml
79
-
80
- Examples:
81
- - ComfyUI-UniRig/nodes/comfy-env.toml -> unirig_nodes_a1b2c3d4
82
- - ComfyUI-Pack/comfy-env.toml -> pack__f5e6d7c8 (double underscore = no subfolder)
83
- """
50
+ """Generate env name: <nodename>_<subfolder>_<hash>."""
84
51
  # Get node name
85
52
  node_name = sanitize_name(node_dir.name)
86
53
 
@@ -109,27 +76,7 @@ def get_central_env_path(node_dir: Path, config_path: Path) -> Path:
109
76
 
110
77
 
111
78
  def write_marker(config_path: Path, env_path: Path) -> None:
112
- """
113
- Write marker file linking node to central env.
114
-
115
- Args:
116
- config_path: Path to comfy-env.toml
117
- env_path: Path to central environment
118
- """
119
- tomli_w = _get_tomli_w()
120
- if not tomli_w:
121
- # Fallback to manual TOML writing
122
- marker_path = config_path.parent / MARKER_FILE
123
- content = f'''[env]
124
- name = "{env_path.name}"
125
- path = "{env_path}"
126
- config_hash = "{compute_config_hash(config_path)}"
127
- created = "{datetime.now().isoformat()}"
128
- comfy_env_version = "{__version__}"
129
- '''
130
- marker_path.write_text(content)
131
- return
132
-
79
+ """Write marker file linking node to central env."""
133
80
  marker_path = config_path.parent / MARKER_FILE
134
81
  marker_data = {
135
82
  "env": {
@@ -144,24 +91,8 @@ comfy_env_version = "{__version__}"
144
91
 
145
92
 
146
93
  def write_env_metadata(env_path: Path, marker_path: Path) -> None:
147
- """
148
- Write metadata file in env for orphan detection.
149
-
150
- Args:
151
- env_path: Path to central environment
152
- marker_path: Path to marker file in node folder
153
- """
154
- tomli_w = _get_tomli_w()
94
+ """Write metadata file for orphan detection."""
155
95
  metadata_path = env_path / METADATA_FILE
156
-
157
- if not tomli_w:
158
- # Fallback to manual TOML writing
159
- content = f'''marker_path = "{marker_path}"
160
- created = "{datetime.now().isoformat()}"
161
- '''
162
- metadata_path.write_text(content)
163
- return
164
-
165
96
  metadata = {
166
97
  "marker_path": str(marker_path),
167
98
  "created": datetime.now().isoformat(),
@@ -170,22 +101,9 @@ created = "{datetime.now().isoformat()}"
170
101
 
171
102
 
172
103
  def read_marker(marker_path: Path) -> Optional[dict]:
173
- """
174
- Read marker file, return None if invalid/missing.
175
-
176
- Args:
177
- marker_path: Path to .comfy-env-marker.toml
178
-
179
- Returns:
180
- Parsed marker data or None
181
- """
104
+ """Read marker file, return None if invalid/missing."""
182
105
  if not marker_path.exists():
183
106
  return None
184
-
185
- tomli = _get_tomli()
186
- if not tomli:
187
- return None
188
-
189
107
  try:
190
108
  with open(marker_path, "rb") as f:
191
109
  return tomli.load(f)
@@ -194,23 +112,10 @@ def read_marker(marker_path: Path) -> Optional[dict]:
194
112
 
195
113
 
196
114
  def read_env_metadata(env_path: Path) -> Optional[dict]:
197
- """
198
- Read metadata file from env, return None if invalid/missing.
199
-
200
- Args:
201
- env_path: Path to central environment
202
-
203
- Returns:
204
- Parsed metadata or None
205
- """
115
+ """Read metadata file from env, return None if invalid/missing."""
206
116
  metadata_path = env_path / METADATA_FILE
207
117
  if not metadata_path.exists():
208
118
  return None
209
-
210
- tomli = _get_tomli()
211
- if not tomli:
212
- return None
213
-
214
119
  try:
215
120
  with open(metadata_path, "rb") as f:
216
121
  return tomli.load(f)
@@ -219,21 +124,7 @@ def read_env_metadata(env_path: Path) -> Optional[dict]:
219
124
 
220
125
 
221
126
  def resolve_env_path(node_dir: Path) -> Tuple[Optional[Path], Optional[Path], Optional[Path]]:
222
- """
223
- Resolve environment path with fallback chain.
224
-
225
- Args:
226
- node_dir: Directory containing comfy-env.toml or marker file
227
-
228
- Returns:
229
- (env_path, site_packages, lib_dir) or (None, None, None)
230
-
231
- Fallback order:
232
- 1. Marker file -> central cache
233
- 2. _env_<name> (current location)
234
- 3. .pixi/envs/default (old pixi)
235
- 4. .venv (venv support)
236
- """
127
+ """Resolve environment path. Returns (env_path, site_packages, lib_dir)."""
237
128
  # 1. Check marker file -> central cache
238
129
  marker_path = node_dir / MARKER_FILE
239
130
  marker = read_marker(marker_path)
@@ -263,15 +154,7 @@ def resolve_env_path(node_dir: Path) -> Tuple[Optional[Path], Optional[Path], Op
263
154
 
264
155
 
265
156
  def _get_env_paths(env_path: Path) -> Tuple[Path, Optional[Path], Optional[Path]]:
266
- """
267
- Get site-packages and lib paths from an environment.
268
-
269
- Args:
270
- env_path: Path to environment root
271
-
272
- Returns:
273
- (env_path, site_packages, lib_dir)
274
- """
157
+ """Get site-packages and lib paths from an environment."""
275
158
  import glob
276
159
 
277
160
  if sys.platform == "win32":
@@ -287,18 +170,7 @@ def _get_env_paths(env_path: Path) -> Tuple[Path, Optional[Path], Optional[Path]
287
170
 
288
171
 
289
172
  def cleanup_orphaned_envs(log: Callable[[str], None] = print) -> int:
290
- """
291
- Scan central cache and remove orphaned environments.
292
-
293
- An env is orphaned if its marker file no longer exists
294
- (meaning the node was deleted).
295
-
296
- Args:
297
- log: Logging function
298
-
299
- Returns:
300
- Number of envs cleaned up
301
- """
173
+ """Remove orphaned environments. Returns count cleaned."""
302
174
  cache_dir = get_cache_dir()
303
175
  if not cache_dir.exists():
304
176
  return 0
@@ -309,7 +309,7 @@ def cmd_info(args) -> int:
309
309
  def cmd_doctor(args) -> int:
310
310
  """Handle doctor command."""
311
311
  from .install import verify_installation
312
- from .config.parser import discover_env_config, load_env_from_file
312
+ from .config.parser import load_config, discover_config
313
313
 
314
314
  print("Running diagnostics...")
315
315
  print("=" * 40)
@@ -325,21 +325,19 @@ def cmd_doctor(args) -> int:
325
325
  if args.package:
326
326
  packages = [args.package]
327
327
  elif args.config:
328
- config = load_env_from_file(Path(args.config))
328
+ config = load_config(Path(args.config))
329
329
  if config:
330
- packages = (config.requirements or []) + (config.no_deps_requirements or [])
330
+ # Get packages from pypi-dependencies
331
+ pypi_deps = config.pixi_passthrough.get("pypi-dependencies", {})
332
+ packages = list(pypi_deps.keys()) + config.cuda_packages
331
333
  else:
332
- config = discover_env_config(Path.cwd())
334
+ config = discover_config(Path.cwd())
333
335
  if config:
334
- packages = (config.requirements or []) + (config.no_deps_requirements or [])
336
+ pypi_deps = config.pixi_passthrough.get("pypi-dependencies", {})
337
+ packages = list(pypi_deps.keys()) + config.cuda_packages
335
338
 
336
339
  if packages:
337
- pkg_names = []
338
- for pkg in packages:
339
- name = pkg.split("==")[0].split(">=")[0].split("[")[0]
340
- pkg_names.append(name)
341
-
342
- all_ok = verify_installation(pkg_names)
340
+ all_ok = verify_installation(packages)
343
341
  if all_ok:
344
342
  print("\nAll packages verified!")
345
343
  return 0
@@ -0,0 +1,17 @@
1
+ """Config parsing for comfy-env."""
2
+
3
+ from .parser import (
4
+ ComfyEnvConfig,
5
+ NodeReq,
6
+ load_config,
7
+ discover_config,
8
+ CONFIG_FILE_NAME,
9
+ )
10
+
11
+ __all__ = [
12
+ "ComfyEnvConfig",
13
+ "NodeReq",
14
+ "load_config",
15
+ "discover_config",
16
+ "CONFIG_FILE_NAME",
17
+ ]
@@ -1,100 +1,53 @@
1
- """Load configuration from comfy-env.toml.
2
-
3
- comfy-env.toml is a superset of pixi.toml. Custom sections we handle:
4
- - python = "3.11" - Python version for isolated envs
5
- - [cuda] packages = [...] - CUDA packages (triggers find-links + PyTorch detection)
6
- - [node_reqs] - Other ComfyUI nodes to clone
7
-
8
- Everything else passes through to pixi.toml directly.
9
-
10
- Example config:
11
-
12
- python = "3.11"
13
-
14
- [cuda]
15
- packages = ["cumesh"]
16
-
17
- [dependencies]
18
- mesalib = "*"
19
- cgal = "*"
20
-
21
- [pypi-dependencies]
22
- numpy = ">=1.21.0,<2"
23
- trimesh = { version = ">=4.0.0", extras = ["easy"] }
24
-
25
- [target.linux-64.pypi-dependencies]
26
- embreex = "*"
1
+ """
2
+ Configuration parsing for comfy-env.
27
3
 
28
- [node_reqs]
29
- SomeNode = "owner/repo"
4
+ Loads comfy-env.toml (a superset of pixi.toml) and provides typed config objects.
30
5
  """
31
6
 
32
7
  import copy
33
8
  import sys
9
+ from dataclasses import dataclass, field
34
10
  from pathlib import Path
35
11
  from typing import Optional, Dict, Any, List
12
+ import tomli
36
13
 
37
- # Use built-in tomllib (Python 3.11+) or tomli fallback
38
- if sys.version_info >= (3, 11):
39
- import tomllib
40
- else:
41
- try:
42
- import tomli as tomllib
43
- except ImportError:
44
- tomllib = None # type: ignore
45
-
46
- from .types import ComfyEnvConfig, NodeReq
47
-
48
-
14
+ # --- Types&Constants ---
49
15
  CONFIG_FILE_NAME = "comfy-env.toml"
50
16
 
51
- # Sections we handle specially (not passed through to pixi.toml)
52
- CUSTOM_SECTIONS = {"python", "cuda", "node_reqs", "apt", "env_vars"}
53
-
17
+ @dataclass
18
+ class NodeReq:
19
+ """A node dependency (another ComfyUI custom node)."""
20
+ name: str
21
+ repo: str # GitHub repo, e.g., "owner/repo"
54
22
 
55
- def load_config(path: Path) -> ComfyEnvConfig:
56
- """
57
- Load configuration from a TOML file.
23
+ @dataclass
24
+ class ComfyEnvConfig:
25
+ """Configuration from comfy-env.toml."""
26
+ python: Optional[str] = None
27
+ cuda_packages: List[str] = field(default_factory=list)
28
+ apt_packages: List[str] = field(default_factory=list)
29
+ env_vars: Dict[str, str] = field(default_factory=dict)
30
+ node_reqs: List[NodeReq] = field(default_factory=list)
31
+ pixi_passthrough: Dict[str, Any] = field(default_factory=dict)
58
32
 
59
- Args:
60
- path: Path to comfy-env.toml
33
+ @property
34
+ def has_cuda(self) -> bool:
35
+ return bool(self.cuda_packages)
36
+ # --- Types&Constants ---
61
37
 
62
- Returns:
63
- ComfyEnvConfig instance
64
-
65
- Raises:
66
- FileNotFoundError: If config file doesn't exist
67
- ImportError: If tomli not installed (Python < 3.11)
68
- """
69
- if tomllib is None:
70
- raise ImportError(
71
- "TOML parsing requires tomli for Python < 3.11. "
72
- "Install with: pip install tomli"
73
- )
74
38
 
39
+ def load_config(path: Path) -> ComfyEnvConfig:
40
+ """Load config from a TOML file."""
75
41
  path = Path(path)
76
42
  if not path.exists():
77
43
  raise FileNotFoundError(f"Config file not found: {path}")
78
-
79
44
  with open(path, "rb") as f:
80
- data = tomllib.load(f)
81
-
45
+ data = tomli.load(f)
82
46
  return _parse_config(data)
83
47
 
84
48
 
85
49
  def discover_config(node_dir: Path) -> Optional[ComfyEnvConfig]:
86
- """
87
- Find and load comfy-env.toml from a directory.
88
-
89
- Args:
90
- node_dir: Directory to search
91
-
92
- Returns:
93
- ComfyEnvConfig if found, None otherwise
94
- """
95
- if tomllib is None:
96
- return None
97
-
50
+ """Find and load comfy-env.toml from a directory."""
98
51
  config_path = Path(node_dir) / CONFIG_FILE_NAME
99
52
  if config_path.exists():
100
53
  return load_config(config_path)