开发者技巧与窍门#

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

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

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

当对应的代码行在此期间发生更改时,GitHub 会隐藏 PR 上的讨论。这个用户脚本提供了一个快捷键(在撰写本文时为 Control-Alt-P,但请查看代码以确保),可以一次性展开所有此类隐藏的讨论,以便您可以及时了解情况。

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

在您的本地fork中,在.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未被正确捕获,我们的持续集成测试将出错,因此建议同时使用-Werror::FutureWarning标志运行pytest

审查的标准回复#

将其中一些存储在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 代码中很容易出现内存错误,尤其是在代码严重依赖指针运算的情况下。

内存错误可以以多种方式表现出来。最容易调试的通常是分段错误和相关的 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 特定错误或测试失败的说明。这基于使用 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(又名 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 生态系统的新工具。它被其他几个软件包使用,这些软件包已经编写了关于它是什么以及如何工作的良好指南。