使用 Git repo 安裝 Python 套件
最近,隨著一些在維護 Python 專案的規模越來越大,正所謂合久必分,為了讓專案主要的功能可以有效率地使用系統資源,我們計劃將這些 Python 專案依據不同的功能拆開。
拆專案的過程中,免不了會有模組間的依賴問題,例如一些 Utility 類的工具、定義的資訊等等。
除了怎麼拆比較乾淨之外,我們也要考慮「定版」的問題,因為當被依賴方(如 Utility)更新的時候,應該由依賴方(如 Project A)主動更新所引用之版號,否則被 Utility 一旦更新就有可能直接讓所有的依賴方 Project A 和 Project B 壞掉。
今天我們就來探討將共用模組拆分出來的主要邏輯!
模組拆分及版本依賴
根據拆分的模組,可以簡單分成 Parent 和 Child 兩種
Parent - 被依賴方,共用之邏輯、定義,被多個子專案使用
Child - 依賴方,使用特定版本的 Parent Package
Child 可以依賴 Parent 的某個 tag(版本),當 Parent 更新時,Child 能主動升級成最新的版本,當然也能選擇維持使用舊版的 Parent,保有其彈性。
例如在 Utility 這個 Parent 中,將 to_taiwan_time
這個函式修訂了一個版本,將回傳型別由 str
改成 datetime
,如果 Utility 和 Project A 及 B 都是同一個專案的話,此函式一經修改則 A 和 B 所使用到的地方就要跟著變動。
而定版的好處就是能讓依賴方保有選擇彈性,像是 Project A 能保持原本的 V1 版本,Project B 能使用最新版。
拆分與引用的兩種方式
當我們把一個 Python 專案拆開來的時候,可以將共用的程式碼拆出來用 Git submodule 的方式置入在同一個專案底下直接引用;又或是直接做成一個 Python Package,透過 pip install
的方式來安裝之後引用。
Git submodule
先從最基礎、設定較為簡單方式來講起,使用 Git submodule。
我們可以透過下述指令將 Parent 加到 Child 的 Submodule 中:
1 | $ git submodule add ssh://git.server/parent.git libs/parent |
假設 Parent repo 有以下 commits 與對應的 tag
1 | a1b2c3d <- tag: 0.1 |
當 Submodule 被加入後,Child 會指向某個特定 Commit,例如 0.1
版。若要將 Submodule 更新成指向 0.2
版,則需要做以下操作:
1 | $ cd libs/parent |
意思就是說,Submodule 是一種嵌入式的 Git repo,會在 Child 專案中保留 Parent 的版本(Commit hash)。
因此在每次切換 Parent 版本的時候,都需要進入 Submodule(Parent)中切換 Commit,再回到主專案(Child)中將此變更記錄起來。
然而,使用 Git submodule 的形式像是將 Parent 專案放到 Child 專案中,除了在版本切換上需要進入 Submodule 來手動切換,在 Python 的引用上也比較麻煩一些,更乾淨一點的做法是將 Parent 專案包成 Python package。
Python package
一旦將專案包成 Python package,我們便可以透過 pip install
來安裝。例如使用 Submodule 時能執行 pip install -e libs/parent
,以 Symbolic link 的方式將 Parent 安裝到目前的 Child 專案中。
那麼要怎麼將 Python 打包成 Package 呢?有以下兩個必要元素:
pyproject.toml
,放在專案根目錄,用來定義 Package 名稱、版本、Dependencies 等,如1
2
3[project]
name = "parent-package"
version = "0.1"建立對應的資料夾與
__init__.py
,告訴 Pythonparent_package
是一個 Package1
2parent_package/
__init__.py
除了透過 Submodule 加上 pip install -e
的做法,將 Package 推上 PyPI(Python Package Index,一個可以上傳 Python package 的空間)也是一個選擇,這樣便能透過 pip install
來直接安裝這個 Package。
然而,公開的 PyPI 不一定是每間公司合適的選擇,很有可能我們只想在內部使用而已。因此另一個辦法就是自己維護一個 Private PyPI。
由於要維護一個額外的服務,實際上麻煩了不少,其實還有一種直接從 Git repo 抓下來安裝的辦法。
透過 Pip 從 Git repo 安裝
我們一樣可以把 Parent 按照上述 Python package 的方式建立(確認有 pyproject.toml
、package 目錄),並且建立 Git tag
1 | $ git tag 0.2 |
接著在 Child 裡面使用 Pip 安裝,就只要透過以下指令
1 | $ pip install git+ssh://git.server/parent.git@0.2 |
就能成功安裝版本為 0.2
的 Parent package 了。
除此之外,我們也可以將 Parent package 直接寫入 requirements.txt
後
1 | git+ssh://git.server/parent.git@0.2 |
執行 pip install -r requirements.txt
來安裝。
SSH 設定說明
值得一提的是,假設我們的 Git server 位於 git.server:12345
,由於 SSH 在連線時預設會綁入帳號,並且從你的 ~/.ssh/id_rsa
中取得非對稱式加密的密鑰,我們需要指定一組帳號(不然會直接透過 $USER
拿系統的帳號,可能會和 Git 的帳號不一致)
1 | Host my.git.server |
這樣設定完之後,就能使用 pip install git+ssh://git.server
的形式從 Git repo 抓取 Python package 了。