Python 教程#
在本教程中,我们将按照指南的快速参考部分和更详细的创建第一个 PR 的步骤部分中指定的步骤,为 Arrow 贡献一个实际的功能。如果发现此处缺少任何信息,请随时导航到那里。
此功能贡献将添加到 PyArrow 的 compute 模块中。但如果您是纠正 bug 或添加绑定,也可以遵循这些步骤。
本教程与创建第一个 PR 的步骤不同,因为它将针对一个特定的案例。本教程并非一步一步的操作指南。
让我们开始吧!
设置#
让我们来设置 Arrow 仓库。我们在此假定 Git 已安装。否则请参阅设置部分。
将 Apache Arrow 仓库 Fork 后,我们将克隆它并将主仓库的链接添加到我们的 upstream 中。
$ git clone https://github.com/<your username>/arrow.git
$ cd arrow
$ git remote add upstream https://github.com/apache/arrow
构建 PyArrow#
构建 PyArrow 的脚本因您使用的操作系统而异。因此,本教程中我们将只引用构建过程的说明。
为新功能创建 GitHub Issue#
我们将添加一个新功能,它模仿了 arrow.compute
模块中现有的函数 min_max
,但在两个方向上将区间扩大 1。请注意,这是本指南目的而虚构的函数。
请参阅此链接中 pc.min_max
的示例。
首先我们需要创建一个 GitHub issue,因为目前还没有。创建 GitHub 账号后,我们将导航到 GitHub issue 面板并点击 New issue 按钮。
我们应该确保将自己分配给该 issue,以告知其他人我们正在处理它。您可以通过向创建的 issue 添加评论 take
来完成此操作。
另请参阅
要获取有关 GitHub issue 的更多信息,请参阅指南的寻找适合首次贡献的问题 🔎部分。
在新的分支上开始工作#
在我们开始添加功能之前,我们应该从更新后的主分支创建一个新分支。
$ git checkout main
$ git fetch upstream
$ git pull --ff-only upstream main
$ git checkout -b ARROW-14977
让我们研究一下 Arrow 库,看看 pc.min_max
函数在哪里定义/与 C++ 连接,并了解我们可以在哪里实现新功能。

我们可以尝试在 GitHub Apache Arrow 仓库中搜索函数引用。#

并在 pyarrow
文件夹中搜索 test_compute.py
文件。#
从搜索结果中我们可以看到该函数在 python/pyarrow/tests/test_compute.py
文件中进行测试,这意味着该函数在 compute.py
文件中定义。
检查 compute.py
文件后,我们可以看到,与 _compute.pyx
一起,C++ 函数被封装到 Python 中。我们将在 compute.py
文件的末尾定义新功能。
让我们在 arrow/python
目录下的 Python 控制台中运行一些代码,以便了解更多关于 pc.min_max
的信息。
$ cd python
$ python
Python 3.9.7 (default, Oct 22 2021, 13:24:00)
[Clang 13.0.0 (clang-1300.0.29.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
我们已经从 shell 进入 Python 控制台,可以进行一些研究
>>> import pyarrow.compute as pc
>>> data = [4, 5, 6, None, 1]
>>> data
[4, 5, 6, None, 1]
>>> pc.min_max(data)
<pyarrow.StructScalar: [('min', 1), ('max', 6)]>
>>> pc.min_max(data, skip_nulls=False)
<pyarrow.StructScalar: [('min', None), ('max', None)]>
我们将把我们的新功能命名为 pc.tutorial_min_max
。我们希望该函数(接受相同输入数据)的结果是 [('min-', 0), ('max+', 7)]
。如果我们指定应该包含 null 值,结果应该等于 pc.min_max
,即 [('min', None), ('max', None)]
。
让我们将第一个试用代码添加到 arrow/python/pyarrow/compute.py
中,在那里我们首先测试对 C++ 中的“min_max”函数的调用
def tutorial_min_max(values, skip_nulls=True):
"""
Add docstrings
Parameters
----------
values : Array
Returns
-------
result : TODO
Examples
--------
>>> import pyarrow.compute as pc
>>> data = [4, 5, 6, None, 1]
>>> pc.tutorial_min_max(data)
<pyarrow.StructScalar: [('min-', 0), ('max+', 7)]>
"""
options = ScalarAggregateOptions(skip_nulls=skip_nulls)
return call_function("min_max", [values], options)
为了看看这是否有效,我们需要再次导入 pyarrow.compute
并尝试
>>> import pyarrow.compute as pc
>>> data = [4, 5, 6, None, 1]
>>> pc.tutorial_min_max(data)
<pyarrow.StructScalar: [('min', 1), ('max', 6)]>
它奏效了。现在我们必须修正界限以获得修正后的区间。为此,我们必须对 pyarrow.StructScalar
进行一些研究。在 test_scalars.py 中的 test_struct_duplicate_fields
下,我们可以看到创建 StructScalar
的示例。我们可以再次运行 Python 控制台并尝试自己创建一个。
>>> import pyarrow as pa
>>> ty = pa.struct([
... pa.field('min-', pa.int64()),
... pa.field('max+', pa.int64()),
... ])
>>> pa.scalar([('min-', 3), ('max+', 9)], type=ty)
<pyarrow.StructScalar: [('min-', 3), ('max+', 9)]>
注意
在文档尚不完善的情况下,单元测试是寻找代码示例的好地方。
通过新获得的关于 StructScalar
和 pc.min_max
函数附加选项的知识,我们可以完成工作。
def tutorial_min_max(values, skip_nulls=True):
"""
Compute the minimum-1 and maximum+1 values of a numeric array.
This is a made-up feature for the tutorial purposes.
Parameters
----------
values : Array
skip_nulls : bool, default True
If True, ignore nulls in the input.
Returns
-------
result : StructScalar of min-1 and max+1
Examples
--------
>>> import pyarrow.compute as pc
>>> data = [4, 5, 6, None, 1]
>>> pc.tutorial_min_max(data)
<pyarrow.StructScalar: [('min-', 0), ('max+', 7)]>
"""
options = ScalarAggregateOptions(skip_nulls=skip_nulls)
min_max = call_function("min_max", [values], options)
if min_max[0].as_py() is not None:
min_t = min_max[0].as_py()-1
max_t = min_max[1].as_py()+1
else:
min_t = min_max[0].as_py()
max_t = min_max[1].as_py()
ty = pa.struct([
pa.field('min-', pa.int64()),
pa.field('max+', pa.int64()),
])
return pa.scalar([('min-', min_t), ('max+', max_t)], type=ty)
添加测试#
现在我们应该向 python/pyarrow/tests/test_compute.py
添加一个单元测试并运行 pytest。
def test_tutorial_min_max():
arr = [4, 5, 6, None, 1]
l1 = {'min-': 0, 'max+': 7}
l2 = {'min-': None, 'max+': None}
assert pc.tutorial_min_max(arr).as_py() == l1
assert pc.tutorial_min_max(arr,
skip_nulls=False).as_py() == l2
添加单元测试后,我们可以从 shell 运行 pytest。要运行特定的单元测试,请将测试名称传递给 -k
参数。
$ cd python
$ python -m pytest pyarrow/tests/test_compute.py -k test_tutorial_min_max
======================== test session starts ==========================
platform darwin -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /Users/alenkafrim/repos/arrow/python, configfile: setup.cfg
plugins: hypothesis-6.24.1, lazy-fixture-0.6.3
collected 204 items / 203 deselected / 1 selected
pyarrow/tests/test_compute.py . [100%]
======================== 1 passed, 203 deselected in 0.16s ============
$ python -m pytest pyarrow/tests/test_compute.py
======================== test session starts ===========================
platform darwin -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /Users/alenkafrim/repos/arrow/python, configfile: setup.cfg
plugins: hypothesis-6.24.1, lazy-fixture-0.6.3
collected 204 items
pyarrow/tests/test_compute.py ................................... [ 46%]
................................................. [100%]
========================= 204 passed in 0.49s ==========================
另请参阅
有关测试的更多信息,请参阅测试 🧪部分。
检查代码风格#
最后,我们还需要检查代码风格。在 Arrow 中,我们使用一个名为 Archery 的工具来检查代码是否符合 PEP 8 风格指南。
$ archery lint --python --fix
INFO:archery:Running Python formatter (autopep8)
INFO:archery:Running Python linter (flake8)
/Users/alenkafrim/repos/arrow/python/pyarrow/tests/test_compute.py:2288:80: E501 line too long (88 > 79 characters)
使用 --fix
命令,Archery 将尝试修复代码风格问题,但有些问题(如行长)无法自动修复。我们应该自己进行必要的修正并再次运行 Archery。
$ archery lint --python --fix
INFO:archery:Running Python formatter (autopep8)
INFO:archery:Running Python linter (flake8)
完成。现在让我们创建 Pull Request!
创建 Pull Request#
首先,让我们在 shell 中使用 git status
检查我们的更改,看看哪些文件已被修改,并只提交我们正在处理的文件。
$ git status
On branch ARROW-14977
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: python/pyarrow/compute.py
modified: python/pyarrow/tests/test_compute.py
no changes added to commit (use "git add" and/or "git commit -a")
并使用 git diff
查看文件中的更改,以便发现我们可能犯的任何错误。
$ git diff
diff --git a/python/pyarrow/compute.py b/python/pyarrow/compute.py
index 9dac606c3..e8fc775d8 100644
--- a/python/pyarrow/compute.py
+++ b/python/pyarrow/compute.py
@@ -774,3 +774,45 @@ def bottom_k_unstable(values, k, sort_keys=None, *, memory_pool=None):
sort_keys = map(lambda key_name: (key_name, "ascending"), sort_keys)
options = SelectKOptions(k, sort_keys)
return call_function("select_k_unstable", [values], options, memory_pool)
+
+
+def tutorial_min_max(values, skip_nulls=True):
+ """
+ Compute the minimum-1 and maximum-1 values of a numeric array.
+
+ This is a made-up feature for the tutorial purposes.
+
+ Parameters
+ ----------
+ values : Array
+ skip_nulls : bool, default True
+ If True, ignore nulls in the input.
+
+ Returns
+ -------
+ result : StructScalar of min-1 and max+1
+
+ Examples
+ --------
+ >>> import pyarrow.compute as pc
+ >>> data = [4, 5, 6, None, 1]
+ >>> pc.tutorial_min_max(data)
+ <pyarrow.StructScalar: [('min-', 0), ('max+', 7)]>
+ """
+
+ options = ScalarAggregateOptions(skip_nulls=skip_nulls)
+ min_max = call_function("min_max", [values], options)
+
...
一切正常。现在我们可以进行提交(将我们的更改保存到分支历史记录中)
$ git commit -am "Adding a new compute feature for tutorial purposes"
[ARROW-14977 170ef85be] Adding a new compute feature for tutorial purposes
2 files changed, 51 insertions(+)
我们可以使用 git log
来查看提交历史
$ git log
commit 170ef85beb8ee629be651e3f93bcc4a69e29cfb8 (HEAD -> ARROW-14977)
Author: Alenka Frim <frim.alenka@gmail.com>
Date: Tue Dec 7 13:45:06 2021 +0100
Adding a new compute feature for tutorial purposes
commit 8cebc4948ab5c5792c20a3f463e2043e01c49828 (main)
Author: Sutou Kouhei <kou@clear-code.com>
Date: Sun Dec 5 15:19:46 2021 +0900
ARROW-14981: [CI][Docs] Upload built documents
We can use this in release process instead of building on release
manager's local environment.
Closes #11856 from kou/ci-docs-upload
Authored-by: Sutou Kouhei <kou@clear-code.com>
Signed-off-by: Sutou Kouhei <kou@clear-code.com>
...
如果我们是在一段时间前开始的分支,我们可能需要 rebase 到 upstream main 以确保没有合并冲突
$ git pull upstream main --rebase
现在我们可以将我们的工作推送到 GitHub 上名为 origin
的 Fork 的 Arrow 仓库。
$ git push origin ARROW-14977
Enumerating objects: 13, done.
Counting objects: 100% (13/13), done.
Delta compression using up to 8 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (7/7), 1.19 KiB | 1.19 MiB/s, done.
Total 7 (delta 6), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (6/6), completed with 6 local objects.
remote:
remote: Create a pull request for 'ARROW-14977' on GitHub by visiting:
remote: https://github.com/AlenkaF/arrow/pull/new/ARROW-14977
remote:
To https://github.com/AlenkaF/arrow.git
* [new branch] ARROW-14977 -> ARROW-14977
现在我们必须去 GitHub 上的 Arrow 仓库创建 Pull Request。在 GitHub Arrow 页面(主仓库或 Fork 的仓库)上,您将看到一个黄色通知栏,提示您最近将更改推送到了 ARROW-14977 分支。太好了,现在我们可以通过点击 Compare & pull request 来创建 Pull Request。

Apache Arrow 仓库上的通知栏。#
首先,我们需要将标题更改为 ARROW-14977: [Python] Add a “made-up” feature for the guide tutorial,以便与 issue 匹配。请注意,添加了一个标点符号!
额外提示:创建本教程时,我们使用的是 Jira issue 跟踪器。由于我们目前使用 GitHub issues,标题前缀将是 GH-14977: [Python] Add a “made-up” feature for the guide tutorial.
我们还将添加一个描述,以便向其他人清楚地说明我们正在做什么。
当我点击 Create pull request 后,我的代码就可以作为 Apache Arrow 仓库中的 Pull Request 进行评审了。

看,这就是我们的 Pull Request!#
Pull Request 会连接到 issue,然后 CI 会运行。经过一段时间并收到评审意见后,我们可以修改代码、评论、解决讨论等。我们创建的 Pull Request 可以在这里查看。
另请参阅
有关 Pull Request 工作流程的更多信息,请参阅Pull Request 生命周期。