Await 做些相當庸俗的夢

使用 Poetry 管理 Python 專案

雖然後來 Python 寫得越來越少,但是還是要和一些使用 Python 的專案打交道,並且發現許多同學對於 Python 的虛擬環境,包管理等問題並不是很熟悉,因此寫這篇部落格梳理一下,希望對其他人有所幫助。

TL;DR

  • 使用 pyenv 切換不同版本的 python
  • 使用 poetry 在不同 python 下管理專案依賴和虛擬環境
  • 絕不使用 sudo pip install 安裝 python 包
  • 絕不使用 sudo make install 安裝 python

Python 版本選擇問題

除了特殊的本身就對於多版本軟體有良好支援的發行版(例如 NixOS)以外,目前來說(2021 年)系統上安裝的 python 最多隻有兩個大版本,2.x 和 3.x,這些 python 已經被系統包管理器處理好了各自的依賴關係,有時候甚至會用到系統關鍵元件中去。因此,除非知道自己在做什麼,絕對不要使用 sudo pip install 或者 make install 來在全域性安裝 python 庫 或者全新的 python 版本。

我所使用的多版本管理方案是 pyenv,能夠在不同的 python 版本中自由切換,類似於 rbenv 或者 opam switch

如果你只想要 python3,對小版本號沒有太苛刻的需求,那麼沒必要去使用 pyenv,可以在開發時直接使用後文提到的 poetry 來管理專案。

Poetry 和虛擬環境

在開發一個專案的時候,很可能需要安裝許多軟體包,這些軟體包有可能還需要指定版本,因此與本地版本衝突,這時我們就需要虛擬環境進行隔離。虛擬環境是一個乾淨的 python 環境,在這個環境中進行 python 軟體包的安裝並不會影響其他虛擬環境或者系統環境, python3.3 之後通常使用自帶的 venv 來實現。

在開發時,很常見的需求是需要復現環境,安裝需要的軟體包,透過 requirements.txt (原生 python)或者透過 Pipfile (pipenv)都可以,前者對於軟體包的互相依賴沒有很好的支援,後者的 lock 時間在專案稍微大一點的時候就令人發怵。

poetry 是一個更現代的 python 軟體包管理器。

常見的使用方式

  1. 新建一個專案:poetry new <project-name>

  2. 在當前專案中新增依賴:poetry add <package-name>,加上 --dev 引數表示為開發依賴。

  3. 安裝所有的依賴:poetry install

  4. 在虛擬環境中執行某個指令碼或者程式:poetry run <cmd>。其中 <cmd> 可以是 python <filename>.py,也可以是一個存在於虛擬環境中的可執行檔案的名字,例如 flaskfava 等等。

  5. 刪除現有的虛擬環境:poetry env rm pytyon

  6. 如果需要使用國內映象,則編輯 pyproject.toml,在 pyproject.toml 里加入

    1
    2
    3
    
    [[tool.poetry.source]]
    name = "zju"
    url = "https://mirrors.zju.edu.cn/pypi/web/simple"
    

安裝個人使用的軟體包

透過 pip install --user 能夠為當前環境中 pip 所對應的 python 版本 在使用者目錄下安裝軟體包。如果對於個人所需要使用的軟體包,同時系統包管理器又沒有提供的,推薦在(使用 pyenv )選擇了合適的 python 版本之後,用這種方式安裝。

同時,需要將安裝之後的 bin 路徑新增到環境變數中,通常是 $HOME/.local/bin。新增後即可直接在命令列中使用了。

進階使用

構建軟體包並且指定可執行的指令碼

執行 poetry build 即可構建軟體包。

指定可執行程式的函式入口:

1
2
[tool.poetry.scripts]
abc = "bca.abc:main"

表示安裝了最終構建的 wheel 包以後,有一個可執行檔案叫做 abc,執行之後會執行 bca/abc.py 中的 main 函式的內容。

透過 Poetry 執行 Python 服務和指令碼

有時候,系統沒有相應的軟體包可供安裝(例如一些 GitHub 上的自動指令碼),那麼使用 poetry 來配置並執行也是一個不錯的選擇。

只需要建立一個工程,只使用 poetry add <...>poetry run <...> 即可,既不會影響本地的環境,也可以在遷移和升級的時候更方便。不需要編寫任何程式碼,。

對於一些需要使用守護程序執行的服務,可以使用 systemd 管理。以下是我使用的一個 Unit file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[Unit]
Description=IUyhiuasd
After=network.target

[Service]
Type=simple
RemainAfterExit=true
WorkingDirectory=/usr/share/webapps/IUyhiuasd
ExecStart=/usr/bin/poetry run gunicorn wsgi:app
StandardOutput=journal

[Install]
WantedBy=multi-user.target


可能相關的