How Progress Tracking Works in Holehe with TrioProgress
Holehe implements progress tracking via a custom TrioProgress class that acts as a Trio instrument, automatically updating a tqdm progress bar every time a module-checking task completes.
Asynchronous OSINT tool Holehe checks account existence across hundreds of websites concurrently. To keep users informed during long-running scans, its developers built a lightweight progress tracking system that hooks into Trio's low-level instrumentation API rather than cluttering module code with manual updates.
The TrioProgress Instrument Architecture
Holehe's progress tracking centers on the TrioProgress class in holehe/instruments.py. This class implements trio.abc.Instrument, Trio's official interface for observing task lifecycle events.
# holehe/instruments.py (simplified)
import trio
from tqdm import tqdm
class TrioProgress(trio.abc.Instrument):
def __init__(self, total):
self.tqdm = tqdm(total=total)
def task_exited(self, task):
# Filter for module-specific tasks
if task.name.split(".")[-1] == "launch_module":
self.tqdm.update(1)
The task_exited hook fires automatically whenever any Trio task finishes. By checking that the task name ends with "launch_module", the instrument distinguishes actual website-checking coroutines from internal Trio tasks, ensuring accurate progress counting.
Registering and Running the Instrument
Progress tracking activation happens in holehe/core.py within the maincore() function. Here's the complete lifecycle:
# holehe/core.py (key sections)
from holehe.instruments import TrioProgress
async def maincore():
websites = [...] # List of modules to run
# 1. Initialize progress bar with total module count
instrument = TrioProgress(len(websites))
# 2. Register with Trio runtime
trio.lowlevel.add_instrument(instrument)
# 3. Launch all module checks concurrently
async with trio.open_nursery() as nursery:
for website in websites:
nursery.start_soon(launch_module, website, email, client, out)
# 4. Clean up instrument when done
trio.lowlevel.remove_instrument(instrument)
The instrument remains active throughout the nursery's execution, receiving callbacks for every task exit. No try/finally or manual step counting occurs inside launch_module—progress updates happen transparently through Trio's instrumentation layer.
Why This Design Pattern Matters
Decoupling progress tracking from business logic offers three concrete advantages in Holehe's codebase:
- Zero module modifications — Website checkers in
holehe/modules/need no awareness of UI concerns - Automatic accuracy — The instrument counts actual task completions, not scheduled starts or timeouts
- Clean teardown —
remove_instrument()guarantees the progress bar stops receiving updates before result processing begins
This pattern scales naturally: adding new websites to check automatically extends the progress bar's total without code changes.
Complete Working Example
To observe the TrioProgress pattern in isolation:
import trio
from tqdm import tqdm
class DemoProgress(trio.abc.Instrument):
def __init__(self, total):
self.tqdm = tqdm(total=total, desc="Processing")
def task_exited(self, task):
if "worker" in task.name:
self.tqdm.update(1)
async def worker(n, delay):
await trio.sleep(delay)
return n * 2
async def main():
tasks = [(i, 0.1 * i) for i in range(1, 6)]
instrument = DemoProgress(len(tasks))
trio.lowlevel.add_instrument(instrument)
async with trio.open_nursery() as nursery:
for n, delay in tasks:
nursery.start_soon(worker, n, delay, name=f"worker_{n}")
trio.lowlevel.remove_instrument(instrument)
instrument.tqdm.close()
trio.run(main)
This produces output matching Holehe's behavior:
Processing: 60%|████████████ | 3/5 [00:00<00:00, 9.87it/s]
Summary
TrioProgressinholehe/instruments.pyimplementstrio.abc.Instrumentto observe task exits- Registration via
trio.lowlevel.add_instrument()inholehe/core.pyenables automatic callbacks - Filtering by task name (
"launch_module") ensures only relevant coroutines increment progress - Decoupled design keeps progress logic separate from website-checking modules
- Clean lifecycle with explicit
remove_instrument()prevents ghost updates
Frequently Asked Questions
What is a Trio instrument?
A Trio instrument is any class implementing trio.abc.Instrument that receives callbacks for task scheduling, entry, and exit events. Instruments observe the runtime without modifying task behavior, making them ideal for logging, profiling, and progress tracking as used in Holehe.
Why does TrioProgress check the task name?
Task name filtering prevents counting internal Trio tasks like nursery management or system callbacks. Holehe names all module-running tasks with "launch_module" suffix, so task.name.split(".")[-1] == "launch_module" identifies genuine website checks accurately.
Can I use TrioProgress with other async libraries?
No—trio.abc.Instrument is Trio-specific. Libraries using asyncio would need equivalent instrumentation via asyncio.Task callbacks or context variables. Holehe's strict Trio dependency makes instruments the optimal choice.
Where does the tqdm progress bar appear?
The progress bar renders in the terminal via standard error stream. When running Holehe with holehe -T 15 user@example.com, the bar displays above module results and clears automatically on completion, matching typical CLI tool behavior.
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 →