feat: 使用 uv 锁定主程序依赖并强化插件恢复边界 (#6364)

This commit is contained in:
InfinityPacer
2026-08-20 12:17:19 +08:00
committed by GitHub
parent 27ae1b5290
commit 23f5d59c74
59 changed files with 6804 additions and 1797 deletions
+6 -5
View File
@@ -11,7 +11,7 @@ curl -fsSL https://raw.githubusercontent.com/jxxghp/MoviePilot/v3/scripts/bootst
脚本会自动:
- 检测操作系统
- 自动检查并尽量安装 `git``curl``Python 3.11+`
- 自动检查并尽量安装 `git``curl``uv 0.12.5``Python 3.12+`
- 克隆 `MoviePilot`
- 安装后端依赖
- 按当前仓库 `version.py` 中的 `FRONTEND_VERSION` 下载对应前端 release 的 `dist.zip`
@@ -24,8 +24,8 @@ curl -fsSL https://raw.githubusercontent.com/jxxghp/MoviePilot/v3/scripts/bootst
说明:
- 如果系统里已经有可用的 `Python 3.11+`,脚本会优先直接复用本地解释器
- 如果系统里没有可用`Python 3.11+`,脚本会再尝试自动补齐运行环境
- 如果系统里已经有可用的 `Python 3.12+`,脚本会优先直接复用本地解释器
- 如果系统里没有可用解释器,脚本会通过固定版本的 uv 安装 Python 3.12
- Linux 下安装系统依赖时通常需要 `sudo`
- 复用已有仓库时,脚本现在只会因为已跟踪源码改动而阻止自动更新,不会再被 `.DS_Store` 之类未跟踪文件卡住
@@ -156,7 +156,7 @@ moviepilot commands
```shell
moviepilot install deps
moviepilot install deps --python python3.11
moviepilot install deps --python python3.12
moviepilot install deps --venv /path/to/venv
moviepilot install deps --recreate
moviepilot install deps --config-dir /path/to/moviepilot-config
@@ -164,7 +164,8 @@ moviepilot install deps --config-dir /path/to/moviepilot-config
说明:
- 默认会自动选择本地已安装的 `Python 3.11+` 解释器
- 默认会自动选择本地已安装的 `Python 3.12+` 解释器
- 安装器要求 `uv 0.12.5`,并按仓库提交的 `uv.lock` 同步依赖;不会在本地重新解析一套未锁定结果
- `moviepilot_rust` 加速扩展通过 `moviepilot-rust` PyPI 依赖安装,主项目本地安装不需要 Rust toolchain
- 安装完成后可在前端“高级设置 - 实验室”中关闭或重新开启 Rust 加速;如果后端未加载扩展,该开关会保持关闭且不可操作
+64 -54
View File
@@ -6,54 +6,41 @@
在开始之前,请确保您的系统已安装以下软件:
- **Python 3.11 或更高版本**
- **pip** (Python 包管理器)
- **Python 3.12+**
- **uv 0.12.5**Python 版本、虚拟环境和依赖锁定工具)
- **Git** (用于版本控制)
- **RAR 解压工具**:本地开发如需测试或使用 `.rar` 字幕包解压,请安装 `unar``unrar``7z``bsdtar` 之一;Docker 镜像会内置 `unar`
Rust 加速扩展通过 `moviepilot-rust` PyPI 包安装,主项目本地开发不再需要 Rust toolchain。需要修改或发布 Rust 扩展时,请在 `MoviePilot-Rust` 仓库中构建。
### 1. 创建虚拟环境
### 1. 创建锁定环境
在项目根目录下创建并激活虚拟环境
仓库通过 `pyproject.toml` 声明直接依赖,并提交统一的 `uv.lock`。在项目根目录执行
- 在 Windows 上:
```bash
uv sync --locked
```
```bash
python -m venv venv
.\venv\Scripts\activate
```
`uv` 会创建或更新 `.venv`,并安装运行时与默认 `dev` 依赖组。命令中的
`--locked` 会在 `pyproject.toml``uv.lock` 不一致时直接失败,避免开发环境静默解析出一套
未提交的依赖结果。只需要生产运行依赖时使用:
- 在 macOS/Linux 上:
```bash
uv sync --locked --no-dev --no-install-project
```
```bash
python3 -m venv venv
source venv/bin/activate
```
### 2. 依赖分层与事实源
虚拟环境确保项目的依赖项与系统全局环境隔离,防止冲突。
主程序只维护以下依赖事实源:
### 2. 依赖分层与安装
主程序依赖按使用场景分层,避免运行时镜像携带只在开发、测试或构建时需要的工具:
| 文件 | 用途 | 典型安装场景 |
| 位置 | 用途 | 维护方式 |
| --- | --- | --- |
| `requirements.in` | 主程序运行依赖。只放启动、后台任务、插件运行框架和内置功能在生产环境需要导入的包。 | Docker 镜像、CLI 本地运行、运行时依赖自愈。 |
| `requirements-dev.in` | 开发、测试、静态检查和源码构建辅助依赖。 | CI 单测、本地跑测、Pylint、显式源码构建。 |
| `requirements.txt` | 兼容入口,默认只委托到 `requirements.in`。它不是跨平台完整锁文件,不应在本地开发机上直接维护一份平台相关锁定结果。 | 旧脚本、Docker 运行时恢复、CLI 安装入口。 |
| `pyproject.toml``[project].dependencies` | 主程序生产运行依赖。 | 开发者按直接依赖的兼容范围维护。 |
| `pyproject.toml``[dependency-groups].dev` | pytest、覆盖率、Pylint 和源码构建等开发工具。 | 不进入 Docker 生产运行环境。 |
| `uv.lock` | Python 3.12+ 和受支持平台共享的完整解析结果。 | 修改 `pyproject.toml` 后由 `uv lock` 更新并提交。 |
运行主程序只需要安装运行时依赖:
```bash
pip install -r requirements.txt
```
开发、测试、静态检查或执行源码编译时安装开发依赖入口:
```bash
pip install -r requirements-dev.in
```
主程序不再维护 `requirements.in``requirements-dev.in``requirements.txt`,也不生成
平台专属的 requirements 锁文件。Docker、CLI 和 CI 都以提交的 `uv.lock` 为安装输入。
### 2.1 本地启动脚本
@@ -96,10 +83,41 @@ chmod +x scripts/start-local.sh
新增或升级依赖时,先确认依赖属于哪个层级:
1. **运行时依赖**:被 `app/` 生产代码直接导入,或是生产功能、后台任务、插件框架启动必需,写入 `requirements.in`
2. **开发 / 测试 / 静态检查 / 构建依赖**:只用于单测、覆盖率、lint 辅助、源码构建等,不应进入生产运行时,写入 `requirements-dev.in`
3. **工具依赖**`pip-tools`、`uv`、`safety` 这类安装或审计工具不属于主程序运行依赖,按脚本或 CI 场景显式安装
4. **插件依赖**:由插件声明并在插件安装阶段处理,不直接并入主程序 `requirements.in`
1. **运行时依赖**:被 `app/` 生产代码直接导入,或是生产功能、后台任务、插件框架启动必需,写入 `[project].dependencies`
2. **开发 / 测试 / 静态检查 / 构建依赖**:只用于单测、覆盖率、lint 辅助、源码构建等,写入 `[dependency-groups].dev`
3. **工具依赖**:仓库要求使用 `uv 0.12.5`;不应为了安装工具而把它加入主程序运行依赖
4. **插件依赖**:由插件清单声明并在插件安装阶段处理,不直接并入主程序依赖
修改后更新并校验锁文件:
```bash
uv lock
uv lock --check
uv sync --locked
uv pip check
```
`uv.lock` 同时覆盖 Linux x86_64/arm64、macOS x86_64/arm64 和 Windows x64。统一锁文件只
固定解析结果,不能替代这些平台的真实安装门禁;平台条件依赖变更必须通过对应 CI 环境验证。
### 3.1 插件依赖清单
新插件可以在插件根目录使用 `pyproject.toml`,宿主只读取 `[project].dependencies` 作为运行依赖:
```toml
[project]
name = "example-plugin"
version = "1.0.0"
dependencies = ["example-package>=1,<2"]
```
插件依赖遵循以下合同:
- `pyproject.toml` 优先于历史 `requirements.txt`;两者同时存在时只读取前者;
- `[dependency-groups]` 属于插件自身的开发、测试或构建环境,宿主不安装其中内容;
- `pyproject.toml` 存在但格式或依赖声明无效时直接报错,不回退到 `requirements.txt`
- 仅有 `requirements.txt` 的历史插件继续按原方式安装;
- 宿主不消费插件自己的 `uv.lock`,因为多个插件共享同一主程序环境,不能分别同步独立锁文件。
### 4. 准备资源与插件目录
@@ -131,44 +149,36 @@ python -m scripts.generate_plugin_market_default \
### 5. 运行安全检查
我们使用 `safety` 工具检查依赖项中是否存在已知安全漏洞。更新运行时依赖后,应至少检查运行时入口;更新开发测试依赖时,也应覆盖开发入口。
#### 安装 safety
您可以使用以下命令安装 `safety`
```bash
pip install safety
```
我们使用 `safety` 工具检查 `pyproject.toml``uv.lock` 中是否存在已知安全漏洞。该检查是
依赖变更的人工门禁,当前不属于自动 CI。
#### 执行安全检查
运行以下命令检查运行时入口
可以通过 `uvx` 在隔离工具环境中运行 `safety`,无需把它加入主程序依赖
```bash
safety check -r requirements.txt --policy-file=safety.policy.yml > safety_report.txt
uvx safety scan --target . --policy-file safety.policy.yml
```
这将生成一个名为 `safety_report.txt` 的报告文件,您可以查看其中的漏洞报告并进行相应处理
Safety 直接识别项目清单和锁文件,不需要生成或维护 requirements 文件
### 6. 提交代码前的检查
在提交代码之前,请确保完成以下步骤:
1. **确认依赖分层正确**:运行时包进入 `requirements.in`;测试、覆盖率、静态检查和构建辅助进入 `requirements-dev.in`;插件依赖不并入主程序运行时依赖。
1. **确认依赖分层正确**:运行时包进入 `[project].dependencies`;测试、覆盖率、静态检查和构建辅助进入 `[dependency-groups].dev`;插件依赖不并入主程序运行时依赖。
2. **运行安全检查**:确保 `safety` 检查通过,没有新的安全漏洞。
3. **运行测试**:如果项目中包含测试,请确保所有测试都通过。运行以下命令以执行测试:
```bash
pytest
uv run --locked --no-sync pytest
```
### 7. 参考资源
- [pip-tools 官方文档](https://github.com/jazzband/pip-tools)
- [uv 官方文档](https://docs.astral.sh/uv/)
- [safety 官方文档](https://pyup.io/safety/)
- [Safety CLI 官方文档](https://docs.safetycli.com/)
- [MoviePilot-Resources](https://github.com/jxxghp/MoviePilot-Resources)
- [MoviePilot-Plugins](https://github.com/jxxghp/MoviePilot-Plugins)
@@ -608,7 +608,7 @@ app/application/plugin/folders.py # 插件文件夹清理用例
- `PluginManager()` 仍返回同一实例,`app.sdk.plugins.PluginManager` 身份测试保持。
- 启停、更新、热重载、配置更新、动态路由刷新顺序不变。
- PluginManager 本身不再直接导入 DB、市场 client、pip、压缩包和备份实现;具体安装阶段由 Application command 和注入的包/依赖端口完成。
- PluginManager 本身不再直接导入 DB、市场 client、包管理器、压缩包和备份实现;具体安装阶段由 Application command 和注入的包/依赖端口完成。
- 所有旧公共方法在 V3 保留,内部只做委托。
### 6.9 外部 Adapter 直接持久化并承载业务用例
@@ -619,7 +619,7 @@ app/application/plugin/folders.py # 插件文件夹清理用例
- 市场索引和发布信息请求。
- 插件包下载、解压、校验、备份和恢复。
- requirements 解析、冲突判断、pip 安装与降级策略。
- 插件 `pyproject.toml` / `requirements.txt` 选择、约束判断、uv 安装与降级策略。
- 同步/异步重复实现。
- 市场缓存、旧同步/异步安装入口和旧私有方法兼容。
@@ -1384,7 +1384,7 @@ done_when: []
### 12.2 持续门禁与同职责域细化(阶段 3-5)
- 本轮纳入阶段 3 的写端点不再直接持有数据库事务;当前机器基线中的 endpoint→Session、endpoint→Model、Application→DB 和目标 Adapter/Runtime→DB 边均为 0。后续只允许防止这些边重新引入,不再把历史边数量当作未完成任务。
- PluginManager 不直接做市场、pip、压缩包和备份实现;外部 Adapter 不导入 Oper。
- PluginManager 不直接做市场、包管理、压缩包和备份实现;外部 Adapter 不导入 Oper。
- 重点 Chain 的垂直切片和 `ChainBase` 脱离真实 Runtime 的单测属于同一职责域内的持续细化,不再作为跨层拆分阻塞项。
### 12.3 长期 ABI、性能与实现预算(阶段 6-7)
+14 -11
View File
@@ -4,8 +4,9 @@
| Item | Detail |
|---|---|
| Language | Python 3.11+ |
| CI Python version | Python 3.12 |
| Language | Python 3.12+ |
| Primary CI Python version | Python 3.12 |
| Dependency compatibility CI | Supported platform matrix on Python 3.12, plus newer interpreter coverage on Linux x86_64 |
| Async runtime | asyncio (native), integrated with FastAPI/Uvicorn |
---
@@ -104,11 +105,12 @@
| Item | Detail |
|---|---|
| Runtime source | `requirements.in` — production/runtime dependencies only |
| Dev/test/lint/build source | `requirements-dev.in` — includes runtime plus pytest, coverage tooling, pylint, and build support |
| Compatibility entry | `requirements.txt` — delegates to `requirements.in`; not a committed cross-platform lock |
| Runtime install | `pip install -r requirements.txt` |
| Dev/test/lint/build install | `pip install -r requirements-dev.in` |
| Project metadata | `pyproject.toml` — runtime dependencies in `[project].dependencies`, development tooling in `[dependency-groups].dev` |
| Lock | `uv.lock` — committed resolution for Python 3.12+ and supported platforms |
| Package manager | uv 0.12.5 |
| Runtime install | `uv sync --locked --no-dev --no-install-project` |
| Dev/test/lint/build install | `uv sync --locked` |
| Supported platforms | Linux x86_64/arm64, macOS x86_64/arm64, Windows x64 |
---
@@ -127,9 +129,10 @@
| Tool | Purpose | Command |
|---|---|---|
| pytest | Test runner | `pytest tests/test_xxx.py` |
| pylint | Static analysis | `pylint app/` |
| safety | Dependency vulnerability scan | `safety check -r requirements.txt --policy-file=safety.policy.yml` |
| pytest | Test runner | `uv run --locked --no-sync pytest tests/test_xxx.py` |
| pylint | Static analysis | `uv run --locked --no-sync pylint app/` |
| uv | Lock and environment consistency | `uv lock --check && uv pip check` |
| safety | Manual dependency vulnerability scan | `uvx safety scan --target . --policy-file safety.policy.yml` |
---
@@ -142,4 +145,4 @@
| Frontend | Vue/TypeScript SPA served from `public/`; source in `MoviePilot-Frontend` repo |
| Frontend proxy | Local Node `service.js` proxies `/api` and `/cookiecloud` to the backend |
*Last Updated: 2026-05-25*
*Last Updated: 2026-08-19*
+26 -30
View File
@@ -7,16 +7,11 @@ This document is the project command reference, not an exhaustive shell allowlis
## Development Environment Setup
```bash
# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate # macOS / Linux
.\venv\Scripts\activate # Windows
# Create the locked development/test environment
uv sync --locked
# Install runtime dependencies
pip install -r requirements.txt
# Install development/test/lint/build dependencies
pip install -r requirements-dev.in
# Create a runtime-only environment
uv sync --locked --no-dev --no-install-project
```
---
@@ -24,17 +19,21 @@ pip install -r requirements-dev.in
## Dependency Management
```bash
# Install runtime dependencies
pip install -r requirements.txt
# Verify that project metadata and lock agree
uv lock --check
# Install test/lint/build dependencies
pip install -r requirements-dev.in
# Update the lock after editing pyproject.toml
uv lock
# Verify installed dependency consistency
uv pip check
```
**Rules:**
- Runtime dependencies belong in `requirements.in`.
- Test, coverage, lint, and explicit build tooling belong in `requirements-dev.in`.
- `requirements.txt` is a compatibility entry that delegates to `requirements.in`; do not replace it with a local cross-platform lock file.
- Runtime dependencies belong in `[project].dependencies` in `pyproject.toml`.
- Test, coverage, lint, and explicit build tooling belong in `[dependency-groups].dev`.
- Commit the updated `uv.lock`; do not maintain or generate main-program requirements files.
- Use uv 0.12.5 and Python 3.12+.
---
@@ -42,16 +41,16 @@ pip install -r requirements-dev.in
```bash
# Run a specific test file
pytest tests/test_xxx.py
uv run --locked --no-sync pytest tests/test_xxx.py
# Run all tests
pytest
uv run --locked --no-sync pytest
# Run tests with verbose output
pytest -v tests/test_xxx.py
uv run --locked --no-sync pytest -v tests/test_xxx.py
# Run a specific test function
pytest tests/test_xxx.py::test_function_name
uv run --locked --no-sync pytest tests/test_xxx.py::test_function_name
```
**Rules:**
@@ -65,10 +64,10 @@ pytest tests/test_xxx.py::test_function_name
```bash
# Run pylint on the application package
pylint app/
uv run --locked --no-sync pylint app/
# Run pylint on a specific module
pylint app/chain/download.py
uv run --locked --no-sync pylint app/chain/download.py
```
**Rules:**
@@ -80,15 +79,12 @@ pylint app/chain/download.py
## Security Scan
```bash
# Run safety check against the runtime compatibility entry
safety check -r requirements.txt --policy-file=safety.policy.yml
# Save report to file
safety check -r requirements.txt --policy-file=safety.policy.yml > safety_report.txt
# Scan pyproject.toml and uv.lock
uvx safety scan --target . --policy-file=safety.policy.yml
```
**Rules:**
- Run after runtime dependency changes; include `requirements-dev.in` when development/test/lint/build dependencies change.
- Run manually after runtime or development dependency changes; this is not currently an automated CI job.
- No new high-severity vulnerabilities may be introduced.
---
@@ -132,7 +128,7 @@ curl -fsSL https://raw.githubusercontent.com/jxxghp/MoviePilot/v3/scripts/bootst
# Install backend dependencies
moviepilot install deps
moviepilot install deps --python python3.11
moviepilot install deps --python python3.12
moviepilot install deps --venv /path/to/venv
moviepilot install deps --recreate
@@ -307,4 +303,4 @@ python -m scripts.generate_plugin_market_default \
- The marked list must be nonempty and include `jxxghp/MoviePilot-Plugins`.
- This command rewrites only `ConfigModel.PLUGIN_MARKET`; inspect the resulting diff before committing or packaging.
*Last Updated: 2026-08-06*
*Last Updated: 2026-08-19*
+3 -3
View File
@@ -11,7 +11,7 @@
## Python Version and Typing
- Target: **Python 3.11+**. CI runs Python 3.12.
- Target: **Python 3.12+**. Python 3.12 is the primary CI version; compatibility CI also verifies newer interpreters.
- **Type annotations are required** on all public methods and function signatures.
- Use `Optional[X]` for nullable types (do not use `X | None` — keep consistency with the existing codebase style).
- Use `Union[X, Y]` for multi-type parameters.
@@ -114,11 +114,11 @@ except:
## What Not To Do
- Do not introduce new third-party libraries without placing them in the correct dependency entry: runtime packages in `requirements.in`, test/lint/build tooling in `requirements-dev.in`.
- Do not introduce new third-party libraries without placing them in the correct `pyproject.toml` dependency group and updating `uv.lock`: runtime packages belong in `[project].dependencies`, test/lint/build tooling in `[dependency-groups].dev`.
- Do not use `requests` or `httpx` directly for external HTTP calls - host code uses `RequestUtils` from `app/adapters/network/http.py`; plugins use `app.sdk.network`.
- Do not issue raw SQLAlchemy queries from chains, modules, or endpoints — use the Oper classes in `app/db/oper/`.
- Do not add TODO or FIXME without context. Only keep one if it is genuinely deferred and cannot be addressed in the current task.
- Do not add noisy markers like `# change starts here`, `# important`, or `# this is a fix`.
- Do not write comments that restate what the code already clearly says.
*Last Updated: 2026-08-14*
*Last Updated: 2026-08-19*
+7 -7
View File
@@ -6,10 +6,10 @@
```bash
# Minimum: run tests directly related to the change
pytest tests/test_<domain>.py
uv run --locked --no-sync pytest tests/test_<domain>.py
# If the change affects common modules, startup flow, CLI, or agent runtime
pytest
uv run --locked --no-sync pytest
```
### When to Expand Scope
@@ -42,7 +42,7 @@ Run the full test suite when changing:
## Static Analysis
```bash
pylint app/
uv run --locked --no-sync pylint app/
```
- After any Python code change, ensure no new **error-level** pylint issues are introduced.
@@ -54,10 +54,10 @@ pylint app/
## Dependency Security Scan
```bash
safety check -r requirements.txt --policy-file=safety.policy.yml
uvx safety scan --target . --policy-file safety.policy.yml
```
- Run after runtime dependency changes; scan the development dependency entry as well when `requirements-dev.in` changes.
- Run manually after runtime or development dependency changes; Safety scans `pyproject.toml` and `uv.lock` directly, and this check is not currently an automated CI job.
- No new high-severity vulnerabilities may be introduced.
- If a vulnerability cannot be patched immediately, document it explicitly in the PR description.
@@ -131,11 +131,11 @@ Before marking any task as complete:
- [ ] Related pytest tests pass
- [ ] No new pylint error-level issues in `pylint app/`
- [ ] If dependencies changed: the package is in the correct runtime or dev dependency entry, and `safety check` passes for the affected entry
- [ ] If dependencies changed: the package is in the correct `pyproject.toml` group, `uv.lock` is current, locked sync and `uv pip check` pass, and the manual Safety scan passes
- [ ] If CLI behavior changed: `docs/cli.md` and related tests are updated
- [ ] If MCP/API behavior changed: `docs/mcp-api.md` and related skill files are updated
- [ ] If database schema changed: a new Alembic migration exists under `database/versions/`
- [ ] No secrets are included in code, logs, or committed files
- [ ] Public or cross-module contracts and non-obvious business behavior have useful Chinese documentation
*Last Updated: 2026-08-13*
*Last Updated: 2026-08-19*
@@ -101,10 +101,10 @@ ci: improve docker build cache
When updating a dependency:
1. Decide the dependency layer: runtime packages go to `requirements.in`; test, coverage, lint, and explicit build tooling go to `requirements-dev.in`.
2. Keep `requirements.txt` as the compatibility entry that delegates to `requirements.in`; do not commit a locally generated cross-platform lock file.
3. Run `safety check -r requirements.txt --policy-file=safety.policy.yml`; include the dev dependency entry when `requirements-dev.in` changed.
4. Run the full test suite: `pytest`.
1. Decide the dependency layer: runtime packages go to `[project].dependencies`; test, coverage, lint, and explicit build tooling go to `[dependency-groups].dev`.
2. Run `uv lock`, commit the updated `uv.lock`, and verify it with `uv lock --check`.
3. Run `uv sync --locked`, `uv pip check`, and the manual `uvx safety scan --target . --policy-file safety.policy.yml` check.
4. Run the full test suite: `uv run --locked --no-sync pytest`.
---
@@ -120,4 +120,4 @@ moviepilot update frontend
Bootstrap installer changes live in `scripts/bootstrap-local.sh`. Only modify this script if the task explicitly involves the bootstrap flow.
*Last Updated: 2026-05-25*
*Last Updated: 2026-08-19*
+1 -1
View File
@@ -29,7 +29,7 @@ chmod +x moviepilot-site-collector-linux
当前自动构建产物尚未接入 Windows 或 Apple 代码签名。Windows SmartScreen 或 macOS Gatekeeper 可能因此显示安全提示。仅在文件来自 MoviePilot 官方 GitHub Release,且校验摘要一致时运行;不要从聊天、网盘或第三方站点接收采集器。
如果系统阻止运行,可改用随 MoviePilot 源码提供的本地采集脚本;该方式需要 Python 3.11 及完整后端依赖,不适合作为普通用户的首选路径。
如果系统阻止运行,可改用随 MoviePilot 源码提供的本地采集脚本;该方式需要 Python 3.12+ 及完整后端依赖,不适合作为普通用户的首选路径。
## 维护者发布流程
+9 -8
View File
@@ -7,15 +7,16 @@
pytest 是唯一运行入口。`tests/conftest.py` 在收集前完成隔离引导,因此任何方式启动 pytest 都会自动隔离。
```bash
pytest tests # 全量
pytest tests/test_xxx.py # 单文件
pytest tests/test_xxx.py::SomeTest::test_y # 单用例
python tests/run.py # 等价于 pytest 全量(参数透传)
uv run --locked --no-sync pytest tests # 全量
uv run --locked --no-sync pytest tests/test_xxx.py # 单文件
uv run --locked --no-sync pytest tests/test_xxx.py::SomeTest::test_y # 单用例
uv run --locked --no-sync python tests/run.py # 等价于 pytest 全量(参数透传)
```
- 不再使用 `python -m unittest discover`:它不导入 `tests` 包、收不到纯函数用例,且绕过 `conftest.py` 的隔离。
- 不再依赖 `python tests/test_xxx.py` 直跑:所有 `if __name__ == "__main__": unittest.main()` 尾巴已移除。
- **复现 CI 用干净环境**建议用一个仅 `pip install -r requirements-dev.in` 的虚拟环境运行,避免本地额外包或编译产物掩盖问题。
- **复现 CI 用干净环境**使用 `uv sync --locked``uv.lock` 创建环境,再以
`uv run --locked --no-sync` 运行测试,避免本地额外包、未锁定解析结果或编译产物掩盖问题。
## 隔离模型(`tests/conftest.py`
@@ -138,6 +139,6 @@ def test_recognize_prefers_explicit_identity(sample_meta, monkeypatch):
## CI 与 PR
- **门禁**`.github/workflows/test.yml` 在指向 `v3` 的 `pull_request` / `push` 及手动触发时, `python tests/run.py` 跑全量单测。
- **PR**:产品代码、测试基础设施、依赖或运行行为发生变化时,运行 `python tests/run.py`,确认本次改动涉及的路径通过且 socket 探针零真实出站。若存在无关失败,必须在当前 `upstream/v3` 基线上独立复现并在 PR 中如实说明;不得静默扩大当前 PR 去修复基线问题。纯文档变更按实际内容执行文本、结构和 diff 检查,CI 仍会运行全量门禁。
- 复现 CI 用仅安装 `requirements-dev.in` 的干净环境;`requirements.in` 只承载运行时依赖,pytest 与覆盖率插件由开发依赖入口提供
- **门禁**`.github/workflows/test.yml` 在指向 `v3` 的 `pull_request` / `push` 及手动触发时, `uv.lock` 同步环境并用 `tests/run.py` 跑全量单测。
- **PR**:产品代码、测试基础设施、依赖或运行行为发生变化时,运行 `uv run --locked --no-sync python tests/run.py`,确认本次改动涉及的路径通过且 socket 探针零真实出站。若存在无关失败,必须在当前 `upstream/v3` 基线上独立复现并在 PR 中如实说明;不得静默扩大当前 PR 去修复基线问题。纯文档变更按实际内容执行文本、结构和 diff 检查,CI 仍会运行全量门禁。
- 复现 CI 使用 `uv sync --locked`;主程序运行依赖位于 `[project].dependencies`,pytest 与覆盖率工具位于默认 `dev` 依赖组