Believe me I can fly
This is a python script that start a frontend server and auto open browser (requires Python3.8+):
#!/usr/bin/env python3
"""前端服务启动脚本(会自动打开浏览器)
Usage::
$ python <me>.py # 如果上一次打开浏览器距离现在超过一天,就自动打开浏览器
Or:
$ python <me>.py --local # 指定采用本地的后端服务
Or:
$ python <me>.py --open # 不管上一次打开浏览器是什么时候,都再次打开浏览器
"""
from __future__ import annotations
import contextlib
import multiprocessing
import os
import platform
import shlex
import subprocess
import sys
import time
from datetime import datetime, timedelta
from pathlib import Path
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from multiprocessing.managers import DictProxy
try:
from loguru import logger
except ImportError:
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__) # type:ignore[assignment]
CMD = "npm run dev:api"
__version__ = "0.2.0"
__updated_at__ = "2025.08.04"
def open_chrome(comm_dict: DictProxy[str, bool] | None = None) -> None:
open_browser = "explorer" if platform.system() == "Windows" else "open"
frontend_url = "http://localhost:3000"
seconds = 5 # 延迟5秒等待npm那边启动
for _ in range(seconds * 10):
time.sleep(0.1)
if comm_dict is not None and comm_dict.get("stop"):
# 如果npm run xxx命令启动失败了,直接退出
logger.debug("Skip browser open as explicit asked to.")
return
logger.info(f"Going to open {frontend_url=}")
subprocess.run([open_browser, frontend_url])
def run_and_echo(
cmd: str, env: dict[str, str] | None = None, verbose: bool = True
) -> int:
if verbose:
print("-->", cmd)
if env is not None:
env = dict(os.environ, **env)
r = subprocess.run(shlex.split(cmd), env=env)
return r.returncode
def get_acceptable_delta_time(hided_file: Path, fmt: str) -> timedelta | None:
if "--open" in sys.argv or not hided_file.exists():
return None
delta_time = datetime.now() - datetime.strptime(hided_file.read_text(), fmt)
# 距离上一次启动,已经超过一天的话,就自动打开浏览器
if delta_time.days >= 1:
return None
return delta_time
def run_in_manager(comm_dict: DictProxy[str, bool]) -> None:
env = None
if "--local" in sys.argv:
env = {"VITE_BACKEND_BASE_URL": "http://localhost:9999"}
hided_file = Path(__file__).parent / ".last_open.txt.bak"
fmt = "%Y-%m-%d %H:%M:%S"
process = None
with contextlib.suppress(ValueError):
if delta_time := get_acceptable_delta_time(hided_file, fmt):
logger.info(f"No need to open browser as {delta_time = }")
else:
# 启动子进程去打开浏览器,以免阻塞主进程
process = multiprocessing.Process(target=open_chrome, args=(comm_dict,))
process.start()
try:
rc = run_and_echo(CMD, env=env)
except KeyboardInterrupt:
hided_file.write_text(f"{datetime.now():{fmt}}")
raise
if rc != 0:
if process is not None and process.is_alive():
comm_dict["stop"] = True
process.terminate()
process.join()
sys.exit(1)
def main() -> None:
if sys.argv[1:] and sys.argv[1] in ("-h", "--help"):
print(__doc__.replace("<me>", Path(__file__).stem))
return
me = Path(__file__).resolve().as_posix()
if me.startswith("/Users/mac10.12/coding/"):
dst = me.replace("/coding/", "/frontend/")
os.chdir(Path(dst).parent)
logger.info(f"Using {dst} instead~")
subprocess.run(["python", dst])
return
run_and_echo("git pull")
with multiprocessing.Manager() as pm:
comm_dict = pm.dict()
run_in_manager(comm_dict)
if __name__ == "__main__":
main()

浙公网安备 33010602011771号