首页
/ Qlib 代码规范与开发指南:Numpydoc、CI 静态检查与可编辑安装全流程

Qlib 代码规范与开发指南:Numpydoc、CI 静态检查与可编辑安装全流程

2026-09-05 20:56:53作者:明树来

本文基于 Qlib 官方开发者文档 docs/developer/code_standard_and_dev_guide.rst 展开,系统梳理 Qlib 项目的代码标准(Docstring 规范)与开发环境搭建方法。读完本篇,你将能够:按 Qlib 的 Numpydoc 风格编写函数文档;完整复现 Qlib 持续集成(CI)中 black、pylint、flake8 等静态检查命令并在本地修复风格问题;通过 pre-commit 钩子实现提交前自动格式化;以及使用可编辑安装模式(editable install)进行本地二次开发。

一、代码标准(Code Standard)

Qlib 对代码质量的第一道要求来自文档:项目中所有公开函数/方法应遵循 Numpydoc Style 风格的 Docstring,即采用 Parameters / Returns / Examples 等分节描述参数的结构化文档风格。这种风格对 Sphinx 自动生成的 API 文档(见 docs/reference/api.rst)友好,也是 Qlib 文档站构建(make docs-gen,对应 Sphinx 构建 docs/ 目录)能够呈现一致排版的前提。

以一个典型的 Numpydoc 分节为例,函数文档应包含:

  • Parameters:每个参数名、类型与含义逐行说明;
  • Returns:返回值类型与含义;
  • Examples:可运行的调用示例(Qlib 的 notebook 示例会被 CI 执行,见下文 nbqa/nbconvert 检查)。

二、持续集成(CI):每次提交如何被校验

Qlib 的 CI 会在每次 push 或 PR 时对代码运行静态检查与单元测试,结果反馈在 PR 页面的 "check" 区域。当前仓库的 CI 定义在 .github/workflows/test_qlib_from_source.yml 中,其在矩阵化环境(Windows / Ubuntu / macOS,Python 3.8–3.12)上依次执行:

  1. make dev 安装开发依赖;
  2. make blackmake pylintmake flake8make mypymake nbqa 静态检查;
  3. 下载测试数据后用 jupyter nbconvert --execute 执行 notebook(make nbconvert);
  4. tests/ 目录下运行 python -m pytest . -m "not slow" --durations=0 单元测试。

以下按原文档的 4 个检查项逐一展开,并给出 Makefile 中实际执行的命令细节。

2.1 Black 格式检查

原文档给出:若 PR 未通过 black 检查(常见错误是 space 与 tab 混用),执行:

pip install black
python -m black . -l 120

Makefile 看,CI 实际执行的检查命令是:

black . -l 120 --check --diff --exclude qlib/_version.py

即行宽限制为 120 列,且排除自动生成文件 qlib/_version.py(由 setuptools-scm 在构建时写入,见 pyproject.tomlwrite_to = "qlib/_version.py")。本地修复时运行不带 --check 的格式化命令即可自动改写文件。

此外,Qlib 还会用 nbqa 对 Jupyter notebook 做同样的 black 检查(nbqa black . -l 120 --check --diff),保证 examples/ 下的 notebook 代码同样符合格式标准。

2.2 Pylint 风格检查

Qlib 使用 pylint 做较深入的静态分析。原文档指出:当 pylint 的某些限制不够合理时,可以用行内注释忽略特定错误,例如:

return -ICLoss()(pred, target, index)  # pylint: disable=E1130

Makefile 的实际命令可以看到,Qlib 的 pylint 检查并非"零容忍",而是通过 --disable= 关闭了大批已知历史问题码(如 C0103 invalid-name、W0212E1102 等),并通过 --const-rgx='[a-z_][a-z0-9_]{2,30}' 规定常量命名规范;同时 --init-hook 中调高了 astroid 推断上限(max_inferred = 500)与递归限制,以处理大型 DataFrame 类型推断场景。pylint 对 qlibscripts 两个目录分别执行检查。

项目根目录的 .pylintrc 还声明了:

[TYPECHECK]
generated-members=numpy.*, torch.*

这解释了为什么 Qlib 源码中可以放心地动态访问 numpy/torch 属性而不触发 E1101 类误报——这也与 Qlib 大量使用这两个库的数据/模型代码结构相一致。

2.3 Flake8 检查

原文档给出的本地修复命令是:

flake8 --ignore E501,F541,E402,F401,W503,E741,E266,E203,E302,E731,E262,F523,F821,F811,F841,E713,E265,W291,E712,E722,W293 qlib

而从 Makefile 看,CI 上实际执行的命令为:

flake8 --ignore=E501,F541,E266,E402,W503,E731,E203 --per-file-ignores="__init__.py:F401,F403" qlib

两者核心忽略项一致(行宽 E501 由 black 统一管,F401 未使用导入等),CI 版本额外用 --per-file-ignores 单独豁免 __init__.py 中的 F401/F403(re-export 惯用法)。开发者日常以 Makefile 版本为准即可:make flake8

2.4 补充:mypy 与 pre-commit

除原文档列出的三项外,从 Makefile.github/workflows/test_qlib_from_source.yml 看,Qlib 的完整 lint 链还包括 mypy 类型检查(聚合目标为 lint: black pylint flake8 mypy nbqa)。.mypy.ini 中可以看到 Qlib 采用了渐进式类型化策略:

[mypy]
exclude = (?x)(
    ^qlib/backtest/high_performance_ds\.py$
    | ^qlib/contrib
    | ^qlib/data
    ...
)
ignore_missing_imports = true
disallow_incomplete_defs = true
follow_imports = skip

qlib/contribqlib/dataqlib/workflow 等目录暂不强制类型检查,ignore_missing_imports 则容忍第三方库缺失类型存根。这意味着新贡献代码时,类型检查压力主要集中在核心框架目录,而非全部模块。

2.5 pre-commit:提交前自动格式化

原文档推荐安装 pre-commit 让 git commit 时自动执行 black 与 flake8:

pip install -e .[dev]
pre-commit install

仓库中对应的配置文件是 .pre-commit-config.yaml,其中固定了工具版本与参数:

repos:
-   repo: https://github.com/psf/black
    rev: 23.7.0
    hooks:
    -   id: black
        args: ["qlib", "-l 120"]

-   repo: https://github.com/PyCQA/flake8
    rev: 4.0.1
    hooks:
        - id: flake8
          args: ["--ignore=E501,F541,E266,E402,W503,E731,E203"]

注意两点:钩子只对 qlib 目录生效,行宽参数 -l 120 与 Makefile 中的 black 检查保持一致;工具通过 rev 锁定版本(black 23.7.0、flake8 4.0.1),保证团队间格式化行为一致。

三、开发指南(Development Guidance)

作为开发者,你通常希望修改 Qlib 后立即在环境中生效而无需重装。原文档给出的方案是可编辑安装:

pip install -e ".[dev]"

结合 pyproject.toml 可以确认各选项的实际内容:

  • [dev] 附加依赖:pyteststatsmodels
  • 仓库还提供其他开发相关附加依赖:[lint](black、pylint、mypy<1.5.0、flake8、nbqa)、[docs](sphinx、sphinx_rtd_theme 等)、[package](twine、build)、[test](yahooquery、baostock)、[analysis](plotly、statsmodels)等;
  • 安装时 setup.py 会用 Cython 编译两个 C++ 扩展模块 qlib.data._libs.rollingqlib.data._libs.expanding(源码为 qlib/data/_libs/rolling.pyxqlib/data/_libs/expanding.pyx),因此可编辑安装环境需要可用的 C++ 编译器

安装完成后,日常开发循环建议如下(均以仓库根目录为起点):

# 1. 开发依赖 + 本地钩子
pip install -e ".[dev]"
pre-commit install

# 2. 修改 qlib/ 下任意源码后立即生效,无需重装

# 3. 提交前本地跑完整 lint 链
make lint        # 等价于 black pylint flake8 mypy nbqa

# 4. 运行单元测试(跳过标记为 slow 的测试)
cd tests && python -m pytest . -m "not slow" --durations=0

pytest 的标记定义在 tests/pytest.inislow 标记用于排除耗时测试(CI 正是用 -m "not slow" 与之配合);若需完整覆盖,去掉该过滤参数即可。

四、常见检查项速查表

检查工具 本地执行 关键参数(源自 Makefile / 配置文件) 作用
black make black -l 120 --check --diff --exclude qlib/_version.py 代码格式,120 列行宽
pylint make pylint 大量 --disable 历史问题码;--const-rgx='[a-z_][a-z0-9_]{2,30}';对 qlibscripts 分别检查 深层风格与潜在缺陷
flake8 make flake8 --ignore=E501,F541,E266,E402,W503,E731,E203 --per-file-ignores="__init__.py:F401,F403" 轻量风格检查
mypy make mypy .mypy.ini 的 exclude 与 ignore_missing_imports 渐进式类型检查
nbqa / nbconvert make nbqa / make nbconvert nbqa black . -l 120 --check --diff notebook 代码检查与执行验证
pytest cd tests && python -m pytest . -m "not slow" 标记定义见 tests/pytest.ini 单元测试

五、小结

Qlib 的开发标准可以归纳为三条主线:文档层面采用 Numpydoc 风格 Docstring;质量层面由 black(格式)、pylint(深度风格)、flake8(轻量风格)、mypy(渐进类型化)、nbqa/nbconvert(notebook)与 pytest 组成的 CI 链条把关,命令均以 Makefile 中的目标为权威实现;开发层面通过 pip install -e ".[dev]" 可编辑安装加 pre-commit 钩子实现"改完即生效、提交前自动格式化"的高效循环。遵循这些约定,你的改动在合入前就能与 Qlib 上游 CI 的行为保持一致。

登录后查看全文
热门项目推荐
相关项目推荐