# How Book-to-Skill Probes for Optional Dependencies: Detection Logic Explained

> Learn how Book-to-Skill probes for optional dependencies using importability checks and system binary availability. Understand the detection logic for graceful degradation.

- Repository: [Virgilio Junior/book-to-skill](https://github.com/virgiliojr94/book-to-skill)
- Tags: deep-dive
- Published: 2026-08-29

---

**Book-to-Skill detects optional dependencies by verifying Python module importability with `importlib.util.find_spec` and system binary availability via `shutil.which`, orchestrated through functions in [`book_to_skill/dependencies.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/dependencies.py) to enable graceful degradation when extraction enhancers are absent.**

Unlike monolithic tools that fail on missing requirements, Book-to-Skill treats most third-party packages and external utilities as *optional*—they improve extraction fidelity but are not hard requirements (with the sole exception of Calibre for MOBI/AZW processing). The repository implements a two-stage probing system that defines dependency groups per format and validates them at runtime.

## Defining Dependency Groups in [`dependencies.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/dependencies.py)

The probing logic centers on a declarative data structure defined at the top of **[[`book_to_skill/dependencies.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/dependencies.py)](https://github.com/virgiliojr94/book-to-skill/blob/master/book_to_skill/dependencies.py)** (lines 13‑70). Each supported format declares its Python modules, system commands, and fallback rules using dictionaries that specify whether **any** or **all** dependencies must be present.

For PDF text extraction, the configuration looks like this:

```python
{
    "label": "PDF (text‑heavy)",
    "modules": ["pypdf", "pdfminer"],
    "any_of_modules": True,
    "any_tool_suffices": True,
    "system": [("pdftotext", "poppler‑utils",
                "sudo apt install poppler-utils")],
    "note": "any one of pdftotext / pypdf / pdfminer is enough",
}

```

This structure maps file extensions to their optimal and fallback parsers, storing installation hints for both PyPI packages and system-level tools.

## Runtime Probing Mechanism

When the extractor initializes, it invokes either **`prepare_dependencies()`** (for per-format checks during extraction) or **`run_dependency_check()`** (when users run `extract.py --check`). Both functions rely on a shared set of utility helpers to probe the environment without importing heavy libraries upfront.

### Python Module Detection

The function **`python_module_available(module_name)`** checks if a package is installed using `importlib.util.find_spec`, which locates the module specification without executing import side effects:

```python
present_modules = [m for m in group["modules"] if python_module_available(m)]

```

This approach avoids the performance penalty of actually importing libraries like `pypdf` or `pdfminer.six` just to verify their existence.

### System Binary Detection

For external tools such as `pdftotext` (from poppler-utils), the system uses **`shutil.which(cmd)`** to verify the binary exists on the system PATH:

```python
system_present = [c for c, _, _ in group["system"] if shutil.which(c)]

```

### The Core Probing Loop

The **`run_dependency_check()`** function iterates over `DEPENDENCY_GROUPS` and aggregates status reports. It prints a ✓/✗ status for each component and compiles missing items into installation commands. According to the source code, if all optional dependencies are satisfied, the probe outputs:

```

All optional dependencies are installed. You're ready for every format.

```

## Installation Handling and Graceful Fallbacks

When dependencies are missing, Book-to-Skill does not halt execution. Instead, **`offer_dependency_install()`** evaluates the `install_mode` parameter (set via the `--install-missing` CLI flag or the `BOOK_SKILL_INSTALL_MISSING` environment variable) to decide whether to prompt the user, auto-install packages, or proceed with fallbacks.

For automatic remediation, **`install_python_packages()`** executes `pip install` for missing Python modules:

```python
if ext == ".pdf" and not shutil.which("pdftotext"):
    offer_dependency_install(
        feature="PDF text extraction",
        module_names=["pypdf", "pdfminer"],
        fallback="any installed Python PDF parser; extraction fails if none are available",
        install_mode=install_mode,
    )

```

The per-format probe (`prepare_dependencies()`) limits its scope to the file extension being processed, ensuring extraction proceeds with the best available tool while degrading to fallback parsers if the optimal ones are absent.

## Checking Dependencies via CLI

Users can audit their environment without processing a book by running the standalone dependency check:

```bash
python -m book_to_skill extract.py --check

```

This produces a formatted report listing each format, the status of its Python modules (e.g., `pypdf`, `pdfminer.six`), and system utilities (e.g., `pdftotext`), along with exact installation commands for missing components.

To enable automatic installation during extraction:

```bash
python -m book_to_skill extract.py mybook.pdf --install-missing yes

```

This triggers the detection flow: identifying missing modules and binaries, installing Python packages via pip, and continuing extraction using the newly available or fallback parsers.

For programmatic integration, developers can invoke the probe directly:

```python
from book_to_skill.dependencies import prepare_dependencies, normalize_install_mode

install_mode = normalize_install_mode(["--install-missing", "yes"])
prepare_dependencies(ext=".pdf", extraction_mode="technical", install_mode=install_mode)

```

## Summary

- **Centralized definitions**: All optional dependencies are declared in [`book_to_skill/dependencies.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/dependencies.py) (lines 13‑70) with fallback rules and installation hints.
- **Non-invasive detection**: The system uses `importlib.util.find_spec` for Python packages and `shutil.which` for binaries, avoiding heavy imports during checks.
- **Flexible installation**: The `offer_dependency_install()` function respects user preferences via CLI flags (`--install-missing`) or environment variables (`BOOK_SKILL_INSTALL_MISSING`).
- **Graceful degradation**: Extraction proceeds using available fallbacks when optional enhancers are missing, with only format-specific hard requirements (like Calibre for MOBI) causing failure.

## Frequently Asked Questions

### How does Book-to-Skill verify if a Python package is installed without importing it?

According to the source code in [`book_to_skill/dependencies.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/dependencies.py), the `python_module_available()` function uses `importlib.util.find_spec(module_name)` to locate the module specification without executing the import statement. This lightweight check avoids the memory and startup overhead of loading heavy libraries like `pdfminer.six` or `pypdf` just for verification purposes.

### What happens if an optional system tool like `pdftotext` is missing?

The probing logic in `run_dependency_check()` marks the tool as missing (✗) and checks if any Python-based fallback modules are available. If `any_tool_suffices` is set to `True` in the dependency group definition, the extractor proceeds using alternative parsers like `pypdf` or `pdfminer`, though potentially with reduced extraction quality for complex layouts.

### Can Book-to-Skill automatically install missing dependencies?

Yes, when invoked with `--install-missing yes` or when the `BOOK_SKILL_INSTALL_MISSING` environment variable is set, the `offer_dependency_install()` function triggers `install_python_packages()` to run `pip install` for missing Python modules. System tools must still be installed manually, but the dependency check outputs the exact command (e.g., `sudo apt install poppler-utils`) required.

### Which dependency is mandatory rather than optional?

While Book-to-Skill treats most third-party packages as optional, **Calibre** is a hard requirement specifically for processing MOBI and AZW ebook formats. All other dependencies—including PDF parsers, OCR engines, and text extraction libraries—operate on a fallback system where the tool degrades gracefully if the optimal extractor is unavailable.