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 from response["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'): Uses pip3 to ensure the Python 3 package manager is invoked
  • Windows systems: Falls back to pip for 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,
)

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() in holehe/core.py queries pypi.org/pypi/holehe/json on every startup
  • Version mismatch triggers a pip subprocess—pip3 on Unix, pip on 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:

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 →