hoodini.utils.runtime_env

Runtime environment utilities for managing LD_LIBRARY_PATH across conda/pixi/mamba.

This module handles automatic detection and setup of library paths needed by Playwright Firefox in conda/mamba/pixi environments without requiring sudo.

  1"""
  2Runtime environment utilities for managing LD_LIBRARY_PATH across conda/pixi/mamba.
  3
  4This module handles automatic detection and setup of library paths needed by
  5Playwright Firefox in conda/mamba/pixi environments without requiring sudo.
  6"""
  7
  8import os
  9from pathlib import Path
 10
 11
 12def find_candidate_lib_dirs() -> list[str]:
 13    """
 14    Find candidate library directories from conda/mamba/pixi environments.
 15
 16    Searches for:
 17    - $CONDA_PREFIX/lib (conda/mamba active environment)
 18    - ~/.pixi/envs/*/lib (pixi environments)
 19    - ~/.mamba/envs/*/lib (mamba environments)
 20
 21    Returns:
 22        List of absolute paths to lib directories, deduplicated and ordered.
 23    """
 24    candidates: list[str] = []
 25
 26    # Check CONDA_PREFIX (active conda/mamba environment)
 27    if conda_prefix := os.environ.get("CONDA_PREFIX"):
 28        lib_dir = os.path.join(conda_prefix, "lib")
 29        if os.path.isdir(lib_dir):
 30            candidates.append(lib_dir)
 31
 32    # Check pixi environments
 33    pixi_envs_dir = Path.home() / ".pixi" / "envs"
 34    if pixi_envs_dir.exists():
 35        for env_dir in pixi_envs_dir.iterdir():
 36            if env_dir.is_dir():
 37                lib_dir = env_dir / "lib"
 38                if lib_dir.exists():
 39                    candidates.append(str(lib_dir))
 40
 41    # Check mamba environments
 42    mamba_envs_dir = Path.home() / ".mamba" / "envs"
 43    if mamba_envs_dir.exists():
 44        for env_dir in mamba_envs_dir.iterdir():
 45            if env_dir.is_dir():
 46                lib_dir = env_dir / "lib"
 47                if lib_dir.exists():
 48                    candidates.append(str(lib_dir))
 49
 50    # Deduplicate while preserving order
 51    seen = set()
 52    unique_candidates = []
 53    for path in candidates:
 54        if path not in seen:
 55            seen.add(path)
 56            unique_candidates.append(path)
 57
 58    return unique_candidates
 59
 60
 61def verify_gtk_availability() -> str | None:
 62    """
 63    Verify that GTK3 libraries are available in the current environment.
 64
 65    Returns:
 66        Path to the lib directory containing libgtk-3.so.0, or None if not found.
 67    """
 68    for lib_dir in find_candidate_lib_dirs():
 69        gtk_lib = Path(lib_dir) / "libgtk-3.so.0"
 70        if gtk_lib.exists():
 71            return lib_dir
 72    return None
 73
 74
 75def apply_ld_library_path() -> None:
 76    """
 77    Apply LD_LIBRARY_PATH modifications for Playwright Firefox in conda/pixi/mamba.
 78
 79    Prepends candidate library directories to LD_LIBRARY_PATH so that Playwright
 80    Firefox can find GTK3 and other required libraries.
 81
 82    This should be called before launching Playwright browser instances.
 83    """
 84    candidates = find_candidate_lib_dirs()
 85    if not candidates:
 86        # No conda/pixi/mamba environments found, nothing to do
 87        return
 88
 89    current_ld_path = os.environ.get("LD_LIBRARY_PATH", "")
 90
 91    # Build new LD_LIBRARY_PATH: candidates first, then existing path
 92    ld_path_parts = candidates.copy()
 93    if current_ld_path:
 94        ld_path_parts.append(current_ld_path)
 95
 96    new_ld_path = ":".join(ld_path_parts)
 97    os.environ["LD_LIBRARY_PATH"] = new_ld_path
 98
 99
100__all__ = [
101    "find_candidate_lib_dirs",
102    "verify_gtk_availability",
103    "apply_ld_library_path",
104]
def find_candidate_lib_dirs() -> list[str]:
13def find_candidate_lib_dirs() -> list[str]:
14    """
15    Find candidate library directories from conda/mamba/pixi environments.
16
17    Searches for:
18    - $CONDA_PREFIX/lib (conda/mamba active environment)
19    - ~/.pixi/envs/*/lib (pixi environments)
20    - ~/.mamba/envs/*/lib (mamba environments)
21
22    Returns:
23        List of absolute paths to lib directories, deduplicated and ordered.
24    """
25    candidates: list[str] = []
26
27    # Check CONDA_PREFIX (active conda/mamba environment)
28    if conda_prefix := os.environ.get("CONDA_PREFIX"):
29        lib_dir = os.path.join(conda_prefix, "lib")
30        if os.path.isdir(lib_dir):
31            candidates.append(lib_dir)
32
33    # Check pixi environments
34    pixi_envs_dir = Path.home() / ".pixi" / "envs"
35    if pixi_envs_dir.exists():
36        for env_dir in pixi_envs_dir.iterdir():
37            if env_dir.is_dir():
38                lib_dir = env_dir / "lib"
39                if lib_dir.exists():
40                    candidates.append(str(lib_dir))
41
42    # Check mamba environments
43    mamba_envs_dir = Path.home() / ".mamba" / "envs"
44    if mamba_envs_dir.exists():
45        for env_dir in mamba_envs_dir.iterdir():
46            if env_dir.is_dir():
47                lib_dir = env_dir / "lib"
48                if lib_dir.exists():
49                    candidates.append(str(lib_dir))
50
51    # Deduplicate while preserving order
52    seen = set()
53    unique_candidates = []
54    for path in candidates:
55        if path not in seen:
56            seen.add(path)
57            unique_candidates.append(path)
58
59    return unique_candidates

Find candidate library directories from conda/mamba/pixi environments.

Searches for:

  • $CONDA_PREFIX/lib (conda/mamba active environment)
  • ~/.pixi/envs/*/lib (pixi environments)
  • ~/.mamba/envs/*/lib (mamba environments)

Returns: List of absolute paths to lib directories, deduplicated and ordered.

def verify_gtk_availability() -> str | None:
62def verify_gtk_availability() -> str | None:
63    """
64    Verify that GTK3 libraries are available in the current environment.
65
66    Returns:
67        Path to the lib directory containing libgtk-3.so.0, or None if not found.
68    """
69    for lib_dir in find_candidate_lib_dirs():
70        gtk_lib = Path(lib_dir) / "libgtk-3.so.0"
71        if gtk_lib.exists():
72            return lib_dir
73    return None

Verify that GTK3 libraries are available in the current environment.

Returns: Path to the lib directory containing libgtk-3.so.0, or None if not found.

def apply_ld_library_path() -> None:
76def apply_ld_library_path() -> None:
77    """
78    Apply LD_LIBRARY_PATH modifications for Playwright Firefox in conda/pixi/mamba.
79
80    Prepends candidate library directories to LD_LIBRARY_PATH so that Playwright
81    Firefox can find GTK3 and other required libraries.
82
83    This should be called before launching Playwright browser instances.
84    """
85    candidates = find_candidate_lib_dirs()
86    if not candidates:
87        # No conda/pixi/mamba environments found, nothing to do
88        return
89
90    current_ld_path = os.environ.get("LD_LIBRARY_PATH", "")
91
92    # Build new LD_LIBRARY_PATH: candidates first, then existing path
93    ld_path_parts = candidates.copy()
94    if current_ld_path:
95        ld_path_parts.append(current_ld_path)
96
97    new_ld_path = ":".join(ld_path_parts)
98    os.environ["LD_LIBRARY_PATH"] = new_ld_path

Apply LD_LIBRARY_PATH modifications for Playwright Firefox in conda/pixi/mamba.

Prepends candidate library directories to LD_LIBRARY_PATH so that Playwright Firefox can find GTK3 and other required libraries.

This should be called before launching Playwright browser instances.