一日一技:爲 Python 項目編寫 Makefile
攝影:產品經理
某種海鮮鍋
本文翻譯自 Writing Makefiles for Python Projects[1]。原作者:Bastian Venthur.
作爲 Makefiles
的粉絲,我幾乎在每一個業餘項目裏面都使用它們。並且我也主張在工作項目中使用。
對開源項目來說,Makefiles
讓代碼貢獻者知道怎麼構建、測試、部署項目。並且,如果你正確使用了 Makefiles
,他們可以大大簡化你的 CI/CD 流程腳本。因爲你只需要簡單地調用對應的 make
命令就可以了。最重要的是,Makefiles
可以簡化你的開發工作。
對 Python 項目來說,我總是使用虛擬環境,因此我使用了兩個不同的 Makefiles
策略:
-
假設 make 命令是在虛擬環境裏面執行的
-
通過 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