贡献者指南#

注意

本文档假设您对使用 GitHub 拉取请求为开源科学 Python 项目做贡献有一定的了解。如果您不属于这种情况,建议您先查阅新贡献者常见问题。如果您正在使用 LLM 或任何其他 AI 模型,您仍然需要遵循此处描述的流程。

开发工作流程#

  1. 如果您是首次贡献者

    • 转到networkx/networkx并点击“fork”按钮创建您自己的项目副本。

    • 将项目克隆到您的本地计算机

      git clone git@github.com:your-username/networkx.git
      
    • 导航到 networkx 文件夹并添加上游仓库

      git remote add upstream git@github.com:networkx/networkx.git
      
    • 现在,您有远程仓库,命名为

      • upstream,指代 networkx 仓库

      • origin,指代您的个人 fork

    • 接下来,您需要设置构建环境。以下是两种流行的环境管理器的说明

      • venv (基于 pip)

        # Create a virtualenv named ``networkx-dev`` that lives in the directory of
        # the same name
        python -m venv networkx-dev
        # Activate it
        source networkx-dev/bin/activate
        # Install main development and runtime dependencies of networkx
        pip install -r requirements/default.txt -r requirements/test.txt -r requirements/developer.txt
        #
        # (Optional) Install pygraphviz and pydot packages
        # These packages require that you have your system properly configured
        # and what that involves differs on various systems.
        # pip install -r requirements/extra.txt
        #
        # Build and install networkx from source
        pip install -e .
        # Test your installation
        pytest --pyargs networkx
        
      • conda (Anaconda 或 Miniconda)

        # Create a conda environment named ``networkx-dev``
        conda create --name networkx-dev
        # Activate it
        conda activate networkx-dev
        # Install main development and runtime dependencies of networkx
        conda install -c conda-forge --file requirements/default.txt --file requirements/test.txt --file requirements/developer.txt
        #
        # (Optional) Install pygraphviz and pydot packages
        # These packages require that you have your system properly configured
        # and what that involves differs on various systems.
        # conda install -c conda-forge --file requirements/extra.txt
        #
        # Install networkx from source
        pip install -e .
        # Test your installation
        pytest --pyargs networkx
        
    • 最后,我们建议您安装 pre-commit,它可以检查您的代码是否符合格式规范

      pre-commit install
      
  2. 开发您的贡献

    • 拉取上游的最新更改

      git checkout main
      git pull upstream main
      
    • 为您想开发的特性创建一个分支。由于分支名将出现在合并消息中,请使用一个合适的名字,例如 'bugfix-for-issue-1480'

      git checkout -b bugfix-for-issue-1480 main
      
    • 随着开发进度进行本地提交 (git addgit commit)

  3. 测试您的贡献

    • 在本地运行测试套件(详情见测试

      PYTHONPATH=. pytest networkx
      
    • 在提交拉取请求*之前*在本地运行测试有助于及早发现问题并减轻持续集成系统的负载。

  4. 确保您的贡献格式正确。

    • 如果您按照步骤 1 的建议安装了 pre-commit,所有必要的 linting 检查应在提交时自动运行。如果存在任何格式问题,提交将不会成功,并且 linting 建议将自动应用到补丁中。只需第二次执行 git addgit commit 即可接受建议的格式更改。

    • 如果上述操作因任何原因失败,您还可以通过以下命令对整个代码库运行 linter

      pre-commit run --all-files
      
  5. 提交您的贡献

    • 将您的更改推送到您在 GitHub 上的 fork

      git push origin bugfix-for-issue-1480
      
    • 前往 GitHub。新分支将显示一个绿色的“拉取请求”按钮,点击它。

    • 如果您愿意,可以在邮件列表上发布消息解释您的更改或请求评审。

  6. 评审流程

    • 每次拉取请求 (PR) 更新都会触发一组持续集成服务,这些服务会检查代码是否符合标准并通过所有测试。您的 PR 必须通过这些检查才能合并。如果其中一项检查失败,您可以通过点击“失败”图标(红叉)并检查构建和测试日志来找出原因。

    • 评审者(其他开发者和感兴趣的社区成员)将在您的 PR 上撰写内联和/或一般评论,帮助您改进其实现、文档和风格。项目中工作的每一位开发者都会经历代码评审,我们将其视为一种友好的交流,从中我们都能学到东西,并且整体代码质量受益。因此,请不要让评审打消您贡献的积极性:它的唯一目的是提高项目质量,而不是批评(毕竟,我们非常感谢您捐献的时间!)。

    • 要更新您的 PR,请在您的本地仓库中进行更改并提交。一旦这些更改被推送到(与之前相同的分支),PR 将自动更新。

    注意

    如果 PR 关闭了一个 issue,请确保 GitHub 知道在 PR 合并时自动关闭该 issue。例如,如果 PR 关闭了 issue 号 1480,您可以在 PR 描述或提交消息中使用短语“Fixes #1480”。

  7. 记录弃用和 API 变更

    如果您的更改引入了任何 API 修改,包括弃用,请确保 PR 带有 type: API 标签。

    设置函数以进行弃用

    • 使用弃用警告来提醒用户。例如

      msg = "curly_hair is deprecated and will be removed in v3.0. Use sum() instead."
      warnings.warn(msg, DeprecationWarning)
      
    • 将警告过滤器添加到 networkx/conftest.py

      warnings.filterwarnings(
          "ignore", category=DeprecationWarning, message=<start of message>
      )
      
    • doc/developer/deprecations.rst 中添加提醒,以便团队将来移除已弃用的功能。例如

      * In ``utils/misc.py`` remove ``generate_unique_node`` and related tests.
      

    注意

    致评审者:请确保合并消息包含对更改的简要描述,如果 PR 关闭了一个 issue,请添加例如“Closes #123”,其中 123 是 issue 号。

upstream main 的分歧#

如果 GitHub 指示您的拉取请求分支无法再自动合并,请将主分支合并到您的分支中

git fetch upstream main
git merge upstream/main

如果发生任何冲突,需要在继续之前解决它们。使用以下命令查看哪些文件存在冲突

git status

将显示类似以下的消息

Unmerged paths:
  (use "git add <file>..." to mark resolution)

  both modified:   file_with_conflict.txt

在冲突的文件中,您会找到类似这样的部分

<<<<<<< HEAD
The way the text looks in your branch
=======
The way the text looks in the main branch
>>>>>>> main

选择应保留的文本版本,并删除其余部分

The way the text looks in your branch

现在,添加已修复的文件

git add file_with_conflict.txt

解决所有合并冲突后,执行

git commit

注意

高阶 Git 用户可能希望使用 rebase 而非 merge,但我们无论如何都会 squash 和 merge PR。

指南#

  • 所有代码都应该有测试。

  • 所有代码都应该有文档,并遵循与 NumPy 和 SciPy 相同的标准

  • 所有更改都会经过评审。如果您提交拉取请求后没有收到回复,请在邮件列表上询问。

  • 默认依赖项列在 requirements/default.txt 中,额外(即可选)依赖项列在 requirements/extra.txt 中。我们不常添加新的默认和额外依赖项。如果您正在考虑添加具有依赖项的代码,您应该首先考虑添加一个画廊示例。通常,新的提议依赖项将首先作为额外依赖项添加。额外依赖项应该易于在所有平台上安装且被广泛使用。新的默认依赖项应该易于在所有平台上安装,在社区中被广泛使用,并且在 NetworkX 中有广泛使用的潜力。

  • 使用以下导入约定

    import numpy as np
    import scipy as sp
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    import pandas as pd
    import networkx as nx
    

    在导入 scipy 并使用 sp 作为别名后

    import scipy as sp
    

    从顶层 sp 命名空间访问相关的 scipy 子包,例如

    sp.sparse.linalg
    

    而不是 from scipy.sparse import linalgimport scipy.sparse.linalg as spla

    例如,许多库都有 linalg 子包:nx.linalg, np.linalg, sp.linalg, sp.sparse.linalg。上述导入模式使得任何特定 linalg 实例的来源清晰明确。

  • 使用 networkx/utils/decorators.py 中的装饰器 not_implemented_for 来指定函数不接受 ‘directed’, ‘undirected’, ‘multigraph’ 或 ‘graph’。被装饰函数的第一个参数应该是需要检查的图对象。

    @nx.not_implemented_for("directed", "multigraph")
    def function_not_for_MultiDiGraph(G, others):
        # function not for graphs that are directed *and* multigraph
        pass
    
    
    @nx.not_implemented_for("directed")
    @nx.not_implemented_for("multigraph")
    def function_only_for_Graph(G, others):
        # function not for directed graphs *or* for multigraphs
        pass
    

测试#

networkx 有一个广泛的测试套件,以确保在您的系统上正确执行。在合并拉取请求之前,测试套件必须通过,并且应该添加测试来覆盖对代码库的任何修改。我们使用 pytest 测试框架,测试文件位于各种 networkx/submodule/tests 文件夹中。

运行所有测试

$ PYTHONPATH=. pytest networkx

或运行特定子模块的测试

$ PYTHONPATH=. pytest networkx/readwrite

或运行特定文件的测试

$ PYTHONPATH=. pytest networkx/readwrite/tests/test_edgelist.py

或运行该文件中的单个测试

$ PYTHONPATH=. pytest networkx/readwrite/tests/test_edgelist.py::test_parse_edgelist_with_data_list

使用 --doctest-modules 运行 doctest。例如,使用以下命令运行所有测试和所有 doctest

$ PYTHONPATH=. pytest --doctest-modules networkx

理想情况下,一个模块的测试应该覆盖该模块中的所有代码,即语句覆盖率应达到 100%。

要测量测试覆盖率,运行

$ PYTHONPATH=. pytest --cov=networkx networkx

这将打印一个报告,其中 networkx 中每个文件占一行,详细说明测试覆盖率

Name                                             Stmts   Miss Branch BrPart  Cover
----------------------------------------------------------------------------------
networkx/__init__.py                                33      2      2      1    91%
networkx/algorithms/__init__.py                    114      0      0      0   100%
networkx/algorithms/approximation/__init__.py       12      0      0      0   100%
networkx/algorithms/approximation/clique.py         42      1     18      1    97%
...

添加测试#

如果您是测试新手,请查看现有测试文件以获取示例。不要因为测试而阻止您提交贡献!如果您不确定如何操作或遇到困难,无论如何都请提交您的拉取请求。我们将在代码评审期间帮助您创建测试并解决任何问题。

图像比较#

运行图像比较

$ PYTHONPATH=. pytest --mpl --pyargs networkx.drawing

--mpl 参数告诉 pytest 使用 pytest-mpl 将生成的图与存储在 networkx/drawing/tests/baseline 中的基准图进行比较。

要添加新测试,请在 networkx/drawing/tests 中添加一个测试函数,该函数返回一个 Matplotlib 图形对象(或任何具有 savefig 方法的图形对象),并按如下方式进行装饰

@pytest.mark.mpl_image_compare
def test_barbell():
    fig = plt.figure()
    barbell = nx.barbell_graph(4, 6)
    # make sure to fix any randomness
    pos = nx.spring_layout(barbell, seed=42)
    nx.draw(barbell, pos=pos)
    return fig

然后创建一个基准图像以便稍后进行比较

$ pytest -k test_barbell --mpl-generate-path=networkx/drawing/tests/baseline

注意

为了防止仓库体积过大,我们倾向于限制包含的基准图像的大小和数量。

并测试

$ pytest -k test_barbell --mpl

文档#

我们使用 Sphinx 生成 API 和参考文档。

预构建的版本可以在以下位置找到

包括稳定版和最新(即开发中)版本。

说明#

安装 NetworkX 及其依赖项后,进入根目录并执行以下命令安装构建文档所需的 Python 包

pip install -r requirements/doc.txt

构建示例画廊还需要 requirements/extra.txtrequirements/example.txt 中列出的依赖项

pip install -r requirements/extra.txt
pip install -r requirements/example.txt

要构建 HTML 文档,请进入 doc/ 目录并执行

make html

这将生成一个 build/html 子目录,其中包含构建好的文档。如果未安装 extra.txtexample.txt 中的依赖项,请使用以下命令构建不生成图形的 HTML 文档

make html-noplot

要构建 PDF 文档,请进入

make latexpdf

您需要安装 LaTeX。

注意

sphinx 支持许多其他输出格式。输入不带任何参数的 make 命令可以查看所有内置选项。

添加示例#

画廊示例由 sphinx-gallery 管理。示例画廊的源文件是 examples/ 目录下的 .py 脚本,这些脚本生成一个或多个图形。在构建文档时,它们由 sphinx-gallery 自动执行。输出被收集并组装到画廊中。

在本地构建示例画廊需要在您的开发环境中安装 requirements/example.txt 中的额外依赖项。

您可以通过在仓库的 examples 目录下的某个子目录中放置一个新的 .py 文件来添加新的绘图。参考其他示例以了解格式。

注意

画廊示例的文件名应以 plot_ 开头,例如 plot_new_example.py

创建优秀画廊绘图的一般指南

  • 示例应该突出一个单一的特性/命令。

  • 尽量使示例尽可能简单。

  • 示例所需的数据应包含在与示例脚本相同的目录中。

  • 添加注释以解释代码中不明显的部分。

  • 描述您展示的特性,并链接到文档中的其他相关部分。

添加参考文献#

如果您正在贡献一个新算法(或改进现有算法),函数的 docstring 中也应该提供参考文献或资源。对于已发表论文的引用,我们尽量遵循芝加哥引用格式。生成此格式引用的最快方法是在Google Scholar上搜索论文并点击 cite 按钮。它将弹出论文的多种引用格式,复制其中的 Chicago 格式即可。

我们倾向于为 URL 添加 DOI 链接。如果 DOI 链接解析到文章的付费版本,我们倾向于添加 arXiv 版本(如果可用)或论文的任何其他可公开访问副本的链接。

参考文献示例

.. [1] Cheong, Se-Hang, and Yain-Whar Si. "Force-directed algorithms for schematic drawings and
placement: A survey." Information Visualization 19, no. 1 (2020): 65-91.
https://doi.org/10.1177%2F1473871618821740

如果资源是以 PDF/DOCX/PPT 格式上传到网络上(讲义、演示文稿),最好使用互联网档案馆 (wayback machine) 为资源创建快照并链接到互联网档案馆的链接。资源的 URL 可能会改变,这会导致文档中的链接不可达。

在文档中使用数学公式和 Latex 格式#

处理包含数学符号或公式的 docstring 时,使用原始字符串 (r""") 以确保正确渲染。虽然 LaTeX 格式可以改善渲染文档的外观,但最好保持简单易读。

数学公式示例

.. math::
    Ax = \lambda x
\[Ax = \lambda x\]

一些行内数学公式

These are Cheeger's Inequalities for \d-Regular graphs:
$\frac{d- \lambda_2}{2} \leq h(G) \leq \sqrt{2d(d- \lambda_2)}$

这些是 d-正则图的 Cheeger 不等式: \(\frac{d- \lambda_2}{2} \leq h(G) \leq \sqrt{2d(d- \lambda_2)}\)

Bug (错误)#

请在GitHub 上报告 Bug

政策#

所有与项目的互动都须遵守NetworkX 行为准则

我们也遵循以下政策