Created
February 1, 2025 06:28
-
-
Save rickd-uk/c19fe466ccb4102f2f487d512a67377c to your computer and use it in GitHub Desktop.
Start Python HTTP Server for DEV
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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