开发者提示与技巧#

提高效率和保持理智的技巧#

本节收集了一些有用的建议和工具,它们可能会在您审查拉取请求、运行单元测试等方面提高您的生活质量。其中一些技巧是用户脚本,需要浏览器扩展,例如 TamperMonkeyGreaseMonkey;要设置用户脚本,您必须安装、启用并运行其中一个扩展。我们以 GitHub Gist 形式提供用户脚本;要安装它们,请点击 Gist 页面上的“Raw”按钮。

折叠和展开拉取请求中过时的差异#

当代码的相应行在此期间发生更改时,GitHub 会隐藏 PR 上的讨论。此用户脚本提供了一个快捷方式(撰写本文时是 Control-Alt-P,但请查看代码以确保)来一次性展开所有这些隐藏的讨论,以便您可以追溯查看。

将拉取请求检出为远程跟踪分支#

在您的本地分支中,将以下行添加到您的 .git/config 文件中的 [remote "upstream"] 标题下:

fetch = +refs/pull/*/head:refs/remotes/upstream/pr/*

然后您可以使用 git checkout pr/PR_NUMBER 导航到给定编号的拉取请求代码。(在此 gist 中阅读更多。

在拉取请求中显示代码覆盖率#

要叠加 CodeCov 持续集成生成的代码覆盖率报告,请考虑使用此浏览器扩展。每行的覆盖率将以行号后的颜色背景显示。

有用的 pytest 别名和标志#

完整的测试套件运行时间相当长。为了加快迭代速度,可以使用 pytest 选择器选择一部分测试。特别是,可以根据其节点 ID 运行单个测试

pytest -v sklearn/linear_model/tests/test_logistic.py::test_sparsify

或者使用 -k pytest 参数根据测试名称选择测试。例如,

pytest sklearn/tests/test_common.py -v -k LogisticRegression

将运行 LogisticRegression 估计器的所有通用测试

当单元测试失败时,以下技巧可以使调试更容易

  1. 命令行参数 pytest -l 指示 pytest 在发生故障时打印局部变量。

  2. 参数 pytest --pdb 在失败时进入 Python 调试器。要改为进入功能强大的 IPython 调试器 ipdb,您可以设置一个 shell 别名:

    pytest --pdbcls=IPython.terminal.debugger:TerminalPdb --capture no
    

其他可能有用的 pytest 选项包括

  • -x 在第一个失败的测试上退出,

  • --lf 重新运行上次运行中失败的测试,

  • --ff 重新运行所有先前的测试,首先运行失败的测试,

  • -s 以便 pytest 不捕获 print() 语句的输出,

  • --tb=short--tb=line 控制日志的长度,

  • --runxfail 也运行标记为已知失败(XFAIL)的测试并报告错误。

由于如果 FutureWarning 未正确捕获,我们的持续集成测试将报错,因此也建议将 pytest-Werror::FutureWarning 标志一起运行。

审查的标准回复#

将其中一些存储在 GitHub 的保存回复中可能对审查很有帮助

问题:使用问题

You are asking a usage question. The issue tracker is for bugs and new features. For usage questions, it is recommended to try [Stack Overflow](https://stackoverflow.com/questions/tagged/scikit-learn) or [the Mailing List](https://mail.python.org/mailman/listinfo/scikit-learn).

Unfortunately, we need to close this issue as this issue tracker is a communication tool used for the development of scikit-learn. The additional activity created by usage questions crowds it too much and impedes this development. The conversation can continue here, however there is no guarantee that it will receive attention from core developers.

问题:欢迎更新文档

Please feel free to offer a pull request updating the documentation if you feel it could be improved.

问题:用于 bug 的独立示例

Please provide [self-contained example code](https://scikit-learn.cn/dev/developers/minimal_reproducer.html), including imports and data (if possible), so that other contributors can just run it and reproduce your issue. Ideally your example code should be minimal.

问题:软件版本

To help diagnose your issue, please paste the output of:
```py
import sklearn; sklearn.show_versions()
```
Thanks.

问题:代码块

Readability can be greatly improved if you [format](https://help.github.com/articles/creating-and-highlighting-code-blocks/) your code snippets and complete error messages appropriately. For example:

    ```python
    print(something)
    ```

generates:

```python
print(something)
```

And:

    ```pytb
    Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
    ImportError: No module named 'hello'
    ```

generates:

```pytb
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
ImportError: No module named 'hello'
```

You can edit your issue descriptions and comments at any time to improve readability. This helps maintainers a lot. Thanks!

问题/评论:链接到代码

Friendly advice: for clarity's sake, you can link to code like [this](https://help.github.com/articles/creating-a-permanent-link-to-a-code-snippet/).

问题/评论:链接到评论

Please use links to comments, which make it a lot easier to see what you are referring to, rather than just linking to the issue. See [this](https://stackoverflow.com/questions/25163598/how-do-i-reference-a-specific-issue-comment-on-github) for more details.

PR-NEW:更好的描述和标题

Thanks for the pull request! Please make the title of the PR more descriptive. The title will become the commit message when this is merged. You should state what issue (or PR) it fixes/resolves in the description using the syntax described [here](https://scikit-learn.cn/dev/developers/contributing.html#contributing-pull-requests).

PR-NEW:修复 #

Please use "Fix #issueNumber" in your PR description (and you can do it more than once). This way the associated issue gets closed automatically when the PR is merged. For more details, look at [this](https://github.com/blog/1506-closing-issues-via-pull-requests).

PR-NEW 或问题:维护成本

Every feature we include has a [maintenance cost](https://scikit-learn.cn/dev/faq.html#why-are-you-so-selective-on-what-algorithms-you-include-in-scikit-learn). Our maintainers are mostly volunteers. For a new feature to be included, we need evidence that it is often useful and, ideally, [well-established](https://scikit-learn.cn/dev/faq.html#what-are-the-inclusion-criteria-for-new-algorithms) in the literature or in practice. Also, we expect PR authors to take part in the maintenance for the code they submit, at least initially. That doesn't stop you implementing it for yourself and publishing it in a separate repository, or even [scikit-learn-contrib](https://scikit-learn-contrib.github.io).

PR-WIP:合并前需要什么?

Please clarify (perhaps as a TODO list in the PR description) what work you believe still needs to be done before it can be reviewed for merge. When it is ready, please prefix the PR title with `[MRG]`.

PR-WIP:需要回归测试

Please add a [non-regression test](https://en.wikipedia.org/wiki/Non-regression_testing) that would fail at main but pass in this PR.

PR-MRG:耐心

Before merging, we generally require two core developers to agree that your pull request is desirable and ready. [Please be patient](https://scikit-learn.cn/dev/faq.html#why-is-my-pull-request-not-getting-any-attention), as we mostly rely on volunteered time from busy core developers. (You are also welcome to help us out with [reviewing other PRs](https://scikit-learn.cn/dev/developers/contributing.html#code-review-guidelines).)

PR-MRG:添加到新内容

Please add an entry to the future changelog by adding an RST fragment into the module associated with your change located in `doc/whats_new/upcoming_changes`. Refer to the following [README](https://github.com/scikit-learn/scikit-learn/blob/main/doc/whats_new/upcoming_changes/README.md) for full instructions.

PR:不要更改不相关的部分

Please do not change unrelated lines. It makes your contribution harder to review and may introduce merge conflicts to other pull requests.

调试 CI 问题#

CI 问题可能因多种原因而产生,因此这绝不是一份全面的指南,而是一系列有用的提示和技巧。

使用锁文件获取与 CI 接近的环境#

conda-lock 可用于创建与 CI 上完全相同的 conda 和 pip 包的 conda 环境。例如,以下命令将创建一个名为 scikit-learn-doc 的 conda 环境,该环境与 CI 类似

conda-lock install -n scikit-learn-doc build_tools/circle/doc_linux-64_conda.lock

注意

它仅在您拥有与 CI 构建相同的操作系统时才有效(检查锁文件中的 platform:)。例如,之前的命令仅在您使用 Linux 机器时才有效。此外,这可能不允许您重现一些更与 CI 环境特性相关的其他问题,例如 sklearn.show_versions() 中 OpenBLAS 报告的 CPU 架构。

如果您没有与 CI 构建相同的操作系统,您仍然可以从正确的环境 yaml 文件创建 conda 环境,尽管它不会像使用关联的锁文件那样接近 CI 环境。例如,对于文档构建

conda env create -n scikit-learn-doc -f build_tools/circle/doc_environment.yml -y

由于多种原因,这可能不会为您提供与 CI 中完全相同的软件包版本,例如

  • 从上次在 main 分支中更新锁文件到您运行 conda create 命令之间,某些软件包可能发布了新版本。您总是可以尝试查看锁文件中的版本,并为一些您认为有助于重现问题的特定软件包手动指定版本。

  • 默认安装的软件包可能因操作系统而异。例如,安装 numpy 时,Linux 上默认的 BLAS 库是 OpenBLAS,而 Windows 上是 MKL。

此外,问题可能与操作系统有关,因此能够重现的唯一方法是拥有与 CI 构建相同的操作系统。

使用 valgrind 调试 Cython 中的内存错误#

虽然 python/numpy 的内置内存管理相对健壮,但它可能导致某些例程的性能损失。因此,scikit-learn 中的许多高性能代码都是用 cython 编写的。然而,这种性能提升伴随着一个权衡:cython 代码中很容易出现内存 bug,尤其是在代码严重依赖指针算术的情况下。

内存错误可以以多种方式表现出来。最容易调试的通常是段错误和相关的 glibc 错误。未初始化的变量可能导致难以追踪的意外行为。调试这些类型错误时一个非常有用的工具是 valgrind

Valgrind 是一个命令行工具,可以跟踪各种代码中的内存错误。请按照以下步骤操作:

  1. 在您的系统上安装 valgrind

  2. 下载 python valgrind 抑制文件:valgrind-python.supp

  3. 按照 README.valgrind 文件中的说明自定义您的 python 抑制。如果您不这样做,将会出现与 python 解释器相关的虚假输出,而不是您自己的代码。

  4. 按如下方式运行 valgrind

    valgrind -v --suppressions=valgrind-python.supp python my_test_script.py
    

结果将是所有内存相关错误的列表,它们引用了 Cython 从 .pyx 文件生成的 C 代码中的行。如果您检查 .c 文件中引用的行,您会看到注释,指示您的 .pyx 源文件中的相应位置。希望输出能为您提供关于内存错误来源的线索。

有关 valgrind 及其各种选项的更多信息,请参阅 valgrind 网站上的教程和文档。

在 x86_64 机器上为 ARM64 平台构建和测试#

基于 ARM 的机器是移动、边缘或其他低能耗部署(包括在云端,例如 Scaleway 或 AWS Graviton)的常用目标。

以下是在 x86_64 主机笔记本电脑或工作站上设置本地开发环境以重现 ARM 特有 bug 或测试失败的说明。这基于使用 docker 进行 QEMU 用户模式仿真以方便(参见 multiarch/qemu-user-static)。

注意

以下说明以 ARM64 为例,但它们也适用于 ppc64le,只需适当更改 Docker 镜像和 Miniforge 路径即可。

在主机文件系统上准备一个文件夹,并下载必要的工具和源代码

mkdir arm64
pushd arm64
wget https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-aarch64.sh
git clone https://github.com/scikit-learn/scikit-learn.git

使用 docker 安装 QEMU 用户模式并运行 ARM64v8 容器,该容器可访问共享文件夹下的 /io 挂载点

docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker run -v `pwd`:/io --rm -it arm64v8/ubuntu /bin/bash

在容器中,为 ARM64 (a.k.a. aarch64) 架构安装 miniforge3

bash Miniforge3-Linux-aarch64.sh
# Choose to install miniforge3 under: `/io/miniforge3`

每当您重新启动一个新容器时,您都需要重新初始化之前安装在 /io/miniforge3 下的 conda 环境

/io/miniforge3/bin/conda init
source /root/.bashrc

因为 /root 主文件夹是临时 docker 容器的一部分。另一方面,存储在 /io 下的每个文件或目录都是持久的。

然后你可以像往常一样构建 scikit-learn(你需要像往常一样使用 apt 或 conda 安装编译器工具和依赖项)。构建 scikit-learn 需要大量时间,因为存在仿真层,但是如果你将 scikit-learn 文件夹放在 /io 挂载点下,它只需要完成一次。

然后使用 pytest 只运行您感兴趣的模块的测试。

Meson 构建后端#

自 scikit-learn 1.5.0 起,我们使用 meson-python 作为构建工具。Meson 是 scikit-learn 和 PyData 生态系统的新工具。它已被其他几个包使用,它们编写了关于 Meson 是什么以及如何工作的好指南。