← 精品代码功能 · 可复用实现库

精品功能 13:可复现数据校验与自动判分(TODO 清单)

核心实现

带 sha256 的数据下载(datasets.py 第 121-148 行):

def fetch(key: str, log=print) -> Path:
    d = DATASETS[key]
    dest = path_of(key)
    if dest.is_file() and (not d.sha256 or sha256_of(dest) == d.sha256):
        log(f"  已有  {d.path}")
    else:
        part = dest.with_suffix(dest.suffix + ".part")     # 先下到 .part
        with urllib.request.urlopen(req, timeout=120) as resp, open(part, "wb") as out:
            shutil.copyfileobj(resp, out)
        got = sha256_of(part)
        if d.sha256 and got != d.sha256:                   # 校验
            part.unlink()                                  # 失败删除,不留坏文件
            raise RuntimeError(f"{d.path} 校验失败:期望 sha256={d.sha256},实际 {got}。\n"
                               "可能是网络中断或上游文件变了,重新运行一次;仍失败请告诉助手。")
        part.replace(dest)                                 # 校验通过才改名

TODO 清单机制(conftest.py 第 45-61 行)——最值得学的一段:

def pytest_runtest_makereport(item, call):
    # 模板里没填的函数会抛 NotImplementedError。记下来,结尾单独列一张"还没写"清单,
    # 一眼看出"是还没写"而不是"写错了"。
    if call.excinfo is not None and call.excinfo.errisinstance(NotImplementedError):
        item.user_properties.append(("todo", str(call.excinfo.value)))

def pytest_terminal_summary(terminalreporter):
    todos = []
    for rep in terminalreporter.stats.get("failed", []) + terminalreporter.stats.get("error", []):
        for key, value in getattr(rep, "user_properties", []):
            if key == "todo":
                todos.append((rep.nodeid.split("::")[-1], value))
    if todos:
        terminalreporter.section("还没写的 TODO(不是写错,是还没填)")
        for test, msg in todos:
            terminalreporter.write_line(f"  {test:<40} {msg}")

三态实现开关(course.py 第 32、43-50、96-107 行):

IMPL_DIRS = {"work": "交付", "ref": "参考解", "template": "模板"}

def load_module(n: int, impl: str):
    # 用 importlib 按路径加载指定实现,并起不同的模块名避免冲突
    ...

course check N --ref 应全过,--template 应全卡 TODO——课程开发者用它自检。

设计亮点

  1. 先 .part 后校验再改名:半截文件永不被当成正式数据(原子性,见第 19 课)
  2. 错误信息可操作:"重新运行一次;仍失败请告诉助手"
  3. TODO vs 写错:用 pytest 的 user_properties + terminal_summary 钩子,把

NotImplementedError 单独汇总——把产品体验做进测试输出

  1. 三态可切换:一套测试分别判"学生/参考/空模板",课程开发更稳
  2. 用文献已知答案断言:TP53 最长 ORF=393aa、PBMC 过滤后 2638 细胞等(可外部核对)
  3. Pytest 钩子:框架留扩展点,与事件总线同源

可复用性评估

开源化建议

对照开源

相关课程

第 20 课(数据分析实验工程化)