Python 教學#

在本教學中,我們將依照快速參考章節以及更詳細的製作您的第一個 PR 的步驟章節中指定的步驟,實際對 Arrow 做出功能貢獻。當您發現此處缺少某些資訊時,請隨時瀏覽這些章節。

功能貢獻將被添加到 PyArrow 中的 compute 模組。但是,如果您正在修正錯誤或新增綁定,也可以按照這些步驟操作。

本教學與製作您的第一個 PR 的步驟不同,因為我們將處理一個特定的案例。本教學並非旨在作為逐步指南。

開始吧!

設定#

讓我們設定 Arrow 儲存庫。我們在此假設 Git 已經安裝。否則,請參閱設定章節。

一旦 Apache Arrow 儲存庫 被 fork 後,我們將克隆它,並將主要儲存庫的連結添加到我們的上游。

$ 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 儀表板,然後點擊 New issue 按鈕。

我們應該確保將自己分配給該 issue,以讓其他人知道我們正在處理它。您可以使用新增註解 take 到已建立的 issue 來完成此操作。

另請參閱

要獲取有關 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)]。如果我們指定應包含 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.pytest_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)]>

注意

在我們還沒有好的文件的情況下,單元測試可能是尋找程式碼範例的好地方。

憑藉關於 StructScalarpc.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 到上游主分支,以確保沒有合併衝突

$ 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。

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] 為指南教學新增一個「虛構」功能,以便與 issue 相符。請注意,已新增標點符號!

額外說明:當建立本教學時,我們一直在使用 Jira issue 追蹤器。由於我們目前正在使用 GitHub issue,因此標題將以 GH-14977: [Python] 為指南教學新增一個「虛構」功能 為前綴.

我們還將新增描述,以使其他人清楚我們嘗試做什麼。

一旦我點擊 Create pull request,我的程式碼就可以作為 Apache Arrow 儲存庫中的 Pull Request 進行審查。

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

這就是我們的 Pull Request!#

Pull Request 已連接到 issue,並且 CI 正在執行。經過一段時間並獲得審查後,我們可以更正程式碼、評論、解決對話等等。我們建立的 Pull Request 可以在 此處 查看。

另請參閱

有關 Pull Request 工作流程的更多資訊,請參閱 Pull request 的生命週期