.. _developers-tips: =========================== 开发者技巧与窍门 =========================== 提高生产力和保持理智的技巧 ======================================= 在本节中,我们收集了一些有用的建议和工具,这些建议和工具可能会在你审查拉取请求、运行单元测试等方面提高你的生活质量。其中一些技巧包括需要浏览器扩展的用户脚本,例如 `TamperMonkey`_ 或 `GreaseMonkey`_ ;要设置用户脚本,你必须安装、启用并运行其中一个扩展。我们以 GitHub gists 的形式提供用户脚本;要安装它们,请点击 gist 页面上的“Raw”按钮。 .. _TamperMonkey: https://tampermonkey.net/ .. _GreaseMonkey: https://www.greasespot.net/ 在拉取请求中折叠和展开过时的差异 ----------------------------------------------------- 当相应的代码行在期间被更改时,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_tips: 有用的 pytest 别名和标志 ------------------------ 完整的测试套件运行时间相当长。为了更快地迭代,可以使用 pytest 选择器选择测试的子集。特别是,可以基于其节点 ID 运行 `单个测试 `_ : .. prompt:: bash $ pytest -v sklearn/linear_model/tests/test_logistic.py::test_sparsify 或者使用 `-k pytest 参数 `_ 根据测试名称选择测试。例如: .. prompt:: bash $ pytest sklearn/tests/test_common.py -v -k LogisticRegression 将运行 ``LogisticRegression`` 估计器的所有 :term:`common tests` 。 当单元测试失败时,以下技巧可以使调试更容易: 1. 命令行参数 ``pytest -l`` 指示 pytest 在失败时打印局部变量。 2. 参数 ``pytest --pdb`` 在失败时进入 Python 调试器。要改为进入丰富的 IPython 调试器 ``ipdb`` ,可以设置一个 shell 别名: .. prompt:: bash $ 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`` 标志。 .. _saved_replies: 评审的标准回复 ---------------- 在GitHub的 `保存回复 `_ 中存储一些这些回复可能会有所帮助: .. highlight:: none .. 注意,将此内容放在文字块的单行中是最容易使其可复制并在屏幕上显示的方式。 问题:使用问题 :: 您正在提出一个使用问题。问题跟踪器用于错误和新功能。对于使用问题,建议尝试[Stack Overflow](https://stackoverflow.com/questions/tagged/scikit-learn)或[邮件列表](https://mail.python.org/mailman/listinfo/scikit-learn)。 不幸的是,我们需要关闭此问题,因为此问题跟踪器是用于scikit-learn开发的沟通工具。使用问题产生的额外活动使其过于拥挤,并阻碍了这一开发。然而,对话可以在这里继续,但不能保证核心开发者会关注它。 问题:欢迎更新文档 :: 如果您觉得可以改进文档,请随时提供一个更新文档的拉取请求。 问题:自包含的错误示例 :: 请提供[自包含的示例代码](https://scikit-learn.org/dev/developers/minimal_reproducer.html),包括导入和数据(如果可能),以便其他贡献者可以直接运行并重现您的问题。理想情况下,您的示例代码应该是最小的。 问题:软件版本 :: 为了帮助诊断您的问题,请粘贴以下输出: py import sklearn; sklearn.show_versions() 谢谢。 问题:代码块 :: 如果您[格式化](https://help.github.com/articles/creating-and-highlighting-code-blocks/)您的代码片段和完整的错误消息,可读性可以大大提高。例如: python print(something) 生成: python print(something) 并且: pytb Traceback (most recent call last): File "", line 1, in ImportError: No module named 'hello' 生成: pytb Traceback (most recent call last): File "", line 1, in ImportError: No module named 'hello' 您可以随时编辑您的议题描述和评论,以提高可读性。这对维护者非常有帮助。谢谢! 议题/评论:链接到代码 :: 友好建议:为了清晰起见,您可以像[这样](https://help.github.com/articles/creating-a-permanent-link-to-a-code-snippet/)链接到代码。 议题/评论:链接到评论 :: 请使用链接到评论,这样更容易看到您所指的内容,而不是仅仅链接到议题。更多详情请参见[这里](https://stackoverflow.com/questions/25163598/how-do-i-reference-a-specific-issue-comment-on-github)。 PR-NEW:更好的描述和标题 :: 感谢您的拉取请求!请使PR的标题更具描述性。标题将在合并时成为提交消息。您应该在描述中使用[这里](https://scikit-learn.org/dev/developers/contributing.html#contributing-pull-requests)描述的语法说明它修复/解决了哪个议题(或PR)。 PR-NEW:修复 # :: 请在您的PR描述中使用“Fix #issueNumber”(您可以多次使用)。这样,当PR合并时,相关议题会自动关闭。更多详情请参见[这里](https://github.com/blog/1506-closing-issues-via-pull-requests)。 PR-NEW 或 议题:维护成本 :: 我们包含的每个功能都有[维护成本](https://scikit-learn.org/dev/faq.html#为什么你们对包含的算法如此挑剔)。我们的维护者大多是志愿者。对于一个新功能来说,要被包含在内,我们需要证据证明它经常有用,理想情况下,在文献或实践中[已经确立](https://scikit-learn.org/dev/faq.html#新算法包含的标准是什么)。此外,我们期望PR作者至少在最初阶段参与他们提交代码的维护。这并不妨碍你自己实现它并在一个单独的仓库中发布,甚至是在[scikit-learn-contrib](https://scikit-learn-contrib.github.io)中。 PR-WIP: 合并前需要什么? :: 请澄清(可能在PR描述中作为一个TODO列表)你认为在可以审查合并之前还需要完成哪些工作。当它准备好时,请在PR标题前加上 `[MRG]` 。 PR-WIP: 需要回归测试 :: 请添加一个[非回归测试](https://en.wikipedia.org/wiki/Non-regression_testing),该测试在主分支上会失败,但在这个PR中会通过。 PR-WIP: PEP8 :: 你有一些[PEP8](https://www.python.org/dev/peps/pep-0008/)违规,你可以在Circle CI的 `lint` 作业中看到详细信息。配置你的代码编辑器实时检查这类错误可能是有价值的,这样你可以在提交之前捕获它们。 PR-MRG: 耐心 :: 在合并之前,我们通常需要两位核心开发者同意你的拉取请求是可取且准备好的。[请耐心等待](https://scikit-learn.org/dev/faq.html#为什么我的拉取请求没有得到任何关注),因为我们主要依赖忙碌的核心开发者的志愿时间。(你也很欢迎帮助我们[审查其他PR](https://scikit-learn.org/dev/developers/contributing.html#代码审查指南)。) PR-MRG: 添加到新功能介绍 :: 请在 `doc/whats_new/v*.rst` 的变更日志中添加一条记录。与其他记录一样,请使用 `:pr:` 引用此拉取请求,并使用 `:user:` 给自己(以及适用的其他贡献者)记功。 PR: 不要更改无关内容 :: 请不要更改无关的行。这会使您的贡献更难以审查,并可能引入与其他拉取请求的合并冲突。 .. highlight:: default 使用 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: .. prompt:: bash $ valgrind -v --suppressions=valgrind-python.supp python my_test_script.py .. _valgrind: https://valgrind.org .. _ `README.valgrind` : https://github.com/python/cpython/blob/master/Misc/README.valgrind .. _ `valgrind-python.supp` : https://github.com/python/cpython/blob/master/Misc/valgrind-python.supp 结果将是一个包含所有内存相关错误的列表,这些错误引用由 Cython 从你的 .pyx 文件生成的 C 代码中的行。如果你检查 .c 文件中引用的行,你会看到指示相应位置在你的 .pyx 源文件中的注释。希望输出能给你关于内存错误来源的线索。 有关 Valgrind 及其众多选项的更多信息,请参阅 `Valgrind 网站 `_ 上的教程和文档。 .. _arm64_dev_env: 在 x86_64 机器上为 ARM64 平台构建和测试 =============================================================== 基于 ARM 的机器是移动、边缘或其他低能耗部署(包括在云中,例如在 Scaleway 或 AWS Graviton 上)的流行目标。 以下是设置本地开发环境以在 x86_64 主机笔记本电脑或工作站上重现 ARM 特定错误或测试失败的说明。这是基于使用 Docker 进行方便的 QEMU 用户模式仿真(参见 https://github.com/multiarch/qemu-user-static)。 .. note:: 以下说明以 ARM64 为例,但它们也适用于 ppc64le,只需相应更改 Docker 镜像和 Miniforge 路径。 在主机文件系统上准备一个文件夹并下载必要的工具和源代码: .. prompt:: bash $ 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 用户模式并在 `/io` 挂载点下访问共享文件夹的情况下运行 ARM64v8 容器: .. prompt:: bash $ 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: .. prompt:: bash $ bash Miniforge3-Linux-aarch64.sh # 选择在 `/io/miniforge3` 下安装 miniforge3 每次重启新容器时,您需要重新初始化之前安装在 `/io/miniforge3` 下的 conda 环境: .. prompt:: bash $ /io/miniforge3/bin/conda init source /root/.bashrc 因为 `/root` 主文件夹是临时 Docker 容器的一部分。另一方面,存储在 `/io` 下的每个文件或目录都是持久的。 然后,您可以像往常一样构建 scikit-learn(您需要像往常一样使用 apt 或 conda 安装编译器工具和依赖项)。由于模拟层的原因,构建 scikit-learn 需要很长时间,但如果您将 scikit-learn 文件夹放在 `/io` 挂载点下,则只需执行一次。 然后使用 pytest 仅运行您感兴趣调试的模块的测试。 .. _meson_build_backend: Meson 构建后端 ================ 自 scikit-learn 1.5.0 起,我们使用 meson-python 作为构建工具。Meson 是 scikit-learn 和 PyData 生态系统的新工具。它被其他几个编写了关于它是什么以及如何工作的优秀指南的包所使用。 - `pandas 设置文档 `_ : pandas 的设置与我们的类似(没有 spin 或 dev.py) - `scipy Meson 文档 `_ 提供了更多关于 Meson 幕后工作原理的背景信息