mirror of
https://github.com/LearningCircuit/local-deep-research.git
synced 2026-06-16 12:02:34 +03:00
* feat: Add pre-commit hook to enforce pathlib usage (issue #640) - Created check-pathlib-usage.py pre-commit hook using AST parsing - Detects os.path usage and suggests pathlib alternatives - Fixed os.path.normpath usage in auth/routes.py to use PurePosixPath - Added hook configuration to .pre-commit-config.yaml The hook provides helpful suggestions for replacing os.path calls with their pathlib equivalents for better cross-platform compatibility. Co-Authored-By: djpetti <djpetti@users.noreply.github.com> * feat: Add missing pathlib pre-commit hook script Co-Authored-By: djpetti <djpetti@users.noreply.github.com> * refactor: Migrate core src modules from os.path to pathlib - Fixed web/app_factory.py, config/llm_config.py, metrics/token_counter.py - Fixed utilities/es_utils.py, web/routes/benchmark_routes.py - Fixed web/routes/settings_routes.py, web_search_engines/engines/search_engine_local.py - Replaced os.path.join() with Path() / syntax - Replaced os.path.exists() with Path().exists() - Replaced os.path.basename() with Path().name - Replaced os.path.dirname() with Path().parent Part of the migration to modern pathlib API for better cross-platform compatibility and cleaner code. Co-Authored-By: djpetti <djpetti@users.noreply.github.com> * refactor: Migrate from os.path to pathlib in src and tests (issue #640) Replaced os.path usage with pathlib.Path throughout: - src/local_deep_research/benchmarks: All os.path.join, exists, dirname, basename, abspath replaced - tests directory: Complete migration of all test files - Improved cross-platform compatibility and code readability - Kept os.path.expandvars in env_settings.py (no pathlib equivalent) Part of pre-commit hook enforcement for pathlib usage. Remaining work: examples/ and scripts/ directories. Co-Authored-By: djpetti * fix: Complete migration from os.path to pathlib.Path (issue #640) Completed manual migration of all os.path usage to pathlib.Path across: - scripts/ directory (3 files) - examples/ directory (25 files total) - examples/benchmarks/ (8 files) - examples/optimization/ (16 files) - examples/show_env_vars.py - src/local_deep_research/settings/env_settings.py Changes made: - Replaced os.path.join() with Path() / syntax - Replaced os.path.exists() with Path().exists() - Replaced os.path.dirname() with Path().parent - Replaced os.path.basename() with Path().name or Path().stem - Replaced os.path.abspath() with Path().resolve() - Replaced os.makedirs() with Path().mkdir(parents=True, exist_ok=True) - Added pathlib import where needed Note: Kept os.path.expandvars in env_settings.py as there is no pathlib equivalent. Added comment explaining this limitation. This completes the pathlib migration for issue #640. Co-Authored-By: djpetti * fix: Allow os.path.expandvars in pathlib pre-commit hook Updated the check-pathlib-usage.py pre-commit hook to skip checking os.path.expandvars since it has no pathlib equivalent. Changes: - Added exception for expandvars in both visit_Attribute and visit_Call methods - Added comment in equivalents dictionary noting expandvars is allowed - This allows env_settings.py to use os.path.expandvars without failing checks This resolves the pre-commit CI failure while maintaining the pathlib enforcement for all other os.path methods. Co-Authored-By: djpetti --------- Co-authored-by: djpetti
343 lines
11 KiB
Python
343 lines
11 KiB
Python
import os
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
import psutil
|
|
|
|
|
|
def kill_flask_servers():
|
|
"""
|
|
Kill all Python processes running main.py (Flask servers)
|
|
Uses psutil to find and kill processes more reliably than just using command line
|
|
"""
|
|
killed_pids = []
|
|
|
|
for proc in psutil.process_iter(["pid", "name", "cmdline"]):
|
|
try:
|
|
cmdline = proc.info["cmdline"]
|
|
if (
|
|
cmdline
|
|
and "python" in proc.info["name"].lower()
|
|
and any(
|
|
"local_deep_research.web.app" in arg
|
|
for arg in cmdline
|
|
if arg
|
|
)
|
|
):
|
|
pid = proc.info["pid"]
|
|
print(f"Found Flask server process {pid}, killing...")
|
|
|
|
# Windows requires SIGTERM
|
|
os.kill(pid, signal.SIGTERM)
|
|
killed_pids.append(pid)
|
|
|
|
except (
|
|
psutil.NoSuchProcess,
|
|
psutil.AccessDenied,
|
|
psutil.ZombieProcess,
|
|
):
|
|
pass
|
|
|
|
if killed_pids:
|
|
print(
|
|
f"Killed Flask server processes with PIDs: {', '.join(map(str, killed_pids))}"
|
|
)
|
|
else:
|
|
print("No Flask server processes found.")
|
|
|
|
# Give processes time to die
|
|
time.sleep(1)
|
|
|
|
# Verify all are dead
|
|
for pid in killed_pids:
|
|
if psutil.pid_exists(pid):
|
|
print(f"Warning: Process {pid} still exists after kill signal.")
|
|
else:
|
|
print(f"Confirmed process {pid} was terminated.")
|
|
|
|
return killed_pids
|
|
|
|
|
|
def check_flask_servers():
|
|
"""Check if any Flask servers are running and return their PIDs."""
|
|
running_servers = []
|
|
|
|
for proc in psutil.process_iter(["pid", "name", "cmdline"]):
|
|
try:
|
|
cmdline = proc.info["cmdline"]
|
|
if (
|
|
cmdline
|
|
and "python" in proc.info["name"].lower()
|
|
and any(
|
|
"local_deep_research.web.app" in arg
|
|
for arg in cmdline
|
|
if arg
|
|
)
|
|
):
|
|
pid = proc.info["pid"]
|
|
running_servers.append(pid)
|
|
except (
|
|
psutil.NoSuchProcess,
|
|
psutil.AccessDenied,
|
|
psutil.ZombieProcess,
|
|
):
|
|
pass
|
|
|
|
return running_servers
|
|
|
|
|
|
def start_flask_server(port=5000):
|
|
"""Start a Flask server in the background."""
|
|
print(f"Starting Flask server on port {port}...")
|
|
|
|
# Get the virtual environment Python executable
|
|
# Try multiple common venv locations
|
|
venv_paths = [
|
|
Path("venv_dev") / "bin" / "python", # Linux/Mac
|
|
Path("venv") / "bin" / "python", # Linux/Mac
|
|
Path(".venv") / "Scripts" / "python.exe", # Windows
|
|
Path("venv_dev") / "Scripts" / "python.exe", # Windows
|
|
Path("venv") / "Scripts" / "python.exe", # Windows
|
|
]
|
|
|
|
venv_path = None
|
|
for path in venv_paths:
|
|
if path.exists():
|
|
venv_path = str(path)
|
|
break
|
|
|
|
if not venv_path:
|
|
print(
|
|
"Error: Could not find Python executable in any common venv location"
|
|
)
|
|
return None
|
|
|
|
try:
|
|
# First, check if port is already in use
|
|
import socket
|
|
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
result = sock.connect_ex(("127.0.0.1", port))
|
|
sock.close()
|
|
|
|
if result == 0:
|
|
print(f"Error: Port {port} is already in use")
|
|
return None
|
|
|
|
# Start the Flask server directly without background flags
|
|
# to better catch any startup errors
|
|
process = subprocess.Popen(
|
|
[
|
|
venv_path,
|
|
"-m",
|
|
"local_deep_research.web.app",
|
|
"--port",
|
|
str(port),
|
|
],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
universal_newlines=True, # Use text mode for easier output reading
|
|
)
|
|
|
|
# Wait a moment to ensure the server has time to start
|
|
time.sleep(3)
|
|
|
|
# Check if the process is still running
|
|
if process.poll() is None:
|
|
print(f"Flask server started on port {port} with PID {process.pid}")
|
|
|
|
# Try to read initial output to see if there are errors
|
|
stdout_data, stderr_data = "", ""
|
|
try:
|
|
# Read with timeout to avoid blocking
|
|
import select
|
|
|
|
if os.name == "nt": # Windows
|
|
# Windows doesn't support select on pipes, use non-blocking mode
|
|
import msvcrt
|
|
|
|
if (
|
|
process.stdout
|
|
and msvcrt.get_osfhandle(process.stdout.fileno()) != -1
|
|
):
|
|
stdout_data = process.stdout.read(1024) or ""
|
|
if (
|
|
process.stderr
|
|
and msvcrt.get_osfhandle(process.stderr.fileno()) != -1
|
|
):
|
|
stderr_data = process.stderr.read(1024) or ""
|
|
else: # Unix
|
|
if (
|
|
process.stdout
|
|
and select.select([process.stdout], [], [], 0.5)[0]
|
|
):
|
|
stdout_data = process.stdout.read(1024) or ""
|
|
if (
|
|
process.stderr
|
|
and select.select([process.stderr], [], [], 0.5)[0]
|
|
):
|
|
stderr_data = process.stderr.read(1024) or ""
|
|
except Exception as e:
|
|
print(f"Warning: Could not read process output: {e}")
|
|
|
|
# Log any output for debugging
|
|
if stdout_data:
|
|
print(f"Server stdout: {stdout_data.strip()}")
|
|
if stderr_data:
|
|
print(f"Server stderr: {stderr_data.strip()}")
|
|
|
|
# Test if the server is actually responding
|
|
try:
|
|
import urllib.request
|
|
|
|
with urllib.request.urlopen(
|
|
f"http://localhost:{port}/", timeout=2
|
|
) as response:
|
|
if response.status == 200:
|
|
print(
|
|
f"Server is responsive at http://localhost:{port}/"
|
|
)
|
|
else:
|
|
print(
|
|
f"Warning: Server responded with status {response.status}"
|
|
)
|
|
except Exception as e:
|
|
print(f"Warning: Could not connect to server: {e}")
|
|
print(
|
|
"The process is running but the server may not be responding to requests"
|
|
)
|
|
|
|
return process.pid
|
|
else:
|
|
stdout, stderr = process.communicate()
|
|
print(
|
|
f"Error starting Flask server. Server process exited with code {process.returncode}"
|
|
)
|
|
if stdout:
|
|
print(f"Server stdout: {stdout.strip()}")
|
|
if stderr:
|
|
print(f"Server stderr: {stderr.strip()}")
|
|
return None
|
|
|
|
except Exception as e:
|
|
print(f"Error starting Flask server: {e!s}")
|
|
return None
|
|
|
|
|
|
def start_flask_server_windows(port=5000):
|
|
"""Start a Flask server using Windows 'start' command which is more reliable for Windows environments."""
|
|
print(
|
|
f"Starting Flask server on port {port} using Windows 'start' command..."
|
|
)
|
|
|
|
# Get the virtual environment Python executable
|
|
# Try multiple common venv locations
|
|
venv_paths = [
|
|
Path("venv_dev") / "Scripts" / "python.exe", # Windows
|
|
Path("venv") / "Scripts" / "python.exe", # Windows
|
|
Path(".venv") / "Scripts" / "python.exe", # Windows
|
|
]
|
|
|
|
venv_path = None
|
|
for path in venv_paths:
|
|
if path.exists():
|
|
venv_path = str(path)
|
|
break
|
|
|
|
if not venv_path:
|
|
print(
|
|
"Error: Could not find Python executable in any common venv location"
|
|
)
|
|
return None
|
|
|
|
try:
|
|
# Use Windows 'start' command to launch in a new window
|
|
# This is more reliable on Windows for Flask apps
|
|
cmd = f'start "Flask Server" /MIN "{venv_path}" -m local_deep_research.web.app --port {port}'
|
|
subprocess.run(cmd, shell=True, check=True)
|
|
|
|
print(f"Flask server starting on port {port}")
|
|
print(
|
|
"Note: The process is running in a minimized window. Close that window to stop the server."
|
|
)
|
|
|
|
# Wait a moment to ensure the server has time to start
|
|
time.sleep(3)
|
|
|
|
# We can't get the PID easily with this method, but return True to indicate success
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"Error starting Flask server: {e!s}")
|
|
return None
|
|
|
|
|
|
def restart_server(port=5000):
|
|
"""Kill all Flask servers and start a new one."""
|
|
kill_flask_servers()
|
|
print("All Flask server processes terminated.")
|
|
pid = start_flask_server(port)
|
|
if pid:
|
|
print(f"New Flask server started on port {port} with PID {pid}")
|
|
return True
|
|
else:
|
|
print("Failed to start new Flask server")
|
|
return False
|
|
|
|
|
|
def show_status():
|
|
"""Show status of running Flask servers."""
|
|
running_servers = check_flask_servers()
|
|
|
|
if running_servers:
|
|
print(f"Found {len(running_servers)} running Flask server(s):")
|
|
for pid in running_servers:
|
|
try:
|
|
proc = psutil.Process(pid)
|
|
cmdline = proc.cmdline()
|
|
port = None
|
|
|
|
# Try to extract the port number
|
|
for i, arg in enumerate(cmdline):
|
|
if arg == "--port" and i + 1 < len(cmdline):
|
|
port = cmdline[i + 1]
|
|
|
|
if port:
|
|
print(f" - PID {pid}: Running on port {port}")
|
|
else:
|
|
print(f" - PID {pid}: Running (port unknown)")
|
|
|
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
print(f" - PID {pid}: [Process information unavailable]")
|
|
else:
|
|
print("No Flask servers currently running.")
|
|
|
|
return len(running_servers) > 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) > 1:
|
|
if sys.argv[1] == "restart":
|
|
kill_flask_servers()
|
|
print("All Flask server processes terminated.")
|
|
if os.name == "nt": # Windows
|
|
start_flask_server_windows()
|
|
else:
|
|
start_flask_server()
|
|
elif sys.argv[1] == "start":
|
|
if os.name == "nt": # Windows
|
|
start_flask_server_windows()
|
|
else:
|
|
start_flask_server()
|
|
elif sys.argv[1] == "status":
|
|
show_status()
|
|
else:
|
|
print(
|
|
"Unknown command. Usage: python kill_servers.py [restart|start|status]"
|
|
)
|
|
else:
|
|
kill_flask_servers()
|