From 3cd6aecd316f112e7a4e1f991fe7a05ed0563216 Mon Sep 17 00:00:00 2001 From: tiey Date: Wed, 27 May 2026 09:30:50 -0400 Subject: [PATCH 1/2] =?UTF-8?q?release(v0.15.1):=20=E4=B8=8A=E9=99=90=20ty?= =?UTF-8?q?per<0.26=EF=BC=88vendored=20click=20=E8=87=B4=20CLI=20=E6=95=B4?= =?UTF-8?q?=E6=A0=91=E6=9E=84=E5=BB=BA=E5=A4=B1=E8=B4=A5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Typer 0.26.0 把 click 硬 fork 成 vendored 副本(typer/_click/)并删掉对 独立 click 的依赖。本仓库 _DeepTradeGroup 继承 typer.core.TyperGroup,同时 db_cmd / 插件 pass-through 又直接用独立 click(click.Context / @click.command / @click.pass_context)。两个 click 是不同的类,Typer 构建命令树时识别不出 独立的 click.core.Context context 形参,报 "Type not yet supported: ",导致任意子命令启动即崩(用户实测 plugin upgrade)。 dev/CI 未暴露是因为 uv.lock 锁定 typer 0.25.0;只有 pipx 等按 typer>=0.12 重新解析的环境会拉到 0.26.2。 收紧约束为 typer>=0.12,<0.26,恢复 Typer 与仓库共用同一个 click 的不变量。 顺带修复 README 版本徽章(v0.15.0 漏更,仍停在 0.14.0),test_version_consistency 现已对齐 0.15.1。 Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 11 +++++++++++ README.md | 2 +- deeptrade/__init__.py | 2 +- pyproject.toml | 11 +++++++++-- uv.lock | 4 ++-- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6af6bf..742c732 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ All notable changes to DeepTrade. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and SemVer. +## [v0.15.1] — 2026-05-27 — 上限 typer<0.26(vendored click 导致 CLI 整树构建失败) + +修复一个安装期回归:`deeptrade` 任意命令启动即崩。**Typer 0.26.0 把 click 硬 fork 成自带的 vendored 副本**(`typer/_click/`),并从依赖里删掉了对独立 `click` 的声明。本仓库的 CLI 设计**同时**用了两边:`cli.py::_DeepTradeGroup` 继承 `typer.core.TyperGroup`(>=0.26 走 vendored click),而 `db_cmd(ctx: click.Context)`、插件 pass-through 的裸 `@click.command` 与 `@click.pass_context` 用的是独立 `click`。两个 click 是两个不同的类,Typer 构建命令树时用 `lenient_issubclass(param.annotation, typer._click.Context)` 识别 context 形参,独立的 `click.core.Context` 识别不出来 → 当成普通 CLI 参数 → `RuntimeError: Type not yet supported: `。这会让**整棵命令树**构建失败,任何子命令(用户实测 `plugin upgrade`)都进不去。 + +dev/CI 没暴露是因为 `uv.lock` 锁的是 typer **0.25.0**(最后一个未 vendor、仍 `click>=8.2.1` 的版本);只有 `pipx install` 这类按 `typer>=0.12` 重新解析的环境会拉到 0.26.2。 + +### Fixed + +- **`pyproject.toml` 把 `typer>=0.12` 收紧为 `typer>=0.12,<0.26`。** 恢复"Typer 与本仓库共用同一个 click"的不变量。这同时堵住一个潜伏的运行期 bug:插件分发(`deeptrade …`)从 Typer group 里返回的是独立 click 构造的命令,在 vendored-click Typer 下同样会行为异常。 +- 已有 pipx 安装的临时自救(无需等发布):`pipx runpip deeptrade-quant install "typer<0.26"` 把 venv 内的 typer 降回 0.25.x 即可恢复。 + ## [v0.15.0] — 2026-05-25 — yaml cache_overrides 自动注入(消除"声明僵尸文本") 修复一个跨插件的契约 bug:插件在 `deeptrade_plugin.yaml::permissions.tushare_apis.cache_overrides` 里声明了某个 Tushare API 的 cache class(例如 `cyq_perf: trade_day_mutable`),但 yaml 只参与安装期校验,**运行时并不会被框架自动注入到 TushareClient**。需要插件作者在调用 `make_tushare_client(cache_overrides=...)` 时显式传一遍同样的 dict —— 一旦插件绕开 helper、直接 `TushareClient(...)`(出于自定义 transport / event_cb / rps 的需要),yaml 声明就完全变成"僵尸文本",运行时仍会打 `Tushare API 'cyq_perf' has no explicit cache class; defaulting to trade_day_immutable` 的 INFO 警告,且实际使用的也是默认 `trade_day_immutable` 而非声明值。已知 `limit-up-board / accumulation-probe-washout / checkmate` 三个插件都中招。 diff --git a/README.md b/README.md index 299c986..2f11aa9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ > 📖 **在线文档**:[deeptrade.tiey.ai](https://deeptrade.tiey.ai) — 用户手册 + 开发者手册 + 官方插件目录 -[![tests](https://img.shields.io/badge/tests-passing-brightgreen)](#) [![python](https://img.shields.io/badge/python-3.11+-blue)](#) [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![version](https://img.shields.io/badge/version-0.14.0-blue)](CHANGELOG.md) +[![tests](https://img.shields.io/badge/tests-passing-brightgreen)](#) [![python](https://img.shields.io/badge/python-3.11+-blue)](#) [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![version](https://img.shields.io/badge/version-0.15.1-blue)](CHANGELOG.md) ## ✨ 主要特性 diff --git a/deeptrade/__init__.py b/deeptrade/__init__.py index a5fb871..916ea91 100644 --- a/deeptrade/__init__.py +++ b/deeptrade/__init__.py @@ -2,5 +2,5 @@ from __future__ import annotations -__version__ = "0.15.0" +__version__ = "0.15.1" __all__ = ["__version__"] diff --git a/pyproject.toml b/pyproject.toml index f6f3442..08ca7a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "deeptrade-quant" -version = "0.15.0" +version = "0.15.1" description = "LLM-driven A-share (Shanghai/Shenzhen main board) stock screening CLI" readme = "README.md" requires-python = ">=3.11" @@ -13,7 +13,14 @@ license = { text = "MIT" } keywords = ["stock", "a-share", "llm", "tushare", "deepseek", "duckdb", "cli"] dependencies = [ - "typer>=0.12", + # Cap below 0.26: Typer 0.26.0 hard-forked its own vendored copy of click + # (typer/_click/) and dropped the standalone `click` dependency. This repo + # subclasses typer.core.TyperGroup *and* uses standalone click directly + # (raw @click.command plugin pass-through, @click.pass_context, + # click.Context). That design assumes Typer and the app share ONE click; + # under >=0.26 the two click copies diverge and the command tree fails to + # build ("Type not yet supported: "). + "typer>=0.12,<0.26", "questionary>=2.0", "rich>=13.7", "duckdb>=1.0", diff --git a/uv.lock b/uv.lock index d1cea73..8de7828 100644 --- a/uv.lock +++ b/uv.lock @@ -315,7 +315,7 @@ wheels = [ [[package]] name = "deeptrade-quant" -version = "0.15.0" +version = "0.15.1" source = { editable = "." } dependencies = [ { name = "click" }, @@ -370,7 +370,7 @@ requires-dist = [ { name = "tenacity", specifier = ">=8.0" }, { name = "tushare", marker = "extra == 'dev'", specifier = ">=1.4" }, { name = "tushare", marker = "extra == 'plugin-runtime'", specifier = ">=1.4" }, - { name = "typer", specifier = ">=0.12" }, + { name = "typer", specifier = ">=0.12,<0.26" }, { name = "types-pyyaml", marker = "extra == 'dev'", specifier = ">=6.0" }, ] provides-extras = ["plugin-runtime", "dev"] From 570dbeef0366b16f49fb1d8c5e46691bee097e35 Mon Sep 17 00:00:00 2001 From: tiey Date: Wed, 27 May 2026 09:46:10 -0400 Subject: [PATCH 2/2] =?UTF-8?q?docs(release):=20README=20=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=BE=BD=E7=AB=A0=E7=BA=B3=E5=85=A5=E5=8F=91=E5=B8=83?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E6=B8=85=E5=8D=95=EF=BC=88=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E4=B8=89=E5=A4=84=E8=80=8C=E9=9D=9E=E4=B8=A4=E5=A4=84=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v0.15.1 发布时踩到:tests/test_version_consistency.py 还校验 README 徽章, 但 CLAUDE.md 只写了 pyproject + __init__.py 两处,导致 v0.15.0 把徽章漏在 0.14.0、本次 pytest 才暴露。补全为三处并更新 grep 命令与发布步骤。 Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d1c903d..43ed694 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,19 +24,20 @@ CI (`.github/workflows/ci.yml`) runs ruff → mypy → pytest → `python -m bui ## Releasing -The package version lives in **two** places and they MUST be bumped together: +The package version lives in **three** places and they MUST be bumped together: - `pyproject.toml::project.version` — what hatchling stamps onto the wheel filename and PyPI metadata. **This is the source of truth for the build.** - `deeptrade/__init__.py::__version__` — runtime introspection only. +- `README.md` version badge (`version-X.Y.Z-blue` in the shields.io URL) — display only, but **`tests/test_version_consistency.py` fails CI if it drifts** from the other two, so a stale badge blocks the release. -Bumping only `__init__.py` will silently produce a wheel with the previous version in its filename and tank the release at PyPI upload (HTTP 400 "File already exists" — PyPI filenames are immutable, you cannot overwrite). Always grep both files before tagging: +Bumping only `__init__.py` will silently produce a wheel with the previous version in its filename and tank the release at PyPI upload (HTTP 400 "File already exists" — PyPI filenames are immutable, you cannot overwrite). Forgetting the badge fails the test suite before you ever tag. Always grep all three before tagging: ```bash -grep -n version pyproject.toml deeptrade/__init__.py +grep -n version pyproject.toml deeptrade/__init__.py README.md ``` Release sequence: -1. Bump both files + update `CHANGELOG.md` in the same commit (or two adjacent commits). +1. Bump all three version sources + update `CHANGELOG.md` in the same commit (or two adjacent commits). 2. Push to main and verify CI green. 3. `git tag -a vX.Y.Z -m "..."` then `git push origin vX.Y.Z` — the tag push is what triggers `release.yml`.