一日一技:爲 Python 項目編寫 Makefile

攝影:產品經理

某種海鮮鍋

本文翻譯自 Writing Makefiles for Python Projects[1]。原作者:Bastian Venthur.

作爲 Makefiles的粉絲,我幾乎在每一個業餘項目裏面都使用它們。並且我也主張在工作項目中使用。

對開源項目來說,Makefiles 讓代碼貢獻者知道怎麼構建、測試、部署項目。並且,如果你正確使用了 Makefiles,他們可以大大簡化你的 CI/CD 流程腳本。因爲你只需要簡單地調用對應的 make 命令就可以了。最重要的是,Makefiles 可以簡化你的開發工作。

對 Python 項目來說,我總是使用虛擬環境,因此我使用了兩個不同的 Makefiles 策略:

  1. 假設 make 命令是在虛擬環境裏面執行的

  2. 通過 make 命令來封裝虛擬環境的命令

假設 make 命令是在虛擬環境中執行的

我們來看一個非常簡單的 Makefile 文件,這個文件可以讓你實現構建、測試和發佈 Python 項目:

all: lint test

.PHONY: test
test:
    pytest

.PHONY: lint
lint:
    flake8

.PHONY: release
release:
    python3 setup.py sdist bdist_wheel upload

clean:
    find . -type f -name *.pyc -delete
    find . -type d -name __pycache__ -delete

這幾段代碼寫的非常直接,所有潛在貢獻者立刻就知道你項目的入口在哪裏了。

假設已經有一個虛擬環境了,那麼你需要首先激活它,然後再運行 make 命令:

$ . venv/bin/activate
$ make test

當然,不方便的地方在於,你的每一個 shell 窗口都必須手動激活虛擬環境。所以當你使用 tmux  激活一個新的終端窗口或者把 vim 放到後臺上去運行的時候,就很麻煩。

在 make 命令裏面激活虛擬環境看起來是很難做到的,因爲每一段代碼甚至每一個命令都會在它自己的 shell 裏面運行。但是我們稍後看一個辦法繞過這個限制,比如說使用.ONESHELL標誌,但這無法解決新開新的代碼片段運行在新 shell 的問題。

在 make 命令裏面封裝虛擬環境的調用命令

第二個方法基本上解決了在 make 命令裏面激活虛擬環境的問題。這個辦法是從 makefile.venv[2] 裏面學到的,我簡化了一下:

# system python interpreter. used only to create virtual environment
PY = python3
VENV = venv
BIN=$(VENV)/bin

# make it work on windows too
ifeq ($(OS), Windows_NT)
    BIN=$(VENV)/Scripts
    PY=python
endif


all: lint test

$(VENV): requirements.txt requirements-dev.txt setup.py
    $(PY) -m venv $(VENV)
    $(BIN)/pip install --upgrade -r requirements.txt
    $(BIN)/pip install --upgrade -r requirements-dev.txt
    $(BIN)/pip install -e .
    touch $(VENV)

.PHONY: test
test: $(VENV)
    $(BIN)/pytest

.PHONY: lint
lint: $(VENV)
    $(BIN)/flake8

.PHONY: release
release: $(VENV)
    $(BIN)/python setup.py sdist bdist_wheel upload

clean:
    rm -rf $(VENV)
    find . -type f -name *.pyc -delete
    find . -type d -name __pycache__ -delete

僅從功能上看,這個 Makefile 跟剛纔的差不多,但是代碼看起來更復雜了。所以我們現在一行一行來看看它是怎麼實現的。

如果虛擬環境已經激活,或者pytest, flake8這些包已經安裝到了系統 Python 環境裏面,那麼我們直接調用他們就可以了。但是現在,在新的 Makefile 文件中,我們顯式地使用虛擬環境中的絕對路徑來調用他們。爲了確保虛擬環境存在,每一段代碼都依賴於$(VENV)這一項。這一項確保了當前有一個最新的虛擬環境可用。

這種方案有效,是因爲當我們執行. venv/bin/activate的時候,本來虛擬環境就是把它自己的絕對路徑放到了環境變量裏面。因此每一次調用 Python 或者其他包的時候,都是使用虛擬環境中安裝的。

雖然 Makefile 文件變得有點複雜了,但是我們要測試代碼的時候,還是僅僅需要簡單地執行一下命令:

$ make test

就可以了,我們不需要再去關心虛擬環境是不是已經安裝了之類的問題。如果你不需要支持 Windows,甚至可以從 Makefile 裏面移除 Windows 相關的部分。這樣一來,這個 Makefile 文件即使對於不怎麼用的人來說也不難理解。

哪一種更好?

我覺得第二種方案更方便。雖然第一種方法我已經快樂地用了幾年了,而第二種方法是最近才學到的。之前我確實沒有注意到這種方法。但我注意到幾乎所有使用 Makefile 的 Python 項目都用的第一種方法,我也想知道爲什麼。

Kingname 點評

我在 Python 項目和 Golang 項目裏面經常使用 Makefile,其中,Python 項目我主要用來刪除__pycache__,而 Golang 項目中,由於我使用的是 VSCode 來開發,它的 lint 有點問題,所以代碼寫完以後,我會使用 Makefile 來執行一段gofmt命令,把所有.go文件都格式化。

但 Makefile 有一個非常智障的地方——它裏面的縮進必須使用製表符,不能使用空格。所以要寫 Makefile 的時候,我還必須用 vim 來寫。因爲我的 PyCharm 已經調成把所有制表符換成空格的設置了。而如果在 Makefile 的縮進裏面混入了空格,它就會報錯。

如果大家對 Makefile 有興趣的話,我給大家寫一篇從入門到精通的文章。有興趣的同學請留言~

參考資料

[1]

Writing Makefiles for Python Projects: https://venthur.de/2021-03-31-python-makefiles.html

[2]

makefile.venv: https://github.com/sio/Makefile.venv

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/SnGdETFUf-QjeoVPqH_PCg