Skip to content

Instantly share code, notes, and snippets.

@rickd-uk
Created February 1, 2025 06:28
Show Gist options
  • Save rickd-uk/c19fe466ccb4102f2f487d512a67377c to your computer and use it in GitHub Desktop.
Save rickd-uk/c19fe466ccb4102f2f487d512a67377c to your computer and use it in GitHub Desktop.
Start Python HTTP Server for DEV
#!/usr/bin/env python3
import socket
import sys
import os
import signal
import subprocess
import atexit
import psutil
from pathlib import Path
def find_python_http_servers():
"""Find running Python HTTP servers and their ports."""
servers = []
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
try:
cmdline = proc.info['cmdline']
if cmdline and 'python' in cmdline[0] and '-m' in cmdline and 'http.server' in cmdline:
port = int(cmdline[-1]) # Last argument is the port
servers.append({'pid': proc.info['pid'], 'port': port})
except (psutil.NoSuchProcess, psutil.AccessDenied, ValueError):
pass
return sorted(servers, key=lambda x: x['port'])
def is_port_used_by_python(port, python_servers):
"""Check if port is used by a Python HTTP server."""
return any(server['port'] == port for server in python_servers)
def find_next_available_port(start_port=8000, max_attempts=100):
"""Find the next available port, checking if it's used by Python servers first."""
python_servers = find_python_http_servers()
if python_servers:
print("\033[94mExisting Python HTTP servers:\033[0m")
for server in python_servers:
print(f" PID: {server['pid']}, Port: {server['port']}")
for port in range(start_port, start_port + max_attempts):
# First check if port is used by Python server
if is_port_used_by_python(port, python_servers):
continue
# Then check if port is free
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try:
s.bind(('', port))
return port, False # Port is free, no need to kill
except OSError:
# If port is used but not by Python, try next port
continue
# If we get here, kill Python servers and try again
if python_servers:
print("\033[93mNo free ports found. Killing existing Python HTTP servers...\033[0m")
for server in python_servers:
try:
os.kill(server['pid'], signal.SIGTERM)
print(f" Killed server on port {server['port']} (PID: {server['pid']})")
except ProcessLookupError:
pass
# Try one more time to find a free port
for port in range(start_port, start_port + max_attempts):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try:
s.bind(('', port))
return port, True # Port found after killing
except OSError:
continue
raise RuntimeError(f"Could not find a free port after {max_attempts} attempts")
def cleanup_port_file():
"""Remove the port file on exit."""
port_file = Path.home() / '.hsrv_port'
if port_file.exists():
port_file.unlink()
def save_port(port):
"""Save the current port to a file."""
port_file = Path.home() / '.hsrv_port'
port_file.write_text(str(port))
atexit.register(cleanup_port_file)
def main():
try:
# Find an available port and determine if we killed servers
port, killed_servers = find_next_available_port()
if killed_servers:
# Small delay to ensure ports are freed
import time
time.sleep(0.5)
# Save the port number
save_port(port)
# Start the HTTP server
print(f"\033[92mStarting HTTP server on port {port}...\033[0m")
print(f"\033[94mURL: http://localhost:{port}\033[0m")
# Create the server process
server_process = subprocess.Popen([sys.executable, '-m', 'http.server', str(port)])
def signal_handler(signum, frame):
"""Handle interrupt signals."""
print("\n\033[93mShutting down server...\033[0m")
server_process.terminate()
cleanup_port_file()
sys.exit(0)
# Register signal handlers
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# Wait for the server process to complete
server_process.wait()
except Exception as e:
print(f"\033[91mError: {str(e)}\033[0m")
sys.exit(1)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment