Manhole - In-process Python Debugging Shell

1.8.1 · active · verified Sat Apr 11

Manhole is an in-process Python service that accepts Unix domain socket connections to provide stack traces for all threads and an interactive Python prompt. It can operate as a daemon thread or a signal handler. It is inspired by Twisted's manhole and focuses on simplicity with no external dependencies. The current version is 1.8.1, and releases appear to be on a somewhat irregular, feature-driven cadence.

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to install `manhole` in a Python application and then connect to it using the `manhole-cli` tool. The application will print a message indicating the socket path, run in the background, and then `manhole-cli` will be used to establish an interactive session. Inside the manhole, you can inspect variables or execute code. For a richer interactive experience (history, editing), `socat readline unix-connect:/tmp/manhole-PID` is recommended instead of `manhole-cli`.

import manhole
import time
import os
import sys

def main():
    print(f"Manhole will listen on /tmp/manhole-{os.getpid()}\n")
    manhole.install(sigmask=["USR1"], verbose=True) # Install manhole with default settings
    print("Manhole installed. Waiting for connection...")
    
    # Simulate a running application
    i = 0
    while True:
        print(f"App running... {i}")
        time.sleep(2)
        i += 1
        if i == 5: # Optionally demonstrate manual activation/deactivation
            print("Sending USR1 to self to potentially activate/deactivate manhole if configured as 'activate_on'")
            try:
                os.kill(os.getpid(), 10) # SIGUSR1
            except AttributeError:
                print("SIGUSR1 not available on this OS, skipping manual signal.")


if __name__ == '__main__':
    import subprocess
    # Start the application with manhole installed
    app_process = subprocess.Popen([sys.executable, __file__])
    time.sleep(3)
    
    # Connect to the manhole using manhole-cli
    print(f"\nConnecting to manhole-cli for PID: {app_process.pid}...")
    try:
        # The `manhole-cli` attempts to connect to /tmp/manhole-<PID>
        # Note: 'socat readline' provides a better interactive experience.
        # 'manhole-cli' is simpler for demonstration.
        subprocess.run(['manhole-cli', str(app_process.pid)], check=True)
    except FileNotFoundError:
        print("manhole-cli not found. Please ensure it's installed and in your PATH.")
    except subprocess.CalledProcessError as e:
        print(f"manhole-cli failed: {e}")
    finally:
        app_process.terminate()
        app_process.wait()
        print("\nApplication terminated.")

view raw JSON →