Source code for scitex_container.host._mounts

#!/usr/bin/env python3
# Timestamp: "2026-02-25"
# File: src/scitex_container/host/_mounts.py
"""Mount configuration for host packages bound into containers."""

from __future__ import annotations

import shutil
from pathlib import Path

from scitex_container._compat import supports_return_as

# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------

TEXLIVE_BINARIES: list[str] = [
    "pdflatex",
    "bibtex",
    "latexmk",
    "latexdiff",
    "kpsewhich",
    "makeindex",
    "biber",
]

TEXLIVE_DIRS: list[str] = [
    "share/texlive",
    "share/texmf-dist",
]

_IMAGEMAGICK_DIRS: list[str] = [
    "etc/ImageMagick-6",
]

_IMAGEMAGICK_BINARIES: list[str] = [
    "convert",
    "identify",
    "mogrify",
]


# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------


def _bin_dir_for_prefix(prefix: str) -> str:
    """Return the bin directory for a given prefix (e.g. /usr → /usr/bin)."""
    return str(Path(prefix) / "bin")


def _resolve_binary(name: str, prefix: str) -> str | None:
    """Return absolute path to a binary under prefix/bin, or via PATH lookup."""
    candidate = Path(prefix) / "bin" / name
    if candidate.exists():
        return str(candidate)
    found = shutil.which(name)
    return found


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------


[docs] @supports_return_as def get_texlive_binds(prefix: str = "/usr") -> list[dict]: """Generate bind mount entries for TeXLive. Parameters ---------- prefix : str Installation prefix (typically ``/usr`` for system-wide apt installs). Returns ------- list[dict] Each entry has keys ``host``, ``container``, and ``mode``:: [ {"host": "/usr/share/texlive", "container": "/usr/share/texlive", "mode": "ro"}, {"host": "/usr/bin/pdflatex", "container": "/usr/bin/pdflatex", "mode": "ro"}, ... ] """ mounts: list[dict] = [] # Directory mounts (share/texlive, share/texmf-dist, ...) for rel_dir in TEXLIVE_DIRS: host_path = Path(prefix) / rel_dir if host_path.exists(): container_path = Path("/") / rel_dir # mirror path in container mounts.append( { "host": str(host_path), "container": str(container_path), "mode": "ro", } ) # Binary mounts bin_dir = Path(prefix) / "bin" container_bin = Path("/usr/bin") for binary in TEXLIVE_BINARIES: host_bin = bin_dir / binary if host_bin.exists(): mounts.append( { "host": str(host_bin), "container": str(container_bin / binary), "mode": "ro", } ) return mounts
[docs] @supports_return_as def get_mount_config( texlive_prefix: str = "", host_mounts_raw: str = "", ) -> dict: """Parse mount configuration and return structured bind mount info. Parameters ---------- texlive_prefix : str Installation prefix for TeXLive (e.g. ``/usr``). When empty, defaults to ``/usr`` if TeXLive binaries are found there, otherwise skips TeXLive mounts. host_mounts_raw : str Colon-separated raw bind specs in ``host:container`` or ``host:container:mode`` format, separated by commas or newlines. Example: ``"/data:/data:ro,/scratch:/scratch"`` Returns ------- dict Structured mount information:: { "bind_args": ["--bind", "/usr/share/texlive:/usr/share/texlive:ro", ...], "path_additions": ["/usr/bin"], "mounts": [{"host": "...", "container": "...", "mode": "ro"}, ...], } """ mounts: list[dict] = [] path_additions: list[str] = [] # --- TeXLive mounts --- prefix = texlive_prefix or "/usr" texlive_mounts = get_texlive_binds(prefix=prefix) if texlive_mounts: mounts.extend(texlive_mounts) bin_dir = str(Path(prefix) / "bin") if bin_dir not in path_additions: path_additions.append(bin_dir) # --- Raw extra mounts --- if host_mounts_raw: # Accept comma or newline as separator raw_entries = [ e.strip() for e in host_mounts_raw.replace("\n", ",").split(",") if e.strip() ] for entry in raw_entries: parts = entry.split(":") if len(parts) == 2: # noqa: PLR2004 host_path, container_path = parts mode = "rw" elif len(parts) == 3: # noqa: PLR2004 host_path, container_path, mode = parts else: # Malformed — skip continue mounts.append( { "host": host_path, "container": container_path, "mode": mode, } ) # --- Build --bind args --- bind_args: list[str] = [] for mount in mounts: spec = f"{mount['host']}:{mount['container']}:{mount['mode']}" bind_args.extend(["--bind", spec]) return { "bind_args": bind_args, "path_additions": path_additions, "mounts": mounts, }
# EOF