CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-sh

Python subprocess replacement that allows calling system commands as Python functions

Pending
Overview
Eval results
Files

process-management.mddocs/

Process Management

Advanced process control including background execution, process monitoring, signal handling, and process lifecycle management. Provides comprehensive control over long-running commands and process interactions.

Capabilities

Process Lifecycle Control

Manage the lifecycle of running processes with methods to wait, terminate, and kill processes.

class RunningCommand:
    def wait(self):
        """
        Wait for the process to complete.
        
        Returns:
        None
        
        Raises:
        ErrorReturnCode: If process exits with non-zero status
        """
    
    def kill(self):
        """
        Forcefully kill the process with SIGKILL.
        
        Returns:
        None
        """
    
    def terminate(self):
        """
        Gracefully terminate the process with SIGTERM.
        
        Returns:
        None
        """

Usage examples:

import sh
import time

# Start a long-running process
proc = sh.sleep(30, _bg=True)

# Check if we need to cancel it
time.sleep(5)
if some_condition:
    proc.terminate()  # Try graceful termination first
    time.sleep(2)
    if proc.is_alive():
        proc.kill()     # Force kill if still running

# Wait for normal completion
try:
    proc.wait()
    print("Process completed normally")
except sh.ErrorReturnCode as e:
    print(f"Process failed: {e}")

Process Status Monitoring

Monitor process status and retrieve process information.

class RunningCommand:
    def is_alive(self) -> bool:
        """
        Check if the process is still running.
        
        Returns:
        bool: True if process is running, False otherwise
        """
    
    @property
    def pid(self) -> int:
        """Process ID of the running command."""
    
    @property
    def exit_code(self) -> int:
        """Exit code of the process (None if still running)."""

Usage examples:

import sh
import time

# Monitor multiple background processes
processes = [
    sh.ping("-c", "10", "google.com", _bg=True),
    sh.wget("http://example.com/large-file.zip", _bg=True),
    sh.rsync("-av", "/source/", "/dest/", _bg=True)
]

# Monitor all processes
while any(proc.is_alive() for proc in processes):
    for i, proc in enumerate(processes):
        if proc.is_alive():
            print(f"Process {i} (PID {proc.pid}) still running...")
        else:
            print(f"Process {i} completed with exit code {proc.exit_code}")
    time.sleep(5)

print("All processes completed")

Signal Handling

Send signals to running processes for advanced process control.

class RunningCommand:
    def signal(self, sig):
        """
        Send a signal to the process.
        
        Parameters:
        - sig: int or signal constant (e.g., signal.SIGUSR1)
        
        Returns:
        None
        """

Usage examples:

import sh
import signal
import time

# Start a process that handles signals
proc = sh.tail("-f", "/var/log/system.log", _bg=True)

# Let it run for a while
time.sleep(10)

# Send a custom signal (if the process handles it)
proc.signal(signal.SIGUSR1)

# Send SIGTERM for graceful shutdown
proc.signal(signal.SIGTERM)

# Wait a bit, then force kill if needed
time.sleep(2)
if proc.is_alive():
    proc.signal(signal.SIGKILL)

Process Groups and Session Management

Manage process groups and sessions for complex process hierarchies.

def __call__(self, *args, _new_session=False, **kwargs):
    """
    Execute command with process group control.
    
    Parameters:
    - _new_session: bool = True to start in new session
    
    Returns:
    RunningCommand: Process object
    """

Usage examples:

import sh

# Start process in new session (detached from parent)
daemon_proc = sh.python("daemon.py", _bg=True, _new_session=True)

# This process will continue even if parent script exits
print(f"Daemon started with PID {daemon_proc.pid}")

# The daemon is now independent of this script's lifecycle

Timeout Management

Handle process timeouts and long-running command management.

def __call__(self, *args, _timeout=None, **kwargs):
    """
    Execute command with timeout.
    
    Parameters:  
    - _timeout: int/float = seconds to wait before killing process
    
    Returns:
    str: Command output
    
    Raises:
    TimeoutException: If command exceeds timeout
    """

Usage examples:

import sh

# Set timeout for potentially hanging commands
try:
    # Network command that might hang
    result = sh.curl("http://slow-server.com", _timeout=30)
    print("Download completed:", result)
except sh.TimeoutException:
    print("Download timed out after 30 seconds")

# Background process with timeout monitoring
proc = sh.sleep(60, _bg=True)

start_time = time.time()
timeout = 10

while proc.is_alive():
    if time.time() - start_time > timeout:
        print("Manually timing out process")
        proc.terminate()
        break
    time.sleep(1)

Process Resource Management

Monitor and control process resources.

class RunningCommand:
    @property
    def process(self):
        """Access to underlying subprocess.Popen object."""

Usage examples:

import sh
import psutil  # External library for extended process info

# Get detailed process information
proc = sh.find("/", "-name", "*.log", _bg=True)

# Access underlying process for advanced control
popen = proc.process
print(f"Process memory usage: {popen.memory_info()}")

# Use with psutil for extended monitoring
if hasattr(psutil, 'Process'):
    ps_proc = psutil.Process(proc.pid)
    print(f"CPU usage: {ps_proc.cpu_percent()}")
    print(f"Memory usage: {ps_proc.memory_info()}")

Batch Process Management

Manage multiple related processes as a group.

import sh
import time

class ProcessManager:
    def __init__(self):
        self.processes = []
    
    def start(self, command, *args, **kwargs):
        """Start a process and add to management."""
        proc = command(*args, _bg=True, **kwargs)
        self.processes.append(proc)
        return proc
    
    def wait_all(self):
        """Wait for all processes to complete."""
        for proc in self.processes:
            proc.wait()
    
    def kill_all(self):
        """Kill all running processes."""
        for proc in self.processes:
            if proc.is_alive():
                proc.kill()
    
    def status(self):
        """Get status of all processes."""
        running = sum(1 for p in self.processes if p.is_alive())
        total = len(self.processes)
        return f"{running}/{total} processes running"

# Usage
manager = ProcessManager()

# Start multiple related processes
manager.start(sh.rsync, "-av", "/data1/", "/backup/")
manager.start(sh.rsync, "-av", "/data2/", "/backup/")  
manager.start(sh.rsync, "-av", "/data3/", "/backup/")

# Monitor progress
while any(p.is_alive() for p in manager.processes):
    print(manager.status())
    time.sleep(5)

print("All backups completed")

Install with Tessl CLI

npx tessl i tessl/pypi-sh

docs

command-execution.md

contrib.md

error-handling.md

index.md

io-handling.md

process-management.md

utilities.md

tile.json