How Holehe Discovers Its Modules: Dynamic Import System Explained

Holehe discovers its modules dynamically at runtime using the import_submodules helper function defined in 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 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

At the heart of Holehe's module discovery lies the import_submodules function located at lines 37–49 of holehe/core.py. This utility leverages Python's standard library to perform dynamic imports at runtime rather than relying on hardcoded import statements.


# 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 handle nested directory structures:


# 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, the main execution flow invokes:


# 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)
  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 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 (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 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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →