开发工作流程¶
注意
本指南适用于已熟悉在 GitHub 上为开源项目做出贡献的人员。如果您不熟悉 GitHub,请先阅读 设置开发环境 指南。
贡献清单¶
以下是在向 SymPy 发起拉取请求时需要完成的清单。在合并拉取请求之前,需要完成所有这些操作。在打开拉取请求之前,或者甚至在提交更改之前,不必完成所有这些操作,但通常最好先检查基本事项,然后再打开拉取请求,或者甚至在提交更改之前。
确保 代码质量检查 通过。
./bin/test quality flake8 sympy/
添加测试. 所有新功能都应该进行测试。错误修复应该添加回归测试。测试使用 pytest
assert f(x) == y
样式编写,并包含在sympy/
源代码中的相应tests
目录中。请参阅关于 编写测试 的指南。新的公共函数和方法应该具有文档字符串。
文档字符串应该包含 doctest.
确保所有测试都通过。 你可能想在提交之前在本地运行相关的测试套件子集(例如,
./bin/test solvers
)。当你打开一个 pull request 时,所有测试将在 CI 上运行。在 PR 可以合并之前,CI 必须全部变为绿色。(首次贡献者)将您的姓名添加到
.mailmap
文件中。如果未正确执行此操作,GitHub 上的“测试/作者”CI 构建将会失败。在 pull request 描述中交叉引用相关问题。如果 pull request 修复了某个问题(即,PR 合并后应该关闭该问题),请使用 “fixes #123” 语法 。
在原始问题中添加一个评论,交叉引用 pull request 以提高可见度。如果没有相应的 issue,这也没关系。除非你的 PR 之外还需要进一步改进,否则没有必要打开 issue。
添加发布说明条目。这应该在打开 pull request 时在 pull request 描述字段中完成。它可以在 pull request 合并之前随时编辑。
回复审查意见。所有 SymPy pull request 必须在合并之前由其他人进行审查。
选择要修复的问题¶
开始使用主代码库的最佳方法是修复一些现有错误。仔细阅读问题跟踪器中的 “易于修复”问题,看看是否有您感兴趣的问题。如果您想尝试修复它,请在 issue 中创建一条消息,说明您想处理它。如果不清楚如何修复它,请在 issue 本身或 邮件列表 上寻求修复方法的建议。
SymPy 的代码被组织成 Python 包和模块。核心代码位于 sympy/core
目录中,sympy 目录中的其他包包含更具体的代码。例如,sympy/printing
包含处理如何将 SymPy 对象打印到终端和 Jupyter 的代码。
如果您进行的更改还没有问题,则不需要先打开问题。只有当您觉得需要在创建 pull request 之前讨论更改时,这才是必需的,例如,如果您不确定某个问题是否真的是错误,或者您不确定某个新功能是否在范围之内。直接打开 pull request 并进行讨论也是可以的。当有实际的代码可以讨论时,讨论更容易进行,因此,如果您有更改,即使它们尚未完全准备好合并,也最好创建 pull request。
创建一个新分支¶
在对代码进行更改之前,首先要做的是在 git 中创建一个分支。
记住,永远不要提交到 master
。 master
只能用于从主 sympy/sympy 仓库中拉取上游更改。如果您提交到 master
,将很难拉取这些更改,如果您希望一次创建多个 pull request,也会很困难。
首先选择您的分支的名称。请参见下面的 分支名称。要创建和检出(即使其成为工作分支)一个新分支,请运行
# Pull any upstream changes from the main SymPy repo first
git checkout master
git pull
git branch <your-branch-name>
git checkout <your-branch-name>
最后两条命令也可以组合成一条命令
git checkout -b <your-branch-name>
要查看所有分支,并将当前分支突出显示,请键入
git branch
请记住,永远不要在 master 中键入以下命令:git merge
,git add
,git commit
,git rebase
。如果您不小心在本地 master 中提交了一些更改,则必须进行硬重置以丢弃这些提交。
分支名称¶
使用一个简短易于键入的分支名称,该名称在某种程度上与更改相关。请记住,希望尝试您的代码的开发人员需要在命令行中键入您的分支名称才能执行此操作。
避免在分支名称中使用 issue 编号,因为这些编号不易键入(大多数 SymPy issue 编号都是 5 位数),而且在不查找 issue 的情况下,它们无法真正反映更改的内容。
一些好的分支名称示例是
fix-solve-bug
typo-fix
core-improvements
add-simplify-tests
最终,分支名称并不那么重要,因此不要花太多时间考虑它。它的唯一功能是将此贡献的代码与您可能进行的其他贡献区分开来。
修改代码¶
在进行修复时,请记住,每个贡献都应该遵循一些要求
代码质量¶
SymPy 贡献必须具有足够的代码质量才能被接受。有一些代码质量检查将在您创建 pull request 后自动在 CI 上运行,但您也可以使用以下命令在本地运行它们:
./bin/test quality
flake8 sympy/
此外,所有测试都必须通过。CI 将自动运行测试,但您也可以 自己运行它们。建议在提交之前至少运行与您修改的代码相关的测试,以确保您没有犯任何错误或意外破坏了某些内容。
提交 pull request 后,您应该在检查完成后检查 GitHub Actions 检查,以查看是否存在任何测试失败。如果存在,则需要在接受 pull request 之前修复它们。
添加测试¶
所有新功能都应该进行测试。如果您修复了错误,则应该附带一个回归测试。也就是说,在修复错误之前会失败但在现在会通过的测试。通常,您可以使用 issue 中的代码示例作为测试用例,但也可以简化这些示例或编写您自己的示例,只要它测试了相关问题即可。
测试位于代码旁边的 tests/
目录中,文件名为 test_<thing>.py
。在大多数情况下,如果您修改了 sympy/<submodule>/<file>.py
,则该功能的测试将放在 sympy/<submodule>/tests/test_<file>.py
中。例如,sympy/simplify/sqrtdenest.py
中函数的测试位于 sympy/simplify/tests/test_sqrtdenest.py
中。此规则有一些例外,因此通常尝试找到某个函数的现有测试位置,并将您的测试添加在其旁边。
测试遵循一个简单的模式,从阅读现有的测试文件应该可以看出。测试位于以 test_
开头的函数中,并包含类似以下内容的行:
assert function(arguments) == result
例如
# from sympy/functions/elementary/tests/test_trigonometric.py
def test_cos_series():
assert cos(x).series(x, 0, 9) == \
1 - x**2/2 + x**4/24 - x**6/720 + x**8/40320 + O(x**9)
如果相关,可以将新的测试用例添加到现有的测试函数中,或者您可以创建新的测试函数。
文档¶
所有新的方法、函数和类都应该有一个 docstring,说明如何使用它们。docstring 是一个三引号字符串,紧随 def
行,描述该函数。docstring 应遵循 Docstring 样式指南 中概述的格式。
每个 docstring 中应该包含的一件重要的事情是示例。示例也称为doctest,因为它们使用 bin/doctest
脚本进行测试,以确保它们的输出是正确的。
Doctest 需要包含对每个使用的函数的导入,并定义任何使用的符号。用户应该能够将示例输入复制粘贴到自己的 Python 会话中,并获得完全相同的输出。 from sympy import *
在 doctest 中是不允许的,因为这会导致不清楚哪些函数来自 SymPy。
docstring 样式指南 详细介绍了如何在 docstring 中格式化示例。
请记住,doctest 不是 测试。可以将它们视为碰巧经过测试的示例。一些关键区别
编写 doctest 以使其具有信息量;编写常规测试以检查回归和极端情况。
doctest 可以随时更改;常规测试不应该更改。
尤其是,我们应该能够随时更改或删除任何 doctest,如果这样做可以让 docstring 更容易理解。
以下是一个带有 doctest 的示例 docstring(来自 sympy/functions/special/delta_functions.py
)。
def fdiff(self, argindex=1):
"""
Returns the first derivative of a Heaviside Function.
Examples
========
>>> from sympy import Heaviside, diff
>>> from sympy.abc import x
>>> Heaviside(x).fdiff()
DiracDelta(x)
>>> Heaviside(x**2 - 1).fdiff()
DiracDelta(x**2 - 1)
>>> diff(Heaviside(x)).fdiff()
DiracDelta(x, 1)
"""
if argindex == 1:
return DiracDelta(self.args[0])
else:
raise ArgumentIndexError(self, argindex)
此外,所有公共函数 docstring 都应该包含在 Sphinx API 文档中。根据模块的不同,这可能意味着您需要在相应的 doc/src/modules/<module>.rst
文件中添加 .. autofunction::
行。您应该 构建文档 并查看它,以确保呈现的 HTML 中没有标记错误。
如果您想编写更长篇幅的指南或教程,您可以将其包含在 Sphinx 文档中,作为 Markdown 或 RST 文件,而不是将其放在 docstring 中。虽然这不是新贡献的要求,但我们一直在寻找为我们的文档添加新的、写得好的长篇指南。
在 GitHub 上创建 pull request 后,CI 将自动构建文档预览,您可以查看它。在 pull request 页面上,滚动到底部,找到检查位置,并找到链接,上面写着“单击此处查看文档预览”。
运行测试¶
有几种方法可以运行 SymPy 测试,但最简单的方法是使用 bin/test
脚本。
该脚本接受许多选项和参数。运行 bin/test --help
以查看所有支持的参数。在幕后,它使用 pytest
,如果您愿意,也可以直接使用它。
要运行所有测试,请使用以下命令
$ ./bin/test
要运行特定文件的测试,请使用
$ ./bin/test test_basic
其中 test_basic
来自文件 sympy/core/basic.py
。
要运行模块的测试,请使用
$ ./bin/test /core /utilities
这将运行 core
和 utilities
模块的测试。
类似地,使用以下命令运行质量测试
$ ./bin/test code_quality
提交更改¶
更改准备就绪后,您应该提交它们。您可以检查哪些文件已更改
git status
检查总更改
git diff
如果您创建了任何新文件,请使用以下命令添加它们
git add new_file.py
您已准备好本地提交更改。提交还包含一个 commit message
,用于描述提交。有关编写良好提交消息的指南,请参见下一部分。输入
git commit
在这种情况下,编辑器窗口会自动出现。在 Linux 中,默认情况下是 vim。您可以通过更改 $EDITOR
shell 变量来更改弹出的编辑器。
此外,借助 -a
选项,您可以告诉 commit
命令自动暂存已修改和已删除的文件,但不会影响您未告诉 git 的新文件,例如:
git commit -a
如果您只想暂存部分更改,可以使用交互式提交功能。只需输入
git commit --interactive
并在生成的界面中选择您想要的更改。
删除垃圾文件¶
许多编辑器可以在您的 SymPy 目录中创建一些配置文件、二进制文件或临时文件,这些文件应在合并您的提交之前删除。
追踪单个文件可能很麻烦。
您可能会想到使用 .gitignore
,但是,编辑 .gitignore
本身需要社区的同意。
使用 .git/info/exclude
是最好的方法,因为它只在本地应用。
https://githubdocs.cn/get-started/getting-started-with-git/ignoring-files
编写提交消息¶
提交消息分为两个部分:标题(第一行)和正文。这两部分由一个空行隔开。
提交消息总结了提交的内容。就像代码一样,您的提交消息将成为项目 git 历史的永久部分。因此,您应该花一些精力使它们高质量。提交消息是为了人类阅读者准备的,既是为了现在将要审查您代码的人,也是为了将来可能在研究代码中的一些更改时偶然发现您的提交的人。因此,如果有必要,请包含有助于其他人理解您的提交的信息。
像 git shortlog
和 GitHub UI 这样的工具默认情况下只显示提交的第一行,因此在第一行中传达提交的最重要方面很重要。
将第一行限制在 71 个字符或更少,后续行限制在 78 个字符或更少。这样,日志的一行形式就可以在不换行的情况下显示摘要。
确保在摘要之后留一个空行
不要在第一行的末尾使用句号(句点)。后续行应使用句号。
如果可能,提供提交的上下文,
例如
integrals: Improved speed of heurisch()
,而不是只写Improved speed of heurisch()
引用任何相关的 issue 号码。您不需要引用更改本身的 pull 请求,但应通过
#12345
或https://github.com/sympy/sympy/issues/12345
引用已修复的 issue。您还应提供 issue 的简要摘要,而不仅仅是引用 issue 号码,这样人们就不必到处寻找上下文。提交并不总是可以在分支的上下文中看到,因此,为每个提交提供一些上下文通常会有所帮助。不过,这不是必需的,因为查看提交元数据以查看修改了哪些文件,或者查看提交历史以查看附近的相关提交并不难。
使用纯英语。用完整的句子书写。
描述实际发生了哪些更改。不要只是写一些类似
Modified solvers.py
的内容。人们已经可以通过提交 diff 看到修改了哪些文件。消息的作用是告诉他们 diff 实际上做了什么,这样他们就不必试图弄清楚。同样,虽然应该如上所述交叉引用相关的 issue,但消息应包含足够的基本摘要,让人们可以在不查找 issue 的情况下理解发生了什么。issue 可以为感兴趣的人提供更详细的上下文。尽量避免使用简短的提交消息,例如 “Fix”,以及没有提供任何上下文的提交消息,例如 “Found the bug”。如果有疑问,较长的提交消息可能比较短的提交消息更好。避免使用
-m
开关向git commit
写入命令行上的提交消息。相反,让它打开您的编辑器,以便您可以编写更长的提交消息。如果仅通过查看 diff 难以弄清楚,则概述提交的作用。
包括其他相关信息,例如
已知问题
一个具体的示例(对于添加新功能/提高性能等的提交)
在合适的情况下使用项目符号列表
随意使用 Unicode 字符,例如来自 SymPy Unicode 美化打印机的输出。
良好提交消息的示例¶
以下是在 SymPy 历史记录中作为 bf0e81e12a2f75711c30f0788daf4e58f72b2a41 的一部分提交的示例提交消息。
integrals: Improved speed of heurisch() and revised tests
Improved speed of anti-derivative candidate expansion and solution
phases using explicit domains and solve_lin_sys(). The upside of
this change is that large integrals (those that generate lots of
monomials) are now computed *much* faster. The downside is that
integrals involving Derivative() don't work anymore. I'm not sure
if they really used to work properly or it was just a coincidence
and/or bad implementation. This needs further investigation.
Example:
In [1]: from sympy.integrals.heurisch import heurisch
In [2]: f = (1 + x + x*exp(x))*(x + log(x) + exp(x) - 1)/(x + log(x) + exp(x))**2/x
In [3]: %time ratsimp(heurisch(f, x))
CPU times: user 7.27 s, sys: 0.04 s, total: 7.31 s
Wall time: 7.32 s
Out[3]:
⎛ 2 x 2⋅x x 2 ⎞
log⎝x + 2⋅x⋅ℯ + 2⋅x⋅log(x) + ℯ + 2⋅ℯ ⋅log(x) + log (x)⎠ 1
──────────────────────────────────────────────────────────── + ───────────────
2 x
x + ℯ + log(x)
Previously it took 450 seconds and 4 GB of RAM to compute.
创建 Pull 请求¶
更改准备好审查后,将它们推送到 GitHub,并创建一个 pull 请求。
在更改完全准备好之前创建 pull 请求也可以,这样可以获得一些早期反馈。最好尽早获得反馈,这样您就不会在上面花费太多时间。如果您的 pull 请求还没有完全准备好合并,请在 GitHub 上以 “DRAFT” 状态打开它。您也可以在 pull 请求标题的开头添加 “ [WIP] ”(表示 “正在进行的工作”)来表明这一点。请确保在您的 PR 准备好进行最终审查时删除 DRAFT 状态或 [WIP]。
编写 pull 请求标题和描述¶
当您创建 pull 请求时,请务必填写 pull 请求描述模板。这包括添加与任何相关 issue 的交叉引用(如果合适,请添加 “fixes”),以及添加发布说明条目。
描述性标题非常重要。pull 请求标题应表明修复了什么。标题不明确的 pull 请求往往会被审阅者忽略。
以下是一些错误的 pull 请求标题的示例
“Modified solvers.py”
“Fix issue #12345”
这些并没有向审阅者表明 pull 请求中实际发生了什么更改,因此,他们很可能会直接忽略它,而不是审查它。一个更好的 pull 请求标题示例是
“Fix a bug with solve() on transcendental functions”
不要在 pull 请求标题中添加 issue 号码或文件名。issue 号码应放在描述中。
如果您还没有准备好合并 pull 请求,请使用 DRAFT 状态或在标题中添加前缀 “ [WIP] ”,并在您准备好时删除该状态/前缀。
描述是用于
展示您所做的事情,也许可以比较 master 中的输出与更改后的输出
引用已解决的 issue,例如 “#1234”;这种格式会自动创建指向相应 issue 或 pull 请求的链接,例如 “这类似于 issue #1234 中的问题……”。这种格式在 pull 请求的讨论部分也适用。
使用类似 “closes #1234” 或 “fixed #1234” 的短语(或 遵循自动关闭语法 的类似短语,也 在本文中讨论)。然后,当您的 pull 请求合并时,这些其他 issue 或 pull 请求将自动关闭。注意:此语法在 pull 请求的讨论中无效。请参阅此 快速指南,了解从 pull 请求中自动关闭 issue 的有效和无效语法。
pull 请求需要一个发布说明条目。请参阅 https://github.com/sympy/sympy/wiki/Writing-Release-Notes,了解如何在 pull 请求描述中编写发布说明。SymPy Bot 会自动检查您的 PR 是否包含发布说明。
最好只是填写 pull 请求模板(在您打开 pull 请求时显示的文本)。如果您填写了模板中的所有部分,您将拥有一个良好的 pull 请求描述。
将您的姓名和电子邮件地址添加到 .mailmap 文件中。¶
每个作者的姓名和电子邮件地址都存储在 AUTHORS 文件中,但此文件不应直接编辑。AUTHORS 文件是基于提交中记录的姓名和电子邮件地址在发布 SymPy 的新版本时自动更新的。使用 git 进行的每次提交都会存储 git 配置的姓名和电子邮件地址(参见 配置 git 设置)。.mailmap 文件用于将提交中记录的姓名/电子邮件与 AUTHORS 文件中列出的作者姓名和电子邮件地址相关联。
第一次创建 pull 请求时,您需要通过添加类似以下内容的行将您的姓名和电子邮件地址添加到 .mailmap 文件中
Joe Bloggs <joe@bloggs.com> joeb <joe@bloggs.com>
.mailmap 文件中的这行将作者姓名与相应的提交相关联。第一个姓名和电子邮件地址最终将出现在 AUTHORS 文件中。第二个条目是提交元数据中记录的内容。(参见 将用户名映射到 AUTHORS 文件条目)
提交元数据中的姓名和电子邮件应与您在进行提交之前使用 git 配置的姓名和电子邮件完全匹配(参见 配置 git 设置)。bin/mailmap_check.py
脚本可以检查这是否已正确完成。如果您已经进行了提交但尚未将自己添加到 .mailmap 文件中,则会看到以下信息
$ python bin/mailmap_check.py
This author is not included in the .mailmap file:
Joe Bloggs <[email protected]>
The .mailmap file needs to be updated because there are commits with
unrecognised author/email metadata.
For instructions on updating the .mailmap file see:
https://docs.sympy.cn/dev/contributing/new-contributors-guide/workflow-process.html#mailmap-instructions
The following authors will be added to the AUTHORS file at the
time of the next SymPy release.
这意味着您应该将您的姓名和电子邮件地址添加到 .mailmap 文件中。如果您在文件末尾添加此信息,则 git diff
将显示
$ git diff
diff --git a/.mailmap b/.mailmap
index 3af6dc1..7fa63b1 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1307,3 +1307,4 @@ zsc347 <[email protected]>
Øyvind Jensen <[email protected]>
Łukasz Pankowski <[email protected]>
彭于斌 <1931127624@qq.com>
+Joe Bloggs <[email protected]>
现在,您可以重新运行 bin/mailmap_check.py
脚本,您应该会看到
$ python bin/mailmap_check.py
The mailmap file was reordered
For instructions on updating the .mailmap file see:
https://docs.sympy.cn/dev/contributing/new-contributors-guide/workflow-process.html#mailmap-instructions
The following authors will be added to the AUTHORS file at the
time of the next SymPy release.
Joe Bloggs <[email protected]>
第一行表示 .mailmap 文件已“重新排序”。这是因为该文件应按字母顺序排列。该脚本会将您的姓名移到正确的位置,因此您现在可以查看更改如下
$ git diff
diff --git a/.mailmap b/.mailmap
index 3af6dc1..7598d94 100644
--- a/.mailmap
+++ b/.mailmap
@@ -562,6 +562,7 @@ Joannah Nanjekye <[email protected]> Joannah Nanjekye <[email protected]
Joannah Nanjekye <[email protected]> nanjekyejoannah <[email protected]>
Joaquim Monserrat <[email protected]>
Jochen Voss <[email protected]>
+Joe Bloggs <[email protected]>
Jogi Miglani <[email protected]> jmig5776 <[email protected]>
Johan Blåbäck <[email protected]> <[email protected]>
Johan Guzman <[email protected]>
现在,如果您重新运行该脚本,您将看到
$ python bin/mailmap_check.py
No changes needed in .mailmap
The following authors will be added to the AUTHORS file at the
time of the next SymPy release.
Joe Bloggs <[email protected]>
这里关键的信息是“No changes needed in .mailmap”,这意味着您已正确更新了 .mailmap 文件。您现在也应该添加并提交这些更改
git add .mailmap
git commit -m 'author: add Joe Bloggs to .mailmap'