Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Test and interact with local web applications using native Python Playwright scripts
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/with_server.py
1#!/usr/bin/env python32"""3Start one or more servers, wait for them to be ready, run a command, then clean up.45Usage:6# Single server7python scripts/with_server.py --server "npm run dev" --port 5173 -- python automation.py8python scripts/with_server.py --server "npm start" --port 3000 -- python test.py910# Multiple servers11python scripts/with_server.py \12--server "cd backend && python server.py" --port 3000 \13--server "cd frontend && npm run dev" --port 5173 \14-- python test.py15"""1617import subprocess18import socket19import time20import sys21import argparse2223def is_server_ready(port, timeout=30):24"""Wait for server to be ready by polling the port."""25start_time = time.time()26while time.time() - start_time < timeout:27try:28with socket.create_connection(('localhost', port), timeout=1):29return True30except (socket.error, ConnectionRefusedError):31time.sleep(0.5)32return False333435def main():36parser = argparse.ArgumentParser(description='Run command with one or more servers')37parser.add_argument('--server', action='append', dest='servers', required=True, help='Server command (can be repeated)')38parser.add_argument('--port', action='append', dest='ports', type=int, required=True, help='Port for each server (must match --server count)')39parser.add_argument('--timeout', type=int, default=30, help='Timeout in seconds per server (default: 30)')40parser.add_argument('command', nargs=argparse.REMAINDER, help='Command to run after server(s) ready')4142args = parser.parse_args()4344# Remove the '--' separator if present45if args.command and args.command[0] == '--':46args.command = args.command[1:]4748if not args.command:49print("Error: No command specified to run")50sys.exit(1)5152# Parse server configurations53if len(args.servers) != len(args.ports):54print("Error: Number of --server and --port arguments must match")55sys.exit(1)5657servers = []58for cmd, port in zip(args.servers, args.ports):59servers.append({'cmd': cmd, 'port': port})6061server_processes = []6263try:64# Start all servers65for i, server in enumerate(servers):66print(f"Starting server {i+1}/{len(servers)}: {server['cmd']}")6768# Use shell=True to support commands with cd and &&69process = subprocess.Popen(70server['cmd'],71shell=True,72stdout=subprocess.PIPE,73stderr=subprocess.PIPE74)75server_processes.append(process)7677# Wait for this server to be ready78print(f"Waiting for server on port {server['port']}...")79if not is_server_ready(server['port'], timeout=args.timeout):80raise RuntimeError(f"Server failed to start on port {server['port']} within {args.timeout}s")8182print(f"Server ready on port {server['port']}")8384print(f"\nAll {len(servers)} server(s) ready")8586# Run the command87print(f"Running: {' '.join(args.command)}\n")88result = subprocess.run(args.command)89sys.exit(result.returncode)9091finally:92# Clean up all servers93print(f"\nStopping {len(server_processes)} server(s)...")94for i, process in enumerate(server_processes):95try:96process.terminate()97process.wait(timeout=5)98except subprocess.TimeoutExpired:99process.kill()100process.wait()101print(f"Server {i+1} stopped")102print("All servers stopped")103104105if __name__ == '__main__':106main()