Python 教程#

在本教程中,我们将按照指南快速参考部分和更详细的提交你的第一个 PR 的步骤部分中指定的步骤,对 Arrow 进行实际的功能贡献。如果此处缺少某些信息,请导航到那里。

功能贡献将添加到 PyArrow 的计算模块中。但如果您正在修正错误或添加绑定,也可以按照这些步骤操作。

本教程与提交你的第一个 PR 的步骤不同,因为我们将处理一个具体的案例。本教程并非分步指南。

让我们开始吧!

设置#

让我们设置 Arrow 仓库。我们假设 Git 已经安装。否则,请参阅设置部分。

Fork Apache Arrow 仓库后,我们将克隆它并将主仓库的链接添加到我们的上游。

$ git clone https://github.com/<your username>/arrow.git
$ cd arrow
$ git remote add upstream https://github.com/apache/arrow

构建 PyArrow#

构建 PyArrow 的脚本因您使用的操作系统而异。因此,在本教程中,我们只参考构建过程的说明。

另请参阅

有关构建过程的**简介**,请参阅构建 Arrow 库 🏋🏿‍♀️部分。

有关如何构建 PyArrow 的**说明**,请参阅在 Linux 和 macOS 上构建部分。

为新功能创建 GitHub issue#

我们将添加一个新功能,该功能模仿`arrow.compute`模块中现有的函数`min_max`,但在两个方向上都将区间扩大 1。请注意,这是为了本指南的目的而编造的函数。

请参阅此链接中的 `pc.min_max` 示例。

首先,我们需要创建一个 GitHub issue,因为它尚不存在。创建 GitHub 帐户后,我们将导航到GitHub issue 面板并点击**新建 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++ 连接,并了解我们可以在哪里实现新功能。

Apache Arrow GitHub repository dashboard where we are searching for a pc.min_max function reference.

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

In the GitHub repository we are searching through the test_compute.py file for the pc.min_max function.

并在 `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)]`。如果我们指定应包含空值,则结果应等于 `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)

完成。现在让我们创建拉取请求!

创建拉取请求#

首先,让我们使用 `git status` 在 shell 中查看我们的更改,以查看哪些文件已更改,并仅提交我们正在处理的文件。

$ 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 <[email protected]>
Date:   Tue Dec 7 13:45:06 2021 +0100

    Adding a new compute feature for tutorial purposes

commit 8cebc4948ab5c5792c20a3f463e2043e01c49828 (main)
Author: Sutou Kouhei <[email protected]>
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 <[email protected]>
    Signed-off-by: Sutou Kouhei <[email protected]>
...

如果我们一段时间之前启动了分支,我们可能需要 rebase 到上游主分支,以确保没有合并冲突。

$ git pull upstream main --rebase

现在我们可以将我们的工作推送到 GitHub 上名为 origin 的 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 存储库 创建一个拉取请求。在 GitHub Arrow 页面(主分支或分支)上,我们将看到一个黄色的通知栏,其中包含我们最近向 ARROW-14977 分支推送的说明。太好了,现在我们可以通过点击 **比较并创建拉取请求(Compare & pull request)** 来创建拉取请求。

GitHub page of the Apache Arrow repository showing a notice bar indicating change has been made in our branch and a Pull Request can be created.

Apache Arrow 存储库上的通知栏。#

首先,我们需要将标题更改为 *ARROW-14977: [Python] 为指南教程添加一个“虚构的”功能*,以便使其与问题匹配。注意添加了一个标点符号!

补充说明:创建本教程时,我们一直在使用 Jira 问题跟踪器。由于我们目前正在使用 GitHub issues,因此标题将以 GH-14977 开头:[Python] 为指南教程添加一个“虚构的”功能.

我们还将添加一个描述,以便其他人清楚地了解我们正在尝试做什么。

点击 **创建拉取请求(Create pull request)** 后,我的代码就可以作为 Apache Arrow 存储库中的拉取请求进行审查了。

GitHub page of the Pull Request showing the title and a description.

这就是我们的拉取请求!#

拉取请求将连接到问题,并且 CI 正在运行。一段时间后,我们收到审查意见,我们可以更正代码、发表评论、解决对话等等。我们创建的拉取请求可以 在这里 查看。

另请参阅

有关拉取请求工作流程的更多信息,请参阅 拉取请求的生命周期