How Holehe Dynamically Imports All Website Modules at Runtime
Holehe automatically discovers and loads every site-specific module using a recursive utility called import_submodules in holehe/core.py, enabling plug-and-play OSINT checks without manual registration.
The dynamic import system is the backbone of Holehe's plugin architecture. As implemented in the megadose/holehe repository, this mechanism allows the tool to scale to hundreds of supported websites simply by adding Python files to the holehe/modules directory—no code changes required.
The import_submodules Utility
Holehe's dynamic import logic lives in import_submodules (lines 37-47 of holehe/core.py). This function walks the package tree and recursively imports every module it finds.
import importlib
import pkgutil
def import_submodules(package, recursive=True):
"""Import every submodule of *package* and return a dict."""
if isinstance(package, str):
package = importlib.import_module(package)
results = {}
for loader, name, is_pkg in pkgutil.walk_packages(package.__path__):
full_name = f"{package.__name__}.{name}"
results[full_name] = importlib.import_module(full_name)
if recursive and is_pkg:
results.update(import_submodules(full_name))
return results
The function accepts either a string package name or module object, uses pkgutil.walk_packages to iterate subdirectories, and builds full dotted names before importing with importlib.import_module. Nested packages trigger recursive calls when is_pkg is true.
How Dynamic Import Works at Runtime
The process unfolds in three stages when you run Holehe.
1. Package Discovery
pkgutil.walk_packages(package.__path__) yields a tuple for every entry in holehe/modules:
loader– the module loader objectname– the module or package nameis_pkg– boolean indicating if it's a sub-package
2. Module Import and Storage
For each discovered name, Holehe constructs the full dotted path and imports it:
full_name = f"{package.__name__}.{name}" # e.g., "holehe.modules.social_media.twitter"
results[full_name] = importlib.import_module(full_name)
This stores imported module objects in a dictionary keyed by their full names, enabling later lookup by any part of the system.
3. Recursive Expansion
When is_pkg is true and recursion is enabled, import_submodules calls itself on the sub-package, merging results upward. This captures deeply nested modules like holehe.modules.social_media.twitter regardless of directory depth.
Loading Modules in maincore
The maincore function (around line 206 in holehe/core.py) invokes the importer and prepares modules for execution:
modules = import_submodules("holehe.modules") # loads all site modules
websites = get_functions(modules, args) # extracts callable coroutines
The modules dictionary now contains every site-specific module—holehe.modules.social_media.twitter, holehe.modules.shopping.amazon, and any others present in the filesystem.
From Modules to Callable Checks
The get_functions helper transforms the imported module objects into actual coroutine functions. Each module exports an async function matching its filename:
# holehe/modules/social_media/twitter.py
async def twitter(email, client, out):
# Password recovery probe against twitter.com
out.append({
"name": "twitter",
"domain": "twitter.com",
"rateLimit": False,
"error": False,
"exists": True,
"emailrecovery": "t****@gmail.com",
"phoneNumber": None,
"others": None,
})
These coroutines are then launched concurrently via Trio to perform OSINT checks across all supported platforms.
Why This Architecture Matters
The dynamic import approach provides three key advantages:
- Zero configuration – New site modules work immediately when dropped into
holehe/modules - Clean separation – Each platform lives in its own file with no central registry to maintain
- Scalable maintenance – Contributors add support for new sites without touching core code
According to the Holehe source code, this pattern is intentionally minimal. The holehe/modules/__init__.py file is empty—its sole purpose is marking the directory as a Python package so importlib can traverse it.
Summary
import_submodulesinholehe/core.py(lines 37-47) implements recursive dynamic import usingpkgutil.walk_packagesandimportlib.import_module- Runtime invocation occurs in
maincorearound line 206, loading all modules underholehe.modulesinto a dictionary - Automatic discovery eliminates manual registration—new Python files in
holehe/modulesbecome available immediately get_functionsconverts imported modules into callable coroutines for concurrent OSINT execution
Frequently Asked Questions
What Python standard library modules does Holehe use for dynamic importing?
Holehe uses pkgutil for package tree walking and importlib for programmatic module importing. These are both part of Python's standard library and require no external dependencies.
Can I disable recursive importing in Holehe's import_submodules?
Yes. The import_submodules function accepts a recursive parameter that defaults to True. Passing recursive=False would limit imports to the top-level package only, though Holehe's maincore calls it with the default behavior to ensure nested site modules load.
How does Holehe handle new website modules I add manually?
Any Python file placed under holehe/modules (or its subdirectories) is automatically imported on the next run. The file should contain an async function matching its basename—pinterest.py defines async def pinterest(...)—which get_functions will discover and execute.
Where does the actual email checking logic live?
Each site-specific coroutine lives in its own module under holehe/modules/, organized by category. For example, Twitter's implementation is in holehe/modules/social_media/twitter.py, while Amazon's is in holehe/modules/shopping/amazon.py. The dynamic import system makes all of them available to the concurrent execution loop without explicit enumeration.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →