CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pydevd

Comprehensive Python debugger backend for IDEs with remote debugging, breakpoints, variable inspection, and performance optimizations

Pending
Overview
Eval results
Files

process-attachment.mddocs/

Process Attachment

Remote process attachment and code injection capabilities for debugging already-running Python processes. This functionality enables debugging scenarios where the target process wasn't started with debugging enabled, allowing developers to attach debuggers to production applications and long-running services.

Capabilities

Process Attachment

Core functions for attaching the debugger to running Python processes using platform-specific injection techniques.

def run_python_code(pid, python_code, connect_debugger_tracing=False, show_debug_info=0):
    """
    Inject and execute Python code in a running Python process.
    
    This is a platform-specific function that routes to the appropriate implementation:
    - Windows: run_python_code_windows
    - Linux: run_python_code_linux
    - macOS: run_python_code_mac
    
    Parameters:
    - pid (int): Process ID of the target Python process
    - python_code (str): Python code to inject and execute (cannot contain single quotes)
    - connect_debugger_tracing (bool): Whether to enable debugger tracing after injection
    - show_debug_info (int): Debug information level (0=none, 1=basic, 2=detailed)
    
    Returns:
    None: Function executes injection but doesn't return a value
    
    Raises:
    - AssertionError: If python_code contains single quotes
    - RuntimeError: If required libraries/executables are not found or injection fails
    - Various platform-specific exceptions during injection process
    """

def run_python_code_windows(pid, python_code, connect_debugger_tracing=False, show_debug_info=0):
    """Windows-specific implementation of code injection using winappdbg and DLL injection."""

def run_python_code_linux(pid, python_code, connect_debugger_tracing=False, show_debug_info=0):
    """Linux-specific implementation of code injection using gdb and shared libraries."""

def run_python_code_mac(pid, python_code, connect_debugger_tracing=False, show_debug_info=0):
    """macOS-specific implementation of code injection using lldb and dynamic libraries."""

Debugger Attachment Script

High-level utilities for setting up debugger attachment to running processes.

def attach(port, host, protocol="", debug_mode=""):
    """
    Attach debugger to the current process and connect to debugger server.
    
    This function is typically called from code injected into a running process
    to establish debugger connection.
    
    Parameters:
    - port (int): Port number of the debugger server
    - host (str): Host address of the debugger server
    - protocol (str): Communication protocol ("", "http", "json")
    - debug_mode (str): Debug mode configuration
    
    Returns:
    None
    """

def get_main_thread_id(unlikely_thread_id=None):
    """
    Identify the main thread in a multi-threaded process.
    
    Parameters:
    - unlikely_thread_id: Thread ID to exclude from consideration
    
    Returns:
    tuple: (thread_id, critical_warning) - thread ID and any warning message
    """

Platform Detection

Utilities for detecting platform capabilities and requirements for process attachment.

def is_windows():
    """
    Check if running on Windows platform.
    
    Returns:
    bool: True if Windows, False otherwise
    """

def is_linux():
    """
    Check if running on Linux platform.
    
    Returns:
    bool: True if Linux, False otherwise
    """

def is_mac():
    """
    Check if running on macOS platform.
    
    Returns:
    bool: True if macOS, False otherwise
    """

def get_platform_attachment_library():
    """
    Get the appropriate native library for process attachment.
    
    Returns:
    str: Path to platform-specific attachment library (.dll, .so, or .dylib)
    
    Raises:
    - RuntimeError: If no compatible library is available for current platform
    """

Usage Examples

Basic Process Attachment

import os
import subprocess
from pydevd_attach_to_process.add_code_to_python_process import run_python_code

# Start a target Python process
target_script = """
import time
counter = 0
while True:
    counter += 1
    print(f"Counter: {counter}")
    time.sleep(1)
"""

# Write target script to file
with open('target_app.py', 'w') as f:
    f.write(target_script)

# Start target process
process = subprocess.Popen(['python', 'target_app.py'])
target_pid = process.pid

print(f"Target process started with PID: {target_pid}")

# Inject debugger setup code into the running process
debugger_setup_code = """
import pydevd
print("Setting up debugger...")
pydevd.settrace(
    host='localhost',
    port=5678,
    suspend=False,
    trace_only_current_thread=False
)
print("Debugger attached successfully!")
"""

try:
    result = run_python_code(
        pid=target_pid,
        python_code=debugger_setup_code,
        connect_debugger_tracing=True,
        show_debug_info=1
    )
    
    print(f"Code injection result: {result}")
    print("You can now connect your IDE to port 5678")
        
except Exception as e:
    print(f"Attachment error: {e}")

finally:
    # Clean up
    process.terminate()
    os.remove('target_app.py')

Code Injection for Debugging Setup

from pydevd_attach_to_process.add_code_to_python_process import add_code_to_python_process

# Code to inject for debugger setup
debugger_setup_code = """
import pydevd
print("Setting up debugger...")
pydevd.settrace(
    host='localhost',
    port=5678,
    suspend=False,
    trace_only_current_thread=False
)
print("Debugger attached successfully!")
"""

# Inject debugger setup code into running process
result = add_code_to_python_process(
    pid=target_pid,
    python_code=debugger_setup_code,
    connect_debugger_tracing=True,
    show_debug_info=1
)

print(f"Code injection result: {result}")

Advanced Process Information and Compatibility

from pydevd_attach_to_process import attach_pydevd
import psutil

def attach_with_compatibility_check(pid, debugger_port):
    """
    Attach to process with comprehensive compatibility checking.
    """
    try:
        # Get process information
        process_info = attach_pydevd.get_process_info(pid)
        
        print(f"Process Information:")
        print(f"  PID: {pid}")
        print(f"  Python Version: {process_info['python_version']}")
        print(f"  Architecture: {process_info['architecture']}")
        print(f"  Executable: {process_info['executable']}")
        print(f"  Compatible: {process_info['compatible']}")
        
        if not process_info['compatible']:
            print(f"  Incompatibility Reason: {process_info['reason']}")
            return False
        
        # Check if process is still running
        if not psutil.pid_exists(pid):
            print(f"Process {pid} no longer exists")
            return False
        
        # Get additional process details
        proc = psutil.Process(pid)
        print(f"  Process Name: {proc.name()}")
        print(f"  Memory Usage: {proc.memory_info().rss / 1024 / 1024:.1f} MB")
        print(f"  CPU Percent: {proc.cpu_percent():.1f}%")
        
        # Attempt attachment
        print(f"\nAttempting to attach debugger to PID {pid}...")
        success = attach_pydevd.attach_to_process(
            pid=pid,
            debugger_port=debugger_port,
            timeout=15.0
        )
        
        if success:
            print(f"✓ Successfully attached debugger")
            print(f"  Connect your IDE to localhost:{debugger_port}")
            return True
        else:
            print("✗ Failed to attach debugger")
            return False
            
    except Exception as e:
        print(f"Error during attachment: {e}")
        return False

# Usage
target_pid = 12345  # Replace with actual PID
attach_with_compatibility_check(target_pid, 5678)

Platform-Specific Attachment

from pydevd_attach_to_process import attach_pydevd
from pydevd_attach_to_process.add_code_to_python_process import (
    run_python_code_windows,
    run_python_code_linux,
    run_python_code_mac,
    is_windows,
    is_linux,
    is_mac
)

def platform_specific_attach(pid, debugger_code):
    """
    Perform platform-specific code injection and attachment.
    """
    show_debug_info = 2  # Detailed debug information
    connect_tracing = True
    
    try:
        if is_windows():
            print("Using Windows attachment method...")
            result = run_python_code_windows(
                pid, debugger_code, connect_tracing, show_debug_info
            )
        elif is_linux():
            print("Using Linux attachment method...")
            result = run_python_code_linux(
                pid, debugger_code, connect_tracing, show_debug_info
            )
        elif is_mac():
            print("Using macOS attachment method...")
            result = run_python_code_mac(
                pid, debugger_code, connect_tracing, show_debug_info
            )
        else:
            raise RuntimeError("Unsupported platform for process attachment")
        
        print(f"Platform-specific attachment result: {result}")
        return True
        
    except Exception as e:
        print(f"Platform-specific attachment failed: {e}")
        return False

# Debugger setup code
setup_code = """
import sys
import pydevd

# Verify Python version compatibility
if sys.version_info >= (3, 8):
    print("Python version compatible, setting up debugger...")
    pydevd.settrace(
        host='0.0.0.0',
        port=5678,
        suspend=False,
        stdout_to_server=True,
        stderr_to_server=True
    )
    print("Remote debugger attached and ready!")
else:
    print(f"Python version {sys.version_info} not supported")
"""

# Perform platform-specific attachment
platform_specific_attach(target_pid, setup_code)

Production Process Debugging

import time
import signal
from pydevd_attach_to_process import attach_pydevd

class ProductionDebugger:
    def __init__(self, target_pid, debugger_port=5678):
        self.target_pid = target_pid
        self.debugger_port = debugger_port
        self.attached = False
        
    def safe_attach(self):
        """
        Safely attach to production process with error handling.
        """
        try:
            # First, verify process exists and is compatible
            process_info = attach_pydevd.get_process_info(self.target_pid)
            
            if not process_info['compatible']:
                raise RuntimeError(f"Process incompatible: {process_info['reason']}")
            
            # Prepare minimal debugger code
            minimal_debugger_code = """
import pydevd
import threading

def setup_debugger():
    try:
        pydevd.settrace(
            host='localhost',
            port={port},
            suspend=False,
            trace_only_current_thread=False,
            patch_multiprocessing=False  # Don't interfere with child processes
        )
        print(f"Debugger attached on port {port}")
    except Exception as e:
        print(f"Debugger setup failed: {{e}}")

# Run in separate thread to avoid blocking main application
debug_thread = threading.Thread(target=setup_debugger, daemon=True)
debug_thread.start()
""".format(port=self.debugger_port)
            
            # Inject debugger code
            success = attach_pydevd.attach_to_process(
                pid=self.target_pid,
                debugger_port=self.debugger_port,
                timeout=5.0  # Short timeout for production
            )
            
            if success:
                self.attached = True
                print(f"✓ Production debugger attached to PID {self.target_pid}")
                print(f"  IDE connection: localhost:{self.debugger_port}")
                return True
            else:
                print("✗ Failed to attach to production process")
                return False
                
        except Exception as e:
            print(f"Production debugging setup failed: {e}")
            return False
    
    def detach(self):
        """
        Detach debugger from production process.
        """
        if self.attached:
            detach_code = """
import pydevd
try:
    pydevd.stoptrace()
    print("Debugger detached successfully")
except:
    pass
"""
            try:
                attach_pydevd.inject_debugger_code(self.target_pid, detach_code)
                self.attached = False
                print(f"Debugger detached from PID {self.target_pid}")
            except Exception as e:
                print(f"Failed to detach debugger: {e}")

# Usage for production debugging
production_pid = 98765  # Replace with production process PID

debugger = ProductionDebugger(production_pid, debugger_port=5679)

# Set up signal handler for clean detachment
def signal_handler(signum, frame):
    print("\nDetaching debugger...")
    debugger.detach()
    exit(0)

signal.signal(signal.SIGINT, signal_handler)

# Attach to production process
if debugger.safe_attach():
    print("Production debugging active. Press Ctrl+C to detach.")
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        pass
    finally:
        debugger.detach()

Platform Requirements

Windows

  • Requires attach_windows.dll (32-bit) or attach_windows_amd64.dll (64-bit)
  • Administrator privileges may be required for some processes
  • Compatible with Windows 7+ and Windows Server 2008+

Linux

  • Requires attach_linux.so (32-bit) or attach_linux_amd64.so (64-bit)
  • Requires ptrace capability (may need CAP_SYS_PTRACE)
  • Compatible with most Linux distributions

macOS

  • Requires attach_mac.dylib (32-bit) or attach_mac_amd64.dylib (64-bit)
  • May require disabling System Integrity Protection (SIP) for some scenarios
  • Compatible with macOS 10.9+

Security Considerations

  • Permissions: Process attachment requires appropriate system permissions
  • Code Injection: Only inject trusted code into processes
  • Production Use: Use minimal debugger setup to avoid performance impact
  • Network Security: Consider firewall rules for debugger ports
  • Process Stability: Attachment may affect target process performance

Install with Tessl CLI

npx tessl i tessl/pypi-pydevd

docs

core-debugging.md

file-system.md

framework-integration.md

index.md

interactive-console.md

ipython-integration.md

process-attachment.md

programmatic-api.md

tile.json