贡献

非常欢迎和感谢您的贡献。点滴帮助都弥足珍贵,所以请不要犹豫!

功能请求和反馈

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

我们也期待听到您的提议和建议。请随时以问题形式提交,并

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

  • 将范围尽可能缩小。这将使其更容易实现。

报告 Bug

问题追踪器中报告 pytest 的 Bug。

如果您正在报告 Bug,请包含

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

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

  • 重现 Bug 的详细步骤。

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

修复 Bug

浏览GitHub 上的 Bug 问题。另请参阅对新贡献者友好的“良好首个问题”

与开发者交流,了解如何修复特定的 Bug。为了表明您将处理某个特定问题,请在该问题上添加相关评论。

别忘了也检查您最喜欢的插件的问题追踪器!

实现功能

浏览GitHub 上的增强功能问题

与开发者交流,了解如何实现特定的功能。

编写文档

Pytest 总是需要更多的文档。具体需要什么?

  • 更多补充文档。您是否发现有什么不清楚的地方?

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

  • Docstrings。它们永远不嫌多。

  • 博客文章、文章等——都非常感谢。

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

注意

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

$ tox -e docs

构建好的文档应位于doc/en/_build/html,其中“en”指文档语言。

Pytest 有一个 API 参考,其中很大一部分是根据文档项目的 docstring 自动生成的。Pytest 使用Sphinx docstring 格式。例如

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 讨论区发布新话题来提交您的插件,指向您现有的 pytest 插件仓库,该仓库必须包含以下内容

  • PyPI 上有打包元数据,包含以pytest-为前缀的名称、版本号、作者、简短和详细描述。

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

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

  • 一个LICENSE文件,包含许可信息,且与其打包元数据中的信息匹配。

  • 一个用于 Bug 报告和增强功能请求的问题追踪器。

  • 一个更新日志

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

以下是仓库转移通常如何进行的概述(以名为joedoe/pytest-xyz的仓库为例)

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

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

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

    • pytest-xyz-admin拥有管理员访问权限;

    • pytest-xyz-developers拥有写入访问权限;

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,py313
    

    上述测试环境通常足以涵盖大多数本地情况。

  6. 编写更新日志条目:changelog/2574.bugfix.rst,使用问题 ID 号和featureimprovementbugfixdocdeprecationbreakingvendorpackagingcontribmisc之一作为问题类型。

  7. 除非您的更改是琐碎的或文档修复(例如,错别字或小部分的重写),否则请将您自己按字母顺序添加到AUTHORS文件中。

详细版本

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

以下是一个简单的概述,包含 pytest 特有的部分

  1. Fork pytest GitHub 仓库。将您的 Fork 仓库命名为pytest是没问题的,因为它将存在于您的用户下。

  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
    

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

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

    $ 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及其钩子

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

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

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

  4. 安装 tox

    Tox 用于运行所有测试,并将自动设置虚拟环境来运行测试。(将隐式使用https://virtualenv.pypa.io/en/stable/

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

    您的系统需要有支持的 Python 版本。现在运行测试就像发出以下命令一样简单

    $ tox -e linting,py
    

    此命令将通过“tox”工具对您的默认 Python 版本运行测试,并执行“lint”编码风格检查。

  6. 您现在可以编辑本地工作副本并根据需要再次运行测试。请遵循PEP-8命名规范。

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

    $ tox -e py313 -- --pdb
    

    或者只在 Python 3.12 上运行特定测试模块中的测试

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

    提交时,pre-commit将在必要时重新格式化文件。

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

    $ 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是与更改相关的问题编号,typefeatureimprovementbugfixdocdeprecationbreakingvendorpackagingcontribmisc之一。如果更改不影响 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表达式根据终端的实际输出进行检查

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. 杂项提交

    • Implement X

    • Fix test_a

    • Add myself to AUTHORS

    • fixup! Fix test_a

    • Update tests/test_integration.py

    • Merge origin/main into PR branch

    • Update tests/test_integration.py

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

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

    • Implement X

    • Add myself to AUTHORS

    • Update CHANGELOG for X

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

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

    • Refactor class X in preparation for feature Y

    • Remove unused method

    • Implement feature Y

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

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

    • Improve internal names in X

    • Add type annotations to Y

    • Remove unnecessary dict access

    • Remove unreachable code due to 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

自动方法

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

手动方法

  1. git checkout origin/1.2.x -b backport-XXXX # 这里使用主 PR 号

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

    nicoddemus merged commit 0f8b462 into pytest-dev:main

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

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

    • 消息前缀为[1.2.x]

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

谁来反向移植

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

  1. 如果 bug 是由核心开发者修复的,那么反向移植是该核心开发者的主要责任。

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

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

  4. 如果非维护者发现main上已修复但尚未反向移植的 bug(由于维护者忘记应用needs backport标签,或根本没有注意到),他们也欢迎打开一个带有反向移植的 PR。该过程很简单,并且对项目的维护非常有帮助。

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

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

处理陈旧的问题/PR

陈旧的问题/PR 是指 pytest 贡献者提出了问题/更改,而作者在一段相当长的时间后尚未回答/实施,或者讨论因人们似乎失去兴趣而简单地中止。

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

pytest 团队非常感谢每一个问题和拉取请求,但作为一个每天提交大量问题和拉取请求的高流量项目,我们通过定期关闭陈旧问题和 PR 来减少它们的数量。以这种方式关闭问题/拉取请求绝不意味着驳回问题/拉取请求所处理的主题,而只是我们清理队列并使维护者的工作更易于管理的一种方式。提交者如果认为有意义,可以随时在他们自己的时间重新打开问题/拉取请求。

何时关闭

以下是维护者决定何时因不活跃而关闭问题/PR 的一些一般规则

  • 标记为questionneeds information的问题:不活跃 14 天后关闭。

  • 标记为proposal的问题:不活跃六个月后关闭。

  • 拉取请求:一个月后,考虑提醒作者,更新链接问题,或考虑关闭。对于接近完成的拉取请求,团队应考虑完成并合并它。

以上不是硬性规定,而仅仅是指导方针,并且可以(而且通常会!)根据具体情况进行审查。

关闭拉取请求

关闭拉取请求时,需要肯定提交者所花费的时间、精力和表现出的兴趣。如前所述,团队的目的不是完全驳回一个停滞不前的拉取请求,而仅仅是为了清理我们的队列,因此在关闭一个已经过期的拉取请求时,需要发送如下消息

Hi <贡献者>,

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

然而,我们注意到您已经有一段时间没有更新此 PR 了。pytest 是一个高活跃度的项目,每天都有大量问题/PR 被提出,因此我们维护者很难跟踪哪些 PR 已准备好合并、待审查或需要更多关注。

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

再次感谢您为此付出的时间,希望您以后能够回来继续!

<再见>

关闭问题

当提交一个拉取请求来解决一个问题时,请在 PR 描述和/或提交中添加类似closes #XYZW的文本(其中XYZW是问题编号)。有关更多信息,请参阅GitHub 文档

当问题是由于用户错误(例如对功能理解错误)引起的时,请礼貌地向用户解释为什么提出的问题实际上不是问题,并请他们在没有进一步问题时关闭问题。如果原始请求者没有响应,问题将按照上面处理陈旧问题/PR一节所述进行处理。