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/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`. 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"]