chore: 初始化 MCM/ICM Toolkit 仓库骨架
审查并精简 ChatGPT 的建库建议后落地的第一版骨架,只定接口不写实现。 - 建议审查:14 个包 → 7 个;约 250 个函数 → 约 40 个;5 个 Result 类 → 1 个 (docs/01-suggestion-review.md) - docs/:架构、路线图(含永久不做清单)、环境、Git 工作流、函数卡片模板 - src/mcm_toolkit/:core / data / stats / models / solve / decision / viz 共 23 个模块,均为签名 + docstring + NotImplementedError - tests/:9 个文件,其中 test_api_surface.py 已通过(20 passed) - examples/ problems/ notebooks/ figures/ tables/ 骨架 - pyproject.toml(核心依赖 4 个 + 6 组 extras)、environment.yml - scripts/:setup-venv.ps1(ASCII + LF)、setup-conda.ps1、doctor.ps1 - 环境:.venv(system-site-packages)与 conda 环境 mcm-toolkit 均已建好, 数值栈版本与 base 完全对齐(numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6,MKL 后端)
This commit is contained in:
commit
5319589dfd
76 files changed
+6812
No files matched your search
@@ -0,0 +1,67 @@
|
||||
"""公开 API 契约测试。
|
||||
|
||||
这组测试**不测算法本身**,只守两件事:
|
||||
1. `__all__` 里承诺的名字,必须真的存在且可调用;
|
||||
2. 子包的导出必须与顶层一致。
|
||||
|
||||
它现在就能通过,并且在后续实现过程中会立刻抓到"改了导出忘了改 `__all__`"这类问题。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
|
||||
import pytest
|
||||
|
||||
import mcm_toolkit as mcm
|
||||
|
||||
SUBPACKAGES = ["core", "data", "stats", "models", "solve", "decision", "viz"]
|
||||
|
||||
|
||||
def test_version_is_string():
|
||||
assert isinstance(mcm.__version__, str)
|
||||
assert mcm.__version__.count(".") == 2
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", SUBPACKAGES)
|
||||
def test_subpackage_importable(name):
|
||||
mod = importlib.import_module(f"mcm_toolkit.{name}")
|
||||
assert hasattr(mod, "__all__"), f"mcm_toolkit.{name} 缺 __all__"
|
||||
assert hasattr(mcm, name), f"顶层未导出子包 {name}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", SUBPACKAGES)
|
||||
def test_subpackage_all_resolves(name):
|
||||
mod = importlib.import_module(f"mcm_toolkit.{name}")
|
||||
missing = [n for n in mod.__all__ if not hasattr(mod, n)]
|
||||
assert not missing, f"mcm_toolkit.{name}.__all__ 里有不存在的名字:{missing}"
|
||||
|
||||
|
||||
def test_toplevel_all_resolves():
|
||||
missing = [n for n in mcm.__all__ if not hasattr(mcm, n)]
|
||||
assert not missing, f"顶层 __all__ 里有不存在的名字:{missing}"
|
||||
|
||||
|
||||
def test_toplevel_shortcuts_are_callable():
|
||||
for name in ["monte_carlo", "bootstrap_ci", "parameter_sweep", "topsis", "compare_models"]:
|
||||
assert callable(getattr(mcm, name)), f"{name} 应当是可调用的快捷函数"
|
||||
|
||||
|
||||
def test_no_star_import_pollution():
|
||||
"""顶层不应把子包内部的函数全部摊平——只允许 __all__ 里那批。"""
|
||||
allowed = set(mcm.__all__) | {"annotations"} # annotations 来自 __future__ 导入
|
||||
leaked = [n for n in dir(mcm) if not n.startswith("_") and n not in allowed]
|
||||
assert not leaked, f"顶层出现了 __all__ 之外的公开名字:{leaked}"
|
||||
|
||||
|
||||
def test_result_dataclass_defaults():
|
||||
r = mcm.Result("demo")
|
||||
assert r.success is True
|
||||
assert r.params is None
|
||||
assert r.metrics is None
|
||||
assert r.message == ""
|
||||
|
||||
|
||||
def test_random_helpers_exist():
|
||||
assert callable(mcm.set_seed)
|
||||
assert callable(mcm.get_rng)
|
||||
Reference in new issue
Block a user