配置见 pyproject.toml:
- line-length=100:贴着现有代码定。若用默认的 88 会重排 122 行,
噪音淹没真正有价值的修复。
- 启用 E/W/F/I/UP/B/C4/SIM/RET/PERF/RUF;**不**启用 PTH。因为本项目的路径
多是"字符串身份"——要塞进 subprocess 命令行、写进 JSON 存档、打印给用户,
全换 pathlib 只是包一层再拆一层,换来 70 处纯风格改动。
- 忽略 RUF001/002/003:中文全角标点是正确写法,不是"歧义字符"。
- 忽略 PERF203:剩下两处都是"逐项容错"语义(逐个牧场存档要立刻上报失败、
收获全部要跳过失败地块)。把 try 提到循环外会毁掉"一项失败不影响其它项"
这个行为,是错的优化。
自动修复 358 项:Dict/List/Optional 换成 PEP 585/604 写法、导入排序、
失效的 noqa、空占位 f-string 等。
手工修复的(这些是真问题,不是风格偏好):
- B904 x9:except 中 raise 补异常链。刻意区分——网络/IO 失败用 from exc 保留
根因;参数解析失败用 from None,因为底层 ValueError 对玩家只是噪音。
- RUF012 x2:StateView 的 plots/animals 原本是**类级共享可变列表**,
改成在 __init__ 里初始化(apply({}) 会提前返回,所以必须显式摆好)。
- F841:TUI 里一个赋值后再没用过的死变量。
- SIM105 x11:try/except/pass 统一改成 contextlib.suppress。
- SIM115:日志句柄改用 with。Popen 已把 fd 复制给子进程,父进程可安全关闭。
- E501 x7:拆超长行;main.py 那处顺手提取变量,避免超长嵌套表达式。
- E741:把 l/r 改成 left_line/right_line。
- 其余:RUF046 / PERF401 / RET504 / RUF005 / UP031 / B007。
ruff format 重排 24 个文件。
验证:97 项测试全绿。
78 lines
2.4 KiB
Python
78 lines
2.4 KiB
Python
"""SimpleFarm 服务器包。
|
||
|
||
把原本"一个进程一局游戏"的单机引擎,改造成一个可以多人同时在线、
|
||
可以后台常驻、既能被人玩也能被 AI 调用的小型游戏服务器。
|
||
|
||
模块划分
|
||
--------
|
||
* :mod:`farm.server.app` —— FastAPI 应用(REST + WebSocket)
|
||
* :mod:`farm.server.actions` —— 动作表:所有入口共用的唯一执行通道
|
||
* :mod:`farm.server.session` —— 多牧场会话与统一时钟
|
||
* :mod:`farm.server.hub` —— WebSocket 连接池与状态推送
|
||
* :mod:`farm.server.daemon` —— 守护进程(start/stop/status)
|
||
* :mod:`farm.server.webui` —— 内嵌网页客户端
|
||
|
||
``app`` 依赖 FastAPI,而 ``daemon`` 只依赖标准库。为了让
|
||
``python main.py stop`` 在没有装 FastAPI 的机器上也能用,这里采用**惰性导出**:
|
||
真正 import 到用例时才加载对应子模块。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
_LAZY = {
|
||
"API_VERSION",
|
||
"SERVER_NAME",
|
||
"ServerConfig",
|
||
"ServerHandle",
|
||
"build_app",
|
||
"create_app",
|
||
"rules_text",
|
||
"brief",
|
||
"Action",
|
||
"Param",
|
||
"ACTIONS",
|
||
"run_action",
|
||
"describe_actions",
|
||
"action_names",
|
||
"FarmSession",
|
||
"SessionManager",
|
||
"FarmClock",
|
||
"validate_farm_id",
|
||
"DEFAULT_FARM",
|
||
"Client",
|
||
"ConnectionHub",
|
||
"Publisher",
|
||
"DaemonManager",
|
||
"pid_alive",
|
||
"DASHBOARD_HTML",
|
||
"dashboard_text",
|
||
}
|
||
|
||
__all__ = sorted(_LAZY)
|
||
|
||
|
||
def __getattr__(name: str) -> Any: # PEP 562:模块级惰性属性
|
||
if name not in _LAZY:
|
||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||
from importlib import import_module
|
||
|
||
if name in ("DaemonManager", "pid_alive"):
|
||
module = import_module("farm.server.daemon")
|
||
elif name in ("DASHBOARD_HTML", "dashboard_text"):
|
||
module = import_module("farm.server.webui")
|
||
elif name in ("Action", "Param", "ACTIONS", "run_action", "describe_actions", "action_names"):
|
||
module = import_module("farm.server.actions")
|
||
elif name in ("FarmSession", "SessionManager", "FarmClock", "validate_farm_id", "DEFAULT_FARM"):
|
||
module = import_module("farm.server.session")
|
||
elif name in ("Client", "ConnectionHub", "Publisher"):
|
||
module = import_module("farm.server.hub")
|
||
else:
|
||
module = import_module("farm.server.app")
|
||
return getattr(module, name)
|
||
|
||
|
||
def __dir__() -> list:
|
||
return sorted(set(list(globals()) + __all__))
|