NumPy 1.26 中文文档(五十一)

2024-07-26 09:52:44 浏览数 (2)

原文:numpy.org/doc/

测试 numpy.i 类型映射

原文:numpy.org/doc/1.26/reference/swig.testing.html

介绍

numpy.i SWIG 接口文件编写测试是一种组合性的头痛。目前,支持 12 种不同的数据类型,每种类型有 74 种不同的参数签名,总共支持 888 个类型映射“开箱即用”。每个类型映射可能需要多个单元测试来验证预期行为,无论是对正确还是不正确的输入。目前,在 numpy/tools/swig 子目录中运行 make test 时会执行超过 1,000 个单独的单元测试。

为了简化这些相似的单元测试,采用了一些高级编程技术,包括 C 和 SWIG 宏,以及 Python 继承。本文档的目的是描述用于验证 numpy.i 类型映射是否按预期工作的测试基础设施。

测试组织

支持三个独立的测试框架,分别用于一维、二维和三维数组。对于一维数组,有两个 C 文件,一个头文件和一个源文件,分别命名为:

代码语言:javascript复制
Vector.h
Vector.cxx 

包含原型和代码的文件,其中有多种函数,其函数参数为一维数组。该文件为:

代码语言:javascript复制
Vector.i 

是一个 SWIG 接口文件,定义了一个名为 Vector 的 Python 模块,用于包装 Vector.h 中的函数,同时利用 numpy.i 中的类型映射正确处理 C 数组。

Makefile 调用 swig 来生成 Vector.pyVector_wrap.cxx,并执行 setup.py 脚本来编译 Vector_wrap.cxx 并链接扩展模块 _Vector.so_Vector.dylib,取决于平台。这个扩展模块和代理文件 Vector.py 都放在 build 目录下的子目录中。

实际测试是通过名为 Python 脚本执行的:

代码语言:javascript复制
testVector.py 

使用标准 Python 库模块 unittest 编写,该模块为 Vector.h 中定义的每个函数的每种支持的数据类型执行多个测试。

二维数组的测试方式完全相同。上述描述同样适用,只是将 Vector 替换为 Matrix。对于三维测试,将 Vector 替换为 Tensor。对于四维测试,将 Vector 替换为 SuperTensor。对于平坦的原位数组测试,将 Vector 替换为 Flat。对于接下来的描述,我们将引用 Vector 测试,但相同的信息也适用于 MatrixTensorSuperTensor 测试。

命令 make test 将确保构建所有测试软件,然后运行所有三个测试脚本。

测试头文件

Vector.h 是一个 C 头文件,定义了一个名为TEST_FUNC_PROTOS的 C 宏,它接受两个参数:TYPE,这是一个数据类型名称,比如unsigned int;和SNAME,这是没有空格的同一数据类型的简称,例如uint。此宏定义了几个具有前缀SNAME的函数原型,并且至少有一个参数是TYPE类型的数组。那些有返回参数的函数返回一个TYPE值。

TEST_FUNC_PROTOS 然后还针对numpy.i所支持的所有数据类型进行了实现:

  • signed char
  • unsigned char
  • short
  • unsigned short
  • int
  • unsigned int
  • long
  • unsigned long
  • long long
  • unsigned long long
  • float
  • double

测试源文件

Vector.cxx 是一个 C 源文件,实现了Vector.h中指定的每个函数原型的可编译代码。它定义了一个 C 宏 TEST_FUNCS,其参数和Vector.h中的TEST_FUNC_PROTOS的工作方式相同。TEST_FUNCS针对上述的 12 种数据类型实现。

测试 SWIG 接口文件

Vector.i 是一个SWIG 接口文件,定义了 python 模块Vector。它遵循使用numpy.i的惯例,如本章所述。它定义了一个SWIG宏%apply_numpy_typemaps,它有一个名为TYPE的单参数。它使用SWIG指令%apply,将提供的 typemap 应用于Vector.h中发现的参数签名。然后,它为numpy.i支持的所有数据类型实现了这个宏。然后它通过%include "Vector.h"来使用numpy.i中的 typemap 包装Vector.h中的所有函数原型。

测试 Python 脚本

使用make构建测试扩展模块后,可以运行testVector.py来执行测试。与其他使用unittest来进行单元测试的脚本一样,testVector.py定义了一个从unittest.TestCase继承的类:

代码语言:javascript复制
class VectorTestCase(unittest.TestCase): 

然而,此类不会直接运行。它作为几个其他 python 类的基类,每个类都专门针对特定的数据类型。VectorTestCase 类存储了两个字符串以供输入信息:

self.typeStr 一个与Vector.hVector.cxx中使用的SNAME前缀之一匹配的字符串。例如,"double"self.typeCode 一个短字符串(通常是单字符),代表 numpy 中的数据类型,并对应于self.typeStr。例如,如果self.typeStr"double",那么self.typeCode应该是"d"

VectorTestCase 类定义的每个测试通过访问Vector模块的字典来提取其尝试测试的 python 函数:

代码语言:javascript复制
length = Vector.__dict__[self.typeStr   "Length"] 

对于双精度测试,这将返回 python 函数Vector.doubleLength

然后我们针对每种支持的数据类型定义了一个新的测试用例类,其定义如下:

代码语言:javascript复制
class doubleTestCase(VectorTestCase):
    def __init__(self, methodName="runTest"):
        VectorTestCase.__init__(self, methodName)
        self.typeStr  = "double"
        self.typeCode = "d" 

这 12 个类都被收集到一个unittest.TestSuite中,然后执行。错误和失败被汇总并作为退出参数返回。任何非零结果表示至少一个测试未通过。

介绍

numpy.i SWIG接口文件编写测试是一个组合头痛的问题。目前支持 12 种不同数据类型,每种数据类型有 74 个不同的参数签名,总共支持 888 个类型映射“开箱即用”。每个类型映射可能需要多个单元测试,以验证对于正确和不正确的输入,行为是否符合预期。当前,当在numpy/tools/swig子目录中运行make test时,会执行超过 1,000 个单独的单元测试。

为了简化这些类似的单元测试,采用了一些高级编程技术,包括 C 和SWIG宏,以及 Python 继承。本文档的目的是描述用于验证numpy.i类型映射是否按预期工作的测试基础设施。

测试组织

支持三种独立的测试框架,分别针对一维、二维和三维数组。对于一维数组,有两个 C 文件,一个头文件和一个源文件,命名为:

代码语言:javascript复制
Vector.h
Vector.cxx 

包含原型和代码的头文件,其中有各种函数,这些函数以一维数组作为函数参数。该文件为:

代码语言:javascript复制
Vector.i 

是一个SWIG接口文件,定义了一个包装Vector.h中函数的 python 模块Vector,同时利用numpy.i中的类型映射来正确处理 C 数组。

Makefile调用swig生成Vector.pyVector_wrap.cxx,还执行setup.py脚本编译Vector_wrap.cxx,并链接扩展模块_Vector.so_Vector.dylib,取决于平台。这个扩展模块和代理文件Vector.py都放在build目录下的一个子目录中。

实际测试是通过名为 Python 脚本进行的:

代码语言:javascript复制
testVector.py 

使用标准 Python 库模块unittest,对Vector.h中定义的每个函数进行多个测试,支持多种数据类型。

二维数组以完全相同的方式进行测试。上述描述适用,但用Matrix替换Vector。对于三维测试,用Tensor替换Vector。对于四维测试,用SuperTensor替换Vector。对于平面原地数组测试,将Flat替换为Vector。在接下来的描述中,我们将参考Vector测试,但相同信息也适用于MatrixTensorSuperTensor测试。

命令make test将确保构建所有测试软件,然后运行所有三个测试脚本。

测试头文件

Vector.h 是一个 C 头文件,定义了一个名为 TEST_FUNC_PROTOS 的 C 宏,它有两个参数:TYPE,这是一个数据类型名称,例如 unsigned int;以及 SNAME,这是没有空格的相同数据类型的简短名称,例如 uint。这个宏定义了几个以 SNAME 为前缀的函数原型,这些函数至少有一个参数是 TYPE 类型的数组。这些函数中有返回参数的函数返回一个 TYPE 值。

TEST_FUNC_PROTOS 接着为 numpy.i 支持的所有数据类型实现:

  • signed char(有符号字符)
  • unsigned char(无符号字符)
  • short(短整数)
  • unsigned short(无符号短整数)
  • int(整数)
  • unsigned int(无符号整数)
  • long(长整数)
  • unsigned long(无符号长整数)
  • long long(长长整数)
  • unsigned long long(无符号长长整数)
  • float(单精度浮点数)
  • double(双精度浮点数)

测试源文件

Vector.cxx 是一个 C 源文件,为 Vector.h 中指定的每个函数原型实现可编译代码。它定义了一个 C 宏 TEST_FUNCS,它与 Vector.h 中的 TEST_FUNC_PROTOS 的参数相同,且工作方式也相同。TEST_FUNCS 为上面提到的 12 种数据类型进行了实现。

测试 SWIG 接口文件

Vector.i 是一个 SWIG 接口文件,定义了 python 模块 Vector。它遵循本章中描述的使用 numpy.i 的约定。它定义了一个 SWIG 宏 %apply_numpy_typemaps,它有一个参数 TYPE。它使用 SWIG 指令 %apply 将提供的类型映射应用于 Vector.h 中找到的参数签名。这一宏随后对 numpy.i 支持的所有数据类型进行实现。接着,它执行 %include "Vector.h" 来使用 numpy.i 中的类型映射包装 Vector.h 中的所有函数原型。

测试 Python 脚本

在使用 make 构建测试扩展模块后,可以运行 testVector.py 来执行测试。与其他使用 unittest 进行单元测试的脚本一样,testVector.py 定义了一个继承自 unittest.TestCase 的类:

代码语言:javascript复制
class VectorTestCase(unittest.TestCase): 

然而,这个类不会直接运行。而是作为几个其他 python 类的基类,每个类都特定于一种特定的数据类型。VectorTestCase 类存储了两个用于类型信息的字符串:

self.typeStr 一个匹配 Vector.hVector.cxx 中使用的 SNAME 前缀之一的字符串。例如,"double"self.typeCode 一个表示 numpy 中数据类型的短字符串(通常是单个字符),对应于 self.typeStr。例如,如果 self.typeStr"double",那么 self.typeCode 应该是 "d"

VectorTestCase 类定义的每个测试通过访问 Vector 模块的字典提取它尝试测试的 python 函数:

代码语言:javascript复制
length = Vector.__dict__[self.typeStr   "Length"] 

在双精度测试的情况下,这将返回 python 函数 Vector.doubleLength

然后,我们为每个支持的数据类型定义一个新的测试用例类,定义如下:

代码语言:javascript复制
class doubleTestCase(VectorTestCase):
    def __init__(self, methodName="runTest"):
        VectorTestCase.__init__(self, methodName)
        self.typeStr  = "double"
        self.typeCode = "d" 

每个这 12 个类都被收集到一个unittest.TestSuite中,然后被执行。错误和失败会被汇总在一起并作为退出参数返回。任何非零结果都表示至少有一个测试未通过。

贡献给 NumPy

原文:numpy.org/doc/1.26/dev/index.html

不会编码?没问题!NumPy 是多方面的,我们可以使用大量帮助。这些都是我们需要帮助的活动(它们都很重要,所以我们按字母顺序列出):

  • 代码维护和开发
  • 社区协调
  • 开发运维
  • 制作教育内容和叙述文档
  • 筹款
  • 市场营销
  • 项目管理
  • 翻译内容
  • 网站设计和开发
  • 撰写技术文档

本文的其余部分讨论了在 NumPy 代码库和文档上的工作。我们正在更新我们对其他活动和角色的描述。如果您对这些其他活动感兴趣,请与我们联系!您可以通过 numpy-discussion 邮件列表 或 GitHub(提出问题或评论相关问题)联系我们!这是我们首选的交流渠道(开源天性就是开放的!),但是如果您希望首先私下讨论,请联系我们的社区协调员 numpy-team@googlegroups.com 或 numpy-team.slack.com (第一次请发送电子邮件至 numpy-team@googlegroups.com 请求邀请)。

开发进程 - 摘要

这是简短摘要,完整的目录链接如下:

如果您是首次贡献者:

前往 github.com/numpy/numpy 并单击“fork”按钮来创建您自己的项目副本。

将项目克隆到本地计算机:

代码语言:javascript复制
git clone --recurse-submodules https://github.com/your-username/numpy.git 

更改目录:

代码语言:javascript复制
cd numpy 

添加上游仓库:

代码语言:javascript复制
git remote add upstream https://github.com/numpy/numpy.git 

现在,git remote -v 将显示两个名为远程仓库:

  • upstream,指的是 numpy 仓库
  • origin,指的是您个人的 fork

从上游拉取最新更改,包括标签:

代码语言:javascript复制
git checkout main
git pull upstream main --tags 

初始化 numpy 的子模块:

代码语言:javascript复制
git submodule update --init 

开发您的贡献:

为要处理的功能创建一个分支。由于分支名称将出现在合并消息中,请使用一个合理的名称,如 ‘linspace-speedups’:

代码语言:javascript复制
git checkout -b linspace-speedups 

随着您的进展本地提交(git addgit commit)使用 格式良好的 提交消息,编写在您的更改之前和之后失败的测试,并在本地运行所有测试。确保在文档字符串中记录任何更改的行为,严格遵守 NumPy 文档字符串标准。

提交您的贡献:

将更改推送回您在 GitHub 上的 fork:

代码语言:javascript复制
git push origin linspace-speedups 

输入您的 GitHub 用户名和密码(重复贡献者或高级用户可以通过连接到 GitHub 使用 SSH 来删除此步骤)。

转到 GitHub。新分支将显示为绿色的拉取请求按钮。确保标题和消息清晰、简洁,并且自解释。然后点击按钮提交它。

如果您的提交引入了新功能或更改了功能,请在邮件列表上解释您的更改。对于错误修复、文档更新等,一般来说是不必要的,不过如果您没有得到任何反应,可以随时要求审查。

审查流程:

  • 审阅者(其他开发人员和感兴趣的社区成员)将就您的拉取请求(PR)撰写内联和/或一般评论,以帮助您改善其实施、文档和风格。项目中的每位开发人员都会经过代码审查,我们已经把这视为友好的对话,从中我们都会学到东西,整体代码质量也会受益。因此,请不要让审查使您不愿意贡献:它的唯一目的是改善项目的质量,而不是批评(毕竟,我们对您捐赠的时间非常感激!)。更多信息请参见我们的审查者指南。
  • 要更新您的 PR,请在本地存储库上进行更改,提交,运行测试,仅在测试通过后将更改推送到您的分支上。一旦这些更改被推送上去(到之前的相同分支),PR 将自动更新。如果您不知道如何修复测试失败,您可以无论如何推送您的更改,并在 PR 评论中寻求帮助。
  • 每次 PR 更新后,各种持续集成(CI)服务都会被触发,用于构建代码、运行单元测试、衡量代码覆盖率和检查您分支的编码风格。在您的 PR 可以合并之前,CI 测试必须通过。如果 CI 失败,您可以通过点击“失败”图标(红色叉号)并检查构建和测试日志来找出失败的原因。为了避免过度使用和浪费这一资源,请在提交之前本地测试您的工作。
  • 在合并之前,PR 必须得到至少一位核心团队成员的批准。批准意味着核心团队成员仔细审查了更改,并且 PR 已经准备好合并。

文档更改

除了对函数的文档字符串进行更改和一般文档中的描述之外,如果您的更改引入了任何面向用户的修改,它们可能需要在发布说明中提及。要添加您的更改到发布说明中,您需要创建一个简短的文件,并放置在doc/release/upcoming_changes中。文件doc/release/upcoming_changes/README.rst详细说明了格式和文件名约定。

如果您的更改引入了弃用,请确保首先在 GitHub 或邮件列表上讨论此事。如果就弃用达成协议,请遵循NEP 23 弃用政策 添加弃用。

交叉引用问题

如果 PR 与任何问题相关,则可以将文本 xref gh-xxxx 添加到 GitHub 评论中,其中 xxxx 是问题编号。同样,如果 PR 解决了一个问题,用 closesfixes 或其他任何 GitHub 接受的 变体 替换 xref

在源代码中,请务必在任何问题或 PR 引用之前加上 gh-xxxx

要了解更详细的讨论,请继续阅读并关注本页面底部的链接。

upstream/main 和您的 feature 分支之间的分歧

如果 GitHub 指示您的 Pull Request 分支无法再自动合并,则必须将自您开始以来所做的更改合并到您的分支中。我们建议的做法是在 main 上变基。

指南

所有代码都应该有测试(有关更多详细信息,请参见下文的 test coverage)。

所有代码都应被文档化。

没有经核心团队成员审查和批准的更改会被提交。如果您的拉取请求一周内没有任何回应,请礼貌地在 PR 上或邮件列表上询问。 ### 风格指南

设置您的编辑器遵循PEP 8(去除末尾空格,不使用制表符等)。使用 pyflakes / flake8 检查代码。

使用 NumPy 数据类型而不是字符串(np.uint8 而不是 "uint8")。

使用以下的导入约定:

代码语言:javascript复制
import numpy as np 

对于 C 代码,请参阅NEP 45。

测试覆盖率

修改代码的拉取请求(PRs)应该有新的测试,或修改现有测试以确保在 PR 之前失败后通过。在推送 PR 之前,应运行测试。

在本地运行 NumPy 的测试套件需要一些额外的包,如 pytesthypothesis。其他测试依赖项列在顶级目录中的 test_requirements.txt 中,并可以通过以下命令方便地安装:

代码语言:javascript复制
$ python -m pip install -r test_requirements.txt 

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

要测量测试覆盖率,请运行:

代码语言:javascript复制
$ spin test --coverage 

这将在 build/coverage 中以 html 格式创建报告,可用浏览器查看,例如:

代码语言:javascript复制
$ firefox build/coverage/index.html 
构建文档

要构建 HTML 文档,请使用:

代码语言:javascript复制
spin docs 

您还可以从 doc 目录运行 makemake help 列出所有目标。

要获取适当的依赖项和其他要求,请参阅构建 NumPy API 和参考文档。

修复警告
  • “找不到引用:R###”可能是在 docstring 的第一行后有一个下划线引用(例如[1]_)。使用以下方法查找源文件:$ cd doc/build; grep -rin R####
  • “重复引用 R###,其他实例在……”可能有一个[2]没有一个[1]在其中一个文档字符串中

开发过程 - 详细信息

故事的剩余部分

  • Git 基础知识
    • 安装 git
    • 获取代码的本地副本
    • 更新代码
    • 为 NumPy 开发设置 git
    • Git 配置
    • 差异规范中的两个和三个点
    • 其他 Git 资源
  • 设置和使用开发环境
    • 推荐的开发设置
    • 使用虚拟环境
    • 测试构建
    • 其他构建选项
    • 运行测试
    • 运行 Linting
    • 重建和清理工作区
    • 调试
    • 了解代码和入门
  • 构建 NumPy API 和参考文档
    • 开发环境
    • 先决条件
    • 说明
  • 开发工作流程
    • 基本工作流程
    • 可能还想做的其他事情
  • 高级调试工具
    • 使用其他工具查找 C 错误
  • 审稿人指南
    • 谁可以成为审稿人?
    • 沟通准则
    • 审稿人清单
    • 审稿时的标准回复
  • NumPy 基准测试
    • 用法
    • 基准测试版本
    • 编写基准测试
  • NumPy C 样式指南
  • 面向下游软件包作者
    • 了解 NumPy 的版本控制和 API/ABI 稳定性
    • 针对 NumPy 主分支或预发布版本进行测试
    • 添加对 NumPy 的依赖
  • 发布版本
    • 如何准备发布
    • 逐步说明
    • 分支演示
  • NumPy 治理
    • NumPy 项目治理和决策
  • 如何贡献到 NumPy 文档
    • 文档团队会议
    • 所需内容
    • 贡献修复
    • 贡献新页面
    • 间接贡献
    • 文档样式
    • 阅读文档

NumPy 特定的工作流程在 numpy 开发工作流程中。

开发流程 - 总结

这是简要摘要,完整的 TOC 链接在下面:

如果您是首次贡献者:

转到github.com/numpy/numpy并单击“fork”按钮以创建项目的自己的副本。

在本地计算机上克隆项目:

代码语言:javascript复制
git clone --recurse-submodules https://github.com/your-username/numpy.git 

更改目录:

代码语言:javascript复制
cd numpy 

添加上游存储库:

代码语言:javascript复制
git remote add upstream https://github.com/numpy/numpy.git 

现在,git remote -v将显示两个名为的远程存储库:

  • 上游,指的是numpy存储库
  • origin,指的是您的个人分支

从上游拉取最新的更改,包括标签:

代码语言:javascript复制
git checkout main
git pull upstream main --tags 

初始化 numpy 的子模块:

代码语言:javascript复制
git submodule update --init 

开发您的贡献:

为您想要处理的功能创建一个分支。由于分支名称将出现在合并消息中,请使用合理的名称,例如’linspace-speedups’:

代码语言:javascript复制
git checkout -b linspace-speedups 

在进展中本地提交(git addgit commit)使用正确的格式提交消息,编写在更改之前和更改之后失败的测试,并在本地运行所有测试。确保在文档字符串中记录任何更改的行为,遵守 NumPy 文档字符串标准。

提交您的贡献:

将您的更改推送回 GitHub 上的个人分支:

代码语言:javascript复制
git push origin linspace-speedups 

输入您的 GitHub 用户名和密码(重复贡献者或高级用户可以通过使用 SSH 连接到 GitHub 来删除此步骤)。

转到 GitHub。新分支将显示一个绿色的 Pull Request 按钮。请确保标题和消息清晰、简洁且自解释。然后点击按钮提交。

如果您的提交引入新功能或更改功能,请在邮件列表上解释您的更改。对于错误修复、文档更新等,通常不需要这样做,但如果您没有得到任何反应,请随时要求审查。

审查流程:

  • 评审人员(其他开发人员和感兴趣的社区成员)将对您的 Pull Request(PR)编写行内和/或常规评论,以帮助您改进其实现、文档和风格。项目中的每个开发人员都要进行代码审查,我们认为这是友好对话,我们都从中学到了东西,并且整体的代码质量得到了提高。因此,请不要让审查使您不敢贡献:它的唯一目的是提高项目的质量,而不是批评(我们非常感谢您愿意捐赠时间!)。有关更多信息,请参阅我们的评审人准则。
  • 要更新您的 PR,在本地仓库上进行更改,提交,运行测试,并且只有测试通过时才推送到您的分支。当这些更改被推送上去(到与之前相同的分支上)时,PR 将自动更新。如果对于如何修复测试失败没有任何想法,您仍然可以推送更改并在 PR 评论中寻求帮助。
  • 每次 PR 更新后,会触发各种持续集成(CI)服务来构建代码,运行单元测试,测量代码覆盖率和检查分支的编码风格。在合并您的 PR 之前,CI 测试必须通过。如果 CI 失败,您可以点击“失败”图标(红叉)查看构建和测试日志,找出失败的原因。为了避免滥用和浪费这些资源,在提交之前,请在本地进行测试工作。
  • 在合并之前,PR 必须由至少一个核心团队成员批准。批准意味着核心团队成员仔细审查了更改,并且 PR 已经准备好进行合并。

文档更改

除了对函数 docstring 的更改和总体文档中的可能描述之外,如果您的更改引入了任何面向用户的修改,可能需要在发布说明中提到。要将您的更改添加到发布说明中,您需要创建一个简短的文件概述,并将其放置在doc/release/upcoming_changes目录中。文件doc/release/upcoming_changes/README.rst详细说明了格式和文件名约定。

如果您的更改引入了废弃,请确保首先在 GitHub 或邮件列表上讨论。如果就废弃达成一致意见,请遵循NEP 23 废弃政策 添加废弃。

交叉引用问题

如果 PR 涉及任何问题,您可以将文本xref gh-xxxx添加到 GitHub 评论中,其中xxxx是问题的编号。同样,如果 PR 解决了一个问题,将xref替换为closesfixes或其他GitHub 接受的形式。

在源代码中,务必在任何问题或 PR 引用前加上gh-xxxx

要进行更详细的讨论,请继续阅读并关注本页面底部的链接。

“上游/主”与您的特性分支之间的分歧

如果 GitHub 指示无法自动合并您拉取请求的分支,则必须将自您开始以来发生的更改合并到您的分支中。我们建议的处理方式是在主分支上变基。

指导方针

所有代码都应该有测试(有关更多详细信息,请参见 test coverage)。

所有代码都应该有文档记录。

任何更改在未经核心团队成员审查和批准之前不能提交。如果在一周内对您的拉取请求没有响应,请在 PR 或者邮件列表上礼貌地询问。 ### 风格指南

设置编辑器遵循PEP 8(删除尾随空格、无制表符等)。使用 pyflakes / flake8 检查代码。

使用 NumPy 数据类型而不是字符串(np.uint8代替"uint8")。

使用以下导入约定:

代码语言:javascript复制
import numpy as np 

对于 C 代码,参见NEP 45。

测试覆盖率

修改代码的拉取请求(PR)应该要么有新的测试,要么修改现有测试以在 PR 之前失败,在 PR 之后通过。在推送 PR 之前,您应该运行测试。

在本地运行 NumPy 的测试套件需要一些额外的包,如pytesthypothesis。额外的测试依赖列在顶层目录下的test_requirements.txt中,并可通过以下方式方便地安装:

代码语言:javascript复制
$ python -m pip install -r test_requirements.txt 

测试一个模块应该尽可能覆盖该模块中的所有代码,即语句覆盖率应达到 100%。

要测量测试覆盖率,请运行:

代码语言:javascript复制
$ spin test --coverage 

这将在build/coverage目录下生成一个html格式的报告,可以用浏览器查看,例如:

代码语言:javascript复制
$ firefox build/coverage/index.html 
构建文档

要构建 HTML 文档,请使用:

代码语言:javascript复制
spin docs 

您也可以在doc目录下运行makemake help列出所有目标。

要获取适当的依赖项和其他要求,请参阅构建 NumPy API 和参考文档。

修复警告
  • “找不到引用:R###” 可能是在文档字符串的第一行引用后面有下划线(例如 [1]_)。使用以下方法查找源文件:$ cd doc/build; grep -rin R####
  • “重复引用 R###,其他实例在…”” 可能是一个文档字符串中的 [2] 没有 [1]
upstream/main和你的特性分支之间的分歧

如果 GitHub 指示你的拉取请求的分支无法自动合并,你必须将自己分支中进行的更改合并到主分支中。我们建议的方法是在主分支上变基。

指南
  • 所有代码都应该有测试(有关更多详细信息,请参阅测试覆盖率)。
  • 所有代码都应该有文档。
  • 任何更改都不会在核心团队成员审查并批准之前提交。如果在一个星期内没有回应你的拉取请求,请在 PR 或者邮件列表上礼貌地询问。
风格指南

设置你的编辑器遵循PEP 8(去除尾随空格,无制表符等)。使用 pyflakes / flake8 检查代码。

使用 NumPy 数据类型而不是字符串(np.uint8而不是"uint8")。

使用以下导入约定:

代码语言:javascript复制
import numpy as np 

对于 C 代码,请参阅NEP 45(“在 NumPy 增强提案中”)。

测试覆盖率

修改代码的拉取请求(PRs)应该有新测试,或者修改现有测试在 PR 之前失败后成功。在推送 PR 之前,你应该运行测试。

本地运行 NumPy 的测试套件需要一些额外的软件包,例如pytesthypothesis。额外的测试依赖项列在顶级目录的test_requirements.txt中,可以通过以下方式方便地安装:

代码语言:javascript复制
$ python -m pip install -r test_requirements.txt 

模块的测试应该尽可能覆盖该模块中的所有代码,即语句覆盖率应达到 100%。

要测量测试覆盖率,请运行:

代码语言:javascript复制
$ spin test --coverage 

这将在build/coverage目录中创建一个html格式的报告,可以在浏览器中查看,例如:

代码语言:javascript复制
$ firefox build/coverage/index.html 
构建文档

要构建 HTML 文档,请使用:

代码语言:javascript复制
spin docs 

你也可以在doc目录中运行make命令。make help列出所有目标。

要获取适当的依赖项和其他要求,请参阅构建 NumPy API 和参考文档。

修复警告
  • “找不到引用:R###” 可能是在文档字符串的第一行引用后面有下划线(例如 [1]_)。使用以下方法查找源文件:$ cd doc/build; grep -rin R####
  • “重复引用 R###,其他实例在…” 可能有一个 [2] 而没有 [1] 在其中的一个文档字符串中
修复警告
  • “引用未找到:R###” 第一行的文档字符串中可能有一个引用后面有下划线 (e.g. [1]_)。使用以下方法来找到源文件:$ cd doc/build; grep -rin R####
  • “重复引用 R###,其他实例在…” 可能有一个 [2] 而没有 [1] 在其中的一个文档字符串中

开发流程 - 详细信息

故事的余下部分

  • Git 基础知识
    • 安装 git
    • 获取代码的本地副本
    • 更新代码
    • 为 NumPy 开发设置 git
    • Git 配置
    • 差异规范中的两个和三个点
    • 其他 Git 资源
  • 设置和使用您的开发环境
    • 推荐的开发环境设置
    • 使用虚拟环境
    • 测试构建
    • 其他构建选项
    • 运行测试
    • 运行代码检查
    • 重建和清理工作区
    • 调试
    • 理解代码和入门
  • 构建 NumPy API 和参考文档
    • 开发环境
    • 先决条件
    • 说明
  • 开发工作流程
    • 基本工作流程
    • 您可能想要做的其他事情
  • 高级调试工具
    • 使用其他工具寻找 C 错误
  • 审核者指南
    • 谁可以成为审核者?
    • 沟通指南
    • 审核者清单
    • 审核的标准回复
  • NumPy 基准测试
    • 用法
    • 基准测试版本
    • 编写基准测试
  • NumPy C 代码风格指南
  • 针对下游包作者
    • 理解 NumPy 的版本和 API/ABI 稳定性
    • 针对 NumPy 主分支或预发布版本进行测试
    • 添加对 NumPy 的依赖
  • 发布一个版本
    • 如何准备发布
    • 逐步指南
    • 分支演示
  • NumPy 治理
    • NumPy 项目治理和决策
  • 如何贡献到 NumPy 文档
    • 文档团队会议
    • 所需内容
    • 贡献修正
    • 贡献新页面
    • 间接贡献
    • 文档风格
    • 文档阅读

NumPy 特定的工作流程在 numpy-development-workflow 中。

用于开发的 Git

原文:numpy.org/doc/1.26/dev/gitwash/index.html

这些页面描述了一般的 git 和 github 工作流程。

这不是一个全面的 git 参考。它是专门针对 github 托管服务的。您可能会发现有更好或更快的方法来完成 git 的工作,但这些应该可以让您开始。

关于学习 git 的一般资源,请参见其他 Git 资源。

请查看 github 帮助页面上可用的 github 安装帮助页面

目录:

  • 安装 git
  • 获取代码的本地副本
  • 更新代码
  • 为 NumPy 开发设置 git
    • 安装 git
    • 创建 GitHub 账户
    • 创建一个 NumPy 分叉
    • 查看一下
    • 可选:设置 SSH 密钥以避免密码
  • Git 配置
    • 概述
    • 详细信息
  • 差异规范中的两点和三点
  • 其他 Git 资源
    • 教程和总结
    • 高级 git 工作流程
    • 在线手册页

设置和使用您的开发环境

原文:numpy.org/doc/1.26/dev/development_environment.html

推荐的开发设置

由于 NumPy 包含了部分需要在使用之前编译的 C 和 Cython 代码,请确保您已安装了必要的编译器和 Python 开发头文件 - 请参阅从源码构建。从版本1.17开始构建 NumPy 需要一个符合 C99 标准的编译器。

编译代码还意味着从开发源代码导入 NumPy 需要一些额外的步骤,下面将对此进行解释。在本章的其余部分,我们假设您已根据 Git 开发中描述的设置了您的 git 存储库。

注意

如果您在从源码构建 NumPy 或设置本地开发环境时遇到问题,可以尝试使用 GitHub Codespaces 构建 NumPy。它允许您在浏览器中直接创建正确的开发环境,减少了安装本地开发环境并处理不兼容依赖项的需求。

如果您的网络连接良好,并且想要一个临时设置,通常在 Codespaces 环境中开发 NumPy 速度更快。有关如何开始使用 Codespaces 的文档,请参阅Codespaces 文档。在为numpy/numpy存储库创建 codespace 时,默认的 2 核机器类型可行;4 核将构建和工作速度稍快(但当然会减少您的免费使用小时数)。一旦您的 codespace 启动,您可以运行conda activate numpy-dev,您的开发环境就完全设置好了 - 然后您可以按照 NumPy 文档的相关部分进行构建、测试、开发、编写文档和贡献到 NumPy。

使用虚拟环境

一个经常被问到的问题是“我如何在发布版本用于工作/研究的同时设置 NumPy 的开发版本?”

实现这一点的一种简单方法是在 site-packages 中安装发布版本,例如使用 pip 或 conda,并在虚拟环境中设置开发版本。

如果您使用 conda,我们建议使用存储库根目录下的environment.yml文件为 numpy 开发创建一个单独的虚拟环境(这将一次性创建环境并安装所有开发依赖项):

代码语言:javascript复制
$ conda env create -f environment.yml  # `mamba` works too for this command
$ conda activate numpy-dev 

如果您安装 Python 的方式与 conda 不同,请先安装virtualenv(可以选择使用virtualenvwrapper),然后创建您的虚拟环境(此处命名为numpy-dev):

代码语言:javascript复制
$ virtualenv numpy-dev 

现在,每当您想切换到虚拟环境时,您可以使用命令source numpy-dev/bin/activate,并使用deactivate退出虚拟环境并回到之前的 shell。

测试构建

在运行测试之前,请先安装测试依赖项:

代码语言:javascript复制
$ python -m pip install -r test_requirements.txt
$ python -m pip install asv # only for running benchmarks 

要构建 NumPy 的开发版本并运行测试,以及使用正确设置的 Python 导入路径生成交互式 shell 等,请使用spin实用程序。要运行测试,请执行以下操作之一:

代码语言:javascript复制
$ spin test -v
$ spin test numpy/random  # to run the tests in a specific module
$ spin test -v -t numpy/core/tests/test_nditer.py::test_iter_c_order 

这首先构建了 NumPy,所以第一次可能需要几分钟。

您还可以使用spin bench进行基准测试。有关更多命令行选项,请参阅spin --help

注意

如果上述命令导致RuntimeError: Cannot parse version 0 untagged.xxxxx,请运行git pull upstream main --tags

可以通过在一个裸--之后传递额外参数来将附加参数转发给pytest。例如,要将带有--pdb标志的测试方法转发到目标,请运行以下命令:

代码语言:javascript复制
$ spin test -t numpy/tests/test_scripts.py::test_f2py -- --pdb 

您还可以使用 python 运算符匹配测试名称通过将-k参数传递给 pytest:

代码语言:javascript复制
$ spin test -v -t numpy/core/tests/test_multiarray.py -- -k "MatMul and not vector" 

注意

请记住,在提交更改之前,所有 NumPy 的测试都应该通过。

注意

测试套件中的一些测试需要大量内存,如果您的系统内存不足,则会跳过这些测试。

其他构建选项

有关更多选项,包括选择编译器、设置自定义编译器标志和控制并行性,请参阅选择编译器和自定义构建(来自 SciPy 文档)。

运行测试

除了使用spin之外,还有各种方法可以运行测试。在解释器中,可以这样运行测试:

代码语言:javascript复制
>>> np.test()  
>>> np.test('full')   # Also run tests marked as slow
>>> np.test('full', verbose=2)   # Additionally print test name/file

An example of a successful test :
``4686 passed, 362 skipped, 9 xfailed, 5 warnings in 213.99 seconds`` 

或者从命令行中执行类似的方式:

代码语言:javascript复制
$ python -c "import numpy as np; np.test()" 

也可以使用pytest numpy运行测试,但是此时将找不到 NumPy 特定的插件,这会导致奇怪的副作用。

运行单个测试文件可能很有用;这比运行整个测试套件或整个模块的速度要快得多(例如:np.random.test())。可以这样做:

代码语言:javascript复制
$ python path_to_testfile/test_file.py 

它还接受额外的参数,比如--pdb,当测试失败或引发异常时,它会将您带入 Python 调试器。

也支持使用tox运行测试。例如,要使用 Python 3.9 构建 NumPy 并运行测试套件,请使用:

代码语言:javascript复制
$ tox -e py39 

获取更多详细信息,请参阅测试指南。

注意:不要在 numpy git repo 的根目录下使用spin运行测试,否则会导致奇怪的测试错误。

运行 Linting

可以对新添加的 Python 代码行执行 lint 检查。

使用 pip 安装所有依赖包:

代码语言:javascript复制
$ python -m pip install -r linter_requirements.txt 

在提交新代码之前运行 lint 检查:

代码语言:javascript复制
$ python tools/linter.py 

要检查当前分支的新添加的 Python 代码与目标分支的所有更改,请运行:

代码语言:javascript复制
$ python tools/linter.py --branch main 

如果没有错误,则脚本将无消息退出。在出现错误时,请检查错误消息以获取详细信息:

代码语言:javascript复制
$ python tools/linter.py --branch main
./numpy/core/tests/test_scalarmath.py:34:5: E303 too many blank lines (3)
1       E303 too many blank lines (3) 

强烈建议在将提交推送到远程分支之前运行 lint 检查,因为 linter 作为 CI 管道的一部分运行。

有关样式指南的更多详细信息:

  • Python 风格指南
  • C 风格指南

重新构建并清理工作空间

更改编译代码后重新构建 NumPy 可以使用与之前相同的构建命令 - 只会重新构建更改的文件。有时需要进行完整构建,这需要首先清理工作空间。这样做的标准方法是(注意:将删除未提交的文件!):

代码语言:javascript复制
$ git clean -xdf 

当您希望放弃所有更改并回到存储库中的最后一个提交时,请使用以下方法之一:

代码语言:javascript复制
$ git checkout .
$ git reset --hard 

调试

另一个经常问到的问题是“如何在 NumPy 中调试 C 代码?”。首先确保您的系统上安装了带有 Python 扩展的 gdb(在 Linux 上通常为默认设置)。您可以查看 gdb 中运行的 Python 版本以验证您的设置:

代码语言:javascript复制
(gdb) python
>import sys
>print(sys.version_info)
>end
sys.version_info(major=3, minor=7, micro=0, releaselevel='final', serial=0) 

大多数 Python 构建不包含调试符号,并且启用了编译器优化。建议使用调试构建的 Python 来获得最佳调试体验,参见高级调试工具。

接下来,您需要编写一个调用要调试的 C 代码执行的 Python 脚本。例如 mytest.py

代码语言:javascript复制
import numpy as np
x = np.arange(5)
np.empty_like(x) 

现在,您可以运行:

代码语言:javascript复制
$ spin gdb mytest.py 

然后在调试器中:

代码语言:javascript复制
(gdb) break array_empty_like
(gdb) run 

现在执行将在相应的 C 函数停止,您可以像往常一样逐步进行。有许多有用的针对 Python 的特定命令。例如,要查看您在 Python 代码中的位置,使用 py-list,要查看 Python 追踪信息,使用 py-bt。有关更多详细信息,请查看使用 Gdb 进行调试。以下是一些常用命令:

  • list:列出指定函数或行。
  • next:步进程序,通过子程序调用。
  • step:继续调试的程序,在信号或断点之后。
  • print:打印表达式 EXP 的值。

对 Python 调试的丰富支持要求安装分发的 python-gdb.py 脚本,在 gdb 可以找到它的路径。如果您从系统软件包管理器安装了 Python 构建,则可能无需手动执行任何操作。但是,如果您从源代码构建了 Python,则可能需要在主目录中创建一个 .gdbinit 文件,将 gdb 指向 Python 安装位置。例如,通过 pyenv 安装的版本的 Python 需要一个包含以下内容的 .gdbinit 文件:

代码语言:javascript复制
add-auto-load-safe-path ~/.pyenv 

使用带有调试支持的 Python 构建 NumPy(在 Linux 发行版中通常打包为 python-dbg)是强烈推荐的。

理解代码和入门

更好地理解代码库的最佳策略是选择您想要更改的内容,并开始阅读代码以弄清楚它的工作原理。如果有疑问,可以在邮件列表上提问。如果您的拉取请求不完美,也没关系,社区总是乐于帮助。作为一个志愿者项目,有时会有事情被遗忘,如果某事已经没有响应了大约两到四周,完全可以提醒我们。

所以,请继续选择您对 NumPy 感到恼火或困惑的内容,尝试使用代码进行实验,参与讨论或查看参考文档以尝试修复它。事情会有所进展,很快您就会对整个项目有相当好的理解。祝您好运!

推荐的开发设置

由于 NumPy 包含需要在使用之前编译的部分 C 和 Cython,因此请确保已安装必要的编译器和 Python 开发标头 - 请参阅 从源代码构建。从版本 1.17 开始构建 NumPy 需要符合 C99 标准的编译器。

编译代码也意味着从开发源导入 NumPy 需要一些额外的步骤,下面将对此进行解释。在本章的其余部分,我们假设您已按照 Git for development 中描述的设置了 git 存储库。

注意

如果您在从源代码构建 NumPy 或设置本地开发环境时遇到问题,可以尝试使用 GitHub Codespaces 构建 NumPy。它允许您在浏览器中创建正确的开发环境,减少安装本地开发环境和处理不兼容依赖项的需求。

如果您的网络连接良好,并且想要一个临时设置,通常在 Codespaces 环境中工作速度更快。有关如何开始使用 Codespaces 的文档,请参阅 the Codespaces docs。在为 numpy/numpy 存储库创建 codespace 时,默认的 2 核机器类型可以工作;4 核将构建和工作速度稍快(但当然会减少您的免费使用小时数)。一旦您的 codespace 启动,您可以运行 conda activate numpy-dev,您的开发环境就设置好了 - 然后您可以按照 NumPy 文档的相关部分进行构建、测试、开发、撰写文档和为 NumPy 做贡献。

使用虚拟环境

一个经常被问到的问题是“我如何设置 NumPy 的开发版本,以便与我用于工作/研究的发布版本并行使用?”。

实现这一目标的一个简单方法是在 site-packages 中安装发布版本,例如使用 pip 或 conda,并在虚拟环境中设置开发版本。

如果您使用 conda,我们建议使用根目录中的environment.yml文件为 numpy 开发创建一个单独的虚拟环境(这将一次性创建环境并安装所有开发依赖项):

代码语言:javascript复制
$ conda env create -f environment.yml  # `mamba` works too for this command
$ conda activate numpy-dev 

如果您安装 Python 的方式不同于 conda,请先安装virtualenv(可选使用virtualenvwrapper),然后使用以下命令创建您的虚拟环境(此处命名为numpy-dev):

代码语言:javascript复制
$ virtualenv numpy-dev 

现在,每当您想切换到虚拟环境时,可以使用命令source numpy-dev/bin/activate,然后使用deactivate退出虚拟环境并返回到之前的 shell。

测试构建

在运行测试之前,首先安装测试依赖项:

代码语言:javascript复制
$ python -m pip install -r test_requirements.txt
$ python -m pip install asv # only for running benchmarks 

要构建 NumPy 的开发版本并运行测试,并生成正确设置的 Python 导入路径等交互式 shell,请使用spin 工具。要运行测试,请执行以下操作之一:

代码语言:javascript复制
$ spin test -v
$ spin test numpy/random  # to run the tests in a specific module
$ spin test -v -t numpy/core/tests/test_nditer.py::test_iter_c_order 

这将首先构建 NumPy,因此第一次可能需要几分钟。

您还可以使用spin bench进行基准测试。请查看spin --help以获取更多命令行选项。

注意

如果上述命令导致RuntimeError: Cannot parse version 0 untagged.xxxxx,请运行git pull upstream main --tags

可以通过在裸--后传递额外参数将额外参数转发给pytest。例如,要运行带有转发到目标的--pdb标志的测试方法,请运行以下命令:

代码语言:javascript复制
$ spin test -t numpy/tests/test_scripts.py::test_f2py -- --pdb 

您还可以通过将-k参数传递给 pytest 来使用 python 运算符匹配测试名称:

代码语言:javascript复制
$ spin test -v -t numpy/core/tests/test_multiarray.py -- -k "MatMul and not vector" 

��意

记住,在提交更改之前,所有 NumPy 的测试都应该通过。

注意

测试套件中的一些测试需要大量内存,如果您的系统内存不足,会被跳过。

其他构建选项

欲了解更多选项,包括选择编译器、设置自定义编译器标志和控制并行性,请参阅选择编译器和自定义构建(来自 SciPy 文档。)

运行测试

除了使用spin,还有各种方式来运行测试。在解释器中,测试可以这样运行:

代码语言:javascript复制
>>> np.test()  
>>> np.test('full')   # Also run tests marked as slow
>>> np.test('full', verbose=2)   # Additionally print test name/file

An example of a successful test :
``4686 passed, 362 skipped, 9 xfailed, 5 warnings in 213.99 seconds`` 

或者可以通过命令行里类似的方式:

代码语言:javascript复制
$ python -c "import numpy as np; np.test()" 

测试也可以使用pytest numpy运行,不过那时将找不到 NumPy 特定的插件,会引起奇怪的副作用。

运行单独的测试文件可能很有用;比起运行整个测试套件或整个模块来说更快(例如:np.random.test())。可以这样做:

代码语言:javascript复制
$ python path_to_testfile/test_file.py 

还可以传递额外参数,比如--pdb,当测试失败或引发异常时会进入 Python 调试器。

使用tox也支持运行测试。例如,要使用 Python 3.9 构建 NumPy 并运行测试套件,请使用:

代码语言:javascript复制
$ tox -e py39 

关于更详细的信息,请参阅 Testing Guidelines。

注:不要在 numpy git repo 的根目录下运行测试,没有spin会导致奇怪的测试错误。

运行 Linting

可对新添加的 Python 代码行执行 Lint 检查。

使用 pip 安装所有依赖包:

代码语言:javascript复制
$ python -m pip install -r linter_requirements.txt 

在提交新代码之前运行 Lint 检查,运行:

代码语言:javascript复制
$ python tools/linter.py 

要检查当前分支中新添加的 Python 代码与目标分支中的所有更改,请运行:

代码语言:javascript复制
$ python tools/linter.py --branch main 

如果没有错误,脚本将以无消息的方式退出。如果有错误,请查看错误消息以了解详情:

代码语言:javascript复制
$ python tools/linter.py --branch main
./numpy/core/tests/test_scalarmath.py:34:5: E303 too many blank lines (3)
1       E303 too many blank lines (3) 

在将提交推送到远程分支之前运行 Lint 检查是明智的,因为 Lint 在 CI 流水线中运行。

更多关于风格指南的细节:

  • Python 风格指南
  • C 风格指南

重建和清理工作空间

修改编译代码后,重新构建 NumPy 可以使用与之前相同的构建命令 - 只有更改了的文件将被重新构建。做一个完整的构建,有时是必要的,需要先清理工作空间。标准的方法是(注意:删除所有未提交的文件!):

代码语言:javascript复制
$ git clean -xdf 

当你想要放弃所有更改并回到存储库中的最后一次提交时,可以使用以下之一:

代码语言:javascript复制
$ git checkout .
$ git reset --hard 

调试

另一个经常被问到的问题是“我如何在 NumPy 中调试 C 代码?”。首先,确保你在系统上安装了带有 Python 扩展的 gdb(通常在 Linux 上是默认安装的)。你可以查看 gdb 中正在运行的 Python 版本,以验证你的设置:

代码语言:javascript复制
(gdb) python
>import sys
>print(sys.version_info)
>end
sys.version_info(major=3, minor=7, micro=0, releaselevel='final', serial=0) 

大多数 Python 构建不包含调试符号,并启用编译器优化。为了获得最佳的调试体验,建议使用 Python 的调试版本。详情请参阅 Advanced debugging tools。

接下来,你需要编写一个调用你想要调试执行的 C 代码的 Python 脚本。例如mytest.py

代码语言:javascript复制
import numpy as np
x = np.arange(5)
np.empty_like(x) 

现在,你可以运行:

代码语言:javascript复制
$ spin gdb mytest.py 

然后在调试器中:

代码语言:javascript复制
(gdb) break array_empty_like
(gdb) run 

现在执行将停止在对应的 C 函数处,然后你可以像平常一样逐步执行代码。有许多有用的 Python 特定命令可用。例如,使用 py-list 来查看你的 Python 代码所在位置,使用 py-bt 来查看 Python 追踪信息。更多详情请参阅DebuggingWithGdb。以下是一些常用命令:

  • list:列出指定的函数或行。
  • next:程序步进,通过子例程调用。
  • step:继续被调试的程序,收到信号或断点后继续执行。
  • print:打印表达式 EXP 的值。

为了支持 Python 调试,需要安装 Python 分发的python-gdb.py脚本,且该脚本需位于 gdb 能找到的路径上。如果你通过系统软件包管理器安装了 Python 构建版本,你可能不需要手动做任何事情。然而,如果你通过源代码构建了 Python,那么你可能需要在你的主目录下创建一个.gdbinit文件,指向 gdb Python 安装位置。例如,通过pyenv安装的 Python 版本需要一个包含以下内容的.gdbinit文件:

代码语言:javascript复制
add-auto-load-safe-path ~/.pyenv 

用支持调试的 Python 构建 NumPy(在 Linux 发行版中通常打包为python-dbg)是强烈推荐的。

理解代码和入门

更好地理解代码库的最佳策略是选择你想要更改的内容,并开始阅读代码以弄清它是如何工作的。有疑问时,可以在邮件列表上提出问题。如果您的拉取请求不完美,社区始终乐意提供帮助。作为一个志愿者项目,有时候会有事情被忽略,如果有事情在两到四周内没有得到回应,完全可以找我们提醒一下。

所以请继续选择一些你对 NumPy 感到困惑或困扰的事情,尝试编写代码,参与讨论,或者查阅参考文档来尝试解决问题。事情会很快有所进展,很快你就会对整个项目有相当深刻的理解。祝你好运!

构建 NumPy API 和参考文档

原文:numpy.org/doc/1.26/dev/howto_build_docs.html

如果你只想获取文档,请注意可以在以下位置找到预先构建的版本:

numpy.org/doc/

可以以多种不同的格式获得文档。

开发环境

在继续之前,请注意文档是使用 make 工具构建的,该工具在 Windows 上不可用。若是 MacOS 或 Linux 用户可以跳转到先决条件。建议 Windows 用户在 GitHub Codespaces 上设置开发环境(请参见推荐的开发环境设置)或使用Windows Subsystem for Linux (WSL)。WSL 是一个可靠的本地设置选项。

先决条件

构建 NumPy 文档和 API 参考需要以下内容:

NumPy

由于主要文档的大部分内容是通过 import numpy 和检查 docstrings 来获取的,所以您需要首先构建并安装 NumPy,以便导入正确的版本。每次获取存储库的最新版本之前,都必须重新构建和安装 NumPy 才能生成文档。这样可以确保 NumPy 版本和 git 存储库版本保持同步。

请注意,您可以将 NumPy 安装到临时位置,并相应地设置 PYTHONPATH 环境变量。或者,如果使用 Python 虚拟环境(例如 condavirtualenvvenv 模块),建议在新的虚拟环境中安装 NumPy。

依赖项

构建 NumPy 文档所需的所有必要依赖项(除了Doxygen)可以通过以下方式安装:

代码语言:javascript复制
pip install -r doc_requirements.txt 

我们目前使用 Sphinx 和 Doxygen 一起为 NumPy 生成 API 和参考文档。此外,构建文档还需要附带 Matplotlib 的 Sphinx 扩展 plot_directive。我们还使用 numpydoc 来呈现生成的 API 文档中的 docstrings。SciPy 已安装,因为文档的某些部分需要使用 SciPy 函数。

关于安装 Doxygen,请查看官方的 下载 和 安装 页面,或者如果您使用的是 Linux,则可以通过发行版的软件包管理器进行安装。

注意

请尝试安装更高版本的Doxygen > 1.8.10,否则在构建过程中可能会出现一些警告。

子模块

如果你通过 git 获取了 NumPy,请还获取包含构建文档所需的其他部分的 git 子模块:

代码语言:javascript复制
git submodule update --init 

说明

现在你已经准备好生成文档了,请执行以下命令:

代码语言:javascript复制
spin docs 

如果你还未构建 NumPy,则会从源代码中构建 NumPy,并运行 Sphinx 来构建html文档。如果一切顺利,会在/doc目录下生成一个build/html子目录,包含构建好的文档。

numpy.org/doc上的 NumPy 文档以及 PDF 格式的文档也是使用make dist构建的。具体请参阅HOWTO RELEASE 进行了解如何更新numpy.org/doc

开发环境

在继续之前,需要注意的是文档是使用make工具构建的,而这个工具在 Windows 上没有原生支持。MacOS 或 Linux 用户可以跳转到先决条件。建议 Windows 用户在 GitHub Codespaces 上设置开发环境(请参阅推荐的开发环境设置)或Windows Subsystem for Linux (WSL)。WSL 是一个用于持久本地设置的良好选择。

先决条件

构建 NumPy 文档和 API 参考需要以下步骤:

NumPy

由于主要文档的大部分内容是通过import numpy和检查 docstrings 从 NumPy 获取的,所以你需要先构建并安装 NumPy,以便导入正确的版本。在生成文档之前,每次获取仓库的最新版本时都需要重新构建和安装 NumPy。这样可以确保 NumPy 版本和 git 仓库版本保持同步。

请注意,你可以将 NumPy 安装到临时位置,并适当设置 PYTHONPATH 环境变量。或者,如果使用 Python 虚拟环境(例如condavirtualenvvenv模块),建议将 NumPy 安装到新的虚拟环境中。

依赖项

构建 NumPy 文档所需的所有依赖项(除了Doxygen)可以使用以下命令安装:

代码语言:javascript复制
pip install -r doc_requirements.txt 

我们目前使用Sphinx与Doxygen一起为 NumPy 生成 API 和参考文档。此外,构建文档需要随附在Matplotlib中的 Sphinx 扩展plot_directive。我们还使用numpydoc在生成的 API 文档中呈现 docstrings。由于某些文档部分需要 SciPy 函数,因此还安装了SciPy。

要安装Doxygen,请参阅官方下载和安装页面。或者,如果您使用的是 Linux,则可以通过发行版包管理器安装它。

注意

尝试安装新版本的Doxygen,版本需大于 1.8.10,否则在构建过程中可能会出现一些警告。

子模块

如果您通过 git 获取了 NumPy,则还需要获取包含构建文档所需的其他部分的 git 子模块:

代码语言:javascript复制
git submodule update --init 
NumPy

由于主要文档的大部分内容都是通过import numpy获取,并且检查 docstrings 而获得的,所以您需要先构建并安装它,以便正确的版本被导入。每次从存储库获取最新版本之前,都需要重新构建和重新安装 NumPy,以确保 NumPy 版本和 git 存储库版本同步。

请注意,您可以将 NumPy 安装到临时位置,并适当设置 PYTHONPATH 环境变量。或者,如果使用 Python 虚拟环境(通过例如 condavirtualenvvenv 模块),建议将 NumPy 安装到新的虚拟环境中。

依赖项

构建 NumPy 文档所需的所有必要依赖项,除了Doxygen之外,都可以使用以下命令安装:

代码语言:javascript复制
pip install -r doc_requirements.txt 

我们目前使用Sphinx与Doxygen一起为 NumPy 生成 API 和参考文档。此外,构建文档需要随附在Matplotlib中的 Sphinx 扩展plot_directive。我们还使用numpydoc在生成的 API 文档中呈现 docstrings。由于某些文档部分需要 SciPy 函数,因此还安装了SciPy。

若要安装 Doxygen,请查看官方的下载和安装页面,或者如果你使用的是 Linux,则可以通过你的发行版软件包管理器来安装它。

注意

尝试安装一个新版本的 Doxygen > 1.8.10,否则在构建过程中可能会收到一些警告。

子模块

如果你通过 git 获取了 NumPy,则还需要获取包含构建文档所需的其他部分的 git 子模块:

代码语言:javascript复制
git submodule update --init 

指示

现在你已经准备好生成文档了,所以写下:

代码语言:javascript复制
spin docs 

如果你还没有构建 NumPy,这将从源代码构建 NumPy,并运行 Sphinx 来构建 html 文档。如果一切顺利,这将在 /doc 目录下生成一个 build/html 子目录,其中包含构建好的文档。

NumPy 文档以 html 和 pdf 格式发布,也是通过 make dist 构建的。有关如何更新 numpy.org/doc,请参阅 发布指南。

开发工作流程

原文:numpy.org/doc/1.26/dev/development_workflow.html

你已经通过创建 NumPy fork 获得了自己的分叉版本的NumPy存储库,通过创建本地副本,你已经按照 Git 配置配置了git,并像将你的存储库链接到上游存储库中所解释的那样,将上游存储库链接了起来。

下面描述的是一种使用 Git 的推荐工作流程。

基本工作流程

简而言之:

  1. 为每次编辑启动一个新的功能分支。请参见下面的内容(#making-a-new-feature-branch)。
  2. 开始动手吧!请参见下面的内容(#editing-workflow)
  3. 完成后:
    • 贡献者:将你的功能分支推送到你自己的 Github 存储库,并创建一个拉取请求。
    • 核心开发者:如果你想在不经过进一步审查的情况下推送更改,请参阅下面的注释(#pushing-to-main)。

这种工作方式有助于保持工作井然有序,并尽可能明确历史记录。

另请参阅

有许多在线教程可以帮助你学习 git。有关特定 git 工作流程的讨论,请参阅这些讨论linux git workflow,以及ipython git workflow。

创建一个新的功能分支

首先,从upstream存储库获取新的提交:

代码语言:javascript复制
git fetch upstream 

然后,在上游存储库的主分支基础上创建一个新的分支:

代码语言:javascript复制
git checkout -b my-new-feature upstream/main 
```### 编辑工作流程

#### 概述

```py
# hack hack
git status # Optional
git diff # Optional
git add modified_file
git commit
# push the branch to your own Github repo
git push origin my-new-feature 
更详细

做一些更改。当你觉得已经完成了一组完整的相关更改时,继续下一步。

可选:使用git status检查哪些文件发生了变化(参见git status)。你会看到一个像下面这样的列表:

代码语言:javascript复制
# On branch my-new-feature
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#  modified:   README
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#  INSTALL
no changes added to commit (use "git add" and/or "git commit -a") 

可选:使用git diff与上一个版本比较更改(参见git diff)。这将打开一个简单的文本浏览器界面,突出显示你的文件与上一个版本之间的差异。

使用git add modified_file添加任何相关修改或新文件(参见git add)。这将把文件放入暂存区,这是一个文件队列,将添加到下一个提交中。只添加具有相关完整更改的文件。将有未完成更改的文件留到以后提交。

要将暂存的文件提交到本地仓库副本,请运行git commit。此时,一个文本编辑器将打开,允许您编写提交消息。请阅读提交消息部分以确保您编写的提交消息格式正确且详细足够。保存消息并关闭编辑器后,您的提交将保存。对于微不足道的提交,可以使用-m标志通过命令行传递简短的提交消息。例如,git commit -am "ENH: Some message"

在某些情况下,您会看到这种形式的提交命令:git commit -a。额外的-a标志自动提交所有修改的文件并删除所有已删除的文件。这可以节省您输入大量git add命令的操作;但是,如果您不小心,它可能会在提交中添加不需要的更改。欲了解更多信息,请参阅为什么使用-a 标志? - 以及tangled working copy problem中的有用用例描述。

将更改推送到您在github上 fork 的仓库:

代码语言:javascript复制
git push origin my-new-feature 

欲了解更多信息,请参阅git push。

注意

假设您已经按照这些页面上的说明操作,git 将创建到您的githhub仓库的默认链接称为origin。在 git >= 1.7 中,您可以使用--set-upstream选项确保将链接到origin永久设置:

代码语言:javascript复制
git push --set-upstream origin my-new-feature 

从现在开始,git将知道my-new-feature与您自己的githhub仓库中的my-new-feature分支相关联。随后的推送调用可以简化为以下方式:

代码语言:javascript复制
git push 

每次创建新分支时,您必须使用--set-upstream参数。

可能出现这样的情况:在您编辑时,upstream上新增了影响您工作的新提交。在这种情况下,请遵循本文档中的在主分支上进行变基部分,将这些更改应用到您的分支上。

编写提交消息

提交消息应清晰并遵循几个基本规则。示例:

代码语言:javascript复制
ENH: add functionality X to numpy.<submodule>.

The first line of the commit message starts with a capitalized acronym
(options listed below) indicating what type of commit this is.  Then a blank
line, then more text if needed.  Lines shouldn't be longer than 72
characters.  If the commit is related to a ticket, indicate that with
"See #3456", "See ticket 3456", "Closes #3456" or similar. 

在提交消息中描述更改的动机,修复错误的性质或有关增强功能的某些细节也是一个好习惯。消息应该能够在不查看代码更改的情况下被理解。像MAINT:fixed another one这样的提交消息是不被推荐的示例;读者必须在其他地方寻找上下文。

提交消息开头的标准首字母缩写为:

代码语言:javascript复制
API: an (incompatible) API change
BENCH: changes to the benchmark suite
BLD: change related to building numpy
BUG: bug fix
DEP: deprecate something, or remove a deprecated object
DEV: development tool or utility
DOC: documentation
ENH: enhancement
MAINT: maintenance commit (refactoring, typos, etc.)
REV: revert an earlier commit
STY: style fix (whitespace, PEP8)
TST: addition or modification of tests
TYP: static typing
REL: related to releasing numpy 
跳过连续集成的命令。

默认情况下,针对每个 PR 都会运行许多连续集成(CI)作业,从在不同操作系统和硬件平台上运行测试套件到构建文档。在某些情况下,您已经知道不需要 CI(或不是全部),例如如果您正在处理 CI 配置文件、自述文件中的文本或其他不涉及常规构建、测试或文档序列的文件。在这种情况下,您可以通过在提交消息中包含以下片段之一来明确跳过 CI:

  • [跳过 ci]:跳过所有 CI 只有在您还没有准备好让检查在您的 PR 上运行时才建议使用(例如,如果这只是一个草稿)。
  • [跳过 actions]:跳过 GitHub Actions 作业 GitHub Actions 是运行大多数 CI 检查的地方,包括代码检查、基本测试在大多数架构和操作系统上运行以及几个编译器和 CPU 优化设置。查看这些检查的配置文件。
  • [跳过 travis]:跳过 TravisCI 作业 TravisCI 将针对 Python 3.9 在 PowerPC 和 s390x 架构上测试您的更改。查看这些检查的配置文件。
  • [跳过 azp]:跳过 Azure 作业 Azure 是进行所有综合测试的地方。这是一个昂贵的运行,如果您只进行文档更改,则通常可以跳过它。查看这些检查的主配置文件。
  • [跳过 circle]:跳过 CircleCI 作业 CircleCI 是我们构建文档并在每个 PR 预览中存储生成的工件的地方。此检查还将运行所有的文档字符串示例并验证它们的结果。如果您没有进行文档更改,但例如更改了函数的 API,则可能需要运行这些测试以验证 doctests 仍然有效。查看这些检查的配置文件。
  • [跳过 cirrus]:跳过 Cirrus 作业 CirrusCI 主要触发 Linux aarch64 和 MacOS Arm64 轮子的上传。查看这些检查的配置文件。
测试构建轮子

Numpy 目前使用cibuildwheel来通过持续集成服务构建轮子。为了节省资源,cibuildwheel 轮子构建器并不默认在每个 PR 或提交到主分支上运行。

如果您希望测试您的拉取请求不会破坏轮子构建器,您可以将[wheel build]附加到提交消息的末尾,也可以将以下标签之一添加到拉取请求中(如果您具有此权限):

  • 36 - Build: 用于改变构建流程/配置的拉取请求
  • 03 - Maintenance: 用于升级依赖关系的拉取请求
  • 14 - Release: 为准备发行的拉取请求

通过 github actions 构建的轮子(包括 64 位 linux,macOS 和 windows,arm64 macOS,和 32 位 windows)将被上传为 zip 文件的工件。 你可以从“Wheel builder”Action的摘要页面访问它们。通过travis CI 构建的 aarch64 轮子不作为工件提供。另外,如果满足以下条件,这些轮子将被上传到anaconda.org/scientific-python-nightly-wheels/

  • 通过每周的 cron 作业或者
  • 如果 Github 操作或者 travis 构建是手动触发的,需要适当的权限

如果构建是由以v开头的标签触发的,这些轮子将被上传到anaconda.org/multibuild-wheels-staging/ ### 获取邮件列表的意见

如果你计划新增功能或者 API 改变,最明智的做法是先给 NumPy 邮件列表发送邮件,请求意见。如果一周内没有收到回复,再次发邮件也没问题。 ### 请求将您的更改与主 repo 合并

当你觉得自己的工作已完成时,你可以创建一个拉取请求(PR)。 Github 有一个很好的帮助页面,详细说明了提出拉取请求的流程。

如果你的更改涉及对 API 的修改或者添加/修改功能,按照doc/release/upcoming_changes/目录中doc/release/upcoming_changes/README.rst文件中的说明和格式添加发行说明。 ### 获取您的 PR 审查

我们会尽快审查拉取请求,通常在一周内。如果你两周内没有收到审查意见,请随时在你的 PR 上添加评论询问意见(这会通知维护者)。

如果你的 PR 很大或者很复杂,在 numpy-discussion 邮件列表上征求意见可能也是有用的。 ### 在主分支上变基

这将根据上游NumPy github仓库的更改来更新你的功能分支。如果你绝对不需要这样做,尽量避免这样做,除非当你完成时。第一步将是将远程仓库与上游的新提交更新:

代码语言:javascript复制
git fetch upstream 

接下来,你需要更新功能分支:

代码语言:javascript复制
# go to the feature branch
git checkout my-new-feature
# make a backup in case you mess up
git branch tmp my-new-feature
# rebase on upstream main branch
git rebase upstream/main 

如果你对已经在上游变更的文件进行了更改,这可能会导致冲突,你需要解决冲突。在这种情况下,请参见下文获取帮助。

最后,在成功的变基之后,请删除备份分支:

代码语言:javascript复制
git branch -D tmp 

注意

与其将上游合并回您的分支,不如在main上进行变基。当在特性分支上工作时,不鼓励使用git mergegit pull。### 从错误中恢复

有时候,你可能会搞砸合并或变基。幸运的是,在 Git 中相对容易从这些错误中恢复。

如果你在变基过程中搞砸了:

代码语言:javascript复制
git rebase --abort 

如果你在变基之后注意到你搞砸了:

代码语言:javascript复制
# reset branch back to the saved point
git reset --hard tmp 

如果您忘记创建备份分支:

代码语言:javascript复制
# look at the reflog of the branch
git reflog show my-feature-branch

8630830 my-feature-branch@{0}: commit: BUG: io: close file handles immediately
278dd2a my-feature-branch@{1}: rebase finished: refs/heads/my-feature-branch onto 11ee694744f2552d
26aa21a my-feature-branch@{2}: commit: BUG: lib: make seek_gzip_factory not leak gzip obj
...

# reset the branch to where it was before the botched rebase
git reset --hard my-feature-branch@{2} 

如果您没有真正出错,但有合并冲突,您需要解决这些冲突。这可能是最棘手的问题之一。有关如何解决冲突的详细说明,请参见这篇关于合并冲突的文章。

再做一些你可能想做的事情

重写提交历史

注意

这只适用于你自己的特性分支。

你的提交中有一个令人尴尬的拼写错误?或者你有几次错误的尝试,你不希望后人看到。

可以通过交互式变基完成此操作。

假设提交历史如下所示:

代码语言:javascript复制
git log --oneline
eadc391 Fix some remaining bugs
a815645 Modify it so that it works
2dec1ac Fix a few bugs   disable
13d7934 First implementation
6ad92e5 * masked is now an instance of a new object, MaskedConstant
29001ed Add pre-nep for a couple of structured_array_extensions.
... 

6ad92e5main分支中的最后一个提交。假设我们要进行以下更改:

  • 13d7934的提交信息改为更合理的内容。
  • 将提交2dec1aca815645eadc391合并为一个。

我们按以下步骤进行:

代码语言:javascript复制
# make a backup of the current state
git branch tmp HEAD
# interactive rebase
git rebase -i 6ad92e5 

这将在编辑器中打开以下文本:

代码语言:javascript复制
pick 13d7934 First implementation
pick 2dec1ac Fix a few bugs   disable
pick a815645 Modify it so that it works
pick eadc391 Fix some remaining bugs

# Rebase 6ad92e5..eadc391 onto 6ad92e5
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
# 

为了实现我们想要的效果,我们将进行以下更改:

代码语言:javascript复制
r 13d7934 First implementation
pick 2dec1ac Fix a few bugs   disable
f a815645 Modify it so that it works
f eadc391 Fix some remaining bugs 

这意味着(i)我们想要编辑13d7934的提交信息,并且(ii)将最后三个提交合并为一个。现在我们保存并退出编辑器。

Git 会立即打开一个编辑器来编辑提交信息。在修改后,我们得到输出:

代码语言:javascript复制
[detached HEAD 721fc64] FOO: First implementation
 2 files changed, 199 insertions( ), 66 deletions(-)
[detached HEAD 0f22701] Fix a few bugs   disable
 1 files changed, 79 insertions( ), 61 deletions(-)
Successfully rebased and updated refs/heads/my-feature-branch. 

现在历史看起来是这样的:

代码语言:javascript复制
0f22701 Fix a few bugs   disable
721fc64 ENH: Sophisticated feature
6ad92e5 * masked is now an instance of a new object, MaskedConstant 

如果出错了,可以通过上面解释的方法进行恢复。

在github上删除一个分支
代码语言:javascript复制
git checkout main
# delete branch locally
git branch -D my-unwanted-branch
# delete branch on github
git push origin --delete my-unwanted-branch 

参见:stackoverflow.com/questions/2003505/how-do-i-delete-a-git-branch-locally-and-remotely

多个人共享一个仓库

如果您想与其他人一起处理某些事情,其中您都将提交到同一个仓库,甚至是同一个分支,那么请通过github共享它。

在创建一个 NumPy fork 中将 NumPy 首先 fork 到您的帐户。

然后,打开你的 forked 仓库的 github 页面,例如https://github.com/your-user-name/numpy

点击“管理员”按钮,并将其他任何人添加为仓库的合作者:

现在所有这些人都可以做到:

代码语言:javascript复制
git clone git@github.com:your-user-name/numpy.git 

请记住,以git@开头的链接使用 ssh 协议,可读可写;以git://开头的链接为只读。

然后,你的合作者可以直接在那个仓库中进行提交,使用常规的方式:

代码语言:javascript复制
git commit -am 'ENH - much better code'
git push origin my-feature-branch # pushes directly into your repo 
检出来自现有 pull request 的更改

如果你想在 pull request 中测试更改或者在新的 pull request 中继续工作,那么提交将被克隆到你 fork 的存储库的本地分支中

首先确保你的上游指向主要存储库,参考将您的存储库链接到上游存储库

然后,获取更改并创建一个本地分支。假设ID是 pull request 的编号,BRANCHNAME是你想要创建的新的本地分支的名称:

代码语言:javascript复制
git fetch upstream pull/$ID/head:$BRANCHNAME 

检出新创建的分支:

代码语言:javascript复制
git checkout $BRANCHNAME 

现在你把更改放入了 pull request 中。

探索你的存储库

要查看存储库分支和提交的图形表示:

代码语言:javascript复制
gitk --all 

查看此分支的线性提交列表:

代码语言:javascript复制
git log 

你也可以查看你的Github存储库的网络图形可视化工具。

回溯

回溯是将在numpy/main提交的新功能/修复复制到稳定版本发布分支的过程。为此,你需要在你要回溯到的分支上创建一个分支,从numpy/main中挑选你想要的提交,然后为包含回溯内容的分支提交一个 pull request。

首先,你需要创建一个你将要工作的分支。它应该是基于较旧版本的 NumPy(而不是主分支):

代码语言:javascript复制
# Make a new branch based on numpy/maintenance/1.8.x,
# backport-3324 is our new name for the branch.
git checkout -b backport-3324 upstream/maintenance/1.8.x 

现在,你需要使用git cherry-pick将 main 分支上的更改应用到这个分支上:

代码语言:javascript复制
# Update remote
git fetch upstream
# Check the commit log for commits to cherry pick
git log upstream/main
# This pull request included commits aa7a047 to c098283 (inclusive)
# so you use the .. syntax (for a range of commits), the ^ makes the
# range inclusive.
git cherry-pick aa7a047^..c098283
...
# Fix any conflicts, then if needed:
git cherry-pick --continue 

你可能会遇到一些合并冲突。解决方法与其他合并/衍合冲突相同。此外,你可以使用git blame来查看主分支和回溯分支之间的差异,确保不会出现问题。

推送新的分支到你的 Github 存储库:

代码语言:javascript复制
git push -u origin backport-3324 

最后使用 Github 发起一个 pull request。确保它是针对维护分支而不是主分支的,Github 通常会建议你将 pull request 发起到主分支。

推送更改到主要存储库

需要对主要 NumPy 存储库拥有提交权限

当你在一个特性分支上有一组“准备好的”更改,准备推送到 NumPy 的mainmaintenance分支时,可以按如下方式将它们推送到upstream

首先,在目标分支上进行合并或衍合。

如果只有少量不相关的提交,则更倾向于使用衍合:

代码语言:javascript复制
git fetch upstream
git rebase upstream/main 

参见在主分支上衍合。

如果所有的提交都是相关的,请创建一个合并提交:

代码语言:javascript复制
git fetch upstream
git merge --no-ff upstream/main 

检查即将推送的内容是否合理:

代码语言:javascript复制
git log -p upstream/main..
git log --oneline --graph 

推送到上游存储库:

代码语言:javascript复制
git push upstream my-feature-branch:main 

注意

通常最好使用-n标志进行git push以先检查一下您将要将更改推送到您希望的位置。

基本工作流程

简而言之:

  1. 为你所做的每一组编辑开始一个特性分支,参见下文。
  2. 开始工作!参见下文
  3. 完成后:
    • 贡献者:将您的功能分支推送到您自己的 Github 存储库,并创建一个拉取请求。
    • 核心开发者:如果您想要在不经过进一步审查的情况下推送更改,请阅读下面的备注(下方)。

这种工作方式有助于保持工作井然有序,使历史尽可能清晰。

参见

有许多在线教程可帮助您学习 git。有关特定 git 工作流程的讨论,请参阅linux git workflow,以及ipython git workflow 。

创建一个新的功能分支

首先,请从upstream存储库中抓取新的提交:

代码语言:javascript复制
git fetch upstream 

然后,请基于上游存储库的主分支创建一个新分支:

代码语言:javascript复制
git checkout -b my-new-feature upstream/main 
```### 编辑工作流程

#### 概述

```py
# hack hack
git status # Optional
git diff # Optional
git add modified_file
git commit
# push the branch to your own Github repo
git push origin my-new-feature 
更详细的内容

进行一些更改。当您感觉自己完成了一组完整的相关更改时,请继续下一步。

可选:使用 git status 检查更改了哪些文件(请参阅git status)。您将看到如下列表:

代码语言:javascript复制
# On branch my-new-feature
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#  modified:   README
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#  INSTALL
no changes added to commit (use "git add" and/or "git commit -a") 

可选:使用 git diff 与先前版本比较变化(查看git diff)。这将带来一个简单的文本浏览器界面,突出显示您的文件与先前版本之间的差异。

使用 git add modified_file 添加任何相关的修改或新文件(请参阅git add)。这将把文件放入暂存区,这是一个将添加到您下一个提交的文件队列。只添加具有相关完整更改的文件。将未完成更改的文件留到后续提交。

要将暂存文件提交到您的本地存储库副本中,请执行 git commit。此时,将打开一个文本编辑器,允许您编写提交消息。阅读提交消息部分 确保您正在编写格式正确且足够详细的提交消息。保存消息并关闭编辑器后,您的提交将被保存。对于琐碎的提交,可以使用 -m 标志通过命令行传递简短的提交消息。例如,git commit -am "ENH: Some message"

在某些情况下,您将看到提交命令的这种形式:git commit -a。额外的-a标志会自动提交所有修改后的文件,并删除所有已删除的文件。这可以帮您节省一些输入大量git add命令的时间;然而,如果不仔细,它可能会给提交添加不想要的更改。更多信息,请参阅为什么使用 -a 标志? - 以及在 纠缠的工作副本问题 中有用的用例描述。

将更改推送到您在github上 fork 的仓库:

代码语言:javascript复制
git push origin my-new-feature 

获取更多信息,请参阅git push。

注意

假设您已经按照这些页面中的说明操作,git 会为您的github仓库创建一个名为origin的默认链接。在 git >= 1.7 中,您可以使用--set-upstream选项确保永久设置到 origin 的链接:

代码语言:javascript复制
git push --set-upstream origin my-new-feature 

从现在开始,git将知道my-new-feature和您自己的github仓库中的my-new-feature分支相关联。此后的 push 调用可以简化为以下形式:

代码语言:javascript复制
git push 

对于每个新分支,您必须使用--set-upstream

可能存在这样的情况,即在您编辑时,upstream中新增了对您工作有影响的新提交。在这种情况下,请按照本文档的 Rebasing on main 部分的指示将这些更改应用到您的分支上。

编写提交消息

提交消息应该清晰并遵循一些基本规则。例如:

代码语言:javascript复制
ENH: add functionality X to numpy.<submodule>.

The first line of the commit message starts with a capitalized acronym
(options listed below) indicating what type of commit this is.  Then a blank
line, then more text if needed.  Lines shouldn't be longer than 72
characters.  If the commit is related to a ticket, indicate that with
"See #3456", "See ticket 3456", "Closes #3456" or similar. 

在提交消息中描述更改的动机、修复错误的性质或有关增强功能的一些细节也很有必要。提交消息应该可以在不查看代码更改的情况下就能理解。MAINT: fixed another one这样的提交消息是一个不好的示例;读者必须去其他地方找上下文。

进行提交消息时,应该使用以下标准首字母缩写:

代码语言:javascript复制
API: an (incompatible) API change
BENCH: changes to the benchmark suite
BLD: change related to building numpy
BUG: bug fix
DEP: deprecate something, or remove a deprecated object
DEV: development tool or utility
DOC: documentation
ENH: enhancement
MAINT: maintenance commit (refactoring, typos, etc.)
REV: revert an earlier commit
STY: style fix (whitespace, PEP8)
TST: addition or modification of tests
TYP: static typing
REL: related to releasing numpy 
跳过持续集成的命令

默认情况下,每个 PR 会运行很多持续集成(CI)作业,从在不同操作系统和硬件平台上运行测试套件到构建文档。在某些情况下,您已经知道不需要 CI(或者不是全部 CI),例如,如果您正在处理 CI 配置文件、README 中的文本或者其他不涉及常规构建、测试或文档序列的文件。在这种情况下,您可以通过在提交消息中包含以下任一片段来显式跳过 CI:

  • [skip ci]:跳过所有 CI 仅在您还没有准备好在 PR 上运行检查时才推荐使用(例如,如果只是草稿而已)。
  • [skip actions]:跳过 GitHub Actions 作业 GitHub Actions是运行大多数 CI 检查的地方,包括 linter、基准测试、运行大多数体系结构和操作系统的基本测试以及几个编译器和 CPU 优化设置。请参阅这些检查的配置文件。
  • [skip travis]:跳过 TravisCI 作业 TravisCI将对您的更改在 PowerPC 和 s390x 架构上的 Python 3.9 进行测试。请参阅这些检查的配置文件。
  • [skip azp]:跳过 Azure 作业 Azure 是运行所有综合测试的地方。这是一个昂贵的运行,如果您只进行文档更改,那么通常可以跳过这个运行。请查看这些检查的主配置文件。
  • [跳过 Circle]: 跳过 CircleCI 作业 CircleCI 是我们构建文档并存储生成的预览文件的地方。此检查还将运行所有文档字符串示例并验证其结果。如果你没有进行文档更改,但进行了函数 API 的更改,例如,你可能需要运行这些测试来验证文档测试仍然有效。请查看这些检查的配置文件。
  • [跳过 Cirrus]: 跳过 Cirrus 作业 CirrusCI主要触发 Linux aarch64 和 MacOS Arm64 的 wheels 上传。请查看这些检查的配置文件。
测试构建 wheels

Numpy 目前使用 cibuildwheel 来通过持续集成服务构建 wheels。为节省资源,默认情况下不会在每个单独的 PR 或提交到主分支时运行 cibuildwheel wheel 构建器。

如果您想测试您的 pull request 是否导致 wheel 构建器失败,您可以将 [wheel build] 添加到提交的提交消息的末尾,或者如果有权限的话,将以下标签之一添加到 pull request 中:

  • 36 - 构建: 用于更改构建过程/配置的 pull requests
  • 03 - 维护: 用于升级依赖项的 pull requests
  • 14 - 发布: 用于准备发布的 pull requests

通过 github actions 构建的 wheels(包括 64 位 linux、macOS 和 windows, arm64 macOS 和 32 位 windows)将以 zip 文件的形式上传为 artifacts。您可以从“Wheel builder” Action 的摘要页面访问它们。通过 travis CI 构建的 aarch64 wheels 不作为 artifacts 提供。此外,如果满足以下条件,wheels 将上传至 anaconda.org/scientific-python-nightly-wheels/

  • 通过每周的定时作业或
  • 如果 github action 或 travis 构建是手动触发的,需要适当的权限

如果构建是由以 v 开头的标签触发,则 wheels 将上传至 anaconda.org/multibuild-wheels-staging/ ### 获取邮件列表的意见

如果您计划进行新的功能或 API 更改,最明智的做法是先发送电子邮件给 NumPy 邮件列表询问意见。如果一周内没有收到回复,再次发送电子邮件也可以。 ### 请求将您的更改与主存储库合并

当您觉得您的工作已经完成,可以创建一个拉取请求(PR)。Github 有一个很好的帮助页面来概述提交拉取请求的过程。

如果您的更改涉及对 API 的修改或添加/修改功能,请在doc/release/upcoming_changes/目录下添加一个发布说明,按照doc/release/upcoming_changes/README.rst文件中的说明和格式操作。 ### 获取您的 PR 审查

我们会尽快审查拉取请求,通常在一周内。如果在两周内没有评论,可以在 PR 上添加评论要求反馈(这将通知维护者)。

如果您的 PR 较大或较复杂,向 numpy-discussion 邮件列表请求输入可能也是有用的。 ### 变基到主分支

这将使用 upstream NumPy github仓库中的更改更新您的特性分支。如果不是绝对需要这样做,请尽量避免这样做,除非您已经完成了。第一步将是使用新提交来更新远程存储库:

代码语言:javascript复制
git fetch upstream 

接下来,您需要更新特性分支:

代码语言:javascript复制
# go to the feature branch
git checkout my-new-feature
# make a backup in case you mess up
git branch tmp my-new-feature
# rebase on upstream main branch
git rebase upstream/main 

如果您对已经发生改变的文件进行了修改,这可能会产生您需要解决的合并冲突。在这种情况下,参见下文获取帮助。

最后,在成功的变基后,删除备份分支:

代码语言:javascript复制
git branch -D tmp 

变基到主分支优先于将 upstream 合并到您的分支。在处理特性分支时,不鼓励使用git mergegit pull。 ### 从搞砸中恢复

有时,您可能会搞砸合并或变基。幸运的是,在 Git 中相对简单地从这些错误中恢复。

如果在变基期间出现了问题:

代码语言:javascript复制
git rebase --abort 

如果您在变基后注意到您搞砸了:

代码语言:javascript复制
# reset branch back to the saved point
git reset --hard tmp 

如果您忘记创建备份分支:

代码语言:javascript复制
# look at the reflog of the branch
git reflog show my-feature-branch

8630830 my-feature-branch@{0}: commit: BUG: io: close file handles immediately
278dd2a my-feature-branch@{1}: rebase finished: refs/heads/my-feature-branch onto 11ee694744f2552d
26aa21a my-feature-branch@{2}: commit: BUG: lib: make seek_gzip_factory not leak gzip obj
...

# reset the branch to where it was before the botched rebase
git reset --hard my-feature-branch@{2} 

如果您实际上并没有搞砸,但存在合并冲突,您需要解决这些问题。这可能是比较棘手的事情之一。有关如何做到这一点的优秀说明,请参阅这篇关于解决冲突的文章。

首先,从upstream存储库中提取新的提交:

代码语言:javascript复制
git fetch upstream 

然后,基于 upstream 存储库的主分支创建新分支:

代码语言:javascript复制
git checkout -b my-new-feature upstream/main 
编辑工作流程
概述
代码语言:javascript复制
# hack hack
git status # Optional
git diff # Optional
git add modified_file
git commit
# push the branch to your own Github repo
git push origin my-new-feature 
更详细

进行一些更改。当您感到已经完成一组相关的完整工作时,可以继续下一步。

可选:使用 git status 检查哪些文件发生了变化(参见 git status)。你会看到像下面这样的列表:

代码语言:javascript复制
# On branch my-new-feature
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#  modified:   README
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#  INSTALL
no changes added to commit (use "git add" and/or "git commit -a") 

可选:使用 git diff(参见 git diff)比较更改与上一个版本。这将带出一个简单的文本浏览器界面,突出显示你的文件与上一个版本之间的差异。

使用 git add modified_file 添加任何相关的修改或新文件(参见 git add)。这将把文件放入暂存区,即下一个提交要添加的文件队列。仅添加具有相关、完整更改的文件。将未完成更改的文件留到后续的提交中。

要将暂存的文件提交到本地副本仓库,请执行 git commit。此时,一个文本编辑器将打开,允许你编写提交消息。请阅读 提交消息部分 以确保你编写了格式正确且足够详细的提交消息。保存消息并关闭编辑器后,你的提交将被保存。对于琐碎的提交,可以使用 -m 标志通过命令行传递短的提交消息。例如,git commit -am "ENH: Some message"

在某些情况下,你可能会看到 git commit -a 形式的提交命令。额外的 -a 标志会自动提交所有修改的文件并删除所有已删除的文件。这可以节省大量输入的 git add 命令;然而,如果不小心,它可能会在提交中添加不需要的更改。更多信息,请参见 为什么要使用 -a 标志? - 以及 包含综合用例描述的有用工作副本问题。

将更改推送到你在 github 上 fork 的仓库:

代码语言:javascript复制
git push origin my-new-feature 

更多信息,请参见 git push。

注意

假设你已经按照这些页面上的说明进行了操作,git 将会创建一个指向你的 github 仓库的默认链接,称为 origin。在 git >= 1.7 中,你可以使用 --set-upstream 选项来确保链接到 origin 是永久设置的:

代码语言:javascript复制
git push --set-upstream origin my-new-feature 

从现在起,git 将会知道 my-new-feature 与你自己的 github 仓库中的 my-new-feature 分支相关。随后的推送操作将被简化为以下内容:

代码语言:javascript复制
git push 

对于你创建的每个新分支,都必须使用 --set-upstream

可能的情况是,在你编辑时,upstream 添加了影响你工作的新提交。在这种情况下,请按照本文档的 Rebasing on main 部分的说明将这些更改应用到你的分支上。

编写提交消息

提交消息应该清晰,并遵循一些基本规则。例如:

代码语言:javascript复制
ENH: add functionality X to numpy.<submodule>.

The first line of the commit message starts with a capitalized acronym
(options listed below) indicating what type of commit this is.  Then a blank
line, then more text if needed.  Lines shouldn't be longer than 72
characters.  If the commit is related to a ticket, indicate that with
"See #3456", "See ticket 3456", "Closes #3456" or similar. 

在提交消息中描述变更的动机,修复错误或增强的性质等具体细节也是很好的。提交消息应该能够在不看代码变更的情况下理解。像MAINT: fixed another one这样的提交消息就是一个反面教材;读者必须去其他地方查看背景信息。

要在提交消息开头使用的标准首字母缩写为:

代码语言:javascript复制
API: an (incompatible) API change
BENCH: changes to the benchmark suite
BLD: change related to building numpy
BUG: bug fix
DEP: deprecate something, or remove a deprecated object
DEV: development tool or utility
DOC: documentation
ENH: enhancement
MAINT: maintenance commit (refactoring, typos, etc.)
REV: revert an earlier commit
STY: style fix (whitespace, PEP8)
TST: addition or modification of tests
TYP: static typing
REL: related to releasing numpy 
跳过持续集成的命令

默认情况下,每个 PR 都会运行大量的持续集成(CI)作业,包括在不同操作系统和硬件平台上运行测试套件以及构建文档。在某些情况下,你已经知道不需要 CI(或者不需要全部),例如如果你在 CI 配置文件、README 中的文本或其他不涉及常规构建、测试或文档序列的文件上工作。在这种情况下,你可以通过在提交消息中包含以下片段之一来明确地跳过 CI:

  • [skip ci]: 跳过所有 CI 只有在你的 PR 还没有准备好进行检查时才推荐使用(例如,如果这只是一个草稿)。
  • [skip actions]: 跳过 GitHub Actions 工作 GitHub Actions 是大多数 CI 检查运行的地方,包括代码检查、基准测试、大部分架构和操作系统的基本测试,以及几个编译器和 CPU 优化设置。点击以查看这些检查的配置文件。
  • [skip travis]: 跳过 TravisCI 工作 TravisCI 将在 PowerPC 和 s390x 架构上测试你的更改对 Python 3.9 的兼容性。点击以查看这些检查的配置文件。
  • [skip azp]: 跳过 Azure 工作 Azure 是运行所有全面测试的地方。这是一个昂贵的运行,如果你只是做文档更改,可能会跳过。点击以查看这些检查的主配置文件。
  • [skip circle]: 跳过 CircleCI 工作 CircleCI 是我们构建文档并在每个 PR 中存储生成的预览文件的地方。此检查还将运行所有的文档字符串示例并验证它们的结果。如果你不对文档进行更改,但对函数的 API 进行更改,例如,你可能需要运行这些测试来验证 doctest 是否仍然有效。点击以查看这些检查的配置文件。
  • [skip cirrus]: 跳过 Cirrus 工作 CirrusCI 主要触发 Linux aarch64 和 MacOS Arm64 轮上传。点击以查看这些检查的配置文件。
测试构建轮上传

Numpy 目前使用 cibuildwheel 来通过持续集成服务构建轮子。为了节省资源,默认情况下不会在每个单独的 PR 或提交到主分支时运行 cibuildwheel 轮子构建器。

如果您想测试您的拉取请求是否破坏了构建程序,您可以在提交消息的末尾附加 [wheel build],或者在拉取请求中添加以下标签之一(如果您有权限这样做的话):

  • 36 - 构建: 用于更改构建过程/配置的拉取请求
  • 03 - 维护: 用于更新依赖项的拉取请求
  • 14 - 发布: 用于准备发布的拉取请求

通过 github actions 构建的轮子(包括 64 位 Linux、macOS 和 Windows,arm64 macOS 和 32 位 Windows)将以 zip 文件的形式上传为工件。您可以从“Wheel builder” Action 的摘要页面访问它们。通过 travis CI 构建的 aarch64 轮子不作为工件提供。此外,轮子将根据以下条件上传到 anaconda.org/scientific-python-nightly-wheels/

  • 由每周的定期 cron 任务执行或
  • 如果 github action 或 travis 构建已手动触发,则需要适当的权限

如果构建是由以 v 开头的仓库标签触发的,则将轮子上传到 anaconda.org/multibuild-wheels-staging/

概述
代码语言:javascript复制
# hack hack
git status # Optional
git diff # Optional
git add modified_file
git commit
# push the branch to your own Github repo
git push origin my-new-feature 
更详细

进行一些更改。当你觉得已经完成了一组相关的工作,并且是可工作的时候,就可以继续下一步了。

可选:使用 git status 检查哪些文件发生了变化(参见 git status)。您将看到类似于这样的列表:

代码语言:javascript复制
# On branch my-new-feature
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#  modified:   README
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#  INSTALL
no changes added to commit (use "git add" and/or "git commit -a") 

可选:使用 git diff (git diff) 比较更改与上一个版本的区别。这将带来一个简单的文本浏览器界面,突出显示您的文件与上一个版本之间的差异。

使用 git add modified_file(参见 git add)添加任何相关的修改或新文件。这将把文件放入暂存区,即将添加到您的下一个提交的文件队列。只添加具有相关、完整更改的文件。将具有未完成更改的文件留到以后提交。

要将暂存文件提交到本地仓库的副本中,请使用git commit。此时,文本编辑器将打开,允许你编写提交消息。请阅读 commit message section 以确保你正在编写格式正确且详细的提交消息。保存消息并关闭编辑器后,你的提交将被保存。对于琐碎的提交,可以通过命令行使用-m标志传递简短的提交消息。例如,git commit -am "ENH: Some message"

在某些情况下,你会看到这种形式的提交命令:git commit -a。额外的-a标志会自动提交所有修改的文件,并删除所有已删除的文件。这可以节省你大量使用git add命令的输入;但是,如果不小心的话,它可能会向提交添加不必要的更改。欲了解更多信息,请参阅为什么使用-a 标志? - 以及在纠缠的工作副本问题中的有用用例描述。

将更改推送到你在github上分叉的仓库:

代码语言:javascript复制
git push origin my-new-feature 

欲了解更多信息,请参阅git push。

注意

假设你已经按照这些页面的说明操作,git 将创建到你的github仓库的默认链接称为origin。在 git >= 1.7 中,你可以使用--set-upstream选项确保链接到 origin 永久设置:

代码语言:javascript复制
git push --set-upstream origin my-new-feature 

从现在开始,git 将知道my-new-feature与你自己的github仓库中的my-new-feature分支相关联。随后的推送调用将简化如下:

代码语言:javascript复制
git push 

你必须为每个创建的新分支使用--set-upstream

可能情况是,当你在编辑时,upstream会添加对你的工作有影响的新提交。在这种情况下,请遵循本文档的 Rebasing on main 部分,将这些更改应用到你的分支上。

编写提交消息

提交消息应明确,并遵循一些基本规则。例如:

代码语言:javascript复制
ENH: add functionality X to numpy.<submodule>.

The first line of the commit message starts with a capitalized acronym
(options listed below) indicating what type of commit this is.  Then a blank
line, then more text if needed.  Lines shouldn't be longer than 72
characters.  If the commit is related to a ticket, indicate that with
"See #3456", "See ticket 3456", "Closes #3456" or similar. 

描述更改的动机,修复 bug 的性质或增强功能的一些细节也是需要在提交消息中包含的好内容。提交消息应可不查看代码更改即可理解。像MAINT: fixed another one这样的提交消息是一个反例;读者必须去其他地方查找上下文。

以标准缩略词开始提交消息的是:

代码语言:javascript复制
API: an (incompatible) API change
BENCH: changes to the benchmark suite
BLD: change related to building numpy
BUG: bug fix
DEP: deprecate something, or remove a deprecated object
DEV: development tool or utility
DOC: documentation
ENH: enhancement
MAINT: maintenance commit (refactoring, typos, etc.)
REV: revert an earlier commit
STY: style fix (whitespace, PEP8)
TST: addition or modification of tests
TYP: static typing
REL: related to releasing numpy 
跳过持续集成的命令

默认情况下,针对每个 PR 都会运行许多持续集成(CI)作业,从在不同操作系统和硬件平台上运行测试套件到构建文档等。在某些情况下,您可能已经知道不需要进行 CI(或者不需要全部 CI),例如,如果您正在处理 CI 配置文件,README 中的文本或其他不涉及常规构建、测试或文档序列的文件。在这种情况下,您可以通过在提交消息中包含以下片段之一来明确跳过 CI:

  • [skip ci]: 跳过所有 CI 仅在您仍未准备好运行检查的情况下才建议使用(例如,如果这只是一个草稿)。
  • [skip actions]: 跳过 GitHub Actions 作业 GitHub Actions是大多数 CI 检查都运行的地方,包括检查程序、基准测试、对大多数架构和操作系统运行基本测试,以及若干编译器和 CPU 优化设置。查看这些检查的配置文件。
  • [skip travis]: 跳过 TravisCI 作业 TravisCI将针对 Python 3.9 在 PowerPC 和 s390x 架构上测试您的更改。查看这些检查的配置文件。
  • [skip azp]: 跳过 Azure 作业 Azure是运行所有综合测试的地方。这是一个昂贵的运行,如果您只进行文档更改,例如,您通常可以跳过这一步。查看这些检查的主配置文件。
  • [skip circle]: 跳过 CircleCI 作业 CircleCI是我们构建文档并在每个 PR 中存储生成的预览文件的地方。此检查还将运行所有的文档字符串示例并验证它们的结果。如果您不进行文档更改,但对函数的 API 进行更改,例如,您可能需要运行这些测试以验证文档测试是否仍然有效。查看这些检查的配置文件。
  • [skip cirrus]: 跳过 Cirrus 作业 CirrusCI主要触发 Linux aarch64 和 MacOS Arm64 的上传。查看这些检查的配置文件。
测试构建轮子

Numpy 目前使用cibuildwheel来通过持续集成服务构建轮子。为了节省资源,cibuildwheel 轮子构建器不会在每个单独的 PR 或提交到主分支上默认运行。

如果您想要测试您的拉取请求不会破坏轮子构建器,您可以在提交的提交消息的末尾附加[wheel build],或者在拉取请求中添加以下标签中的一个(如果您有权限这样做):

  • 36 - 构建:适用于更改构建流程/配置的拉取请求
  • 03 - 维护:适用于升级依赖项的拉取请求
  • 14 - 发布:用于准备发布的拉取请求

通过 github 动作构建的轮子(包括 64 位 linux、macOS 和 windows,arm64 macOS 和 32 位 windows)将作为 zip 文件上传为工件。你可以从“Wheel builder”Action的摘要页面访问它们。通过travis CI 构建的 aarch64 轮子不以工件形式提供。此外,如果满足以下条件,这些轮子将被上传到anaconda.org/scientific-python-nightly-wheels/

  • 通过每周的定期计划作业或
  • 如果 github 动作或 travis 构建已经手动触发,这需要适当的权限

如果构建是由仓库以v开头的标签触发的,这些轮子将被上传到anaconda.org/multibuild-wheels-staging/

跳过持续集成的命令

默认情况下,每个 PR 都会运行许多持续集成(CI)作业,从在不同操作系统和硬件平台上运行测试套件到构建文档。在某些情况下,你可能已经知道不需要 CI(或者不需要全部),例如,如果你在 CI 配置文件、README 中的文本,或者其他不涉及正常构建、测试或文档序列的文件上工作。在这种情况下,你可以通过在提交消息中包含以下片段来显式地跳过 CI:

  • [跳过 ci]:跳过所有 CI 只有在你还没有准备好进行 PR 检查时才建议这样做(例如,如果这只是一个草稿)。
  • [跳过 actions]:跳过 GitHub Actions 作业 GitHub Actions 是大多数 CI 检查的运行位置,包括代码检查器、基准测试、对大多数架构和操作系统运行基本测试,以及几个编译器和 CPU 优化设置。查看这些检查的配置文件。
  • [跳过 travis]:跳过 TravisCI 作业 TravisCI 将在 PowerPC 和 s390x 架构上测试您的更改是否适用于 Python 3.9。查看这些检查的配置文件。
  • [跳过 azp]:跳过 Azure 作业 Azure 是运行所有综合测试的地方。这是一个昂贵的运行,如果你只是进行文档方面的更改,你通常可以跳过。查看这些检查的主配置文件。
  • [跳过 circle]:跳过 CircleCI 作业 CircleCI 是我们构建文档并存储生成的预览内容的地方。此检查还将运行所有文档字符串示例并验证其结果。如果您没有对文档进行更改,但对函数的 API 进行了更改,例如,您可能需要运行这些测试以验证 doctests 仍然有效。查看这些检查的配置文件。
  • [跳过 Cirrus]: 跳过 Cirrus 任务 CirrusCI 主要触发 Linux aarch64 和 MacOS Arm64 轮毂上传。查看这些检查的配置文件。
测试构建轮毂

目前 Numpy 使用 cibuildwheel 来通过持续集成服务构建轮毂。为了节省资源,cibuildwheel 轮毂构建器默认情况下不会在每个单独的 PR 或提交到主存储库上运行。

如果您想要测试您的拉取请求不会破坏轮毂构建器,您可以在提交消息的结尾添加[wheel build],或者如果有权限,可以在拉取请求中添加以下任一标签:

  • 36 - 构建:用于更改构建过程/配置的拉取请求
  • 03 - 维护:用于更新依赖关系的拉取请求
  • 14 - 发布:用于准备发布的拉取请求

通过 github actions 构建的轮毂(包括 64 位 Linux、macOS 和 Windows、arm64 macOS 和 32 位 Windows)将以 zip 文件的形式作为构件上传。您可以从“轮毂构建器”操作的总览页面访问它们。通过 travis CI 构建的 aarch64 轮毂不作为构件提供。此外,如果满足以下条件,这些轮毂将被上传到 anaconda.org/scientific-python-nightly-wheels/

  • 通过每周的定时任务或者
  • 如果 github action 或 travis 构建是手动触发的,这需要适当的权限

如果该构建是由以v开头的 tag 触发的,将轮毂上传到 anaconda.org/multibuild-wheels-staging/

获取邮件列表的意见

如果您计划添加新功能或 API 更改,最明智的做法是先给 NumPy 邮件列表 发电子邮件请求评论。如果一周内没有回复,可以再次发送电子邮件询问。

请将您的更改申请合并到主存储库

当您感到工作已经完成时,可以创建一个拉取请求(PR)。 Github 有一个很好的帮助页面,概述了 提交拉取请求 的过程。

如果您的更改涉及对 API 进行修改或添加/修改了某个函数,需要在 doc/release/upcoming_changes/ 目录中添加一个发布说明,按照 doc/release/upcoming_changes/README.rst 文件中的说明和格式进行操作。

获取您的 PR 审核

我们会尽快审查 PR,通常在一周内,如果在两周内没有收到审查意见,欢迎通过在您的 PR 上添加评论来征求反馈(这将会提醒维护者)。

如果您的 PR 较大或较复杂,询问 numpy-discussion 邮件列表上的意见也可能会有用。

变基到主分支

这会将您的功能分支与上游NumPy 的 GitHub 仓库的更改同步。如果非常不需要这样做,请尽量避免这样做,除非您已经完成。第一步将是从上游更新远程仓库中的新提交:

代码语言:javascript复制
git fetch upstream 

接下来,您需要更新功能分支:

代码语言:javascript复制
# go to the feature branch
git checkout my-new-feature
# make a backup in case you mess up
git branch tmp my-new-feature
# rebase on upstream main branch
git rebase upstream/main 

如果您对已经在上游发生了更改的文件进行了更改,可能会引发您需要解决的合并冲突。有关在这种情况下的帮助,请参见下文。

最后,在成功变基后删除备份分支:

代码语言:javascript复制
git branch -D tmp 

注意

优先使用变基到主分支,而不是把上游合并回您的分支。在处理功能分支时,不建议使用 git mergegit pull

从错误中恢复

有时候,您会在合并或变基时搞砸。幸运的是,在 Git 中,从这样的错误中恢复相对容易。

如果您在变基时搞砸了:

代码语言:javascript复制
git rebase --abort 

如果您在变基之后发现自己搞砸了:

代码语言:javascript复制
# reset branch back to the saved point
git reset --hard tmp 

如果您忘了制作备份分支:

代码语言:javascript复制
# look at the reflog of the branch
git reflog show my-feature-branch

8630830 my-feature-branch@{0}: commit: BUG: io: close file handles immediately
278dd2a my-feature-branch@{1}: rebase finished: refs/heads/my-feature-branch onto 11ee694744f2552d
26aa21a my-feature-branch@{2}: commit: BUG: lib: make seek_gzip_factory not leak gzip obj
...

# reset the branch to where it was before the botched rebase
git reset --hard my-feature-branch@{2} 

如果实际上您没有搞砸,但出现合并冲突,那就需要解决这些问题。这可能是让人比较头痛的事情之一。有关如何处理这个问题的详细描述,请参见这篇合并冲突的文章。

您可能还需要做的其他事情

重写提交历史

注意

仅适用于您自己的功能分支。

在您提交的更改中有一个令人尴尬的拼写错误?或者您可能有几次错误的开始,不希望后人看到。

这可以通过交互式变基来完成。

假设提交历史看起来是这样的:

代码语言:javascript复制
git log --oneline
eadc391 Fix some remaining bugs
a815645 Modify it so that it works
2dec1ac Fix a few bugs   disable
13d7934 First implementation
6ad92e5 * masked is now an instance of a new object, MaskedConstant
29001ed Add pre-nep for a couple of structured_array_extensions.
... 

6ad92e5main 分支中的最后一个提交。假设我们需要做以下更改:

  • 重新编写 13d7934 的提交消息,变得更合理。
  • 将提交 2dec1aca815645eadc391 合并为一个。

我们按照以下步骤进行:

代码语言:javascript复制
# make a backup of the current state
git branch tmp HEAD
# interactive rebase
git rebase -i 6ad92e5 

这将打开一个编辑器,并在其中显示以下文本:

代码语言:javascript复制
pick 13d7934 First implementation
pick 2dec1ac Fix a few bugs   disable
pick a815645 Modify it so that it works
pick eadc391 Fix some remaining bugs

# Rebase 6ad92e5..eadc391 onto 6ad92e5
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
# 

为达到我们的目的,我们将对其进行以下更改:

代码语言:javascript复制
r 13d7934 First implementation
pick 2dec1ac Fix a few bugs   disable
f a815645 Modify it so that it works
f eadc391 Fix some remaining bugs 

这意味着(i)我们想编辑 13d7934 的提交消息,以及(ii)将最后的三个提交合并成一个。现在保存并退出编辑器。

然后 Git 立即会带出一个编辑器,用于编辑提交消息。修改后,我们获得输出:

代码语言:javascript复制
[detached HEAD 721fc64] FOO: First implementation
 2 files changed, 199 insertions( ), 66 deletions(-)
[detached HEAD 0f22701] Fix a few bugs   disable
 1 files changed, 79 insertions( ), 61 deletions(-)
Successfully rebased and updated refs/heads/my-feature-branch. 

此时,历史记录如下:

代码语言:javascript复制
0f22701 Fix a few bugs   disable
721fc64 ENH: Sophisticated feature
6ad92e5 * masked is now an instance of a new object, MaskedConstant 

如果出现问题,可以再次按照上面的方法进行恢复。

删除github上的分支
代码语言:javascript复制
git checkout main
# delete branch locally
git branch -D my-unwanted-branch
# delete branch on github
git push origin --delete my-unwanted-branch 

参见:stackoverflow.com/questions/2003505/how-do-i-delete-a-git-branch-locally-and-remotely

多人共享一个单一的存储库

如果您想和其他人一起处理一些事情,大家一起提交到同一个存储库,甚至是同一个分支中,只需通过github共享即可。

首先将 NumPy 派生到您的帐户中,如创建 NumPy 派生所示。

然后,转到您的派生存储库的 github 页面,例如 https://github.com/your-user-name/numpy

单击“管理”按钮,并将其他人添加为存储库的合作者:

现在,所有这些人都可以执行以下操作:

代码语言:javascript复制
git clone git@github.com:your-user-name/numpy.git 

请记住,以git@开头的链接使用 ssh 协议并具有读写权限;以git://开头的链接是只读的。

您的合作者随后可以直接提交到该存储库,使用如下方式:

代码语言:javascript复制
git commit -am 'ENH - much better code'
git push origin my-feature-branch # pushes directly into your repo 
检出现有拉取请求的更改

如果您想要在拉取请求中测试更改或继续新的拉取请求中的工作,则提交将克隆到您的派生存储库中的本地分支

首先确保您的上游指向主存储库,例如从将您的存储库链接到上游存储库(在 skimage v0.21.0 中)

然后,获取更改并创建一个本地分支。假设ID是拉取请求编号,BRANCHNAME是您希望创建的新的本地分支的名称:

代码语言:javascript复制
git fetch upstream pull/$ID/head:$BRANCHNAME 

检出新创建的分支:

代码语言:javascript复制
git checkout $BRANCHNAME 

现在您在拉取请求中有了更改。

浏览您的存储库

要查看存储库分支和提交的图形表示:

代码语言:javascript复制
gitk --all 

要查看此分支的提交的线性列表:

代码语言:javascript复制
git log 

您还可以查看您的github存储库的网络图形可视化器。

回溯

回溯是将提交的新功能/修复从 numpy/main 复制回到稳定发布分支的过程。要做到这一点,您需要从要回溯的分支创建一个分支,从numpy/main中选择您想要的提交,然后提交包含回溯内容的分支的拉取请求。

首先,您需要创建一个分支,该分支需要基于旧版本的 NumPy(而不是主分支):

代码语言:javascript复制
# Make a new branch based on numpy/maintenance/1.8.x,
# backport-3324 is our new name for the branch.
git checkout -b backport-3324 upstream/maintenance/1.8.x 

现在,您需要使用git cherry-pick将主分支的更改应用到此分支中:

代码语言:javascript复制
# Update remote
git fetch upstream
# Check the commit log for commits to cherry pick
git log upstream/main
# This pull request included commits aa7a047 to c098283 (inclusive)
# so you use the .. syntax (for a range of commits), the ^ makes the
# range inclusive.
git cherry-pick aa7a047^..c098283
...
# Fix any conflicts, then if needed:
git cherry-pick --continue 

在这里你可能会遇到一些冲突,解决冲突的方法与合并/变基冲突相同。只需使用git blame可以查看主分支和后移分支之间的差异,以确保不会出错。

将新分支推送到你的 Github 仓库:

代码语言:javascript复制
git push -u origin backport-3324 

最后,使用 Github 创建一个拉取请求。确保它针对的是 maintenance 分支而不是 main 分支,Github 通常会建议你针对 main 创建拉取请求。

将更改推送到主要仓库

需要对主要的 NumPy 仓库具有提交权限。

当你在一个功能分支上有一组"准备好"的更改,准备推送到 NumPy 的mainmaintenance分支时,可以按如下方式将它们推送到upstream

首先,在���标分支上进行合并或变基。

只有少数不相关的提交,然后优先使用变基:

代码语言:javascript复制
git fetch upstream
git rebase upstream/main 

参见在主分支上进行变基。

如果所有提交都相关,请创建一个合并提交:

代码语言:javascript复制
git fetch upstream
git merge --no-ff upstream/main 

检查你将要推送的内容是否合理:

代码语言:javascript复制
git log -p upstream/main..
git log --oneline --graph 

推送到上游:

代码语言:javascript复制
git push upstream my-feature-branch:main 

注意

使用-n标志进行git push通常是一个好主意,以先检查你要推送的更改是否正确,并将其推送到正确的位置。### 修改提交历史

注意

仅对你自己的功能分支执行此操作。

你在一个提交中有一个令人尴尬的拼写错误吗?或者你已经开始了几次你不想让后世看到的错误。

这可以通过交互式变基来完成。

假设提交历史看起来是这样的:

代码语言:javascript复制
git log --oneline
eadc391 Fix some remaining bugs
a815645 Modify it so that it works
2dec1ac Fix a few bugs   disable
13d7934 First implementation
6ad92e5 * masked is now an instance of a new object, MaskedConstant
29001ed Add pre-nep for a couple of structured_array_extensions.
... 

6ad92e5main分支上的最后一个提交。假设我们要进行以下更改:

  • 重写13d7934的提交消息为一些更明智的东西。
  • 将提交2dec1aca815645eadc391合并为一个提交。

我们按如下方式操作:

代码语言:javascript复制
# make a backup of the current state
git branch tmp HEAD
# interactive rebase
git rebase -i 6ad92e5 

这将打开一个编辑器,其中包含以下文本:

代码语言:javascript复制
pick 13d7934 First implementation
pick 2dec1ac Fix a few bugs   disable
pick a815645 Modify it so that it works
pick eadc391 Fix some remaining bugs

# Rebase 6ad92e5..eadc391 onto 6ad92e5
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
# 

为了实现我们的目标,我们将对其进行以下更改:

代码语言:javascript复制
r 13d7934 First implementation
pick 2dec1ac Fix a few bugs   disable
f a815645 Modify it so that it works
f eadc391 Fix some remaining bugs 

这意味着我们要编辑13d7934的提交信息,并将最后三个提交合并为一个。现在我们保存并退出编辑器。

Git 立即打开一个编辑器来编辑提交消息。修改后,我们会得到输出:

代码语言:javascript复制
[detached HEAD 721fc64] FOO: First implementation
 2 files changed, 199 insertions( ), 66 deletions(-)
[detached HEAD 0f22701] Fix a few bugs   disable
 1 files changed, 79 insertions( ), 61 deletions(-)
Successfully rebased and updated refs/heads/my-feature-branch. 

然后历史看起来是这样的:

代码语言:javascript复制
0f22701 Fix a few bugs   disable
721fc64 ENH: Sophisticated feature
6ad92e5 * masked is now an instance of a new object, MaskedConstant 

如果出错了,可以按照上面的说明进行恢复。

删除在github上的一个分支
代码语言:javascript复制
git checkout main
# delete branch locally
git branch -D my-unwanted-branch
# delete branch on github
git push origin --delete my-unwanted-branch 

参见:stackoverflow.com/questions/2003505/how-do-i-delete-a-git-branch-locally-and-remotely

几个人共享一个仓库

如果你想与他人一起工作,共享同一个仓库,甚至是同一个分支,只需通过github共享即可。

首先将 NumPy fork 到你的账户中,参见创建一个 NumPy fork。

然后,打开你 fork 的仓库的 github 页面,比如https://github.com/your-user-name/numpy

点击“管理”按钮,并将其他任何人添加为存储库的协作者:

现在所有这些人都可以做:

代码语言:javascript复制
git clone git@github.com:your-user-name/numpy.git 

记住,以git@开头的链接使用 ssh 协议并且是可读写的;以git://开头的链接是只读的。

你的协作者随后可以直接向该存储库提交常规提交:

代码语言:javascript复制
git commit -am 'ENH - much better code'
git push origin my-feature-branch # pushes directly into your repo 
检出现有拉取请求中的更改

如果你想要测试拉取请求中的更改或者继续新拉取请求中的工作,那么提交将被克隆到你的 forked 存储库的本地分支中。

首先确保你的上游指向主存储库,就像将你的存储库链接到上游存储库一样

然后,获取更改并创建一个本地分支。假设ID是拉取请求编号,BRANCHNAME是你想创建的新的本地分支的名称:

代码语言:javascript复制
git fetch upstream pull/$ID/head:$BRANCHNAME 

检出新创建的分支:

代码语言:javascript复制
git checkout $BRANCHNAME 

现在你已经有了拉取请求中的更改。

浏览你的存储库

要查看存储库分支和提交的图形表示:

代码语言:javascript复制
gitk --all 

要查看此分支的提交的线性列表:

代码语言:javascript复制
git log 

你也可以查看你的github存储库的网络图可视化器。

回溯

回溯是将在numpy/main提交的新功能/修复复制回稳定发布分支的过程。为此,你可以从你要回溯的分支上创建一个分支,从numpy/main中挑选你想要的提交,然后为包含回溯的分支提交一个拉取请求。

首先,你需要创建你将在其上工作的分支。这需要基于 NumPy 的旧版本(不是主分支):

代码语言:javascript复制
# Make a new branch based on numpy/maintenance/1.8.x,
# backport-3324 is our new name for the branch.
git checkout -b backport-3324 upstream/maintenance/1.8.x 

现在你需要使用git cherry-pick将主分支的更改应用到此分支:

代码语言:javascript复制
# Update remote
git fetch upstream
# Check the commit log for commits to cherry pick
git log upstream/main
# This pull request included commits aa7a047 to c098283 (inclusive)
# so you use the .. syntax (for a range of commits), the ^ makes the
# range inclusive.
git cherry-pick aa7a047^..c098283
...
# Fix any conflicts, then if needed:
git cherry-pick --continue 

你可能会遇到一些冲突在这里挑樱桃。这些冲突的解决方式与合并/重新基础冲突相同。除此之外,你可以使用git blame来查看主分支和回溯分支之间的差异,以确保没有任何问题。

将新分支推送到你的 Github 存储库:

代码语言:javascript复制
git push -u origin backport-3324 

最后使用 Github 创建一个拉取请求。确保它是针对维护分支而不是主分支的,Github 通常会建议你针对主分支创建拉取请求。

将更改推送到主存储库

需要对主 NumPy 存储库具有提交权限。

当你在一个特性分支上有一组“准备就绪”的更改准备好提交给 NumPy 的mainmaintenance分支时,你可以按如下方式将它们推送到upstream

首先,在目标分支上合并或重新基于。

只有少数不相关的提交,那么更喜欢重新基于:

代码语言:javascript复制
git fetch upstream
git rebase upstream/main 

见在主分支上重新基于。

如果所有的提交都是相关的,请创建一个合并提交:

代码语言:javascript复制
git fetch upstream
git merge --no-ff upstream/main 

确认一下你将要推送的内容是否合理:

代码语言:javascript复制
git log -p upstream/main..
git log --oneline --graph 

推送到上游:

代码语言:javascript复制
git push upstream my-feature-branch:main 

注意:

使用-n标志对git push使用是一个好习惯,首先可以检查一下你要推送的改动是否是你想要的,并且推送到了正确的位置。

几个人共享一个仓库

如果你想与他人一起工作,共享同一个仓库,甚至是同一个分支,只需通过github共享即可。

首先将 NumPy fork 到你的账户中,参见创建一个 NumPy fork。

然后,打开你 fork 的仓库的 github 页面,比如https://github.com/your-user-name/numpy

点击“管理”按钮,并将其他任何人添加为存储库的协作者:

[外链图片转存中…(img-hEEPlb2i-1719460667875)]

现在所有这些人都可以做:

代码语言:javascript复制
git clone git@github.com:your-user-name/numpy.git 

记住,以git@开头的链接使用 ssh 协议并且是可读写的;以git://开头的链接是只读的。

你的协作者随后可以直接向该存储库提交常规提交:

代码语言:javascript复制
git commit -am 'ENH - much better code'
git push origin my-feature-branch # pushes directly into your repo 
检出现有拉取请求中的更改

如果你想要测试拉取请求中的更改或者继续新拉取请求中的工作,那么提交将被克隆到你的 forked 存储库的本地分支中。

首先确保你的上游指向主存储库,就像将你的存储库链接到上游存储库一样

然后,获取更改并创建一个本地分支。假设ID是拉取请求编号,BRANCHNAME是你想创建的新的本地分支的名称:

代码语言:javascript复制
git fetch upstream pull/$ID/head:$BRANCHNAME 

检出新创建的分支:

代码语言:javascript复制
git checkout $BRANCHNAME 

现在你已经有了拉取请求中的更改。

浏览你的存储库

要查看存储库分支和提交的图形表示:

代码语言:javascript复制
gitk --all 

要查看此分支的提交的线性列表:

代码语言:javascript复制
git log 

你也可以查看你的github存储库的网络图可视化器。

回溯

回溯是将在numpy/main提交的新功能/修复复制回稳定发布分支的过程。为此,你可以从你要回溯的分支上创建一个分支,从numpy/main中挑选你想要的提交,然后为包含回溯的分支提交一个拉取请求。

首先,你需要创建你将在其上工作的分支。这需要基于 NumPy 的旧版本(不是主分支):

代码语言:javascript复制
# Make a new branch based on numpy/maintenance/1.8.x,
# backport-3324 is our new name for the branch.
git checkout -b backport-3324 upstream/maintenance/1.8.x 

现在你需要使用git cherry-pick将主分支的更改应用到此分支:

代码语言:javascript复制
# Update remote
git fetch upstream
# Check the commit log for commits to cherry pick
git log upstream/main
# This pull request included commits aa7a047 to c098283 (inclusive)
# so you use the .. syntax (for a range of commits), the ^ makes the
# range inclusive.
git cherry-pick aa7a047^..c098283
...
# Fix any conflicts, then if needed:
git cherry-pick --continue 

你可能会遇到一些冲突在这里挑樱桃。这些冲突的解决方式与合并/重新基础冲突相同。除此之外,你可以使用git blame来查看主分支和回溯分支之间的差异,以确保没有任何问题。

将新分支推送到你的 Github 存储库:

代码语言:javascript复制
git push -u origin backport-3324 

最后使用 Github 创建一个拉取请求。确保它是针对维护分支而不是主分支的,Github 通常会建议你针对主分支创建拉取请求。

将更改推送到主存储库

需要对主 NumPy 存储库具有提交权限。

当你在一个特性分支上有一组“准备就绪”的更改准备好提交给 NumPy 的mainmaintenance分支时,你可以按如下方式将它们推送到upstream

首先,在目标分支上合并或重新基于。

只有少数不相关的提交,那么更喜欢重新基于:

代码语言:javascript复制
git fetch upstream
git rebase upstream/main 

见在主分支上重新基于。

如果所有的提交都是相关的,请创建一个合并提交:

代码语言:javascript复制
git fetch upstream
git merge --no-ff upstream/main 

确认一下你将要推送的内容是否合理:

代码语言:javascript复制
git log -p upstream/main..
git log --oneline --graph 

推送到上游:

代码语言:javascript复制
git push upstream my-feature-branch:main 

注意:

使用-n标志对git push使用是一个好习惯,首先可以检查一下你要推送的改动是否是你想要的,并且推送到了正确的位置。

0 人点赞