comfy-env 0.1.14__py3-none-any.whl → 0.1.16__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.
Files changed (51) hide show
  1. comfy_env/__init__.py +115 -62
  2. comfy_env/cli.py +89 -319
  3. comfy_env/config/__init__.py +18 -8
  4. comfy_env/config/parser.py +21 -122
  5. comfy_env/config/types.py +37 -70
  6. comfy_env/detection/__init__.py +77 -0
  7. comfy_env/detection/cuda.py +61 -0
  8. comfy_env/detection/gpu.py +230 -0
  9. comfy_env/detection/platform.py +70 -0
  10. comfy_env/detection/runtime.py +103 -0
  11. comfy_env/environment/__init__.py +53 -0
  12. comfy_env/environment/cache.py +141 -0
  13. comfy_env/environment/libomp.py +41 -0
  14. comfy_env/environment/paths.py +38 -0
  15. comfy_env/environment/setup.py +88 -0
  16. comfy_env/install.py +163 -249
  17. comfy_env/isolation/__init__.py +33 -2
  18. comfy_env/isolation/tensor_utils.py +83 -0
  19. comfy_env/isolation/workers/__init__.py +16 -0
  20. comfy_env/{workers → isolation/workers}/mp.py +1 -1
  21. comfy_env/{workers → isolation/workers}/subprocess.py +2 -2
  22. comfy_env/isolation/wrap.py +149 -409
  23. comfy_env/packages/__init__.py +60 -0
  24. comfy_env/packages/apt.py +36 -0
  25. comfy_env/packages/cuda_wheels.py +97 -0
  26. comfy_env/packages/node_dependencies.py +77 -0
  27. comfy_env/packages/pixi.py +85 -0
  28. comfy_env/packages/toml_generator.py +88 -0
  29. comfy_env-0.1.16.dist-info/METADATA +279 -0
  30. comfy_env-0.1.16.dist-info/RECORD +36 -0
  31. comfy_env/cache.py +0 -331
  32. comfy_env/errors.py +0 -293
  33. comfy_env/nodes.py +0 -187
  34. comfy_env/pixi/__init__.py +0 -48
  35. comfy_env/pixi/core.py +0 -588
  36. comfy_env/pixi/cuda_detection.py +0 -303
  37. comfy_env/pixi/platform/__init__.py +0 -21
  38. comfy_env/pixi/platform/base.py +0 -96
  39. comfy_env/pixi/platform/darwin.py +0 -53
  40. comfy_env/pixi/platform/linux.py +0 -68
  41. comfy_env/pixi/platform/windows.py +0 -284
  42. comfy_env/pixi/resolver.py +0 -198
  43. comfy_env/prestartup.py +0 -192
  44. comfy_env/workers/__init__.py +0 -38
  45. comfy_env/workers/tensor_utils.py +0 -188
  46. comfy_env-0.1.14.dist-info/METADATA +0 -291
  47. comfy_env-0.1.14.dist-info/RECORD +0 -33
  48. /comfy_env/{workers → isolation/workers}/base.py +0 -0
  49. {comfy_env-0.1.14.dist-info → comfy_env-0.1.16.dist-info}/WHEEL +0 -0
  50. {comfy_env-0.1.14.dist-info → comfy_env-0.1.16.dist-info}/entry_points.txt +0 -0
  51. {comfy_env-0.1.14.dist-info → comfy_env-0.1.16.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,70 @@
1
+ """Platform detection - OS, architecture, platform tags."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import platform as platform_module
6
+ import sys
7
+ from dataclasses import dataclass
8
+
9
+
10
+ @dataclass
11
+ class PlatformInfo:
12
+ os_name: str # linux, windows, darwin
13
+ arch: str # x86_64, aarch64, arm64
14
+ platform_tag: str # linux_x86_64, win_amd64, macosx_11_0_arm64
15
+
16
+
17
+ def detect_platform() -> PlatformInfo:
18
+ return PlatformInfo(
19
+ os_name=_get_os_name(),
20
+ arch=platform_module.machine().lower(),
21
+ platform_tag=get_platform_tag(),
22
+ )
23
+
24
+
25
+ def _get_os_name() -> str:
26
+ if sys.platform.startswith('linux'): return 'linux'
27
+ if sys.platform == 'win32': return 'windows'
28
+ if sys.platform == 'darwin': return 'darwin'
29
+ return sys.platform
30
+
31
+
32
+ _PLATFORM_TAGS = {
33
+ ('linux', 'x86_64'): 'linux_x86_64', ('linux', 'amd64'): 'linux_x86_64',
34
+ ('linux', 'aarch64'): 'linux_aarch64',
35
+ ('win32', 'amd64'): 'win_amd64', ('win32', 'x86_64'): 'win_amd64',
36
+ ('darwin', 'arm64'): 'macosx_11_0_arm64',
37
+ ('darwin', 'x86_64'): 'macosx_10_9_x86_64',
38
+ }
39
+
40
+
41
+ def get_platform_tag() -> str:
42
+ key = (sys.platform if sys.platform != 'linux' else 'linux', platform_module.machine().lower())
43
+ return _PLATFORM_TAGS.get(key, f'{sys.platform}_{platform_module.machine().lower()}')
44
+
45
+
46
+ _PIXI_PLATFORMS = {
47
+ ('linux', 'x86_64'): 'linux-64', ('linux', 'amd64'): 'linux-64',
48
+ ('linux', 'aarch64'): 'linux-aarch64',
49
+ ('windows', 'amd64'): 'win-64', ('windows', 'x86_64'): 'win-64',
50
+ ('darwin', 'arm64'): 'osx-arm64',
51
+ ('darwin', 'x86_64'): 'osx-64',
52
+ }
53
+
54
+
55
+ def get_pixi_platform() -> str:
56
+ key = (_get_os_name(), platform_module.machine().lower())
57
+ return _PIXI_PLATFORMS.get(key, f'{key[0]}-{key[1]}')
58
+
59
+
60
+ def get_library_extension() -> str:
61
+ return {'.dll': 'windows', '.dylib': 'darwin'}.get(_get_os_name(), '.so')
62
+
63
+
64
+ def get_executable_suffix() -> str:
65
+ return '.exe' if _get_os_name() == 'windows' else ''
66
+
67
+
68
+ def is_linux() -> bool: return _get_os_name() == 'linux'
69
+ def is_windows() -> bool: return _get_os_name() == 'windows'
70
+ def is_macos() -> bool: return _get_os_name() == 'darwin'
@@ -0,0 +1,103 @@
1
+ """Runtime environment detection - combines all detection into a single snapshot."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from dataclasses import dataclass
7
+ from typing import Dict, Optional
8
+
9
+ from .cuda import detect_cuda_version
10
+ from .gpu import detect_cuda_environment
11
+ from .platform import get_platform_tag, _get_os_name
12
+
13
+
14
+ @dataclass
15
+ class RuntimeEnv:
16
+ """Detected runtime environment for wheel resolution."""
17
+ os_name: str
18
+ platform_tag: str
19
+ python_version: str
20
+ python_short: str
21
+ cuda_version: Optional[str]
22
+ cuda_short: Optional[str]
23
+ torch_version: Optional[str]
24
+ torch_short: Optional[str]
25
+ torch_mm: Optional[str]
26
+ gpu_name: Optional[str] = None
27
+ gpu_compute: Optional[str] = None
28
+
29
+ @classmethod
30
+ def detect(cls, torch_version: Optional[str] = None) -> "RuntimeEnv":
31
+ """Detect runtime environment from current system."""
32
+ py_version = f"{sys.version_info.major}.{sys.version_info.minor}"
33
+ cuda_version = detect_cuda_version()
34
+ torch_version = torch_version or _detect_torch_version()
35
+
36
+ gpu_name, gpu_compute = None, None
37
+ try:
38
+ env = detect_cuda_environment()
39
+ if env.gpus:
40
+ gpu_name, gpu_compute = env.gpus[0].name, env.gpus[0].sm_version()
41
+ except Exception:
42
+ pass
43
+
44
+ return cls(
45
+ os_name=_get_os_name(),
46
+ platform_tag=get_platform_tag(),
47
+ python_version=py_version,
48
+ python_short=py_version.replace(".", ""),
49
+ cuda_version=cuda_version,
50
+ cuda_short=cuda_version.replace(".", "") if cuda_version else None,
51
+ torch_version=torch_version,
52
+ torch_short=torch_version.replace(".", "") if torch_version else None,
53
+ torch_mm="".join(torch_version.split(".")[:2]) if torch_version else None,
54
+ gpu_name=gpu_name,
55
+ gpu_compute=gpu_compute,
56
+ )
57
+
58
+ def as_dict(self) -> Dict[str, str]:
59
+ """Convert to dict for template substitution."""
60
+ result = {
61
+ "os": self.os_name,
62
+ "platform": self.platform_tag,
63
+ "python_version": self.python_version,
64
+ "py_version": self.python_version,
65
+ "py_short": self.python_short,
66
+ "py_minor": self.python_version.split(".")[-1],
67
+ "py_tag": f"cp{self.python_short}",
68
+ }
69
+ if self.cuda_version:
70
+ result.update(cuda_version=self.cuda_version, cuda_short=self.cuda_short,
71
+ cuda_major=self.cuda_version.split(".")[0])
72
+ if self.torch_version:
73
+ result.update(torch_version=self.torch_version, torch_short=self.torch_short,
74
+ torch_mm=self.torch_mm, torch_dotted_mm=".".join(self.torch_version.split(".")[:2]))
75
+ return result
76
+
77
+ def __str__(self) -> str:
78
+ parts = [f"Python {self.python_version}",
79
+ f"CUDA {self.cuda_version}" if self.cuda_version else "CPU"]
80
+ if self.torch_version: parts.append(f"PyTorch {self.torch_version}")
81
+ if self.gpu_name: parts.append(f"GPU: {self.gpu_name}")
82
+ return ", ".join(parts)
83
+
84
+
85
+ def detect_runtime(torch_version: Optional[str] = None) -> RuntimeEnv:
86
+ return RuntimeEnv.detect(torch_version)
87
+
88
+
89
+ def _detect_torch_version() -> Optional[str]:
90
+ try:
91
+ import torch
92
+ return torch.__version__.split('+')[0]
93
+ except ImportError:
94
+ return None
95
+
96
+
97
+ def parse_wheel_requirement(req: str) -> tuple[str, Optional[str]]:
98
+ """Parse 'pkg==1.0' -> ('pkg', '1.0')"""
99
+ for op in ['==', '>=', '<=', '~=', '!=', '>', '<']:
100
+ if op in req:
101
+ parts = req.split(op, 1)
102
+ return (parts[0].strip(), parts[1].strip())
103
+ return (req.strip(), None)
@@ -0,0 +1,53 @@
1
+ """
2
+ Environment layer - Environment management with side effects.
3
+
4
+ Handles environment caching, path resolution, and runtime setup.
5
+ """
6
+
7
+ from .cache import (
8
+ CACHE_DIR,
9
+ MARKER_FILE,
10
+ get_cache_dir,
11
+ get_env_name,
12
+ get_env_path,
13
+ cleanup_orphaned_envs,
14
+ write_marker_file,
15
+ read_marker_file,
16
+ )
17
+ from .paths import (
18
+ resolve_env_path,
19
+ get_site_packages_path,
20
+ get_lib_path,
21
+ copy_files,
22
+ )
23
+ from .setup import (
24
+ setup_env,
25
+ load_env_vars,
26
+ inject_site_packages,
27
+ )
28
+ from .libomp import (
29
+ dedupe_libomp,
30
+ )
31
+
32
+ __all__ = [
33
+ # Cache management
34
+ "CACHE_DIR",
35
+ "MARKER_FILE",
36
+ "get_cache_dir",
37
+ "get_env_name",
38
+ "get_env_path",
39
+ "cleanup_orphaned_envs",
40
+ "write_marker_file",
41
+ "read_marker_file",
42
+ # Path resolution
43
+ "resolve_env_path",
44
+ "get_site_packages_path",
45
+ "get_lib_path",
46
+ "copy_files",
47
+ # Setup helpers
48
+ "setup_env",
49
+ "load_env_vars",
50
+ "inject_site_packages",
51
+ # macOS workaround
52
+ "dedupe_libomp",
53
+ ]
@@ -0,0 +1,141 @@
1
+ """Central environment cache at ~/.comfy-env/envs/"""
2
+
3
+ import glob
4
+ import hashlib
5
+ import shutil
6
+ import sys
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+ from typing import Callable, Optional, Tuple
10
+
11
+ import tomli
12
+ import tomli_w
13
+
14
+ try:
15
+ from .. import __version__
16
+ except ImportError:
17
+ __version__ = "0.0.0-dev"
18
+
19
+ CACHE_DIR = Path.home() / ".comfy-env" / "envs"
20
+ MARKER_FILE = ".comfy-env-marker.toml"
21
+ METADATA_FILE = ".comfy-env-metadata.toml"
22
+
23
+
24
+ def get_cache_dir() -> Path:
25
+ CACHE_DIR.mkdir(parents=True, exist_ok=True)
26
+ return CACHE_DIR
27
+
28
+
29
+ def compute_config_hash(config_path: Path) -> str:
30
+ return hashlib.sha256(config_path.read_bytes()).hexdigest()[:8]
31
+
32
+
33
+ def sanitize_name(name: str) -> str:
34
+ name = name.lower()
35
+ for prefix in ("comfyui-", "comfyui_"):
36
+ if name.startswith(prefix): name = name[len(prefix):]
37
+ return name.replace("-", "_").replace(" ", "_")
38
+
39
+
40
+ def get_env_name(node_dir: Path, config_path: Path) -> str:
41
+ """Generate env name: <nodename>_<subfolder>_<hash>"""
42
+ node_name = sanitize_name(node_dir.name)
43
+ config_parent = config_path.parent
44
+ if config_parent == node_dir:
45
+ subfolder = ""
46
+ else:
47
+ try:
48
+ subfolder = config_parent.relative_to(node_dir).as_posix().replace("/", "_")
49
+ except ValueError:
50
+ subfolder = sanitize_name(config_parent.name)
51
+ return f"{node_name}_{subfolder}_{compute_config_hash(config_path)}"
52
+
53
+
54
+ def get_env_path(node_dir: Path, config_path: Path) -> Path:
55
+ return get_cache_dir() / get_env_name(node_dir, config_path)
56
+
57
+ get_central_env_path = get_env_path
58
+
59
+
60
+ def write_marker_file(config_path: Path, env_path: Path) -> None:
61
+ marker_path = config_path.parent / MARKER_FILE
62
+ marker_path.write_text(tomli_w.dumps({
63
+ "env": {"name": env_path.name, "path": str(env_path),
64
+ "config_hash": compute_config_hash(config_path),
65
+ "created": datetime.now().isoformat(), "comfy_env_version": __version__}
66
+ }))
67
+
68
+ write_marker = write_marker_file
69
+
70
+
71
+ def write_env_metadata(env_path: Path, marker_path: Path) -> None:
72
+ (env_path / METADATA_FILE).write_text(tomli_w.dumps({
73
+ "marker_path": str(marker_path), "created": datetime.now().isoformat()
74
+ }))
75
+
76
+
77
+ def read_marker_file(marker_path: Path) -> Optional[dict]:
78
+ if not marker_path.exists(): return None
79
+ try:
80
+ with open(marker_path, "rb") as f: return tomli.load(f)
81
+ except Exception: return None
82
+
83
+ read_marker = read_marker_file
84
+
85
+
86
+ def read_env_metadata(env_path: Path) -> Optional[dict]:
87
+ metadata_path = env_path / METADATA_FILE
88
+ if not metadata_path.exists(): return None
89
+ try:
90
+ with open(metadata_path, "rb") as f: return tomli.load(f)
91
+ except Exception: return None
92
+
93
+
94
+ def resolve_env_path(node_dir: Path) -> Tuple[Optional[Path], Optional[Path], Optional[Path]]:
95
+ """Resolve env with fallback: marker -> _env_<name> -> .pixi -> .venv"""
96
+ # Marker
97
+ marker = read_marker_file(node_dir / MARKER_FILE)
98
+ if marker and "env" in marker:
99
+ env_path = Path(marker["env"]["path"])
100
+ if env_path.exists(): return _get_env_paths(env_path)
101
+
102
+ # Local _env_<name>
103
+ local_env = node_dir / f"_env_{sanitize_name(node_dir.name)}"
104
+ if local_env.exists(): return _get_env_paths(local_env)
105
+
106
+ # .pixi
107
+ pixi_env = node_dir / ".pixi" / "envs" / "default"
108
+ if pixi_env.exists(): return _get_env_paths(pixi_env)
109
+
110
+ # .venv
111
+ venv = node_dir / ".venv"
112
+ if venv.exists(): return _get_env_paths(venv)
113
+
114
+ return None, None, None
115
+
116
+
117
+ def _get_env_paths(env_path: Path) -> Tuple[Path, Optional[Path], Optional[Path]]:
118
+ if sys.platform == "win32":
119
+ return env_path, env_path / "Lib" / "site-packages", env_path / "Library" / "bin"
120
+ matches = glob.glob(str(env_path / "lib" / "python*" / "site-packages"))
121
+ return env_path, Path(matches[0]) if matches else None, env_path / "lib"
122
+
123
+
124
+ def cleanup_orphaned_envs(log: Callable[[str], None] = print) -> int:
125
+ """Remove envs whose marker files no longer exist."""
126
+ cache_dir = get_cache_dir()
127
+ if not cache_dir.exists(): return 0
128
+
129
+ cleaned = 0
130
+ for env_dir in cache_dir.iterdir():
131
+ if not env_dir.is_dir(): continue
132
+ metadata = read_env_metadata(env_dir)
133
+ if not metadata: continue
134
+ marker_path = metadata.get("marker_path", "")
135
+ if marker_path and not Path(marker_path).exists():
136
+ log(f"[comfy-env] Cleaning: {env_dir.name}")
137
+ try:
138
+ shutil.rmtree(env_dir)
139
+ cleaned += 1
140
+ except Exception: pass
141
+ return cleaned
@@ -0,0 +1,41 @@
1
+ """macOS: Dedupe libomp.dylib to prevent OpenMP runtime conflicts."""
2
+
3
+ import glob
4
+ import os
5
+ import sys
6
+
7
+
8
+ def dedupe_libomp() -> None:
9
+ """Symlink all libomp copies to torch's to prevent dual-runtime crashes."""
10
+ if sys.platform != "darwin":
11
+ return
12
+
13
+ try:
14
+ import torch
15
+ torch_libomp = os.path.join(os.path.dirname(torch.__file__), 'lib', 'libomp.dylib')
16
+ if not os.path.exists(torch_libomp):
17
+ return
18
+ except ImportError:
19
+ return
20
+
21
+ site_packages = os.path.dirname(os.path.dirname(torch.__file__))
22
+ patterns = [
23
+ os.path.join(site_packages, '*', 'Frameworks', 'libomp.dylib'),
24
+ os.path.join(site_packages, '*', '.dylibs', 'libomp.dylib'),
25
+ os.path.join(site_packages, '*', 'lib', 'libomp.dylib'),
26
+ ]
27
+
28
+ for pattern in patterns:
29
+ for libomp in glob.glob(pattern):
30
+ if 'torch' in libomp:
31
+ continue
32
+ if os.path.islink(libomp) and os.path.realpath(libomp) == os.path.realpath(torch_libomp):
33
+ continue
34
+ try:
35
+ if os.path.islink(libomp):
36
+ os.unlink(libomp)
37
+ else:
38
+ os.rename(libomp, libomp + '.bak')
39
+ os.symlink(torch_libomp, libomp)
40
+ except OSError:
41
+ pass
@@ -0,0 +1,38 @@
1
+ """Environment path utilities."""
2
+
3
+ import shutil
4
+ from pathlib import Path
5
+ from typing import Optional, Tuple
6
+
7
+ from .cache import resolve_env_path as _resolve_env_path
8
+
9
+
10
+ def get_site_packages_path(node_dir: Path) -> Optional[Path]:
11
+ _, site_packages, _ = _resolve_env_path(node_dir)
12
+ return site_packages
13
+
14
+
15
+ def get_lib_path(node_dir: Path) -> Optional[Path]:
16
+ _, _, lib_dir = _resolve_env_path(node_dir)
17
+ return lib_dir
18
+
19
+
20
+ def resolve_env_path(node_dir: Path) -> Tuple[Optional[Path], Optional[Path], Optional[Path]]:
21
+ return _resolve_env_path(node_dir)
22
+
23
+
24
+ def copy_files(src, dst, pattern: str = "*", overwrite: bool = False) -> int:
25
+ """Copy files matching pattern from src to dst."""
26
+ src, dst = Path(src), Path(dst)
27
+ if not src.exists(): return 0
28
+
29
+ dst.mkdir(parents=True, exist_ok=True)
30
+ copied = 0
31
+ for f in src.glob(pattern):
32
+ if f.is_file():
33
+ target = dst / f.relative_to(src)
34
+ target.parent.mkdir(parents=True, exist_ok=True)
35
+ if overwrite or not target.exists():
36
+ shutil.copy2(f, target)
37
+ copied += 1
38
+ return copied
@@ -0,0 +1,88 @@
1
+ """Environment setup for ComfyUI prestartup."""
2
+
3
+ import glob
4
+ import os
5
+ import sys
6
+ from pathlib import Path
7
+ from typing import Dict, Optional
8
+
9
+ from .cache import MARKER_FILE, sanitize_name
10
+ from .libomp import dedupe_libomp
11
+
12
+ USE_COMFY_ENV_VAR = "USE_COMFY_ENV"
13
+
14
+
15
+ def is_comfy_env_enabled() -> bool:
16
+ return os.environ.get(USE_COMFY_ENV_VAR, "1").lower() not in ("0", "false", "no", "off")
17
+
18
+
19
+ def load_env_vars(config_path: str) -> Dict[str, str]:
20
+ """Load [env_vars] from comfy-env.toml."""
21
+ if not os.path.exists(config_path): return {}
22
+ try:
23
+ import tomli
24
+ with open(config_path, "rb") as f:
25
+ return {str(k): str(v) for k, v in tomli.load(f).get("env_vars", {}).items()}
26
+ except Exception:
27
+ return {}
28
+
29
+
30
+ def inject_site_packages(env_path: str) -> Optional[str]:
31
+ """Add site-packages to sys.path."""
32
+ if sys.platform == "win32":
33
+ site_packages = os.path.join(env_path, "Lib", "site-packages")
34
+ else:
35
+ matches = glob.glob(os.path.join(env_path, "lib", "python*", "site-packages"))
36
+ site_packages = matches[0] if matches else None
37
+
38
+ if site_packages and os.path.exists(site_packages) and site_packages not in sys.path:
39
+ sys.path.insert(0, site_packages)
40
+ return site_packages
41
+ return None
42
+
43
+
44
+ def setup_env(node_dir: Optional[str] = None) -> None:
45
+ """Set up env for pixi libraries. Call in prestartup_script.py before native imports."""
46
+ if not is_comfy_env_enabled(): return
47
+ dedupe_libomp()
48
+
49
+ if node_dir is None:
50
+ import inspect
51
+ node_dir = str(Path(inspect.stack()[1].filename).parent)
52
+
53
+ # Apply env vars
54
+ for k, v in load_env_vars(os.path.join(node_dir, "comfy-env.toml")).items():
55
+ os.environ[k] = v
56
+
57
+ # Find env: marker -> _env_<name> -> .pixi
58
+ pixi_env = None
59
+ marker_path = os.path.join(node_dir, MARKER_FILE)
60
+ if os.path.exists(marker_path):
61
+ try:
62
+ import tomli
63
+ with open(marker_path, "rb") as f:
64
+ env_path = tomli.load(f).get("env", {}).get("path")
65
+ if env_path and os.path.exists(env_path):
66
+ pixi_env = env_path
67
+ except Exception: pass
68
+
69
+ if not pixi_env:
70
+ local_env = os.path.join(node_dir, f"_env_{sanitize_name(os.path.basename(node_dir))}")
71
+ if os.path.exists(local_env): pixi_env = local_env
72
+
73
+ if not pixi_env:
74
+ old_pixi = os.path.join(node_dir, ".pixi", "envs", "default")
75
+ if os.path.exists(old_pixi): pixi_env = old_pixi
76
+
77
+ if not pixi_env: return
78
+
79
+ # Set library paths
80
+ if sys.platform == "win32":
81
+ lib_dir = os.path.join(pixi_env, "Library", "bin")
82
+ if os.path.exists(lib_dir): os.environ["PATH"] = lib_dir + ";" + os.environ.get("PATH", "")
83
+ else:
84
+ lib_dir = os.path.join(pixi_env, "lib")
85
+ var = "DYLD_LIBRARY_PATH" if sys.platform == "darwin" else "LD_LIBRARY_PATH"
86
+ if os.path.exists(lib_dir): os.environ[var] = lib_dir + ":" + os.environ.get(var, "")
87
+
88
+ inject_site_packages(pixi_env)