贡献

非常欢迎和感谢贡献。点滴帮助都很重要,所以不要犹豫!

功能请求和反馈

您喜欢 pytest 吗?在 Twitter 或您的博客文章中分享一些喜爱吧!

我们也想听取您的建议和意见。随时作为 issue 提交它们,并

  • 详细解释它们应该如何工作。

  • 尽可能保持范围狭窄。这将使其更容易实现。

报告错误

issue 跟踪器中报告 pytest 的错误。

如果您要报告错误,请包括

  • 您的操作系统名称和版本。

  • 关于您的本地设置的任何详细信息,这些信息可能有助于故障排除,特别是 Python 解释器版本、已安装的库和 pytest 版本。

  • 重现错误的详细步骤。

如果您可以编写一个演示测试,该测试目前失败但应该通过 (xfail),那么即使您无法修复错误本身,这也是一个非常有用的提交。

修复错误

浏览 GitHub issues 中的错误。另请参阅对新贡献者友好的“good first issue” issues

开发人员交谈,了解如何修复特定错误。要表明您将要处理特定 issue,请在该 issue 上添加评论说明。

不要忘记查看您最喜欢的插件的 issue 跟踪器!

实现功能

浏览 GitHub issues 中的增强功能

开发人员交谈,了解如何实现特定功能。

编写文档

Pytest 总是可以使用更多文档。到底需要什么?

  • 更多补充文档。您是否发现某些内容不清楚?

  • 文档翻译。我们目前只有英语。

  • 文档字符串。永远不会有太多。

  • 博客文章、文章等 – 都非常受欢迎。

您也可以直接在 GitHub Web 界面中编辑文档文件,而无需使用本地副本。这对于小的修复可能很方便。

注意

使用以下命令在本地构建文档

$ 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 Contributors 团队成员都具有对所有包含的存储库的写入权限。Pytest 核心和插件通常使用拉取请求开发到各个存储库。

pytest-dev 组织的目标是

  • 为流行的 pytest 插件提供中心位置

  • 分担一些维护责任(以防维护者不再希望维护插件)

您可以通过订阅 pytest-dev 邮件列表 并写一封邮件指向您现有的 pytest 插件存储库来提交您的插件,该存储库必须具有以下内容

  • PyPI 存在,其中包含 packaging 元数据,该元数据包含一个 pytest- 前缀名称、版本号、作者、简短和详细描述。

  • 一个 tox 配置,用于使用 tox 运行测试。

  • 一个 README,描述如何使用插件以及它在哪些平台上运行。

  • 一个 LICENSE 文件,其中包含许可信息,并在其 packaging 元数据中包含匹配的信息。

  • 一个 issue 跟踪器,用于错误报告和增强请求。

  • 一个 更新日志

如果没有贡献者强烈反对且两人同意,则可以将存储库转移到 pytest-dev 组织。

以下是存储库转移通常如何进行的概要(使用名为 joedoe/pytest-xyz 的存储库作为示例)

  • joedoe 将存储库所有权转移给 pytest-dev 管理员 calvin

  • calvin 创建 pytest-xyz-adminpytest-xyz-developers 团队,邀请 joedoe 加入这两个团队作为 维护者

  • calvin 将存储库转移到 pytest-dev 并配置团队访问权限

    • pytest-xyz-admin admin 访问权限;

    • pytest-xyz-developers write 访问权限;

pytest-dev/Contributors 团队具有对所有项目的写入权限,并且每个项目管理员都在其中。我们建议每个插件至少有三个人有权发布到 PyPI。

存储库所有者可以放心,除了在极少数情况下,某人在数月的联系尝试后变得无响应,否则没有 pytest-dev 管理员会发布您的存储库或以任何方式取得所有权。如所述,目标是分担维护责任并避免“插件放弃”。

准备拉取请求

简短版本

  1. Fork 存储库。

  2. 如有必要,从上游获取标签(如果您仅克隆了 main,请使用 git fetch --tags https://github.com/pytest-dev/pytest)。

  3. 启用并安装 pre-commit 以确保遵循样式指南和代码检查。

  4. 遵循 PEP-8 进行命名。

  5. 测试使用 tox 运行

    tox -e linting,py39
    

    上面的测试环境通常足以在本地覆盖大多数情况。

  6. 编写 changelog 条目:changelog/2574.bugfix.rst,使用 issue id 号和 featureimprovementbugfixdocdeprecationbreakingvendortrivial 中的一个作为 issue 类型。

  7. 除非您的更改是微不足道的或文档修复(例如,一个小的部分的拼写错误或重述),否则请将您自己添加到 AUTHORS 文件中,按字母顺序排列。

长版本

什么是“拉取请求”?它告知项目的核心开发人员您想要审查和合并的更改。拉取请求存储在 GitHub 服务器上。一旦您发送拉取请求,我们可以讨论其潜在的修改,甚至稍后向其中添加更多提交。在GitHub 帮助中心中有一个关于拉取请求如何工作的优秀教程。

这是一个简单的概述,其中包含 pytest 特有的部分

  1. Fork pytest GitHub 存储库。使用 pytest 作为您的 fork 存储库名称是可以的,因为它将位于您的用户下。

  2. 使用 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
    

    鉴于我们有 “major.minor.micro” 版本号,错误修复通常将在微版本中发布,而功能将在次版本中发布,不兼容的更改将在主版本中发布。

    您将需要标签在本地进行测试,因此请确保您拥有来自主存储库的标签。如果您怀疑您没有,请将主存储库设置上游并获取标签

    $ git remote add upstream https://github.com/pytest-dev/pytest
    $ git fetch upstream --tags
    

    如果您需要一些 Git 方面的帮助,请遵循此快速入门指南:https://git.wiki.kernel.org/index.php/QuickStart

  3. 在 pytest 存储库上安装 pre-commit 及其 hook

    $ pip install --user pre-commit
    $ pre-commit install
    

    之后,每当您提交时,pre-commit 都会运行。

    https://pre-commit.git-scm.cn/ 是一个用于管理和维护多语言 pre-commit hooks 的框架,以确保代码风格和代码格式的一致性。

  4. 安装 tox

    Tox 用于运行所有测试,并将自动设置 virtualenvs 以在其中运行测试。(将隐式使用 https://virtualenv.pypa.io/en/latest/

    $ pip install tox
    
  5. 运行所有测试

    您需要在您的系统中安装 Python 3.8 或更高版本。现在运行测试就像发出此命令一样简单

    $ tox -e linting,py39
    

    此命令将通过 “tox” 工具针对 Python 3.9 运行测试,并执行 “lint” 代码风格检查。

  6. 您现在可以编辑您的本地工作副本,并在必要时再次运行测试。请遵循 PEP-8 进行命名。

    您可以将不同的选项传递给 tox。例如,要在 Python 3.9 上运行测试并将选项传递给 pytest(例如,在失败时进入 pdb)给 pytest,您可以执行

    $ tox -e py39 -- --pdb
    

    或者仅在 Python 3.9 上在特定测试模块中运行测试

    $ tox -e py39 -- testing/test_config.py
    

    提交时,如果需要,pre-commit 将重新格式化文件。

  7. 如果您不想使用 tox 而是喜欢直接运行测试,那么我们建议创建一个虚拟环境并使用带有 dev extra 的可编辑安装

    $ python3 -m venv .venv
    $ source .venv/bin/activate  # Linux
    $ .venv/Scripts/activate.bat  # Windows
    $ pip install -e ".[dev]"
    

    之后,您可以编辑文件并正常运行 pytest

    $ pytest testing/test_config.py
    
  8. changelog 中创建一个新的更新日志条目。该文件应命名为 <issueid>.<type>.rst,其中 issueid 是与更改相关的 issue 编号,typefeatureimprovementbugfixdocdeprecationbreakingvendortrivial 之一。如果更改不影响 pytest 的文档化行为,您可以跳过创建更新日志条目。

  9. 如果尚未添加您自己到 AUTHORS 文件中,请按字母顺序添加。

  10. 在您的测试通过并且您对您的更改感到满意后,提交并推送

    $ git commit -a -m "<commit message>"
    $ git push -u
    
  11. 最后,使用此数据通过 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 表达式根据 termal 的实际输出进行检查

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 提交历史的示例

  1. 杂项提交

    • 实现 X

    • 修复 test_a

    • 我自己 添加到 AUTHORS

    • fixup! 修复 test_a

    • 更新 tests/test_integration.py

    • origin/main 合并到 PR 分支

    • 更新 tests/test_integration.py

    在这种情况下,首选使用 Squash 合并策略:提交历史有点混乱(不是贬义,通常只是因为他们知道更改最终会被压缩在一起而提交更改),因此将所有内容压缩成单个提交是最好的。您必须清理提交消息,确保它包含有用的详细信息。

  2. 与同一主题相关的单独提交

    • 实现 X

    • 我自己 添加到 AUTHORS

    • 更新 CHANGELOG X

    在这种情况下,首选使用 Squash 合并策略:虽然提交历史不像上面的例子那样“混乱”,但单独的提交并没有带来太大的整体价值,特别是当查看几个月/几年后的更改时。

  3. 单独的提交,每个提交都有自己的主题(重构、重命名等),但仍然有一个更大的主题/目的。

    • 重构 X 功能 Y 做准备

    • 删除 未使用的 方法

    • 实现 功能 Y

    在这种情况下,首选使用 Merge 合并策略:每个提交本身都很有价值,即使它们整体上服务于一个共同的主题。稍后查看历史记录,最好将删除未使用的方法单独放在自己的提交中,以及更多信息(例如它是如何首先变得未使用的)。

  4. 单独的提交,每个提交都有自己的主题,但除了改进代码库(使用更现代的技术、改进类型标注、删除混乱等)之外,没有更大的主题/目的。

    • 改进 X 中的 内部名称

    • 添加 类型 注解 Y

    • 删除 不必要的 dict 访问

    • 删除 由于 EOL Python 导致的 无法访问的 代码

    在这种情况下,首选使用 Merge 合并策略:每个提交本身都很有价值,并且每个提交的信息从长远来看都很有价值。

如前所述,这些是总体指南,而不是一成不变的规则。此主题在 #12633 中讨论过。

Backport PRs(如那些从 backport 标签自动创建的)应始终被压缩,因为它们保留了原始 PR 作者。

为下一个补丁版本反向移植错误修复

Pytest 每隔几周或几个月发布一个功能版本。在此期间,针对先前的功能版本发布补丁版本,仅包含错误修复。错误修复通常修复回归,但可能是任何应该在下一个功能版本之前到达用户的更改。

例如,假设最新版本是 1.2.3,并且您想在 1.2.4 中包含一个错误修复(查看 https://github.com/pytest-dev/pytest/releases 获取实际的最新版本)。此过程为

  1. 首先,确保错误已在 main 分支中修复,并使用常规拉取请求,如上所述。如果错误修复不再适用于 main,则这是一个例外。

自动方法

backport 1.2.x 标签添加到您要反向移植的 PR。这将创建一个针对 1.2.x 分支的反向移植 PR。

手动方法

  1. git checkout origin/1.2.x -b backport-XXXX # 在此处使用主 PR 编号

  2. merged 消息中找到 PR 上的合并提交,例如

    nicoddemus 将提交 0f8b462 合并到 pytest-dev:main

  3. git cherry-pick --x -m1 REVISION # 使用您在上面找到的修订版 (0f8b462)。

  4. 打开一个以 1.2.x 为目标的 PR

    • 在消息前加上 [1.2.x]

    • 删除 PR 正文,它通常包含重复的提交消息。

谁进行反向移植

如上所述,错误应首先在 main 上修复(除非在极少数情况下,错误仅发生在以前的版本中)。那么,谁应该执行上述反向移植程序?

  1. 如果错误是由核心开发人员修复的,则该核心开发人员的主要责任是进行反向移植。

  2. 但是,通常合并是由另一位维护人员完成的,在这种情况下,如果他们有时间,他们很乐意执行反向移植程序。

  3. 对于非维护人员提交的错误,预计核心开发人员将进行反向移植,通常是合并 main 上的 PR 的人。

  4. 如果非维护人员注意到 main 上已修复但尚未反向移植的错误(由于维护人员忘记应用 needs backport 标签,或只是完全遗漏),他们也欢迎打开包含反向移植的 PR。该程序很简单,并且确实有助于项目的维护。

以上所有内容都不是规则,而仅仅是我们应该对反向移植期望的一些指南/建议。

反向移植应被压缩(而不是合并),因为这样做可以正确保留原始 PR 作者。

处理过时的 issue/PR

过时的 issue/PR 是指 pytest 贡献者要求提出问题/更改,但作者在相当长一段时间后仍未回答/实施,或者讨论只是因为人们似乎失去了兴趣而结束。

人们不回答问题或不实施请求的更改有很多原因:他们可能变得忙碌、失去兴趣,或者只是忘记了,但事实是,这在开源软件中非常常见。

pytest 团队非常感谢每个 issue 和拉取请求,但作为一个每天提交大量 issue 和拉取请求的高容量项目,我们尝试通过定期关闭它们来减少过时的 issue 和 PR 的数量。当以这种方式关闭 issue/拉取请求时,绝不是对 issue/拉取请求所处理主题的驳回,而只是我们清理队列并使维护人员的工作更易于管理的一种方式。提交者始终可以在以后在他们自己的时间重新打开 issue/拉取请求(如果这样做有意义)。

何时关闭

以下是维护人员在决定何时因缺乏活动而关闭 issue/PR 时使用的一些通用规则

  • 标记为 questionneeds information 的 issue:在不活动 14 天后关闭。

  • 标记为 proposal 的 issue:在不活动六个月后关闭。

  • 拉取请求:一个月后,考虑 ping 作者,更新链接的 issue,或考虑关闭。对于即将完成的拉取请求,团队应考虑完成并合并它。

以上不是硬性规则,而仅仅是指南,并且可以在具体情况下进行审查(并且经常进行审查!)。

关闭拉取请求

关闭拉取请求时,需要承认提交它的人所展示的时间、精力和兴趣。如前所述,团队的意图不是完全驳回停滞的拉取请求,而仅仅是清理我们的队列,因此当关闭过时的拉取请求时,需要像下面这样的消息

嗨 <贡献者>,

首先,我们要感谢您为此付出的时间和精力,pytest 团队深表感谢。

但是,我们注意到您已经有一段时间没有更新此 PR 了。pytest 是一个高活跃度的项目,每天都有许多 issue/PR 被打开,因此我们维护人员很难跟踪哪些 PR 已准备好合并、审查或需要更多关注。

因此,由于这些原因,我们认为最好暂时关闭 PR,但这仅仅是为了清理我们的队列,绝不是拒绝您的更改。我们仍然鼓励您在准备好重新开始时重新打开此 PR(只需单击一下按钮即可)。

再次感谢您为此付出的时间,并希望您稍后可能会重新开始!

<再见>

关闭 issue

当提交拉取请求以修复 issue 时,在 PR 描述和/或提交中添加诸如 closes #XYZW 之类的文本(其中 XYZW 是 issue 编号)。有关更多信息,请参阅 GitHub 文档

当 issue 是由于用户错误(例如,误解了某个功能)引起的时,请礼貌地向用户解释为什么提出的 issue 实际上不是 issue,并要求他们在没有其他问题时关闭 issue。如果原始请求者没有回应,则将按照上面处理过时的 issue/PR部分中的描述处理该 issue。