贡献

Contributing

我们非常欢迎并赞赏您的贡献。每一点帮助都很重要,所以不要犹豫!

功能请求和反馈

Feature requests and feedback

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

我们也希望听到您的建议和主张。欢迎 将它们作为问题提交 并且:

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

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

报告错误

Report bugs

报告 pytest 的错误请在 issue tracker 中进行。

如果您要报告错误,请包括以下内容:

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

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

  • 详细的重现错误的步骤。

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

修复错误

Fix bugs

查看 GitHub issues for bugs。 另见适合新贡献者的 “good first issue” issues

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

不要忘记检查您最喜欢的插件的错误追踪器!

实现功能

Implement features

查看 GitHub issues for enhancements

与开发者交谈 以了解您如何实现特定功能。

编写文档

Write documentation

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

  • 更多补充文档。如果您发现某些地方不清楚,请告诉我们。

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

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

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

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

Note

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

$ tox -e docs

构建的文档应位于 doc/en/_build/html, 其中 ‘en’ 指的是文档语言。

Pytest 有一个 API 参考,大部分内容是从被记录项目的文档字符串中 自动生成的。Pytest 使用 Sphinx 文档字符串格式。 例如:

def my_function(arg: ArgType) -> Foo:
   """执行重要操作。

   这里有更详细的信息,以与主题行分开的段落形式呈现。
   使用完整的句子 -- 句子以大写字母开头,以句号结尾。

   可以包含注解文档:

   :param short_arg: 一个决定某些内容的参数。
   :param long_arg:
         一个长说明,跨越多行,如此溢出。
   :returns: 结果。
   :raises ValueError:
         发生此错误时的详细信息。

   .. versionadded:: 6.0

   当使用类型提示时(如本例),上面的注解中包含类型并不是必需的。
   """

向 pytest-dev 提交插件

Submitting Plugins to pytest-dev

pytest 核心、支持代码和一些插件的开发在 pytest-dev 组织下的仓库中进行:

所有 pytest-dev 贡献者团队成员对所有包含的仓库具有写入访问权限。pytest 核心和插件通常使用 pull requests 向相应的仓库进行开发。

pytest-dev 组织的目标是:

  • 为流行的 pytest 插件提供一个中央位置

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

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

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

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

  • 一个 README,描述如何使用插件以及其运行的平台。

  • 一个 LICENSE 文件,包含许可信息,并在其打包元数据中有相应的信息。

  • 一个用于错误报告和增强请求的问题跟踪器。

  • 一个 changelog

如果没有贡献者强烈反对且有两人同意,则可以将该仓库转移到 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 管理员都不会发布您的仓库或以任何方式取得所有权,除非在极少数情况下,某人在数月的联系尝试后变得没有响应。如前所述,目标是共享维护,避免“插件放弃”。

准备拉取请求

Preparing Pull Requests

简短版本

Short version

  1. Fork 该仓库。

  2. 如有必要,从上游获取标签(如果仅克隆了主分支 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,使用问题 ID 号 和 featureimprovementbugfixdocdeprecationbreakingvendortrivial 中的一个作为问题类型。

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

完整版本

Long version

什么是“拉取请求”?它通知项目的核心开发者您希望审核和合并的更改。拉取请求存储在 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
    

    # 现在,从“main”创建自己的分支:

    $ git checkout -b your-bugfix-branch-name main

    由于我们使用“major.minor.micro”版本号,bug 修复通常会在微版本中发布,而特性会在小版本中发布,不兼容的更改则会在大版本中发布。

    您将需要标签来进行本地测试,因此请确保从主仓库获取标签。如果您怀疑没有获取到,请将主仓库设置为 upstream 并获取标签:

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

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

  3. 安装 pre-commit 及其钩子到 pytest 仓库:

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

    之后,pre-commit 将在您提交时运行。

    https://pre-commit.com/ 是一个管理和维护多语言预提交钩子的框架,以确保代码风格和格式一致。

  4. 安装 tox

    Tox 用于运行所有测试,并将自动设置虚拟环境以运行测试。 (将隐式使用 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),您可以这样做:

    $ tox -e py39 -- --pdb
    

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

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

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

  7. 如果您更喜欢直接运行测试,而不是使用 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
    
  8. changelog 中创建一个新的变更日志条目。文件应命名为 <issueid>.<type>.rst, 其中 issueid 是与更改相关的问题编号,type 是以下之一: featureimprovementbugfixdocdeprecationbreakingvendortrivial。 如果更改不影响 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

编写测试

Writing Tests

为插件或 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 选项的回归测试应该放在 test_cacheprovider.py 中,因为该选项是在 cacheprovider.py 中实现的。如果不确定,可以打开一个 PR,提出您的最佳猜测,我们可以在代码中讨论这个问题。

加入开发团队

Joining the Development Team

任何成功完成了一个不需要开发团队额外工作即可合并的拉取请求的人,若愿意,可以获得提交访问权限(如果我们忘记询问,请友好地提醒我们)。这并不意味着您的贡献工作流程有所改变:每个人都要经过相同的拉取请求和审核流程,没有人会在未经批准的情况下合并自己的拉取请求。不过,这确实意味着您可以更全面地参与开发过程,因为您可以在审核其他贡献者的拉取请求后自己合并它们。

合并/压缩指南

Merge/squash guidelines

当一个拉取请求被批准并准备合并到 main 分支时,可以选择 合并(merge) 提交而不改变,或 压缩(squash) 所有提交为一个单一的提交。

以下是关于如何进行的指南,基于单个拉取请求提交历史的示例:

  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 标签自动创建的请求)应始终 压缩 ,因为它们保留了原始拉取请求的作者。

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

Backporting bug fixes for the next patch release

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

假设最新的发布版本是 1.2.3,而您想在 1.2.4 中包含一个错误修复(请查看 https://github.com/pytest-dev/pytest/releases 以获取实际的最新发布)。这个过程如下:

  1. 首先,确保在 main 分支中修复了该错误,使用常规的拉取请求,如上所述。如果错误修复不再适用于 main,则此步骤不适用。

自动方法:

在您想要回退的拉取请求上添加 backport 1.2.x 标签。这将创建一个针对 1.2.x 分支的回退拉取请求。

手动方法:

  1. git checkout origin/1.2.x -b backport-XXXX # 在这里使用主要拉取请求编号

  2. 在拉取请求的 merged 消息中找到合并提交,例如:

    nicoddemus merged commit 0f8b462 into pytest-dev:main

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

  4. 打开一个目标为 1.2.x 的拉取请求:

    • [1.2.x] 前缀消息。

    • 删除拉取请求正文,它通常包含重复的提交信息。

谁负责反向移植

Who does the backporting

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

  1. 如果错误是由核心开发者修复的,那么该核心开发者有主要责任进行回退。

  2. 然而,合并通常是由其他维护者完成的,在这种情况下,如果他们有时间,进行回退程序是很友好的。

  3. 对于由非维护者提交的错误,通常预计核心开发者会进行回退,通常是合并了 main 上的拉取请求的那位开发者。

  4. 如果非维护者注意到在 main 上修复的错误但尚未回退(由于维护者忘记应用 needs backport 标签,或者单纯遗漏),他们也可以打开一个带有回退的拉取请求。这个过程简单,并且确实有助于项目的维护。

上述所有内容并不是规则,而只是关于我们应期待回退的一些指导方针/建议。

回退应 被压缩(squashed) (而不是 合并(merged) ),因为这样可以正确保留原始拉取请求的作者。

处理过时的问题/PR

Handling stale issues/PRs

过时的问题/拉取请求是指pytest贡献者询问了问题/变更,但作者在相对较长的时间内没有回应/实施,或者讨论因人们似乎失去兴趣而停止。

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

pytest团队非常重视每一个问题和拉取请求,但由于这是一个高流量项目,每天都会提交许多问题和拉取请求,我们试图通过定期关闭它们来减少过时的问题和拉取请求。当以这种方式关闭问题/拉取请求时,这绝不是对该问题/拉取请求所涉及主题的否定,而只是我们清理队列、使维护者的工作更易管理的一种方式。提交者如果认为合适,始终可以在稍后重新打开问题/拉取请求。

何时关闭

When to close

以下是维护者在决定何时因缺乏活动而关闭问题/拉取请求时使用的一些一般规则:

  • 标记为 questionneeds information 的问题:在14天无活动后关闭。

  • 标记为 proposal 的问题:在六个月无活动后关闭。

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

上述内容 不是硬性规则 ,而仅仅是 指导方针 ,可以根据具体情况(且通常会)逐案审查。

关闭拉取请求

Closing pull requests

在关闭拉取请求时,需要承认提交者所展现出的时间、努力和兴趣。正如之前提到的,团队并不想完全拒绝一个停滞的拉取请求,而只是想清理我们的队列,因此在关闭一个变得无效的拉取请求时,像下面这样的消息是适当的:

Hi <contributor>,

首先,我们要感谢你在此项目上投入的时间和努力,pytest团队对此深表感激。

然而,我们注意到你已经有一段时间没有更新这个PR了。pytest是一个活跃的项目,每天都有许多问题和PR被打开,因此我们这些维护者很难追踪哪些PR准备合并,哪些需要审查,或者哪些需要更多关注。

所以出于这些原因,我们认为现在最好关闭这个PR,但这仅仅是为了清理我们的队列,并不是对你修改的拒绝。我们仍然鼓励你在准备好时重新打开这个PR(这只需轻点一下按钮)。

再次感谢你在此项目上的努力,希望你在未来能再次关注这个问题!

<bye>

关闭问题

Closing issues

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

当问题是由于用户错误(例如对功能的误解)引起时,请礼貌地向用户解释所提出的问题实际上并不是问题,并请他们在没有进一步问题时关闭该问题。如果原始请求者没有回复,则将按照上面“处理无效问题/PR”_部分中所述进行处理。