comfy-env 0.0.31__tar.gz → 0.0.33__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 (44) hide show
  1. {comfy_env-0.0.31 → comfy_env-0.0.33}/PKG-INFO +1 -1
  2. {comfy_env-0.0.31 → comfy_env-0.0.33}/pyproject.toml +1 -1
  3. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/env/manager.py +36 -13
  4. comfy_env-0.0.33/src/comfy_env/index_resolver.py +132 -0
  5. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/install.py +41 -12
  6. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/wheel_sources.yml +10 -0
  7. {comfy_env-0.0.31 → comfy_env-0.0.33}/.github/workflows/publish.yml +0 -0
  8. {comfy_env-0.0.31 → comfy_env-0.0.33}/.gitignore +0 -0
  9. {comfy_env-0.0.31 → comfy_env-0.0.33}/LICENSE +0 -0
  10. {comfy_env-0.0.31 → comfy_env-0.0.33}/README.md +0 -0
  11. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/__init__.py +0 -0
  12. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/cli.py +0 -0
  13. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/decorator.py +0 -0
  14. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/env/__init__.py +0 -0
  15. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/env/config.py +0 -0
  16. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/env/config_file.py +0 -0
  17. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/env/cuda_gpu_detection.py +0 -0
  18. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/env/platform/__init__.py +0 -0
  19. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/env/platform/base.py +0 -0
  20. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/env/platform/darwin.py +0 -0
  21. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/env/platform/linux.py +0 -0
  22. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/env/platform/windows.py +0 -0
  23. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/env/security.py +0 -0
  24. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/errors.py +0 -0
  25. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/ipc/__init__.py +0 -0
  26. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/ipc/bridge.py +0 -0
  27. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/ipc/protocol.py +0 -0
  28. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/ipc/tensor.py +0 -0
  29. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/ipc/torch_bridge.py +0 -0
  30. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/ipc/transport.py +0 -0
  31. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/ipc/worker.py +0 -0
  32. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/nodes.py +0 -0
  33. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/pixi.py +0 -0
  34. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/registry.py +0 -0
  35. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/resolver.py +0 -0
  36. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/stubs/__init__.py +0 -0
  37. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/stubs/folder_paths.py +0 -0
  38. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/workers/__init__.py +0 -0
  39. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/workers/base.py +0 -0
  40. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/workers/pool.py +0 -0
  41. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/workers/tensor_utils.py +0 -0
  42. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/workers/torch_mp.py +0 -0
  43. {comfy_env-0.0.31 → comfy_env-0.0.33}/src/comfy_env/workers/venv.py +0 -0
  44. {comfy_env-0.0.31 → comfy_env-0.0.33}/untitled.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comfy-env
3
- Version: 0.0.31
3
+ Version: 0.0.33
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.0.31"
3
+ version = "0.0.33"
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"}
@@ -22,6 +22,7 @@ from .security import (
22
22
  )
23
23
  from ..registry import PACKAGE_REGISTRY, is_registered, get_cuda_short2
24
24
  from ..resolver import RuntimeEnv, parse_wheel_requirement
25
+ from ..index_resolver import resolve_wheel_from_index
25
26
 
26
27
 
27
28
  class IsolatedEnvManager:
@@ -385,26 +386,48 @@ class IsolatedEnvManager:
385
386
  self.log(f" Installing {package} ({method})...")
386
387
 
387
388
  if method == "index":
388
- # PEP 503 index - use --extra-index-url
389
+ # PEP 503 index - try to resolve exact wheel URL first
389
390
  index_url = self._substitute_template(config["index_url"], vars_dict)
390
391
  pkg_spec = f"{package}=={version}" if version else package
391
- self.log(f" Index: {index_url}")
392
- self.log(f" Package: {pkg_spec}")
393
- result = subprocess.run(
394
- pip_args + ["--extra-index-url", index_url, "--no-deps", pkg_spec],
395
- capture_output=True, text=True,
396
- )
392
+ # Try to resolve exact wheel URL from index
393
+ wheel_url = resolve_wheel_from_index(index_url, package, vars_dict, version)
394
+ if wheel_url:
395
+ # Install from resolved URL directly (guarantees we get what we resolved)
396
+ self.log(f" Wheel: {wheel_url}")
397
+ result = subprocess.run(
398
+ pip_args + ["--no-deps", wheel_url],
399
+ capture_output=True, text=True,
400
+ )
401
+ else:
402
+ # Fallback to index-based resolution
403
+ self.log(f" Index: {index_url}")
404
+ self.log(f" Package: {pkg_spec}")
405
+ result = subprocess.run(
406
+ pip_args + ["--extra-index-url", index_url, "--no-deps", pkg_spec],
407
+ capture_output=True, text=True,
408
+ )
397
409
 
398
410
  elif method in ("github_index", "find_links"):
399
411
  # GitHub Pages or generic find-links
400
412
  index_url = self._substitute_template(config["index_url"], vars_dict)
401
413
  pkg_spec = f"{package}=={version}" if version else package
402
- self.log(f" Find-links: {index_url}")
403
- self.log(f" Package: {pkg_spec}")
404
- result = subprocess.run(
405
- pip_args + ["--find-links", index_url, "--no-deps", pkg_spec],
406
- capture_output=True, text=True,
407
- )
414
+ # Try to resolve exact wheel URL from find-links page
415
+ wheel_url = resolve_wheel_from_index(index_url, package, vars_dict, version)
416
+ if wheel_url:
417
+ # Install from resolved URL directly (guarantees we get what we resolved)
418
+ self.log(f" Wheel: {wheel_url}")
419
+ result = subprocess.run(
420
+ pip_args + ["--no-deps", wheel_url],
421
+ capture_output=True, text=True,
422
+ )
423
+ else:
424
+ # Fallback to find-links based resolution
425
+ self.log(f" Find-links: {index_url}")
426
+ self.log(f" Package: {pkg_spec}")
427
+ result = subprocess.run(
428
+ pip_args + ["--find-links", index_url, "--no-deps", pkg_spec],
429
+ capture_output=True, text=True,
430
+ )
408
431
 
409
432
  elif method == "pypi_variant":
410
433
  # Transform package name based on CUDA version
@@ -0,0 +1,132 @@
1
+ """
2
+ Simple index resolver for finding wheel URLs from PEP 503 indexes.
3
+
4
+ This module fetches and parses simple index HTML to find the exact wheel URL
5
+ for a package given the runtime environment (CUDA, torch, python versions).
6
+ """
7
+
8
+ import re
9
+ import urllib.request
10
+ from typing import Optional, Dict
11
+ from urllib.parse import urljoin
12
+
13
+
14
+ def resolve_wheel_from_index(
15
+ index_url: str,
16
+ package: str,
17
+ vars_dict: Dict[str, str],
18
+ version: Optional[str] = None,
19
+ ) -> Optional[str]:
20
+ """
21
+ Resolve the exact wheel URL from a PEP 503 simple index or find-links page.
22
+
23
+ Args:
24
+ index_url: Base URL of the simple index (e.g., https://pozzettiandrea.github.io/cuda-wheels)
25
+ or find-links page (e.g., https://data.pyg.org/whl/torch-2.8.0+cu128.html)
26
+ package: Package name (e.g., "cumesh")
27
+ vars_dict: Environment variables dict with cuda_short, torch_mm, py_tag, platform
28
+ version: Specific version to match, or None for latest
29
+
30
+ Returns:
31
+ Full wheel URL if found, None otherwise
32
+ """
33
+ # PEP 503: normalize package name (lowercase, replace _ with -)
34
+ normalized = package.lower().replace("_", "-")
35
+
36
+ # Try two URL patterns:
37
+ # 1. PEP 503 index: {index_url}/{package}/
38
+ # 2. Find-links page: {index_url} directly (e.g., .html file)
39
+ urls_to_try = []
40
+
41
+ if index_url.endswith('.html'):
42
+ # Direct HTML page (find-links style)
43
+ urls_to_try = [index_url]
44
+ else:
45
+ # PEP 503 style - try package subdirectory first, then root
46
+ urls_to_try = [
47
+ f"{index_url.rstrip('/')}/{normalized}/",
48
+ index_url,
49
+ ]
50
+
51
+ html = None
52
+ base_url = None
53
+ for url in urls_to_try:
54
+ try:
55
+ with urllib.request.urlopen(url, timeout=10) as response:
56
+ html = response.read().decode('utf-8')
57
+ base_url = url
58
+ break
59
+ except Exception:
60
+ continue
61
+
62
+ if not html:
63
+ return None
64
+
65
+ # Extract wheel links from HTML
66
+ # Simple index format: <a href="...">wheel_filename</a>
67
+ wheel_pattern = re.compile(r'<a[^>]+href="([^"]+\.whl)"[^>]*>([^<]+)</a>', re.IGNORECASE)
68
+ wheels = wheel_pattern.findall(html)
69
+
70
+ if not wheels:
71
+ return None
72
+
73
+ # Build matching criteria from vars_dict
74
+ cuda_short = vars_dict.get("cuda_short", "")
75
+ torch_mm = vars_dict.get("torch_mm", "")
76
+ py_tag = vars_dict.get("py_tag", "")
77
+ platform = vars_dict.get("platform", "")
78
+
79
+ # Find matching wheel
80
+ # Wheel filename format: {package}-{version}+cu{cuda}torch{torch}-{pytag}-{pytag}-{platform}.whl
81
+ best_match = None
82
+ best_version = None
83
+
84
+ for href, filename in wheels:
85
+ # Check if wheel matches our environment
86
+ if cuda_short and f"cu{cuda_short}" not in filename.lower():
87
+ continue
88
+ if torch_mm and f"torch{torch_mm}" not in filename.lower():
89
+ continue
90
+ if py_tag and py_tag not in filename:
91
+ continue
92
+ if platform and platform not in filename:
93
+ continue
94
+
95
+ # Extract version from filename
96
+ # Format: package-version+... or package-version-...
97
+ # Package name in wheel can use underscore or hyphen interchangeably
98
+ pkg_pattern = re.escape(package).replace(r'\_', '[-_]').replace(r'\-', '[-_]')
99
+ norm_pattern = re.escape(normalized).replace(r'\_', '[-_]').replace(r'\-', '[-_]')
100
+ match = re.match(rf'{pkg_pattern}[-_]([^+\-]+)', filename, re.IGNORECASE)
101
+ if not match:
102
+ # Try with normalized name
103
+ match = re.match(rf'{norm_pattern}[-_]([^+\-]+)', filename, re.IGNORECASE)
104
+
105
+ if match:
106
+ wheel_version = match.group(1)
107
+
108
+ # If specific version requested, must match
109
+ if version and version != "*" and wheel_version != version:
110
+ continue
111
+
112
+ # Track best (highest) version
113
+ if best_version is None or _version_gt(wheel_version, best_version):
114
+ best_version = wheel_version
115
+ # Resolve relative URL
116
+ if href.startswith('http'):
117
+ best_match = href
118
+ else:
119
+ best_match = urljoin(base_url, href)
120
+
121
+ return best_match
122
+
123
+
124
+ def _version_gt(v1: str, v2: str) -> bool:
125
+ """Compare version strings (simple comparison)."""
126
+ try:
127
+ # Split into parts and compare numerically
128
+ parts1 = [int(x) for x in re.split(r'[.\-+]', v1) if x.isdigit()]
129
+ parts2 = [int(x) for x in re.split(r'[.\-+]', v2) if x.isdigit()]
130
+ return parts1 > parts2
131
+ except (ValueError, AttributeError):
132
+ return v1 > v2
@@ -31,6 +31,7 @@ from .errors import CUDANotFoundError, DependencyError, InstallError, WheelNotFo
31
31
  from .pixi import pixi_install
32
32
  from .registry import PACKAGE_REGISTRY, get_cuda_short2
33
33
  from .resolver import RuntimeEnv, WheelResolver, parse_wheel_requirement
34
+ from .index_resolver import resolve_wheel_from_index
34
35
 
35
36
 
36
37
  def _install_system_packages(
@@ -474,7 +475,7 @@ def _install_cuda_package(
474
475
  method = config["method"]
475
476
 
476
477
  if method == "index":
477
- # PEP 503 index - use pip --extra-index-url
478
+ # PEP 503 index - try to resolve exact wheel URL first
478
479
  index_url = _substitute_template(config["index_url"], env)
479
480
  # Check for version_template (e.g., detectron2 with embedded torch/cuda version)
480
481
  if "version_template" in config:
@@ -483,27 +484,55 @@ def _install_cuda_package(
483
484
  else:
484
485
  pkg_spec = f"{package}=={version}" if version else package
485
486
  log(f" Installing {package} (index)...")
486
- log(f" Index: {index_url}")
487
- log(f" Package: {pkg_spec}")
488
- _pip_install_with_index(pkg_spec, index_url, log)
487
+ # Try to resolve exact wheel URL from index
488
+ actual_version = resolved_version if "version_template" in config else version
489
+ vars_dict = env.as_dict()
490
+ wheel_url = resolve_wheel_from_index(index_url, package, vars_dict, actual_version)
491
+ if wheel_url:
492
+ # Install from resolved URL directly (guarantees we get what we resolved)
493
+ log(f" Wheel: {wheel_url}")
494
+ _pip_install([wheel_url], no_deps=True, log=log)
495
+ else:
496
+ # Fallback to index-based resolution
497
+ log(f" Index: {index_url}")
498
+ log(f" Package: {pkg_spec}")
499
+ _pip_install_with_index(pkg_spec, index_url, log)
489
500
 
490
501
  elif method == "github_index":
491
- # GitHub Pages index - use pip --find-links
502
+ # GitHub Pages index - try to resolve exact wheel URL first
492
503
  index_url = _substitute_template(config["index_url"], env)
493
504
  pkg_spec = f"{package}=={version}" if version else package
494
505
  log(f" Installing {package} (github_index)...")
495
- log(f" Find-links: {index_url}")
496
- log(f" Package: {pkg_spec}")
497
- _pip_install_with_find_links(pkg_spec, index_url, log)
506
+ # Try to resolve exact wheel URL from find-links page
507
+ vars_dict = env.as_dict()
508
+ wheel_url = resolve_wheel_from_index(index_url, package, vars_dict, version)
509
+ if wheel_url:
510
+ # Install from resolved URL directly (guarantees we get what we resolved)
511
+ log(f" Wheel: {wheel_url}")
512
+ _pip_install([wheel_url], no_deps=True, log=log)
513
+ else:
514
+ # Fallback to find-links based resolution
515
+ log(f" Find-links: {index_url}")
516
+ log(f" Package: {pkg_spec}")
517
+ _pip_install_with_find_links(pkg_spec, index_url, log)
498
518
 
499
519
  elif method == "find_links":
500
- # Generic find-links (e.g., PyG) - use pip --find-links
520
+ # Generic find-links (e.g., PyG) - try to resolve exact wheel URL first
501
521
  index_url = _substitute_template(config["index_url"], env)
502
522
  pkg_spec = f"{package}=={version}" if version else package
503
523
  log(f" Installing {package} (find_links)...")
504
- log(f" Find-links: {index_url}")
505
- log(f" Package: {pkg_spec}")
506
- _pip_install_with_find_links(pkg_spec, index_url, log)
524
+ # Try to resolve exact wheel URL from find-links page
525
+ vars_dict = env.as_dict()
526
+ wheel_url = resolve_wheel_from_index(index_url, package, vars_dict, version)
527
+ if wheel_url:
528
+ # Install from resolved URL directly (guarantees we get what we resolved)
529
+ log(f" Wheel: {wheel_url}")
530
+ _pip_install([wheel_url], no_deps=True, log=log)
531
+ else:
532
+ # Fallback to find-links based resolution
533
+ log(f" Find-links: {index_url}")
534
+ log(f" Package: {pkg_spec}")
535
+ _pip_install_with_find_links(pkg_spec, index_url, log)
507
536
 
508
537
  elif method == "pypi_variant":
509
538
  # Transform package name based on CUDA version
@@ -60,50 +60,60 @@ packages:
60
60
  # ===========================================================================
61
61
  # PozzettiAndrea cuda-wheels (unified PEP 503 index)
62
62
  # https://pozzettiandrea.github.io/cuda-wheels
63
+ # Wheel naming: {package}-{version}+cu{cuda_short}torch{torch_mm}-{py_tag}-{py_tag}-{platform}.whl
63
64
  # ===========================================================================
64
65
  nvdiffrast:
65
66
  method: index
66
67
  index_url: "https://pozzettiandrea.github.io/cuda-wheels"
68
+ wheel_template: "https://pozzettiandrea.github.io/cuda-wheels/nvdiffrast/nvdiffrast-{version}%2Bcu{cuda_short}torch{torch_mm}-{py_tag}-{py_tag}-{platform}.whl"
67
69
  description: NVIDIA differentiable rasterizer
68
70
 
69
71
  cumesh:
70
72
  method: index
71
73
  index_url: "https://pozzettiandrea.github.io/cuda-wheels"
74
+ wheel_template: "https://pozzettiandrea.github.io/cuda-wheels/cumesh/cumesh-{version}%2Bcu{cuda_short}torch{torch_mm}-{py_tag}-{py_tag}-{platform}.whl"
72
75
  description: CUDA-accelerated mesh utilities
73
76
 
74
77
  o_voxel:
75
78
  method: index
76
79
  index_url: "https://pozzettiandrea.github.io/cuda-wheels"
80
+ wheel_template: "https://pozzettiandrea.github.io/cuda-wheels/o-voxel/o_voxel-{version}%2Bcu{cuda_short}torch{torch_mm}-{py_tag}-{py_tag}-{platform}.whl"
77
81
  description: O-Voxel CUDA extension for TRELLIS
78
82
 
79
83
  flex_gemm:
80
84
  method: index
81
85
  index_url: "https://pozzettiandrea.github.io/cuda-wheels"
86
+ wheel_template: "https://pozzettiandrea.github.io/cuda-wheels/flex-gemm/flex_gemm-{version}%2Bcu{cuda_short}torch{torch_mm}-{py_tag}-{py_tag}-{platform}.whl"
82
87
  description: Flexible GEMM operations
83
88
 
84
89
  nvdiffrec_render:
85
90
  method: index
86
91
  index_url: "https://pozzettiandrea.github.io/cuda-wheels"
92
+ wheel_template: "https://pozzettiandrea.github.io/cuda-wheels/nvdiffrec-render/nvdiffrec_render-{version}%2Bcu{cuda_short}torch{torch_mm}-{py_tag}-{py_tag}-{platform}.whl"
87
93
  description: NVDiffRec rendering utilities
88
94
 
89
95
  gsplat:
90
96
  method: index
91
97
  index_url: "https://pozzettiandrea.github.io/cuda-wheels"
98
+ wheel_template: "https://pozzettiandrea.github.io/cuda-wheels/gsplat/gsplat-{version}%2Bcu{cuda_short}torch{torch_mm}-{py_tag}-{py_tag}-{platform}.whl"
92
99
  description: Gaussian splatting rasterization
93
100
 
94
101
  cc_torch:
95
102
  method: index
96
103
  index_url: "https://pozzettiandrea.github.io/cuda-wheels"
104
+ wheel_template: "https://pozzettiandrea.github.io/cuda-wheels/cc-torch/cc_torch-{version}%2Bcu{cuda_short}torch{torch_mm}-{py_tag}-{py_tag}-{platform}.whl"
97
105
  description: GPU-accelerated connected components
98
106
 
99
107
  torch_generic_nms:
100
108
  method: index
101
109
  index_url: "https://pozzettiandrea.github.io/cuda-wheels"
110
+ wheel_template: "https://pozzettiandrea.github.io/cuda-wheels/torch-generic-nms/torch_generic_nms-{version}%2Bcu{cuda_short}torch{torch_mm}-{py_tag}-{py_tag}-{platform}.whl"
102
111
  description: GPU-accelerated Non-Maximum Suppression
103
112
 
104
113
  lietorch:
105
114
  method: index
106
115
  index_url: "https://pozzettiandrea.github.io/cuda-wheels"
116
+ wheel_template: "https://pozzettiandrea.github.io/cuda-wheels/lietorch/lietorch-{version}%2Bcu{cuda_short}torch{torch_mm}-{py_tag}-{py_tag}-{platform}.whl"
107
117
  description: Lie group operations for PyTorch (DPVO dependency)
108
118
 
109
119
  # ===========================================================================
File without changes
File without changes
File without changes
File without changes