贡献 Scrapy

重要提示

请仔细检查您正在阅读本文档的最新版本:https://docs.scrapy.net.cn/en/latest/contributing.html

参与本项目即表示您同意遵守我们的行为准则。请将不可接受的行为报告给opensource@zyte.com

有许多方法可以为 Scrapy 做出贡献。以下是一些示例:

  • 问题追踪器中报告错误和请求功能,并尽量遵循下方报告错误中详述的指南。

  • 提交新功能和/或错误修复的补丁。请阅读下方编写补丁提交补丁,了解如何编写和提交补丁的详细信息。

  • 撰写关于 Scrapy 的博客。告诉全世界您如何使用 Scrapy。这将为新手提供更多示例,并有助于提高 Scrapy 项目的知名度。

  • 加入Scrapy subreddit,分享您关于如何改进 Scrapy 的想法。我们始终乐于接受建议。

  • Stack Overflow上回答 Scrapy 问题。

报告错误

注意

将安全问题报告给scrapy-security@googlegroups.com。这是一个私人列表,仅对受信任的 Scrapy 开发者开放,其档案不公开。

一份撰写良好的错误报告非常有帮助,因此在报告新错误时请牢记以下指南。

  • 首先检查常见问题,看看您的问题是否在常见问题中得到了解决

  • 如果您有关于 Scrapy 用法的一般性问题,请在Stack Overflow上提问(使用“scrapy”标签)。

  • 检查未解决的问题,看看该问题是否已有人报告。如果已经报告,请不要忽视该报告,而是检查票证历史和评论。如果您有额外有用的信息,请留下评论,或者考虑发送一个带有修复的拉取请求

  • 搜索scrapy-users列表和Scrapy subreddit,查看该问题是否已在那里讨论过,或者如果您不确定所看到的是否是 bug。您也可以在#scrapy IRC 频道提问。

  • 撰写完整、可复现、具体的错误报告。测试用例越小越好。请记住,其他开发者无法访问您的项目来复现该 bug,因此请包含重现它所需的所有相关文件。例如,请参阅 StackOverflow 关于创建展示该问题的最小、完整、可验证示例的指南。

  • 提供完整可复现示例的最棒方式是发送一个拉取请求,该请求将一个失败的测试用例添加到 Scrapy 测试套件中(参见提交补丁)。即使您不打算亲自修复该问题,这也会很有帮助。

  • 包含scrapy version -v的输出,以便处理您 bug 的开发者准确了解它发生在哪个版本和平台上,这对于复现问题或了解它是否已修复通常非常有帮助。

寻找工作

如果您已决定为 Scrapy 做出贡献,但不知道该贡献什么,您有以下几种选择来寻找待完成的工作:

  • 查看GitHub 贡献页面,其中列出了标记为适合初学者的未解决问题。

    还有需要帮助的问题,但请注意,有些可能需要熟悉 Scrapy 代码库。您也可以选择任何其他未标记为讨论的问题。

  • 如果您喜欢编写文档,也有文档问题,但请注意,有些也可能需要熟悉 Scrapy 代码库。

  • 如果您喜欢编写自动化测试,您可以致力于提高我们的测试覆盖率

  • 如果您喜欢代码清理,我们欢迎修复静态分析工具检测到的问题。请参阅pyproject.toml中可能需要解决的被忽略的问题。

    请注意,有些问题我们根本不打算解决,通常会附上解释原因的评论;不要与那些解释问题内容(针对非描述性问题代码)的评论混淆。

如果您发现了一个问题,在提问之前请务必阅读整个问题讨论串。这包括在其他地方提及问题时,问题讨论串中显示的相关问题和拉取请求。

我们不分配问题,您也不需要宣布您将开始处理某个问题。如果您想处理某个问题,只需直接为其编写补丁即可。

不要仅仅因为存在针对某个问题的开放拉取请求就放弃它。首先检查开放的拉取请求是否活跃。即使有些活跃,如果您认为可以构建更好的实现,请随时使用您的方法创建一个拉取请求。

如果您决定在没有开放问题的情况下处理某项工作,请

  • 不要为代码覆盖率或代码清理创建问题,请直接创建拉取请求。

  • 不要立即同时创建问题和拉取请求。您可以先提出问题以获得关于该问题是否值得解决的反馈,只有在团队反馈积极的情况下再创建拉取请求;或者,如果您认为通过您的代码进行讨论会更容易,则只创建拉取请求。

  • 不要仅仅为了添加文档字符串而添加,或仅仅为了解决被抑制的 Ruff 问题。我们期望文档字符串仅在它们对读者有重要意义时才存在,例如解释一些不容易通过阅读相应代码理解的内容,总结一个冗长、难以阅读的实现,提供关于调用代码的上下文,或指示被调用代码中故意未捕获的异常。

  • 不要为了仅仅触及给定代码行并因此提高行覆盖率而添加尽可能多模拟的测试。虽然我们旨在最大化测试覆盖率,但测试应针对真实场景编写,并尽量减少模拟。我们通常更喜欢端到端测试。

编写补丁

补丁写得越好,被接受的可能性就越大,合并的速度也就越快。

编写良好的补丁应具备以下特点:

  • 包含特定更改所需的最小代码量。小补丁更容易审查和合并。因此,如果您进行了多项更改(或错误修复),请考虑为每次更改提交一个补丁。不要将多项更改合并为一个补丁。对于重大更改,请考虑使用补丁队列。

  • 通过所有单元测试。请参阅下方运行测试

  • 包含一个(或多个)测试用例,用于检查已修复的错误或新增的功能。请参阅下方编写测试

  • 如果您要添加或更改公共(有文档)API,请将文档更改包含在同一个补丁中。请参阅下方文档政策

  • 如果您要添加私有 API,请将正则表达式添加到docs/conf.pycoverage_ignore_pyobjects变量中,以将新的私有 API 从文档覆盖率检查中排除。

    要查看您的私有 API 是否已正确跳过,请按以下方式生成文档覆盖率报告:

    tox -e docs-coverage
    
  • 如果您正在删除已弃用的代码,请首先确保自引入弃用的发布版本以来已过去至少 1 年(12 个月)。请参阅弃用政策

提交补丁

提交补丁的最佳方式是在 GitHub 上提出拉取请求,可以选择先创建一个新问题。

请记住解释修复了什么或新功能(它是什么,为什么需要它等)。您包含的信息越多,核心开发者就越容易理解和接受您的补丁。

如果您的拉取请求旨在解决一个开放问题,请相应地进行链接,例如:

Resolves #123

您也可以在创建补丁之前讨论新功能(或错误修复),但最好是准备好一个补丁来阐明您的论点,并表明您对该主题进行了额外的思考。一个好的起点是在 GitHub 上发送一个拉取请求。它可以足够简单以说明您的想法,并在想法经过验证并证明有用之后再处理文档/测试。或者,您可以先在Scrapy subreddit中开始对话,讨论您的想法。

有时,针对您想解决的问题可能存在一个现有但因某种原因停滞的拉取请求。通常,该拉取请求方向正确,但 Scrapy 维护者要求进行更改,而原始拉取请求作者没有时间处理。在这种情况下,请考虑接手这个拉取请求:创建一个新的拉取请求,包含原始拉取请求的所有提交,以及解决已提出问题的额外更改。这样做非常有帮助;只要通过保留原始作者的提交来承认其贡献,这就不被视为无礼。

您可以通过运行git fetch upstream pull/$PR_NUMBER/head:$BRANCH_NAME_TO_CREATE将现有拉取请求拉取到本地分支(将 'upstream' 替换为 Scrapy 仓库的远程名称,$PR_NUMBER 替换为拉取请求的 ID,$BRANCH_NAME_TO_CREATE 替换为您要在本地创建的分支名称)。另请参阅:https://githubdocs.cn/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally#modifying-an-inactive-pull-request-locally

在编写 GitHub 拉取请求时,请尽量使标题简短但具有描述性。例如,对于 bug #411:“Scrapy hangs if an exception raises in start_requests”,最好使用“Fix hanging when exception occurs in start_requests (#411)”而不是“Fix for #411”。完整的标题有助于快速浏览问题追踪器。

最后,请尝试将美观性更改(PEP 8 合规性、删除未使用的导入等)与功能性更改分开提交。这将使拉取请求更容易审查,并更有可能被合并。

编码风格

为 Scrapy 编写代码时,请遵循以下编码约定:

Pre-commit

我们使用pre-commit在每次提交前自动解决简单的代码问题。

在您创建 Scrapy 仓库的本地克隆副本后

  1. 安装 pre-commit.

  2. 在您的 Scrapy 仓库本地克隆根目录下,运行以下命令:

    pre-commit install
    

现在,每次您创建 Git 提交时,pre-commit 都会检查您的更改。如果发现问题,pre-commit 会中止您的提交,并自动修复这些问题,或者只向您报告。如果它自动修复了这些问题,则再次创建提交应该会成功。否则,您可能需要先手动解决相应的问题。

文档政策

对于 API 成员(类、方法等)的参考文档,请使用文档字符串 (docstrings),并确保 Sphinx 文档使用autodoc扩展来提取文档字符串。API 参考文档应遵循文档字符串约定(PEP 257)并对 IDE 友好:简短、切中要点,并可提供简短示例。

其他类型的文档,例如教程或主题,应包含在docs/目录中的文件中。这包括特定于某个 API 成员但超出 API 参考文档范围的文档。

无论如何,如果某个内容已包含在文档字符串中,请使用autodoc扩展将其提取到文档中,而不是在docs/目录的文件中重复该文档字符串。

涵盖新功能或修改功能的文档更新必须使用 Sphinx 的versionaddedversionchanged指令。请使用VERSION作为版本号,我们将在相应的发布版本之前将其替换为实际版本。当我们发布 Scrapy 的新主要或次要版本时,如果这些指令已超过 3 年,我们将删除它们。

有关已弃用功能的文档必须随着这些功能的弃用而被删除,以免新读者遇到。新的弃用和弃用移除记录在发布说明中。

测试

测试是使用Twisted 单元测试框架实现的。运行测试需要tox

运行测试

要运行所有测试

tox

要运行特定测试(例如tests/test_loader.py),请使用

tox -- tests/test_loader.py

要在特定的 tox 环境中运行测试,请使用-e <name>以及tox.ini中的环境名称。例如,要在 Python 3.10 环境中运行测试,请使用

tox -e py310

您还可以指定一个逗号分隔的环境列表,并使用tox 的并行模式在多个环境中并行运行测试

tox -e py39,py310 -p auto

要将命令行选项传递给pytest,请在调用tox时将它们添加到--之后。使用--会覆盖tox.ini中定义的默认位置参数,因此您也必须在--之后包含这些默认位置参数(scrapy tests

tox -- scrapy tests -x  # stop after first failure

您还可以使用 pytest-xdist 插件。例如,要在 Python 3.10 tox 环境中使用所有 CPU 核心运行所有测试,请使用

tox -e py310 -- scrapy tests -n auto

要查看覆盖率报告,请安装coveragepip install coverage)并运行

coverage report

请参阅coverage --help的输出,了解更多选项,例如 html 或 xml 报告。

编写测试

所有功能(包括新功能和错误修复)都必须包含一个测试用例来检查其是否按预期工作,因此如果您希望补丁更快被接受,请为您的补丁包含测试。

Scrapy 使用单元测试,它们位于tests/目录中。它们的模块名称通常与其测试模块的完整路径相似。例如,项目加载器代码位于

scrapy.loader

它们的单元测试位于

tests/test_loader.py