Skip to content

jettex

The main module that exports all public functions and classes.

Usage

import jettex

# All functions are available directly
jettex.install_tinytex()
jettex.compile_tex("document.tex")

Exports

Installation

Download and install TinyTeX.

Parameters:

Name Type Description Default
version int

TinyTeX version to install - 0: infraonly (minimal, no packages) - 1: default (~90 packages for common documents) - 2: extended (more packages)

1
install_dir Optional[Path]

Custom installation directory (default: platform-specific)

None
force bool

Force reinstall even if already installed

False
add_path bool

Add TinyTeX bin to PATH

True
progress_callback Optional[Callable[[str, int, int], None]]

Optional callback(stage, downloaded, total)

None

Returns:

Name Type Description
Path Path

Installation directory

Raises:

Type Description
RuntimeError

If installation fails

Source code in jettex/install.py
def install_tinytex(
    version: int = 1,
    install_dir: Optional[Path] = None,
    force: bool = False,
    add_path: bool = True,
    progress_callback: Optional[Callable[[str, int, int], None]] = None,
) -> Path:
    """Download and install TinyTeX.

    Args:
        version: TinyTeX version to install
            - 0: infraonly (minimal, no packages)
            - 1: default (~90 packages for common documents)
            - 2: extended (more packages)
        install_dir: Custom installation directory (default: platform-specific)
        force: Force reinstall even if already installed
        add_path: Add TinyTeX bin to PATH
        progress_callback: Optional callback(stage, downloaded, total)

    Returns:
        Path: Installation directory

    Raises:
        RuntimeError: If installation fails
    """
    if install_dir is None:
        install_dir = get_default_install_dir()
    else:
        install_dir = Path(install_dir)

    # Check if already installed
    if not force and is_tinytex_installed():
        existing = get_tinytex_root()
        if existing:
            if add_path:
                bin_dir = get_bin_dir()
                if bin_dir:
                    add_to_path(bin_dir)
            return existing

    # Remove existing installation if force
    if force and install_dir.exists():
        shutil.rmtree(install_dir)

    # Create installation directory
    install_dir.mkdir(parents=True, exist_ok=True)

    # Get download URL
    url = get_download_url(version)

    # Determine archive filename
    archive_name = url.split("/")[-1]

    # Download to temp file
    with tempfile.TemporaryDirectory() as tmpdir:
        tmpdir_path = Path(tmpdir)
        archive_path = tmpdir_path / archive_name

        # Download
        def download_progress(downloaded: int, total: int) -> None:
            if progress_callback:
                progress_callback("downloading", downloaded, total)

        try:
            download_file(url, archive_path, download_progress)
        except requests.RequestException as e:
            raise RuntimeError(f"Failed to download TinyTeX: {e}") from e

        # Extract
        if progress_callback:
            progress_callback("extracting", 0, 0)

        try:
            extract_archive(archive_path, tmpdir_path)
        except Exception as e:
            raise RuntimeError(f"Failed to extract TinyTeX: {e}") from e

        # Find extracted directory (usually named TinyTeX or .TinyTeX)
        extracted_dirs = [
            d for d in tmpdir_path.iterdir()
            if d.is_dir() and d.name != "__MACOSX"
        ]

        if not extracted_dirs:
            raise RuntimeError("No directory found in extracted archive")

        extracted_dir = extracted_dirs[0]

        # Move to installation directory
        if progress_callback:
            progress_callback("installing", 0, 0)

        # If the install_dir is the target, move contents into it
        for item in extracted_dir.iterdir():
            dest = install_dir / item.name
            if dest.exists():
                if dest.is_dir():
                    shutil.rmtree(dest)
                else:
                    dest.unlink()
            shutil.move(str(item), str(dest))

    # Add to PATH if requested
    if add_path:
        bin_dir = get_bin_dir()
        if bin_dir:
            add_to_path(bin_dir)

    if progress_callback:
        progress_callback("complete", 100, 100)

    return install_dir

Uninstall TinyTeX.

Parameters:

Name Type Description Default
install_dir Optional[Path]

Installation directory to remove (default: detect automatically)

None

Returns:

Name Type Description
bool bool

True if uninstalled successfully

Source code in jettex/install.py
def uninstall_tinytex(install_dir: Optional[Path] = None) -> bool:
    """Uninstall TinyTeX.

    Args:
        install_dir: Installation directory to remove (default: detect automatically)

    Returns:
        bool: True if uninstalled successfully
    """
    if install_dir is None:
        install_dir = get_tinytex_root()

    if install_dir is None or not install_dir.exists():
        return False

    try:
        shutil.rmtree(install_dir)
        return True
    except Exception:
        return False

Get the TinyTeX installation root directory.

Returns:

Type Description
Optional[Path]

Path or None: Installation directory if found

Source code in jettex/install.py
def tinytex_root() -> Optional[Path]:
    """Get the TinyTeX installation root directory.

    Returns:
        Path or None: Installation directory if found
    """
    return get_tinytex_root()

Get the installed TinyTeX/TeX Live version.

Returns:

Type Description
Optional[str]

str or None: Version string if available

Source code in jettex/install.py
def tinytex_version() -> Optional[str]:
    """Get the installed TinyTeX/TeX Live version.

    Returns:
        str or None: Version string if available
    """
    from .tlmgr import tlmgr_version
    return tlmgr_version()

Compilation

Compile a LaTeX file using pdflatex.

Parameters:

Name Type Description Default
input_file Union[str, Path]

Path to .tex file

required
output_dir Optional[Union[str, Path]]

Directory for output files

None
interaction str

Interaction mode

'nonstopmode'
synctex bool

Enable SyncTeX

False
shell_escape bool

Enable shell escape

False
extra_args Optional[List[str]]

Additional arguments

None
quiet bool

Suppress output

False
timeout Optional[int]

Compilation timeout in seconds

None

Returns:

Name Type Description
CompileResult CompileResult

Compilation result

Example

result = pdflatex("document.tex") if result.success: ... print(f"PDF created: {result.output_file}")

Source code in jettex/compile.py
def pdflatex(
    input_file: Union[str, Path],
    output_dir: Optional[Union[str, Path]] = None,
    interaction: str = "nonstopmode",
    synctex: bool = False,
    shell_escape: bool = False,
    extra_args: Optional[List[str]] = None,
    quiet: bool = False,
    timeout: Optional[int] = None,
) -> CompileResult:
    """Compile a LaTeX file using pdflatex.

    Args:
        input_file: Path to .tex file
        output_dir: Directory for output files
        interaction: Interaction mode
        synctex: Enable SyncTeX
        shell_escape: Enable shell escape
        extra_args: Additional arguments
        quiet: Suppress output
        timeout: Compilation timeout in seconds

    Returns:
        CompileResult: Compilation result

    Example:
        >>> result = pdflatex("document.tex")
        >>> if result.success:
        ...     print(f"PDF created: {result.output_file}")
    """
    return _compile_latex(
        "pdflatex",
        input_file,
        output_dir=output_dir,
        interaction=interaction,
        synctex=synctex,
        shell_escape=shell_escape,
        extra_args=extra_args,
        quiet=quiet,
        timeout=timeout,
    )

Compile a LaTeX file using xelatex.

XeLaTeX supports OpenType fonts and Unicode natively.

Parameters:

Name Type Description Default
input_file Union[str, Path]

Path to .tex file

required
output_dir Optional[Union[str, Path]]

Directory for output files

None
interaction str

Interaction mode

'nonstopmode'
synctex bool

Enable SyncTeX

False
shell_escape bool

Enable shell escape

False
extra_args Optional[List[str]]

Additional arguments

None
quiet bool

Suppress output

False
timeout Optional[int]

Compilation timeout in seconds

None

Returns:

Name Type Description
CompileResult CompileResult

Compilation result

Source code in jettex/compile.py
def xelatex(
    input_file: Union[str, Path],
    output_dir: Optional[Union[str, Path]] = None,
    interaction: str = "nonstopmode",
    synctex: bool = False,
    shell_escape: bool = False,
    extra_args: Optional[List[str]] = None,
    quiet: bool = False,
    timeout: Optional[int] = None,
) -> CompileResult:
    """Compile a LaTeX file using xelatex.

    XeLaTeX supports OpenType fonts and Unicode natively.

    Args:
        input_file: Path to .tex file
        output_dir: Directory for output files
        interaction: Interaction mode
        synctex: Enable SyncTeX
        shell_escape: Enable shell escape
        extra_args: Additional arguments
        quiet: Suppress output
        timeout: Compilation timeout in seconds

    Returns:
        CompileResult: Compilation result
    """
    return _compile_latex(
        "xelatex",
        input_file,
        output_dir=output_dir,
        interaction=interaction,
        synctex=synctex,
        shell_escape=shell_escape,
        extra_args=extra_args,
        quiet=quiet,
        timeout=timeout,
    )

Compile a LaTeX file using lualatex.

LuaLaTeX provides Lua scripting and OpenType font support.

Parameters:

Name Type Description Default
input_file Union[str, Path]

Path to .tex file

required
output_dir Optional[Union[str, Path]]

Directory for output files

None
interaction str

Interaction mode

'nonstopmode'
synctex bool

Enable SyncTeX

False
shell_escape bool

Enable shell escape

False
extra_args Optional[List[str]]

Additional arguments

None
quiet bool

Suppress output

False
timeout Optional[int]

Compilation timeout in seconds

None

Returns:

Name Type Description
CompileResult CompileResult

Compilation result

Source code in jettex/compile.py
def lualatex(
    input_file: Union[str, Path],
    output_dir: Optional[Union[str, Path]] = None,
    interaction: str = "nonstopmode",
    synctex: bool = False,
    shell_escape: bool = False,
    extra_args: Optional[List[str]] = None,
    quiet: bool = False,
    timeout: Optional[int] = None,
) -> CompileResult:
    """Compile a LaTeX file using lualatex.

    LuaLaTeX provides Lua scripting and OpenType font support.

    Args:
        input_file: Path to .tex file
        output_dir: Directory for output files
        interaction: Interaction mode
        synctex: Enable SyncTeX
        shell_escape: Enable shell escape
        extra_args: Additional arguments
        quiet: Suppress output
        timeout: Compilation timeout in seconds

    Returns:
        CompileResult: Compilation result
    """
    return _compile_latex(
        "lualatex",
        input_file,
        output_dir=output_dir,
        interaction=interaction,
        synctex=synctex,
        shell_escape=shell_escape,
        extra_args=extra_args,
        quiet=quiet,
        timeout=timeout,
    )

Compile a LaTeX file using latexmk.

latexmk automatically runs the correct number of compilations and handles bibliography/index generation.

Parameters:

Name Type Description Default
input_file Union[str, Path]

Path to .tex file

required
output_dir Optional[Union[str, Path]]

Directory for output files

None
engine str

Backend engine (pdflatex, xelatex, lualatex)

'pdflatex'
clean bool

Clean auxiliary files after compilation

False
continuous bool

Continuous compilation mode (watch for changes)

False
extra_args Optional[List[str]]

Additional arguments

None
timeout Optional[int]

Compilation timeout in seconds

None

Returns:

Name Type Description
CompileResult CompileResult

Compilation result

Source code in jettex/compile.py
def latexmk(
    input_file: Union[str, Path],
    output_dir: Optional[Union[str, Path]] = None,
    engine: str = "pdflatex",
    interaction: str = "nonstopmode",
    synctex: bool = False,
    shell_escape: bool = False,
    clean: bool = False,
    continuous: bool = False,
    extra_args: Optional[List[str]] = None,
    quiet: bool = False,
    timeout: Optional[int] = None,
) -> CompileResult:
    """Compile a LaTeX file using latexmk.

    latexmk automatically runs the correct number of compilations
    and handles bibliography/index generation.

    Args:
        input_file: Path to .tex file
        output_dir: Directory for output files
        engine: Backend engine (pdflatex, xelatex, lualatex)
        clean: Clean auxiliary files after compilation
        continuous: Continuous compilation mode (watch for changes)
        extra_args: Additional arguments
        timeout: Compilation timeout in seconds

    Returns:
        CompileResult: Compilation result
    """
    input_path = Path(input_file).resolve()

    if not input_path.exists():
        return CompileResult(
            success=False,
            output_file=None,
            log_file=None,
            stdout="",
            stderr=f"Input file not found: {input_path}",
            returncode=1,
        )

    if output_dir:
        output_path = Path(output_dir).resolve()
        output_path.mkdir(parents=True, exist_ok=True)
    else:
        output_path = input_path.parent

    latexmk_path = _get_engine("latexmk")
    cmd = [str(latexmk_path), "-pdf"]

    # Set engine
    if engine == "xelatex":
        cmd.append("-xelatex")
    elif engine == "lualatex":
        cmd.append("-lualatex")
    # pdflatex is default with -pdf

    if output_dir:
        cmd.append(f"-output-directory={output_path}")

    if clean:
        cmd.append("-c")

    if continuous:
        cmd.append("-pvc")

    cmd.append(f"-interaction={interaction}")

    if synctex:
        cmd.append("-synctex=1")

    if shell_escape:
        # latexmk passes this to the latex engine
        cmd.append("-shell-escape")

    if quiet:
        cmd.append("-quiet")

    if extra_args:
        cmd.extend(extra_args)

    cmd.append(str(input_path))

    result = run_command(
        cmd,
        cwd=input_path.parent,
        capture_output=True,
        check=False,
        timeout=timeout,
    )

    stem = input_path.stem
    output_pdf = output_path / f"{stem}.pdf"
    log_file = output_path / f"{stem}.log"

    return CompileResult(
        success=result.returncode == 0 and output_pdf.exists(),
        output_file=output_pdf if output_pdf.exists() else None,
        log_file=log_file if log_file.exists() else None,
        stdout=result.stdout,
        stderr=result.stderr,
        returncode=result.returncode,
    )

Clean auxiliary files from LaTeX compilation.

Parameters:

Name Type Description Default
input_file Union[str, Path]

Path to .tex file

required
output_dir Optional[Union[str, Path]]

Directory containing auxiliary files

None

Returns:

Name Type Description
int int

Number of files removed

Source code in jettex/compile.py
def clean_auxiliary(
    input_file: Union[str, Path],
    output_dir: Optional[Union[str, Path]] = None,
) -> int:
    """Clean auxiliary files from LaTeX compilation.

    Args:
        input_file: Path to .tex file
        output_dir: Directory containing auxiliary files

    Returns:
        int: Number of files removed
    """
    input_path = Path(input_file).resolve()
    stem = input_path.stem

    if output_dir:
        base_dir = Path(output_dir).resolve()
    else:
        base_dir = input_path.parent

    # Common auxiliary extensions
    aux_extensions = [
        ".aux", ".log", ".out", ".toc", ".lof", ".lot",
        ".fls", ".fdb_latexmk", ".synctex.gz", ".synctex",
        ".bbl", ".blg", ".bcf", ".run.xml",
        ".idx", ".ilg", ".ind",
        ".nav", ".snm", ".vrb",  # Beamer
        ".xdv",  # XeTeX intermediate
    ]

    removed = 0
    for ext in aux_extensions:
        aux_file = base_dir / f"{stem}{ext}"
        if aux_file.exists():
            try:
                aux_file.unlink()
                removed += 1
            except OSError:
                pass

    return removed

Package Management

Install LaTeX packages.

Parameters:

Name Type Description Default
packages List[str]

List of package names to install

required

Returns:

Name Type Description
bool bool

True if installation succeeded

Example

tlmgr_install(['amsmath', 'geometry'])

Source code in jettex/tlmgr.py
def tlmgr_install(packages: List[str]) -> bool:
    """Install LaTeX packages.

    Args:
        packages: List of package names to install

    Returns:
        bool: True if installation succeeded

    Example:
        >>> tlmgr_install(['amsmath', 'geometry'])
    """
    if not packages:
        return True

    result = _run_tlmgr(["install"] + packages, check=False)
    return result.returncode == 0

Remove LaTeX packages.

Parameters:

Name Type Description Default
packages List[str]

List of package names to remove

required

Returns:

Name Type Description
bool bool

True if removal succeeded

Source code in jettex/tlmgr.py
def tlmgr_remove(packages: List[str]) -> bool:
    """Remove LaTeX packages.

    Args:
        packages: List of package names to remove

    Returns:
        bool: True if removal succeeded
    """
    if not packages:
        return True

    result = _run_tlmgr(["remove"] + packages, check=False)
    return result.returncode == 0

Update LaTeX packages or tlmgr itself.

Parameters:

Name Type Description Default
packages Optional[List[str]]

Specific packages to update (optional)

None
all_packages bool

Update all installed packages

False
self_update bool

Update tlmgr itself

False

Returns:

Name Type Description
bool bool

True if update succeeded

Source code in jettex/tlmgr.py
def tlmgr_update(
    packages: Optional[List[str]] = None,
    all_packages: bool = False,
    self_update: bool = False,
) -> bool:
    """Update LaTeX packages or tlmgr itself.

    Args:
        packages: Specific packages to update (optional)
        all_packages: Update all installed packages
        self_update: Update tlmgr itself

    Returns:
        bool: True if update succeeded
    """
    args = ["update"]

    if self_update:
        args.append("--self")

    if all_packages:
        args.append("--all")
    elif packages:
        args.extend(packages)
    else:
        # Nothing to update
        return True

    result = _run_tlmgr(args, check=False)
    return result.returncode == 0

List packages.

Parameters:

Name Type Description Default
only_installed bool

Only list installed packages

True

Returns:

Type Description
List[str]

List[str]: Package names

Source code in jettex/tlmgr.py
def tlmgr_list(only_installed: bool = True) -> List[str]:
    """List packages.

    Args:
        only_installed: Only list installed packages

    Returns:
        List[str]: Package names
    """
    args = ["list"]
    if only_installed:
        args.append("--only-installed")

    result = _run_tlmgr(args, check=False)
    if result.returncode != 0:
        return []

    packages = []
    for line in result.stdout.splitlines():
        # Parse tlmgr list output: "i packagename: description"
        match = re.match(r"^i\s+(\S+):", line)
        if match:
            packages.append(match.group(1))

    return packages

Search for packages.

Parameters:

Name Type Description Default
query str

Search query (package name or filename)

required
file_search bool

Search by filename instead of package name

False
global_search bool

Search all available packages, not just installed

True

Returns:

Type Description
List[Dict[str, str]]

List of dicts with 'package' and optionally 'file' keys

Source code in jettex/tlmgr.py
def tlmgr_search(
    query: str,
    file_search: bool = False,
    global_search: bool = True,
) -> List[Dict[str, str]]:
    """Search for packages.

    Args:
        query: Search query (package name or filename)
        file_search: Search by filename instead of package name
        global_search: Search all available packages, not just installed

    Returns:
        List of dicts with 'package' and optionally 'file' keys
    """
    args = ["search"]

    if global_search:
        args.append("--global")

    if file_search:
        args.append("--file")

    args.append(query)

    result = _run_tlmgr(args, check=False)
    if result.returncode != 0:
        return []

    results = []
    for raw_line in result.stdout.splitlines():
        line = raw_line.strip()
        if not line:
            continue

        # Ignore tlmgr info messages (e.g. "tlmgr: package repository...")
        if line.startswith("tlmgr:"):
            continue

        if file_search:
            # Format: "package:\n    path/to/file"
            # or "package: path/to/file"

            # Check for indentation in raw line indicating a file path
            if (raw_line.startswith(" ") or raw_line.startswith("\t")) and results:
                results[-1]["file"] = line
            elif ":" in line:
                # Format: "package: file" or just "package:"
                parts = line.split(":", 1)
                package = parts[0].strip()

                # Sanity check: package names generally don't have backslashes/slashes
                if "\\" in package or "/" in package:
                    continue

                # Heuristic: tlmgr output "package: file" always has a space after colon.
                # If there's no space and the suffix is non-empty, it's likely a path (C:\...) or URL (http://...)
                # split(":", 1) preserves leading whitespace in the second part.
                if len(parts) > 1:
                    suffix = parts[1]
                    if suffix and not suffix[0].isspace():
                        continue

                entry = {"package": package}

                if file_search and len(parts) > 1:
                    rest = parts[1].strip()
                    if rest:
                        entry["file"] = rest

                results.append(entry)
        else:
            # Package search format varies
            match = re.match(r"^(\S+)\s*[-:]", line)
            if match:
                results.append({"package": match.group(1)})

    return results

Get information about a package.

Parameters:

Name Type Description Default
package str

Package name

required

Returns:

Type Description
Optional[Dict[str, Any]]

Dict with package info or None if not found

Source code in jettex/tlmgr.py
def tlmgr_info(package: str) -> Optional[Dict[str, Any]]:
    """Get information about a package.

    Args:
        package: Package name

    Returns:
        Dict with package info or None if not found
    """
    result = _run_tlmgr(["info", package], check=False)
    if result.returncode != 0:
        return None

    info = {"name": package}

    for line in result.stdout.splitlines():
        if ":" in line:
            key, _, value = line.partition(":")
            key = key.strip().lower().replace(" ", "_")
            value = value.strip()
            if key and value:
                info[key] = value

    return info

Find which package provides a file.

This is the key function for auto-installing missing packages.

Parameters:

Name Type Description Default
filename str

File to search for (e.g., 'geometry.sty')

required

Returns:

Type Description
Optional[str]

str or None: Package name that provides the file

Source code in jettex/tlmgr.py
def find_package_for_file(filename: str) -> Optional[str]:
    """Find which package provides a file.

    This is the key function for auto-installing missing packages.

    Args:
        filename: File to search for (e.g., 'geometry.sty')

    Returns:
        str or None: Package name that provides the file
    """
    results = tlmgr_search(filename, file_search=True, global_search=True)

    if results:
        # Prioritize exact file matches
        # tlmgr search --file returns partial matches too (e.g. lwarp-manyfoot.sty for manyfoot.sty)
        for res in results:
            file_path = res.get("file")
            if file_path and Path(file_path).name == filename:
                return res.get("package")

        # Prioritize package name matching the filename stem
        stem = Path(filename).stem
        for res in results:
            pkg = res.get("package")
            if pkg == stem:
                return pkg

        package = results[0].get("package")
        if package and package == filename:
            # Sometimes tlmgr returns the filename as the package name
            # e.g. "acmart.sty" -> "acmart.sty"
            # We want to install "acmart"
            return Path(filename).stem
        return package

    return None

Utilities

Check if TinyTeX is installed.

Returns:

Name Type Description
bool bool

True if TinyTeX is installed and usable

Source code in jettex/utils.py
def is_tinytex_installed() -> bool:
    """Check if TinyTeX is installed.

    Returns:
        bool: True if TinyTeX is installed and usable
    """
    bin_dir = get_bin_dir()
    if bin_dir is None:
        return False

    # Check for tlmgr which should always exist
    tlmgr = get_executable("tlmgr")
    return tlmgr is not None and tlmgr.exists()

Ensure TinyTeX bin directory is in PATH.

Returns:

Name Type Description
bool bool

True if TinyTeX is available in PATH

Source code in jettex/utils.py
def ensure_tinytex_in_path() -> bool:
    """Ensure TinyTeX bin directory is in PATH.

    Returns:
        bool: True if TinyTeX is available in PATH
    """
    bin_dir = get_bin_dir()
    if bin_dir is None:
        return False

    add_to_path(bin_dir)
    return True

Get the TinyTeX bin directory containing executables.

Returns:

Type Description
Optional[Path]

Path or None: bin directory if found

Source code in jettex/utils.py
def get_bin_dir() -> Optional[Path]:
    """Get the TinyTeX bin directory containing executables.

    Returns:
        Path or None: bin directory if found
    """
    root = get_tinytex_root()
    if root is None:
        return None

    plat = get_platform()
    arch = get_arch()

    # Construct platform-specific bin path
    if plat == "windows":
        bin_subdir = "win32"
    elif plat == "darwin":
        if arch == "aarch64":
            bin_subdir = "universal-darwin"
        else:
            bin_subdir = "universal-darwin"
    else:
        if arch == "aarch64":
            bin_subdir = "aarch64-linux"
        else:
            bin_subdir = "x86_64-linux"

    # Check various possible structures
    candidates = [
        root / "bin" / bin_subdir,
        root / ".TinyTeX" / "bin" / bin_subdir,
        root / "bin" / "windows",
        root / ".TinyTeX" / "bin" / "windows",
        root / "bin",
    ]

    for candidate in candidates:
        if candidate.exists():
            return candidate

    return None

Get the path to a TinyTeX executable.

Parameters:

Name Type Description Default
name str

Executable name (e.g., 'pdflatex', 'tlmgr')

required

Returns:

Type Description
Optional[Path]

Path or None: Full path to executable if found

Source code in jettex/utils.py
def get_executable(name: str) -> Optional[Path]:
    """Get the path to a TinyTeX executable.

    Args:
        name: Executable name (e.g., 'pdflatex', 'tlmgr')

    Returns:
        Path or None: Full path to executable if found
    """
    bin_dir = get_bin_dir()
    if bin_dir is None:
        return None

    plat = get_platform()

    if plat == "windows":
        # TeX Live on Windows ships tools as .exe or .bat (tlmgr is often .bat).
        for suffix in (".exe", ".bat", ".cmd", ".ps1"):
            exe = bin_dir / f"{name}{suffix}"
            if exe.exists():
                return exe
    else:
        exe = bin_dir / name
        if exe.exists():
            return exe

    return None

Data Classes

Result of a LaTeX compilation.

Attributes

success instance-attribute

Whether compilation succeeded.

output_file instance-attribute

Path to the output PDF file if successful.

log_file instance-attribute

Path to the compilation log file.

stdout instance-attribute

Standard output from the compiler.

stderr instance-attribute

Standard error from the compiler.

returncode instance-attribute

Return code from the compiler.

Functions

Information about a missing package.

Attributes

filename instance-attribute

The missing file (e.g., 'geometry.sty').

package instance-attribute

The package that provides this file, if found.

error_message instance-attribute

The original error message.

Functions