chore: 引入 pyright 类型检查清零 + README 工程化章节
pyright(basic 模式)从 15 errors / 10 warnings 降到 0/0: - pyproject: pythonPlatform="All"(termios/msvcrt 双平台分支不再误报), reportUnsupportedDunderAll=false(__init__ 惰性导出属正常写法) - keys.py: 捕获 sys.stdin 局部变量 + 提前返回,消除 fileno() 的 None 收窄告警 - server/app.py: lifespan 改用 @asynccontextmanager 并标注 AsyncIterator[None] - server/actions.py: 显式标注 result: dict[str, Any],消除 isinstance 冗余报错 - server/__main__.py: 用 importlib.util.find_spec 探测 websockets - tests/test_server.py: 同上,uvicorn 探测改为 find_spec - tools/preview_tui.py: TYPE_CHECKING 下引入 RemoteFarm,修正返回类型标注 - farm/tui.py: 取消未使用变量,统一 round/取整写法 其他: - README: 新增第 11 节「工程化:uv + ruff + pyright」,快速开始全部改用 uv sync / uv run;原第 11 节起顺延为 12 - requirements.txt: 由 uv export --no-dev --no-hashes 重新生成(锁定版本) - 清理临时日志文件 验收:ruff check 全通过 / ruff format 26 文件无改动 / pyright 0 错误 / unittest 97 项全部通过
This commit is contained in:
1 parent
fcd4cb4649
commit
d7a7107b0a
11 files changed
+200
-74
No files matched your search
@@ -28,13 +28,13 @@
|
||||
## 1. 快速开始
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt # 只有服务端需要第三方依赖
|
||||
uv sync # 建好环境(只有服务端需要第三方依赖)
|
||||
|
||||
python main.py serve --daemon # ① 后台启动服务器
|
||||
python main.py play --farm 我的农场 # ② 终端里玩(另开一个窗口)
|
||||
python main.py bot --farm ai-1 # ③ 让机器人玩家一起经营
|
||||
python main.py status # ④ 看服务器状态
|
||||
python main.py stop # ⑤ 优雅停机(会把所有牧场存档)
|
||||
uv run main.py serve --daemon # ① 后台启动服务器
|
||||
uv run main.py play --farm 我的农场 # ② 终端里玩(另开一个窗口)
|
||||
uv run main.py bot --farm ai-1 # ③ 让机器人玩家一起经营
|
||||
uv run main.py status # ④ 看服务器状态
|
||||
uv run main.py stop # ⑤ 优雅停机(会把所有牧场存档)
|
||||
```
|
||||
|
||||
浏览器打开 <http://127.0.0.1:8765/> 就是网页版客户端(可多开几个标签页,用不同牧场名,
|
||||
@@ -43,9 +43,12 @@ python main.py stop # ⑤ 优雅停机(会把所有牧场
|
||||
不想装任何第三方库、只想单机玩玩:
|
||||
|
||||
```bash
|
||||
python main.py solo # 本地引擎 + 终端界面,不开服务器
|
||||
uv run main.py solo # 本地引擎 + 终端界面,不开服务器
|
||||
```
|
||||
|
||||
> **没装 uv 也能跑**:把命令里的 `uv run ` 去掉,直接用 `python main.py ...`;
|
||||
> 依赖用 `pip install fastapi 'uvicorn[standard]'` 装。两种方式行为完全一致。
|
||||
|
||||
---
|
||||
|
||||
## 2. 玩法
|
||||
@@ -68,7 +71,7 @@ python main.py solo # 本地引擎 + 终端界面,不开
|
||||
|
||||
## 3. 终端界面
|
||||
|
||||
`python main.py play` 打开。核心交互:
|
||||
`uv run main.py play` 打开。核心交互:
|
||||
|
||||
```
|
||||
[↑↓←→]选地 [P]播种 [W]浇水 [H]收获 [E]全部收获
|
||||
@@ -287,8 +290,8 @@ asyncio.run(main())
|
||||
局域网联机:
|
||||
|
||||
```bash
|
||||
python main.py serve --daemon --host 0.0.0.0 --port 8765 --token 找个密码
|
||||
python main.py play --url http://192.168.1.20:8765 --farm alice --token 找个密码
|
||||
uv run main.py serve --daemon --host 0.0.0.0 --port 8765 --token 找个密码
|
||||
uv run main.py play --url http://192.168.1.20:8765 --farm alice --token 找个密码
|
||||
```
|
||||
|
||||
---
|
||||
@@ -300,9 +303,9 @@ python main.py play --url http://192.168.1.20:8765 --farm alice --token 找个
|
||||
| 项目 | 位置 / 命令 |
|
||||
| --- | --- |
|
||||
| 进程号 | `run/server.pid`(**由服务进程自己写**,见下方说明) |
|
||||
| 日志 | `run/server.log`(`python main.py logs -n 50`) |
|
||||
| 停止 | `python main.py stop`(先走 HTTP 优雅停机,失败再发信号) |
|
||||
| 状态 | `python main.py status` |
|
||||
| 日志 | `run/server.log`(`uv run main.py logs -n 50`) |
|
||||
| 停止 | `uv run main.py stop`(先走 HTTP 优雅停机,失败再发信号) |
|
||||
| 状态 | `uv run main.py status` |
|
||||
| 存档 | `saves/<牧场名>.json`(纯 JSON,可直接阅读分析) |
|
||||
| 接口文档 | <http://127.0.0.1:8765/docs> |
|
||||
|
||||
@@ -321,6 +324,10 @@ python main.py play --url http://192.168.1.20:8765 --farm alice --token 找个
|
||||
## 9. 代码结构
|
||||
|
||||
```
|
||||
pyproject.toml 唯一真源:依赖、Python 版本、ruff 与 pyright 规则
|
||||
uv.lock 锁定的依赖版本(可复现构建,应当提交)
|
||||
.python-version 钉住解释器版本
|
||||
.gitattributes 行尾统一为 LF,避免 Windows 上 CRLF 反复抖动
|
||||
main.py 统一入口:serve / play / do / bot / status ...
|
||||
ai_player.py 外挂式机器人玩家(只用接口,不 import 任何游戏规则)
|
||||
farm/
|
||||
@@ -358,9 +365,9 @@ tests/
|
||||
## 10. 测试
|
||||
|
||||
```bash
|
||||
python -m unittest discover -s tests -v # 全部(97 项)
|
||||
python -m unittest tests.test_engine # 只测游戏规则,不需要 FastAPI
|
||||
python -m unittest tests.test_server.TestLiveServer # 真起服务器 + 真 WebSocket
|
||||
uv run python -m unittest discover -s tests -v # 全部(97 项)
|
||||
uv run python -m unittest tests.test_engine # 只测游戏规则,不需要 FastAPI
|
||||
uv run python -m unittest tests.test_server.TestLiveServer # 真起服务器 + 真 WebSocket
|
||||
```
|
||||
|
||||
覆盖范围:
|
||||
@@ -386,7 +393,63 @@ serve --daemon → status 显示正确 pid → actions 列出 13 个候选动作
|
||||
|
||||
---
|
||||
|
||||
## 11. 边界处理与设计取舍
|
||||
## 11. 工程化:uv + ruff + pyright
|
||||
|
||||
依赖版本、Python 版本、lint 规则、类型检查规则**全部**写在 `pyproject.toml` 里——
|
||||
单一真源,不再存在"requirements 与实际环境不一致"这种事。`uv.lock` 记录完整解析结果,
|
||||
提交进版本库,换台机器 `uv sync` 得到的是同一个环境。
|
||||
|
||||
| 目的 | 命令 |
|
||||
| --- | --- |
|
||||
| 同步环境(含开发依赖) | `uv sync` |
|
||||
| 只装运行依赖 | `uv sync --no-dev` |
|
||||
| 添加依赖 | `uv add <包>` / `uv add --dev <包>` |
|
||||
| 升级锁定版本 | `uv lock --upgrade` |
|
||||
| 导出给 pip 用户 | `uv export --no-dev --no-hashes -o requirements.txt` |
|
||||
| 静态检查 | `uv run ruff check .` |
|
||||
| 自动修复 | `uv run ruff check . --fix` |
|
||||
| 格式化 | `uv run ruff format .` |
|
||||
| 类型检查 | `uv run pyright` |
|
||||
|
||||
### 为什么是 uv
|
||||
|
||||
* **一个工具顶四个**:解释器管理、依赖解析、虚拟环境、命令运行都在里面,
|
||||
不需要 pyenv + pip + venv + pip-tools 拼装;
|
||||
* **可复现**:`uv.lock` 精确到哈希,跨机器一致;
|
||||
* **不用激活环境**:`uv run <命令>` 自动使用项目环境,少一类"我明明装了"的事故。
|
||||
|
||||
### ruff 的取舍
|
||||
|
||||
`ruff check` 管正确性,`ruff format` 管排版,一个工具替代
|
||||
Black + isort + flake8 三件套(避免"格式化完 lint 又报"的拉锯)。
|
||||
|
||||
启用 `E W F I UP B C4 SIM RET PERF RUF`。两个刻意的**不**:
|
||||
|
||||
* **不启用 PTH**(`os.path` → `pathlib`)。本项目的路径大多是"字符串身份":
|
||||
要拼进 `subprocess` 命令行、写进 JSON 存档、打印给用户看。全换成 `Path`
|
||||
只是把字符串包一层再拆一层,换来 70 处纯风格改动,反而淹没真正的问题。
|
||||
* **忽略 RUF001/002/003**。它们会把中文全角标点当成"含糊的 Unicode 字符",
|
||||
但这里的注释与界面文案本来就是中文,全角括号是正确写法。
|
||||
|
||||
另外忽略 `PERF203`(循环里的 `try`):保留下来的两处都是**逐项容错**语义
|
||||
(逐个牧场存档要立刻上报失败、收获全部要跳过失败地块)。把 `try` 提到循环外
|
||||
会让"一项失败不影响其它项"这个行为消失——那是错的优化。
|
||||
|
||||
`line-length = 100` 是**贴着现有代码**定的:全项目只有 18 行超过 100 列;
|
||||
若采用默认的 88,需要额外重排 122 行,diff 噪音会淹没真实改动。
|
||||
|
||||
### pyright 的取舍
|
||||
|
||||
用 `basic` 模式。目标不是"消灭所有 Unknown",而是**能在 CI 里拦住真 bug**:
|
||||
None 误用、属性不存在、参数类型不符。本项目的状态快照是 `dict[str, Any]` 结构,
|
||||
开 `strict` 去追每个 `Unknown` 只会得到一堆噪音。
|
||||
|
||||
配合 `pythonVersion = "3.10"`,守住 `requires-python` 的下限——
|
||||
哪天不小心用上 3.11+ 独有语法,会立刻报错。
|
||||
|
||||
---
|
||||
|
||||
## 12. 边界处理与设计取舍
|
||||
|
||||
**输入校验**(唯一入口 `server/actions.py`):
|
||||
|
||||
|
||||
Reference in new issue
Block a user