How Holehe's Auto‑Update Feature Works: PyPI Version Check and Self‑Upgrade Explained
Holehe automatically checks PyPI for newer releases on every startup and upgrades itself via pip if a version mismatch is detected.
The open‑source OSINT tool holehe (maintained by megadose/holehe) includes a built‑in auto‑update mechanism that ensures users always run the latest version without manual intervention. This article breaks down exactly how the version detection, subprocess upgrade, and exit flow operate in the source code.
PyPI Version Check in holehe/core.py
The core logic lives in holehe/core.py, specifically the check_update() function at lines 65‑85. This function queries the public PyPI JSON API to determine whether the installed version lags behind the published release.
The HTTP Request and Comparison
# https://github.com/megadose/holehe/blob/master/holehe/core.py#L65-L85
def check_update():
"""Check and update holehe if not the last version"""
check_version = httpx.get("https://pypi.org/pypi/holehe/json")
if check_version.json()["info"]["version"] != __version__:
# ... upgrade logic follows
The function performs three key operations:
- Sends a GET request to
https://pypi.org/pypi/holehe/json - Extracts the
"version"field fromresponse["info"] - Compares it against the local
__version__constant defined at line 34
If the versions match, execution continues normally. If they differ, the upgrade sequence triggers immediately.
Cross‑Platform pip Subprocess Upgrade
When an update is required, check_update() spawns a subprocess using subprocess.Popen to run the appropriate pip command for the host operating system.
Platform Detection and Command Selection
# https://github.com/megadose/holehe/blob/master/holehe/core.py#L65-L85 (continued)
def check_update():
"""Check and update holehe if not the last version"""
check_version = httpx.get("https://pypi.org/pypi/holehe/json")
if check_version.json()["info"]["version"] != __version__:
if os.name != 'nt':
p = Popen(["pip3", "install", "--upgrade", "holehe"], stdout=PIPE, stderr=PIPE)
else:
p = Popen(["pip", "install", "--upgrade", "holehe"], stdout=PIPE, stderr=PIPE)
(output, err) = p.communicate()
p_status = p.wait()
print("Holehe has just been updated, you can restart it.")
exit()
The implementation handles platform differences as follows:
- Non‑Windows systems (
os.name != 'nt'): Usespip3to ensure the Python 3 package manager is invoked - Windows systems: Falls back to
pipfor compatibility
Both stdout and stderr are captured to PIPE, and the process blocks until completion via p.communicate() and p.wait(). After the upgrade finishes, the program prints a restart notice and calls exit() to terminate—forcing the user to relaunch and load the updated code.
When the Auto‑Update Runs: Invocation Point
The check_update() function executes before any OSINT modules run, ensuring outdated code never processes email queries.
Call Site in the CLI Entry Point
# https://github.com/megadose/holehe/blob/master/holehe/core.py#L96-L99
parser = ArgumentParser(...)
check_update() # <-- auto-update runs here
args = parser.parse_args()
Located at line 97, this placement guarantees:
- The version check occurs on every CLI execution
- No network modules or async workers start before the update decision completes
- Users cannot accidentally run stale code after a release
This eager evaluation trade‑off favors correctness over startup speed—a reasonable choice for a security research tool where version currency matters.
Manual Update and Programmatic Usage
Users can replicate the auto‑update behavior manually or integrate it into custom workflows.
Trigger Update from Python
from holehe.core import check_update
check_update() # will upgrade if a newer version is available
Normal CLI Execution
$ holehe example@example.com
# If a newer version exists on PyPI, you’ll see:
# Holehe has just been updated, you can restart it.
# The process exits; run the command again to use the new version.
Understanding the Subprocess Internals
import subprocess, sys, os
# For non‑Windows systems
subprocess.Popen(
["pip3", "install", "--upgrade", "holehe"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
Related Components
| File | Role | Key Element |
|---|---|---|
holehe/core.py |
Auto‑update implementation | check_update(), __version__, CLI entry point |
holehe/instruments.py |
Progress reporting | TrioProgress class for async task tracking |
setup.py |
Package metadata | Version string published to PyPI |
While holehe/instruments.py defines TrioProgress for displaying scan progress, it operates independently of the update system. The version constant in setup.py ultimately determines what check_update() fetches from the PyPI API.
Summary
check_update()inholehe/core.pyqueriespypi.org/pypi/holehe/jsonon every startup- Version mismatch triggers a pip subprocess—
pip3on Unix,pipon Windows - The program exits after upgrade, requiring a manual restart to load new code
- Execution occurs before argument parsing completes, preventing stale module runs
- All functionality is self‑contained with no external update service dependencies
Frequently Asked Questions
Does holehe auto‑update require root or administrator privileges?
The update inherits the permissions of the Python environment where holehe is installed. If installed system‑wide via pip with sudo, the update command also requires elevation. For user‑local installs (pip install --user or virtualenvs), no special permissions are needed. The subprocess call does not modify privileges internally.
Can I disable the auto‑update check?
According to the megadose/holehe source code, there is no configuration flag to suppress check_update(). The function executes unconditionally at line 97 of holehe/core.py. To bypass updates, you would need to modify the source or run holehe from a fork with the call removed.
What happens if PyPI is unreachable or the request fails?
The check_update() implementation shown in holehe/core.py does not wrap the httpx.get() call in exception handling. A network timeout, DNS failure, or HTTP error would raise an unhandled exception and crash the program before the OSINT scan begins. Users in air‑gapped environments should be aware of this behavior.
Is the update process atomic or can it leave holehe in a broken state?
The upgrade runs via standard pip install semantics. If the subprocess fails partway through (network interruption, disk full, dependency conflict), pip may leave the environment with a partially installed version. The p.communicate() and p.wait() calls only capture output—the function does not verify success via p_status before printing the restart message.
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 →