贡献¶
非常欢迎和感谢您的贡献。每一小部分的帮助都弥足珍贵,所以请不要犹豫!
功能请求和反馈¶
您喜欢 pytest 吗?在 Twitter 或您的博客文章中分享您的喜爱吧!
我们也希望听到您的建议。欢迎 以 issue 形式提交 它们并
详细解释它们应如何工作。
尽可能缩小范围。这将使实现更容易。
报告 bug¶
在 问题追踪器 中报告 pytest 的 bug。
如果您要报告 bug,请包含
您的操作系统名称和版本。
任何可能有助于故障排除的本地设置详细信息,特别是 Python 解释器版本、已安装的库和 pytest 版本。
重现 bug 的详细步骤。
如果您能编写一个当前失败但应该通过的演示测试(xfail),那也是一个非常有用的提交,即使您无法修复 bug 本身。
修复 bug¶
查看 GitHub 上的 bug issue。另请参阅对新贡献者友好的 “良好第一个 issue”。
与开发者交流,了解如何修复特定 bug。要表明您将处理某个特定 issue,请在该 issue 上添加一条评论。
别忘了也查看您喜欢的插件的问题追踪器!
实现功能¶
与开发者交流,了解如何实现特定功能。
编写文档¶
Pytest 总是需要更多的文档。具体需要什么?
更多补充文档。您是否发现有不清楚的地方?
文档翻译。我们目前只有英文版。
Docstrings(文档字符串)。它们永远不嫌多。
博客文章、文章等——它们都非常受欢迎。
您也可以直接在 GitHub 网页界面编辑文档文件,无需使用本地副本。这对于小型修复可能很方便。
注意
使用以下命令在本地构建文档
$ tox -e docs
构建的文档应在 doc/en/_build/html
中可用,其中 'en' 指的是文档语言。
Pytest 有一个 API 参考,其中很大一部分是 自动生成 自文档项的文档字符串。Pytest 使用 Sphinx 文档字符串格式。例如
def my_function(arg: ArgType) -> Foo:
"""Do important stuff.
More detailed info here, in separate paragraphs from the subject line.
Use proper sentences -- start sentences with capital letters and end
with periods.
Can include annotated documentation:
:param short_arg: An argument which determines stuff.
:param long_arg:
A long explanation which spans multiple lines, overflows
like this.
:returns: The result.
:raises ValueError:
Detailed information when this can happen.
.. versionadded:: 6.0
Including types into the annotations above is not necessary when
type-hinting is being used (as in this example).
"""
向 pytest-dev 提交插件¶
pytest 核心、支持代码和一些插件的开发发生在 pytest-dev
组织下的仓库中
所有 pytest-dev 贡献者团队成员对所有包含的仓库都拥有写入权限。Pytest 核心和插件通常使用 拉取请求 到各自的仓库进行开发。
pytest-dev
组织的目标是
为流行的 pytest 插件提供一个中心位置
分担部分维护责任(以防维护者不再希望维护某个插件)
您可以通过在 pytest-dev GitHub Discussions 中发布新主题来提交您的插件,指向您现有的 pytest 插件仓库,该仓库必须具有以下内容
在 PyPI 上存在,其打包元数据包含以
pytest-
为前缀的名称、版本号、作者、简短和详细描述。一个
README
文件,描述如何使用该插件以及它运行的平台。一个包含许可信息的
LICENSE
文件,其打包元数据中包含匹配信息。一个用于 bug 报告和增强请求的问题追踪器。
一个 更新日志。
如果没有贡献者强烈反对且有两人同意,该仓库就可以被转移到 pytest-dev
组织。
以下是仓库转移通常如何进行的简要说明(以名为 joedoe/pytest-xyz
的仓库为例)
joedoe
将仓库所有权转移给pytest-dev
管理员calvin
。calvin
创建pytest-xyz-admin
和pytest-xyz-developers
团队,邀请joedoe
作为维护者加入这两个团队。calvin
将仓库转移到pytest-dev
并配置团队访问权限pytest-xyz-admin
管理员访问权限;pytest-xyz-developers
写入访问权限;
pytest-dev/Contributors
团队对所有项目都有写入权限,并且每个项目管理员都在其中。我们建议每个插件至少有三个人有权发布到 PyPI。
仓库所有者可以放心,除非在极少数情况下(例如在数月联系尝试后某人不再响应),否则 pytest-dev
管理员绝不会发布您的仓库或以任何方式取得所有权。如前所述,目标是共享维护并避免“插件废弃”。
准备拉取请求¶
简短版本¶
Fork 该仓库。
如有必要,从上游获取标签(如果您只克隆了 main 分支,则使用
git fetch --tags https://github.com/pytest-dev/pytest
)。启用并安装 pre-commit 以确保遵循样式指南和代码检查。
遵循 PEP-8 进行命名。
测试使用
tox
运行tox -e linting,py39
上面的测试环境通常足以覆盖大多数本地情况。
编写
更新日志
条目:changelog/2574.bugfix.rst
,使用 issue ID 号和feature
、improvement
、bugfix
、doc
、deprecation
、breaking
、vendor
或trivial
中的一种作为 issue 类型。除非您的更改是琐碎的或文档修复(例如,错别字或小节的重写),否则请按字母顺序将自己添加到
AUTHORS
文件中。
长版本¶
什么是“拉取请求”?它通知项目的核心开发者您希望审查和合并的更改。拉取请求存储在 GitHub 服务器 上。一旦您发送拉取请求,我们就可以讨论其潜在的修改,甚至可以在之后添加更多提交。在 GitHub 帮助中心 有一个关于拉取请求如何工作的优秀教程。
以下是一个简单的概述,包含 pytest 特定的内容
Fork pytest GitHub 仓库。使用
pytest
作为您的 fork 仓库名称没问题,因为它将存在于您的用户下。使用 git 在本地克隆您的 fork 并创建一个分支
$ git clone git@github.com:YOUR_GITHUB_USERNAME/pytest.git $ cd pytest $ git fetch --tags https://github.com/pytest-dev/pytest # now, create your own branch off "main": $ git checkout -b your-bugfix-branch-name main
鉴于我们有“主版本.次版本.微版本”版本号,bug 修复通常会在微版本发布中发布,而功能将在次版本发布中发布,不兼容的更改则在主版本发布中发布。
您需要标签才能在本地进行测试,因此请确保您拥有主仓库的标签。如果您怀疑没有,请将主仓库设置为上游并获取标签
$ git remote add upstream https://github.com/pytest-dev/pytest $ git fetch upstream --tags
如果您需要 Git 的帮助,请遵循此快速入门指南:https://git.wiki.kernel.org/index.php/QuickStart
在 pytest 仓库上安装 pre-commit 及其钩子
$ pip install --user pre-commit $ pre-commit install
之后,
pre-commit
将在每次提交时运行。https://pre-commit.git-scm.cn/ 是一个用于管理和维护多语言 pre-commit 钩子的框架,以确保代码风格和代码格式保持一致。
安装 tox
Tox 用于运行所有测试,并将自动设置虚拟环境来运行测试。(将隐式使用 https://virtualenv.pypa.io/en/stable/)
$ pip install tox
运行所有测试
您的系统需要安装 Python 3.9 或更高版本。现在运行测试就像发出这个命令一样简单
$ tox -e linting,py39
此命令将通过 "tox" 工具对 Python 3.9 运行测试,并执行 "lint" 编码风格检查。
您现在可以编辑本地工作副本并根据需要再次运行测试。请遵循 PEP-8 进行命名。
您可以向
tox
传递不同的选项。例如,要在 Python 3.9 上运行测试并向 pytest 传递选项(例如在失败时进入 pdb),您可以这样做$ tox -e py39 -- --pdb
或者只在 Python 3.9 上运行特定测试模块中的测试
$ tox -e py39 -- testing/test_config.py
提交时,
pre-commit
将在必要时重新格式化文件。如果不想使用
tox
,而更喜欢直接运行测试,我们建议创建一个虚拟环境,并使用带有dev
额外依赖的可编辑安装$ python3 -m venv .venv $ source .venv/bin/activate # Linux $ .venv/Scripts/activate.bat # Windows $ pip install -e ".[dev]"
之后,您可以编辑文件并正常运行 pytest
$ pytest testing/test_config.py
在
changelog
中创建一个新的更新日志条目。文件应命名为<issueid>.<type>.rst
,其中 issueid 是与更改相关的 issue 号,type 是feature
、improvement
、bugfix
、doc
、deprecation
、breaking
、vendor
或trivial
之一。如果更改不影响 pytest 的已文档化行为,则可以跳过创建更新日志条目。如果还没有,请按字母顺序将自己添加到
AUTHORS
文件中。一旦您的测试通过并且您对您的更改满意,就提交并推送
$ git commit -a -m "<commit message>" $ git push -u
最后,通过 GitHub 网站使用这些数据提交拉取请求
head-fork: YOUR_GITHUB_USERNAME/pytest compare: your-branch-name base-fork: pytest-dev/pytest base: main
编写测试¶
为插件或 pytest 本身编写测试通常使用 pytester fixture,作为“黑盒”测试。
例如,要确保一个简单的测试通过,您可以编写
def test_true_assertion(pytester):
pytester.makepyfile(
"""
def test_foo():
assert True
"""
)
result = pytester.runpytest()
result.assert_outcomes(failed=0, passed=1)
或者,也可以使用 *glob-like* 表达式根据终端的实际输出进行检查
def test_true_assertion(pytester):
pytester.makepyfile(
"""
def test_foo():
assert False
"""
)
result = pytester.runpytest()
result.stdout.fnmatch_lines(["*assert False*", "*1 failed*"])
在选择编写新测试的文件时,请查看现有文件,看看是否有哪个文件看起来合适。例如,关于 --lf
选项中的 bug 的回归测试应该放入 test_cacheprovider.py
中,因为这个选项是在 cacheprovider.py
中实现的。如有疑问,请大胆地提交您的最佳猜测的 PR,我们可以在代码中进行讨论。
加入开发团队¶
任何成功完成拉取请求且不需要开发团队额外工作即可合并的人,如果他们愿意,将获得提交权限(如果我们忘记了,请友好地提醒)。这并不意味着您的贡献工作流程有任何改变:每个人都遵循相同的拉取请求和审查流程,除非已经获得批准,否则没有人会合并自己的拉取请求。然而,这意味着您可以更充分地参与开发过程,因为在审查其他贡献者的拉取请求后,您可以自己合并它们。
合并/压缩指南¶
当一个 PR 被批准并准备集成到 main
分支时,可以选择不更改地合并提交,或者将所有提交压缩为一个单一提交。
以下是根据单个 PR 提交历史示例,关于如何操作的一些指南
杂项提交
实现 X
修复 test_a
将我添加到 AUTHORS
fixup! 修复 test_a
更新 tests/test_integration.py
将 origin/main 合并到 PR 分支
更新 tests/test_integration.py
在这种情况下,倾向于使用压缩合并策略:提交历史有点混乱(不是贬义,通常只是因为知道更改最终会被压缩在一起而提交),所以将所有内容压缩到一个提交中是最好的。您必须清理提交消息,确保它包含有用的详细信息。
与同一主题相关的独立提交
实现 X
将我添加到 AUTHORS
更新 X 的更新日志
在这种情况下,倾向于使用压缩合并策略:虽然提交历史不像上面的示例那样“混乱”,但单独的提交总体上没有带来太多价值,特别是在几个月/几年后查看更改时。
独立的提交,每个都有自己的主题(重构、重命名等),但仍有一个更大的主题/目的。
重构类 X,为功能 Y 做准备
移除未使用的函数
实现功能 Y
在这种情况下,倾向于使用合并策略:每个提交本身都很有价值,即使它们整体服务于一个共同的主题。之后查看历史时,将移除未使用的方法单独放入一个提交中,并附带更多信息(例如它是如何一开始就变得未使用的),这很有用。
独立的提交,每个都有自己的主题,但除了改进代码库(使用更现代的技术、改进类型提示、清除冗余等)之外,没有更大的主题/目的。
改进 X 中的内部名称
为 Y 添加类型注解
移除不必要的字典访问
移除因 Python 生命周期结束而无法访问的代码
在这种情况下,倾向于使用合并策略:每个提交本身都很有价值,并且每个提交中的信息在长期来看都很有价值。
如前所述,这些是总体指导方针,而不是一成不变的规则。这个话题已在 #12633 中讨论过。
回溯 PR(如那些从 backport
标签自动创建的)应始终被压缩,因为它们保留了原始 PR 作者。
为下一个补丁版本回溯 bug 修复¶
Pytest 每隔几周或几个月发布一个功能版本。在此期间,会对上一个功能版本发布补丁版本,仅包含 bug 修复。这些 bug 修复通常修复回归,但也可能是任何应在下一个功能版本发布之前到达用户的更改。
例如,假设最新版本是 1.2.3,您想在 1.2.4 中包含一个 bug 修复(查看 https://github.com/pytest-dev/pytest/releases 获取实际的最新版本)。其过程如下
首先,确保 bug 已在
main
分支中修复,并提交常规的拉取请求,如上所述。如果 bug 修复不再适用于main
,则为例外情况。
自动方法
在您想要回溯的 PR 上添加 backport 1.2.x
标签。这将针对 1.2.x
分支创建一个回溯 PR。
手动方法
git checkout origin/1.2.x -b backport-XXXX
# 这里使用主 PR 号码在 merged 消息中找到 PR 上的合并提交,例如
nicoddemus merged commit 0f8b462 into pytest-dev:main
git cherry-pick -x -m1 REVISION
# 使用您上面找到的修订版本(0f8b462
)。打开一个目标为
1.2.x
的 PR在消息前加上
[1.2.x]
。删除 PR 正文,它通常包含重复的提交消息。
谁进行回溯¶
如上所述,bug 应首先在 main
分支上修复(除了在极少数情况下 bug 只发生在以前的版本中)。那么,谁应该执行上述回溯过程呢?
如果 bug 是由核心开发者修复的,那么回溯是该核心开发者的主要责任。
然而,通常合并是由另一个维护者完成的,在这种情况下,如果他们有时间进行回溯过程,那会很好。
对于非维护者提交的 bug,核心开发者预计会进行回溯,通常是那些在
main
分支上合并 PR 的人。如果非维护者注意到一个 bug 已在
main
分支上修复但尚未回溯(由于维护者忘记应用 needs backport 标签,或根本遗漏),他们也欢迎打开一个带有回溯的 PR。这个过程很简单,对项目的维护非常有帮助。
以上这些都不是规则,而仅仅是关于我们对回溯应有的预期的一些指导/建议。
回溯应被压缩(而不是合并),因为这样做可以正确保留原始 PR 作者。
处理过期 issue/PRs¶
过期 issue/PRs 是指 pytest 贡献者提出问题/更改后,作者在相当长一段时间内未能回答/实现,或者讨论由于人们似乎失去兴趣而简单地中止的情况。
人们未能回答问题或实现所请求的更改有很多原因:他们可能很忙、失去兴趣,或者只是忘记了,但事实是这在开源软件中非常常见。
pytest 团队非常感谢每一个 issue 和拉取请求,但作为一个每天提交大量 issue 和拉取请求的高流量项目,我们试图通过定期关闭它们来减少过期 issue 和 PR 的数量。当一个 issue/拉取请求以这种方式关闭时,绝不是驳回 issue/拉取请求所处理的主题,而只是我们清理队列并使维护者的工作更易于管理的一种方式。提交者如果认为有意义,可以随时在他们自己的时间重新打开 issue/拉取请求。
何时关闭¶
以下是维护者在因不活跃而决定关闭 issue/PRs 时使用的一些通用规则
标记为
question
或needs information
的 issue:不活跃 14 天后关闭。标记为
proposal
的 issue:不活跃六个月后关闭。拉取请求:一个月后,考虑提醒作者,更新关联的 issue,或考虑关闭。对于接近完成的拉取请求,团队应考虑完成并合并它。
以上并非硬性规定,而仅仅是指导方针,并且可以(而且经常!)根据具体情况进行审查。
关闭拉取请求¶
关闭拉取请求时,需要肯定提交者所付出的时间、精力和兴趣。如前所述,团队并非意图完全驳回一个停滞的拉取请求,而仅仅是清理队列,因此在关闭一个过期的拉取请求时,需要发送如下消息
您好,<贡献者>,
首先,我们要感谢您为此付出的时间和精力,pytest 团队对此深表感谢。
然而,我们注意到您已经有一段时间没有更新这个 PR 了。pytest 是一个高度活跃的项目,每天都有许多 issue/PR 被打开,因此我们维护者很难追踪哪些 PR 已准备好合并、待审查或需要更多关注。
因此,出于这些原因,我们认为目前最好关闭此 PR,但这仅仅是为了清理我们的队列,绝不是拒绝您的更改。当您准备好重新开始时,我们仍然鼓励您重新打开此 PR(只需点击一个按钮即可)。
再次感谢您为此付出的时间,希望您能稍后继续处理!
<再见>
关闭 issues¶
当提交一个拉取请求来修复一个 issue 时,在 PR 描述和/或提交中添加类似 closes #XYZW
的文本(其中 XYZW
是 issue 号码)。有关更多信息,请参阅 GitHub 文档。
如果 issue 是由于用户错误(例如对功能理解有误)造成的,请礼貌地向用户解释为何提出的 issue 并非实际问题,并请他们如果没有其他问题就关闭该 issue。如果原始请求者没有回应,该 issue 将按照上面处理过期 issue/PRs 部分的描述进行处理。