ci: add incremental strict type gate

This commit is contained in:
jxxghp
2026-08-21 22:21:23 +08:00
parent 43cccb8ff8
commit 59fa3c0e74
6 changed files with 136 additions and 8 deletions
+3
View File
@@ -50,6 +50,9 @@ jobs:
uv run --locked --no-sync python \
scripts/architecture/baseline.py --check-host
- name: Check governed Python types
run: uv run --locked --no-sync mypy --config-file mypy.ini
pytest:
runs-on: ubuntu-latest
name: Unit Tests (${{ matrix.shard }})
@@ -786,6 +786,17 @@ OTel 初始化只能位于 Startup/AdapterDomain/Application 只依赖 no-op-
4. CI 先检查严格目录;每次迁移扩大 include 范围。
5. 类型错误不能用无界 `Any``cast(Any, ...)` 或全文件 ignore 消音。
**实施记录(2026-08-21**
- 选定 mypy 1.18.x 并写入 `pyproject.toml`/`uv.lock`,仓库只保留这一套新增类型门禁;CI architecture job
使用锁定环境运行 `mypy --config-file mypy.ini`
- 首批 strict 清单包含 4 个已满足合同的 Domain value 文件、correlation/observation、Event Contract V2、
Module Contract V2、typed HostRuntime startup context 和 API context,共 10 个 canonical 文件。
- `mypy.ini` 不包含 `app.* = ignore_errors`、全文件 ignore、无界 `Any``cast(Any, ...)` 消音;历史
`domain/meta` 动态模型只有在逐文件修正后才可加入清单,当前错误不能被 baseline 当作“已通过”。
- 配置约束测试会检查 strict、关键合同文件和至少一个 Domain 文件均在清单中,并实际启动锁定版本 mypy;
当前 10 个源文件零错误通过。
#### ARCH-271:复杂度和端点预算 ratchet
**目标**:阻止大方法继续增长,并让拆分对应真实阶段,而不是机械 helper 化。
+19
View File
@@ -0,0 +1,19 @@
[mypy]
python_version = 3.12
strict = True
warn_unused_configs = True
ignore_missing_imports = True
follow_imports = skip
show_error_codes = True
pretty = True
files =
app/domain/episode.py,
app/domain/media.py,
app/domain/torrent.py,
app/domain/meta/infopath.py,
app/runtime/correlation.py,
app/runtime/observability/__init__.py,
app/runtime/event/contracts.py,
app/runtime/extensions/module/contracts.py,
app/startup/context.py,
app/api/context.py
+1
View File
@@ -103,6 +103,7 @@ dependencies = [
[dependency-groups]
dev = [
"cython~=3.2.5",
"mypy~=1.18.2",
"pylint~=4.0.6",
"pytest~=9.0.3",
"pytest-asyncio~=1.4.0",
+42
View File
@@ -0,0 +1,42 @@
"""渐进式 mypy 门禁配置与可执行性测试。"""
import configparser
import subprocess
import sys
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[1]
MYPY_CONFIG = PROJECT_ROOT / "mypy.ini"
def test_mypy_gate_has_explicit_strict_scope_without_global_ignore() -> None:
"""门禁必须列出治理文件,且不能用 app 级 ignore_errors 消音。"""
parser = configparser.ConfigParser()
parser.read(MYPY_CONFIG, encoding="utf-8")
settings = parser["mypy"]
governed_files = {
item.strip().rstrip(",")
for item in settings["files"].splitlines()
if item.strip()
}
assert settings.getboolean("strict") is True
assert "app/runtime/event/contracts.py" in governed_files
assert "app/runtime/extensions/module/contracts.py" in governed_files
assert "app/startup/context.py" in governed_files
assert "app/api/context.py" in governed_files
assert any(path.startswith("app/domain/") for path in governed_files)
assert "ignore_errors" not in MYPY_CONFIG.read_text(encoding="utf-8")
def test_mypy_governed_scope_passes() -> None:
"""使用锁定到开发依赖的 mypy 执行当前严格文件清单。"""
result = subprocess.run(
[sys.executable, "-m", "mypy", "--config-file", "mypy.ini"],
cwd=PROJECT_ROOT,
capture_output=True,
text=True,
check=False,
)
assert result.returncode == 0, result.stdout + result.stderr
Generated
+60 -8
View File
@@ -3,19 +3,19 @@ revision = 3
requires-python = ">=3.12"
resolution-markers = [
"python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux'",
"python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
"python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
"python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"python_full_version < '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'",
"python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'",
"python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'",
"python_full_version >= '3.14' and platform_machine == 'arm64' and sys_platform == 'darwin'",
"python_full_version == '3.13.*' and platform_machine == 'arm64' and sys_platform == 'darwin'",
"python_full_version < '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin'",
"python_full_version >= '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32'",
"python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'linux'",
"python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'",
"python_full_version == '3.13.*' and platform_machine == 'arm64' and sys_platform == 'darwin'",
"python_full_version == '3.13.*' and platform_machine == 'AMD64' and sys_platform == 'win32'",
"python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
"python_full_version < '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'",
"python_full_version < '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin'",
"python_full_version < '3.13' and platform_machine == 'AMD64' and sys_platform == 'win32'",
]
supported-markers = [
@@ -1860,6 +1860,7 @@ dependencies = [
[package.dev-dependencies]
dev = [
{ name = "cython" },
{ name = "mypy" },
{ name = "pylint" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
@@ -1968,6 +1969,7 @@ requires-dist = [
[package.metadata.requires-dev]
dev = [
{ name = "cython", specifier = "~=3.2.5" },
{ name = "mypy", specifier = "~=1.18.2" },
{ name = "pylint", specifier = "~=4.0.6" },
{ name = "pytest", specifier = "~=9.0.3" },
{ name = "pytest-asyncio", specifier = "~=1.4.0" },
@@ -2047,6 +2049,47 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b0/7a/620f945b96be1f6ee357d211d5bf74ab1b7fe72a9f1525aafbfe3aee6875/mutagen-1.47.0-py3-none-any.whl", hash = "sha256:edd96f50c5907a9539d8e5bba7245f62c9f520aef333d13392a79a4f70aca719", size = 194391, upload-time = "2023-09-03T16:33:29.955Z" },
]
[[package]]
name = "mypy"
version = "1.18.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mypy-extensions" },
{ name = "pathspec" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846, upload-time = "2025-09-19T00:11:10.519Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/07/06/dfdd2bc60c66611dd8335f463818514733bc763e4760dee289dcc33df709/mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34", size = 12908273, upload-time = "2025-09-19T00:10:58.321Z" },
{ url = "https://files.pythonhosted.org/packages/81/14/6a9de6d13a122d5608e1a04130724caf9170333ac5a924e10f670687d3eb/mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764", size = 11920910, upload-time = "2025-09-19T00:10:20.043Z" },
{ url = "https://files.pythonhosted.org/packages/5f/a9/b29de53e42f18e8cc547e38daa9dfa132ffdc64f7250e353f5c8cdd44bee/mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893", size = 12465585, upload-time = "2025-09-19T00:10:33.005Z" },
{ url = "https://files.pythonhosted.org/packages/77/ae/6c3d2c7c61ff21f2bee938c917616c92ebf852f015fb55917fd6e2811db2/mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914", size = 13348562, upload-time = "2025-09-19T00:10:11.51Z" },
{ url = "https://files.pythonhosted.org/packages/4d/31/aec68ab3b4aebdf8f36d191b0685d99faa899ab990753ca0fee60fb99511/mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8", size = 13533296, upload-time = "2025-09-19T00:10:06.568Z" },
{ url = "https://files.pythonhosted.org/packages/9f/83/abcb3ad9478fca3ebeb6a5358bb0b22c95ea42b43b7789c7fb1297ca44f4/mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074", size = 9828828, upload-time = "2025-09-19T00:10:28.203Z" },
{ url = "https://files.pythonhosted.org/packages/5f/04/7f462e6fbba87a72bc8097b93f6842499c428a6ff0c81dd46948d175afe8/mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc", size = 12898728, upload-time = "2025-09-19T00:10:01.33Z" },
{ url = "https://files.pythonhosted.org/packages/99/5b/61ed4efb64f1871b41fd0b82d29a64640f3516078f6c7905b68ab1ad8b13/mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e", size = 11910758, upload-time = "2025-09-19T00:10:42.607Z" },
{ url = "https://files.pythonhosted.org/packages/3c/46/d297d4b683cc89a6e4108c4250a6a6b717f5fa96e1a30a7944a6da44da35/mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986", size = 12475342, upload-time = "2025-09-19T00:11:00.371Z" },
{ url = "https://files.pythonhosted.org/packages/83/45/4798f4d00df13eae3bfdf726c9244bcb495ab5bd588c0eed93a2f2dd67f3/mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d", size = 13338709, upload-time = "2025-09-19T00:11:03.358Z" },
{ url = "https://files.pythonhosted.org/packages/d7/09/479f7358d9625172521a87a9271ddd2441e1dab16a09708f056e97007207/mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba", size = 13529806, upload-time = "2025-09-19T00:10:26.073Z" },
{ url = "https://files.pythonhosted.org/packages/71/cf/ac0f2c7e9d0ea3c75cd99dff7aec1c9df4a1376537cb90e4c882267ee7e9/mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544", size = 9833262, upload-time = "2025-09-19T00:10:40.035Z" },
{ url = "https://files.pythonhosted.org/packages/5a/0c/7d5300883da16f0063ae53996358758b2a2df2a09c72a5061fa79a1f5006/mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce", size = 12893775, upload-time = "2025-09-19T00:10:03.814Z" },
{ url = "https://files.pythonhosted.org/packages/50/df/2cffbf25737bdb236f60c973edf62e3e7b4ee1c25b6878629e88e2cde967/mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d", size = 11936852, upload-time = "2025-09-19T00:10:51.631Z" },
{ url = "https://files.pythonhosted.org/packages/be/50/34059de13dd269227fb4a03be1faee6e2a4b04a2051c82ac0a0b5a773c9a/mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c", size = 12480242, upload-time = "2025-09-19T00:11:07.955Z" },
{ url = "https://files.pythonhosted.org/packages/5b/11/040983fad5132d85914c874a2836252bbc57832065548885b5bb5b0d4359/mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb", size = 13326683, upload-time = "2025-09-19T00:09:55.572Z" },
{ url = "https://files.pythonhosted.org/packages/e9/ba/89b2901dd77414dd7a8c8729985832a5735053be15b744c18e4586e506ef/mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075", size = 13514749, upload-time = "2025-09-19T00:10:44.827Z" },
{ url = "https://files.pythonhosted.org/packages/25/bc/cc98767cffd6b2928ba680f3e5bc969c4152bf7c2d83f92f5a504b92b0eb/mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf", size = 9982959, upload-time = "2025-09-19T00:10:37.344Z" },
{ url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367, upload-time = "2025-09-19T00:10:15.489Z" },
]
[[package]]
name = "mypy-extensions"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
]
[[package]]
name = "numpy"
version = "2.5.2"
@@ -2225,6 +2268,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/6f/c5/7c16e99869e1f422629092cfd23e3b58e461988c3f9c36fd3624bb4142e6/parse-1.22.1-py2.py3-none-any.whl", hash = "sha256:20f0925a46f06602485ac90d751764d0697fd8455aaa97489ba8953a4b66de32", size = 20925, upload-time = "2026-05-26T03:44:51.156Z" },
]
[[package]]
name = "pathspec"
version = "1.1.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" },
]
[[package]]
name = "pillow"
version = "12.2.0"