Files
2026-07-19 21:09:58 +08:00

120 lines
3.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Windows development server controls for the src application."""
from __future__ import annotations
import argparse
import subprocess
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parent
SOURCE_DIR = ROOT / "src"
PORT = 8000
def listener_pid() -> int | None:
result = subprocess.run(
["powershell", "-NoProfile", "-Command", f"(Get-NetTCPConnection -LocalPort {PORT} -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty OwningProcess)"],
capture_output=True,
text=True,
check=False,
)
try:
return int(result.stdout.strip())
except ValueError:
return None
def command_line(pid: int) -> str:
result = subprocess.run(
["powershell", "-NoProfile", "-Command", f"(Get-CimInstance Win32_Process -Filter 'ProcessId = {pid}').CommandLine"],
capture_output=True,
text=True,
check=False,
)
return result.stdout.strip()
def is_src_server(pid: int) -> bool:
command = command_line(pid).lower()
return "-m http.server" in command and str(SOURCE_DIR).lower() in command
def terminate_process(pid: int) -> bool:
result = subprocess.run(
["taskkill", "/PID", str(pid), "/T", "/F"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=False,
)
return result.returncode == 0
def start() -> None:
listener = listener_pid()
if listener:
if is_src_server(listener):
print(f"服务已运行:http://localhost:{PORT}/PID {listener}")
return
answer = input(
f"端口 {PORT} 已被其他程序使用(PID {listener})。"
"是否关闭该进程并强制启动?[y/N]: "
).strip().lower()
if answer not in ("y", "yes"):
print("已取消启动。")
return
if not terminate_process(listener):
print(f"无法关闭 PID {listener},请以管理员身份运行终端后重试。")
return
time.sleep(0.3)
if listener_pid():
print(f"端口 {PORT} 仍被占用,无法启动服务。")
return
print(f"已关闭 PID {listener},正在启动服务。")
process = subprocess.Popen(
[sys.executable, "-m", "http.server", str(PORT), "--directory", str(SOURCE_DIR)],
cwd=ROOT,
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
time.sleep(0.3)
if listener_pid() == process.pid:
print(f"服务已启动:http://localhost:{PORT}/PID {process.pid}")
else:
print("服务启动失败,请确认 Python 可用且端口未被占用。")
def stop() -> None:
listener = listener_pid()
if not listener or not is_src_server(listener):
print("src 开发服务器未启动。")
return
if terminate_process(listener):
print("服务已停止。")
else:
print(f"无法停止 PID {listener},请以管理员身份运行终端后重试。")
def status() -> None:
listener = listener_pid()
if listener and is_src_server(listener):
print(f"服务运行中:http://localhost:{PORT}/PID {listener}")
else:
print("src 开发服务器未启动。")
def main() -> None:
parser = argparse.ArgumentParser(description="控制 src 的本地 HTTP 开发服务器。")
parser.add_argument("command", choices=("start", "stop", "status"), help="执行的操作")
args = parser.parse_args()
{"start": start, "stop": stop, "status": status}[args.command]()
if __name__ == "__main__":
main()