# How Holehe Discovers Its Modules: Dynamic Import System Explained

> Discover how Holehe dynamically imports its modules at runtime using a recursive helper function, automatically discovering service checkers without manual registration.

- Repository: [Palenath/holehe](https://github.com/megadose/holehe)
- Tags: internals
- Published: 2026-08-29

---

**Holehe discovers its modules dynamically at runtime using the `import_submodules` helper function defined in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py), which recursively walks the `holehe.modules` package tree and auto-imports every service checker without manual registration.**

The open-source OSINT tool [Holehe](https://github.com/megadose/holehe) employs a plugin-style architecture that automatically detects available email-checking services at startup. Understanding how Holehe discovers its modules reveals a clean Python pattern using `importlib` and `pkgutil` that enables developers to add new platforms simply by dropping Python files into the modules directory.

## The Core Mechanism: `import_submodules` in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py)

At the heart of Holehe's module discovery lies the **`import_submodules`** function located at lines 37–49 of [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py). This utility leverages Python's standard library to perform dynamic imports at runtime rather than relying on hardcoded import statements.

```python

# From holehe/core.py (lines 37-49)

from importlib import import_module
import pkgutil

def import_submodules(package, recursive=True):
    results = {}
    for loader, name, is_pkg in pkgutil.iter_modules(package.__path__):
        full_name = f"{package.__name__}.{name}"
        results[full_name] = import_module(full_name)
        if recursive and is_pkg:
            results.update(import_submodules(import_module(full_name)))
    return results

```

The function accepts a package name (typically `"holehe.modules"`) and uses **`pkgutil.iter_modules()`** to enumerate all modules within that package's path. For each module discovered, it constructs the fully-qualified name and imports it using **`importlib.import_module()`**, storing the results in a dictionary mapping module names to module objects.

## How the Recursive Discovery Process Works

### Traversing the Package Tree

When `import_submodules` executes, it inspects the `__path__` attribute of the target package using `pkgutil.iter_modules()`. This generator yields tuples containing the loader, module name, and a boolean flag `is_pkg` indicating whether the entry is a sub-package.

For each entry found, the function constructs the full module path (e.g., `"holehe.modules.social_media.twitter"`) and imports it immediately. This approach ensures that every `.py` file inside the modules directory becomes available as an imported module object without requiring explicit imports in the main codebase.

### Recursive Loading of Sub-packages

Lines 46–48 of [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) handle nested directory structures:

```python

# Lines 46-48 of holehe/core.py

if recursive and is_pkg:
    results.update(import_submodules(import_module(full_name)))

```

When the function encounters a sub-package (indicated by `is_pkg=True`), it calls itself recursively and merges the returned dictionary into the main results. This recursive merging ensures that deeply nested modules—such as those organized under `holehe/modules/social_media/` or `holehe/modules/shopping/`—are discovered regardless of directory depth.

### Bootstrapping at Program Start

The discovery mechanism triggers during application initialization. At lines 206–207 of [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py), the main execution flow invokes:

```python

# Line 206 in holehe/core.py

modules = import_submodules("holehe.modules")

```

This single call populates the `modules` dictionary with every available service checker before any email verification begins. The CLI then iterates over these loaded modules to determine which platforms to query for the provided email address.

## Adding New Modules to Holehe

Holehe's discovery system enables a **plug-and-play** development workflow. To add support for a new platform:

1. Create a new Python file inside `holehe/modules/<category>/` (e.g., [`holehe/modules/social_media/newservice.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/newservice.py))
2. Implement the required service functions following the existing module pattern
3. Restart Holehe—the `import_submodules` function automatically imports the new file on startup without requiring changes to [`core.py`](https://github.com/megadose/holehe/blob/main/core.py) or any registry files

This design eliminates the need for manual module registration or configuration file updates. The directory structure itself serves as the single source of truth for available services.

## Summary

- **Dynamic loading**: Holehe uses `import_submodules` in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) (lines 37–49) to discover modules at runtime using `pkgutil` and `importlib`.
- **Recursive discovery**: The function recursively traverses sub-packages (lines 46–48), ensuring nested service categories are fully loaded.
- **Automatic bootstrapping**: Module discovery triggers automatically at line 206 when the program starts, populating the available service list.
- **Zero-configuration extension**: Adding new services requires only dropping Python files into `holehe/modules/`; the dynamic loader handles registration automatically.

## Frequently Asked Questions

### What Python function does Holehe use to discover its modules?

Holehe uses the **`import_submodules`** function defined in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) at lines 37–49. This function utilizes `pkgutil.iter_modules()` to scan the package directory and `importlib.import_module()` to load each discovered module into memory at runtime.

### Where does Holehe trigger the module discovery process?

The discovery process triggers at **line 206 of [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py)**, where the code executes `modules = import_submodules("holehe.modules")`. This occurs during program initialization before the CLI begins processing email verification requests.

### Do I need to manually register new modules when extending Holehe?

No. Holehe requires **zero manual registration**. Simply placing a new Python file inside the `holehe/modules/` directory tree (or any sub-package) automatically includes it in the next runtime discovery cycle. The `import_submodules` function treats the filesystem structure as the canonical registry of available services.

### Which Python standard library modules enable Holehe's dynamic loading?

Holehe relies on **`pkgutil`** (to iterate over package contents) and **`importlib`** (to perform dynamic imports). Specifically, `pkgutil.iter_modules(package.__path__)` identifies available modules, while `importlib.import_module(full_name)` loads them into the Python namespace at runtime.