开发工作流程过程¶
备注
本指南适用于已经熟悉在 GitHub 上为开源项目做出贡献的人。如果你是 GitHub 新手,请先阅读 设置开发环境 指南。
贡献清单¶
以下是向 SymPy 提交拉取请求时需要完成的事项清单。这些事项在拉取请求合并之前都需要完成。在打开拉取请求之前,不必完成所有这些事项,但在打开拉取请求甚至提交更改之前,通常先检查基本事项是一个好主意。
确保 代码质量检查 通过。
./bin/test quality flake8 sympy/
添加测试。 所有新功能都应该进行测试。错误修复应添加回归测试。测试以 pytest
assert f(x) == y
风格编写,并包含在sympy/
源代码的相应tests
目录中。请参阅关于 编写测试 的指南。新的公共函数和方法应该有一个文档字符串。
文档字符串应包含 doctests。
确保所有测试通过。 你可能想在提交之前在本地运行测试套件的相关子集(例如,
./bin/test solvers
)。当你打开一个拉取请求时,所有测试将在CI上运行。CI必须全部通过,PR才能被合并。(仅限首次贡献者) 将您的名字添加到
.mailmap
文件中。如果未正确完成此操作,GitHub 上的“测试 / 作者”CI 构建将会失败。在拉取请求描述中交叉引用相关问题。 如果拉取请求修复了一个问题(即,问题应在PR合并后关闭),请使用 “fixes #123”语法。
在原始问题中添加一个评论,交叉引用拉取请求以提高可见性。如果没有相应的问题,这是可以的。除非你的PR需要进一步改进,否则没有必要打开一个问题。
添加发布说明条目。 这应在打开拉取请求时完成,位于拉取请求描述字段中。在拉取请求合并之前,可以随时编辑。
回应评审意见。 所有 SymPy 的拉取请求在合并之前必须由其他人进行评审。
选择一个问题来修复¶
开始使用主代码库的最佳方式是修复一些现有的错误。浏览问题跟踪器中的“易于修复”问题,看看是否有你感兴趣的。如果你想尝试修复它,请在问题中创建一条消息,表明你想处理它。如果不清楚如何修复,请在问题本身或邮件列表中寻求建议。
SymPy 的代码被组织成 Python 包和模块。核心代码位于 sympy/core
目录中,而 sympy 目录中的其他包则包含更具体的代码。例如,sympy/printing
包含了处理 SymPy 对象如何打印到终端和 Jupyter 的代码。
如果你正在进行的更改还没有相关的问题,那么不需要先打开一个问题。只有当你觉得需要在提交拉取请求之前讨论这个更改时,才需要这样做,例如,如果你不确定某件事是否真的是一个错误,或者你不确定一个新功能是否在范围内。直接打开一个拉取请求并在那里讨论也是可以的。当有实际的代码可以讨论时,讨论会更容易进行,所以如果你有更改,即使它们还没有完全准备好合并,提交一个拉取请求也是更好的选择。
创建一个新分支¶
在对代码进行更改之前,首先要做的是在 git 中创建一个分支。
记住,永远不要提交到 master
。master
只应用于从 sympy/sympy 主仓库拉取上游更改。如果你提交到 master
,将难以拉取这些更改,并且如果你想同时进行多个拉取请求,也会变得困难。
首先为你的分支选择一个名称。请参见下方的 分支名称。要创建并检出(即,使其成为工作分支)一个新分支,请运行
# 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
记住,永远不要在主分支中输入以下命令:git merge
、git add
、git commit
、git rebase
。如果你不小心将一些提交推送到了本地主分支,你将不得不进行硬重置以丢弃这些提交。
分支名称¶
使用一个简短、易于输入的分支名称,该名称与更改内容有一定关联。记住,希望尝试你的代码的开发者需要在命令行中输入你的分支名称。
避免在分支名称中使用问题编号,因为这些编号不容易输入(大多数 SymPy 问题编号是 5 位数长),而且如果不查看问题,它们并不能真正指示更改的内容。
一些好的分支名称示例是
fix-solve-bug
typo-fix
core-improvements
add-simplify-tests
最终分支名称并不是那么重要,所以不要花太多时间去思考它。它的唯一功能是区分这个贡献的代码与你可能做出的其他贡献。
修改代码¶
在修复问题时,请记住每个贡献都应遵循以下几个要求:
代码质量¶
SymPy 的贡献必须具有足够的代码质量才能被接受。一旦你创建了一个拉取请求,CI 上会自动运行一些代码质量检查,但你也可以在本地运行它们。
./bin/test quality
flake8 sympy/
此外,所有测试都必须通过。CI 会自动运行测试,但你也可以 自己运行测试。建议在提交之前至少运行与你修改的代码相关的测试,以确保你没有犯任何错误或意外破坏了某些功能。
一旦你提交了你的拉取请求,你应该在GitHub Actions检查完成后查看是否有任何测试失败。如果有,你需要在拉取请求被接受之前修复它们。
添加测试¶
所有新功能都应该进行测试。如果你在修复一个错误,应该附带一个回归测试。也就是说,一个在错误修复之前会失败但现在通过的测试。通常你可以使用问题中的代码示例作为测试用例,尽管简化这些示例或编写自己的测试用例也是可以的,只要它能测试到相关问题。
测试位于 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)
如果相关,可以将新的测试用例添加到现有的测试函数中,或者您可以创建一个新的测试函数。
文档¶
所有新的方法、函数和类都应该有一个展示如何使用它们的文档字符串。文档字符串是一个紧跟在 def
行之后的三个引号括起来的字符串,用于描述该函数。文档字符串应遵循 文档字符串风格指南 中概述的格式。
每个文档字符串中应该包含的一个重要内容是示例。示例也被称为 doctests,因为它们通过 bin/doctest
脚本进行测试,以确保输出是正确的。
Doctests 需要为使用的每个函数包含导入,并定义使用的任何符号。用户应该能够将示例输入复制并粘贴到他们自己的 Python 会话中,并获得完全相同的输出。from sympy import *
在 doctests 中是不允许的,因为这会使哪些函数来自 SymPy 变得不明确。
The docstring style guide 提供了更多关于如何在docstrings中格式化示例的详细信息。
请记住,doctests 不是 测试。可以把它们看作是碰巧被测试的例子。一些关键区别:
编写doctests以提供信息;编写常规测试以检查回归和边缘情况。
doctests 可以随时更改;常规测试不应更改。
特别是,如果修改或删除任何doctest能使文档字符串更容易理解,我们应该能够随时进行这些操作。
以下是一个带有doctests的示例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)
此外,所有公共函数的文档字符串都应包含在 Sphinx API 文档中。根据模块的不同,这可能意味着你需要在相应的 doc/src/modules/<module>.rst
文件中添加 .. autofunction::
行。你应该 构建文档 并查看它,以确保在渲染的 HTML 中没有标记错误。
如果你想写一个更长的指南或教程,你可以将其作为Markdown或RST文件包含在Sphinx文档中,而不是放在docstring中。虽然这对新贡献不是必需的,但我们一直在寻找添加新的写得好的长篇指南到我们的文档中。
一旦你在GitHub上提交了拉取请求,CI将自动构建一个你可以查看的文档预览。在拉取请求页面,滚动到底部查看检查结果,并找到写着“点击此处查看文档预览”的链接。
运行测试¶
有几种运行 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
你已准备好本地提交更改。提交还包含一个 提交信息
,用于描述它。有关编写良好提交信息的指南,请参见下一节。输入:
git commit
在这种情况下,编辑器窗口将自动出现。在Linux中,默认情况下是vim。您可以通过更改 $EDITOR
shell变量来更改弹出的编辑器。
同样,借助选项 -a
,你可以告诉 commit
命令自动暂存已修改和删除的文件,但新文件(你尚未告知 git 的文件)将不受影响,例如:
git commit -a
如果你想只暂存部分更改,可以使用交互式提交功能。只需输入:
git commit --interactive
并在生成的界面中选择您想要的更改。
删除垃圾文件¶
许多编辑器可以在您的 SymPy 目录中创建一些配置文件、二进制文件或临时文件,这些文件应在合并提交之前删除。
追踪单个文件可能会很麻烦。
你可能会考虑使用 .gitignore
,然而,编辑 .gitignore
本身需要得到社区的同意。
使用 .git/info/exclude
是最好的,因为它仅在本地应用。
https://stackoverflow.com/questions/22906851/什么时候你会使用git-info-exclude而不是gitignore来排除文件
https://docs.github.com/get-started/getting-started-with-git/ignoring-files
编写提交信息¶
提交信息有两部分:标题(第一行)和正文。两者之间用空行分隔。
提交信息总结了提交的内容。就像代码一样,你的提交信息将成为项目 git 历史的一部分。因此,你应该努力使它们保持高质量。提交信息旨在供人类读者阅读,无论是现在正在审查你代码的人,还是将来在研究代码中的某些更改时可能会遇到你的提交的人。因此,如果必要,请在此包含有助于他人理解你的提交的信息。
像 git shortlog
和 GitHub UI 这样的工具默认只显示提交的第一行,因此在第一行传达提交的最重要方面是很重要的。
保持第一行不超过71个字符,后续行不超过78个字符。这样可以让日志的一行形式在不换行的情况下显示摘要。
确保在摘要后留一个空白行
不要在第一行末尾加句号(句点)。后续行应使用句号。
如果可能,请为提交提供上下文,
例如
integrals: 改进了 heurisch() 的速度
而不是仅仅改进了 heurisch() 的速度
引用任何相关的问题编号。你不需要引用变更本身的拉取请求,但应修复的问题应通过
#12345
或https://github.com/sympy/sympy/issues/12345
引用。你还应提供问题的简要总结,而不仅仅是引用问题编号,以便人们不必四处寻找上下文。提交并不总是在分支的上下文中可见,因此为每个提交提供一些上下文通常是有帮助的。不过,这并不是必需的,因为查看提交元数据以了解哪些文件被修改,或者查看提交历史以查看附近的相关提交并不困难。
使用简单的英语。写完整的句子。
描述实际发生了什么变化。不要只是写一些类似
Modified solvers.py
的内容。人们已经可以从提交的差异中看到哪些文件被修改了。提交信息的作用是告诉他们差异实际上做了什么,这样他们就不必自己去弄清楚。同样,尽管相关的问题应该如上所述进行交叉引用,但消息应包含足够的基本总结,以便人们无需查找问题就能理解发生了什么。问题可以为感兴趣的人提供更详细的背景信息。尽量避免简短的提交信息,如“修复”,以及没有上下文的提交信息,如“找到了错误”。如果有疑问,较长的提交信息可能比简短的更好。避免使用
git commit
的-m
开关在命令行上编写提交信息。相反,让它打开你的编辑器,这样你就可以编写更长的提交信息。如果仅通过查看差异难以理解提交的内容,请概述提交的作用。
包含其他相关信息,例如
已知问题
一个具体的例子(用于添加新功能/提升性能等的提交)
在合适的情况下使用项目列表
请随意使用 Unicode 字符,例如 SymPy Unicode 漂亮打印机的输出。
一个好的提交信息的例子¶
以下是一个来自提交 bf0e81e12a2f75711c30f0788daf4e58f72b2a41 的提交信息示例,这是 SymPy 历史的一部分:
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.
发起拉取请求¶
一旦你的更改准备好进行审查,将它们推送到GitHub,并创建一个拉取请求。
在更改完全准备好之前,提出拉取请求也是可以的,这样可以尽早获得一些反馈。在花费太多时间之前尽早获得反馈会更好。如果你的拉取请求还没有完全准备好合并,可以在GitHub上将其打开为“DRAFT”状态。你还可以在拉取请求标题的开头添加“[WIP]”(代表“正在进行的工作”)来表明这一点。只需确保在PR准备好最终审查时移除DRAFT状态或[WIP]。
编写拉取请求的标题和描述¶
当你发起一个拉取请求时,请务必填写拉取请求描述模板。这包括添加对任何相关问题的交叉引用(如果适用,使用“修复”),并添加一个发布说明条目。
描述性标题非常重要。 拉取请求的标题应表明修复了什么。标题不明确的拉取请求往往会被评审者忽略。
不好的拉取请求标题示例有
修改后的 solvers.py
修复问题 #12345
这些确实向评审者指出了在拉取请求中实际更改的内容,因此,他们可能会直接通过而不是进行评审。一个更好的拉取请求标题的例子是
修复了在超越函数上使用solve()的错误
不要在拉取请求的标题中放入问题编号或文件名。问题编号应放在描述中。
如果你还没有准备好让拉取请求被合并,请使用DRAFT状态或在标题中包含前缀“[WIP]”,并在准备好时移除状态/前缀。
描述是一个很好的地方来:
展示你所做的内容,可能通过比较主分支的输出与更改后的输出来实现。
参考已解决的问题,如“#1234”;这种格式将自动创建指向相应问题或拉取请求的链接,例如“这与问题 #1234 中的问题类似…”。这种格式在拉取请求的讨论部分也有效。
使用诸如“closes #1234”或“fixed #1234”(或其他符合自动关闭语法的类似语句,这些语句也在这里讨论)。然后,当你的拉取请求被合并时,这些其他问题或拉取请求将自动关闭。注意:此语法在拉取请求的讨论中不起作用。请参阅此快速指南,了解从拉取请求自动关闭问题的有效和无效语法。
拉取请求需要一个发布说明条目。请参阅 https://github.com/sympy/sympy/wiki/Writing-Release-Notes 了解如何在拉取请求描述中编写发布说明。SymPy Bot 将自动检查您的 PR 是否有发布说明。
最好直接填写拉取请求模板(打开拉取请求时出现的文本)。如果你填写了模板中的所有部分,你将会有一个良好的拉取请求描述。
将您的姓名和电子邮件地址添加到 .mailmap 文件中。¶
每个作者的姓名和电子邮件地址都存储在 AUTHORS 文件中,但不应直接编辑此文件。当发布新版本的 SymPy 时,AUTHORS 文件会根据提交中记录的姓名和电子邮件地址自动更新。使用 git 进行的每次提交都会存储 git 配置的姓名和电子邮件地址(参见 配置 git 设置)。.mailmap 文件用于将提交中记录的姓名/电子邮件与将在 AUTHORS 文件中列出的作者姓名和电子邮件地址关联起来。
第一次提交拉取请求时,您需要通过添加类似的一行来将您的姓名和电子邮件地址添加到 .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 <joe@bloggs.com>
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.org/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 <zsc347@gmail.com>
Øyvind Jensen <jensen.oyvind@gmail.com>
Łukasz Pankowski <lukpank@o2.pl>
+Joe Bloggs <joe@bloggs.com>
现在你可以重新运行 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.org/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 <joe@bloggs.com>
第一行指出 .mailmap 文件被“重新排序”。这是因为文件应按字母顺序排列。脚本已将您的名字移动到正确的位置,因此现在您可以看到更改如下:
$ git diff
diff --git a/.mailmap b/.mailmap
index 3af6dc1..7598d94 100644
--- a/.mailmap
+++ b/.mailmap
@@ -562,6 +562,7 @@ Joannah Nanjekye <joannah.nanjekye@ibm.com> Joannah Nanjekye <jnanjekye@python.o
Joannah Nanjekye <joannah.nanjekye@ibm.com> nanjekyejoannah <joannah.nanjekye@ibm.com>
Joaquim Monserrat <qmonserrat@mailoo.org>
Jochen Voss <voss@seehuhn.de>
+Joe Bloggs <joe@bloggs.com>
Jogi Miglani <jmig5776@gmail.com> jmig5776 <jmig5776@gmail.com>
Johan Blåbäck <johan_bluecreek@riseup.net> <johan.blaback@cea.fr>
Johan Guzman <jguzm022@ucr.edu>
现在如果你重新运行脚本,你将会看到:
$ 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 <joe@bloggs.com>
这里的关键信息是“无需更改 .mailmap”,这意味着您已正确更新了 .mailmap 文件。现在,您应该将这些更改添加并提交:
git add .mailmap
git commit -m 'author: add Joe Bloggs to .mailmap'