一日一技:為 Python 項目編寫 Makefile
本文翻譯自Writing Makefiles for Python Projects[1]。原作者:Bastian Venthur.
作為 Makefiles的粉絲,我?guī)缀踉诿恳粋€業(yè)余項目里面都使用它們。并且我也主張在工作項目中使用。
對開源項目來說,Makefiles 讓代碼貢獻者知道怎么構建、測試、部署項目。并且,如果你正確使用了 Makefiles,他們可以大大簡化你的CI/CD 流程腳本。因為你只需要簡單地調(diào)用對應的 make 命令就可以了。最重要的是,Makefiles 可以簡化你的開發(fā)工作。
對 Python 項目來說,我總是使用虛擬環(huán)境,因此我使用了兩個不同的 Makefiles 策略:
- 假設 make 命令是在虛擬環(huán)境里面執(zhí)行的
- 通過 make 命令來封裝虛擬環(huán)境的命令
假設 make 命令是在虛擬環(huán)境中執(zhí)行的
我們來看一個非常簡單的 Makefile 文件,這個文件可以讓你實現(xiàn)構建、測試和發(fā)布 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
這幾段代碼寫的非常直接,所有潛在貢獻者立刻就知道你項目的入口在哪里了。
假設已經(jīng)有一個虛擬環(huán)境了,那么你需要首先激活它,然后再運行 make 命令:
- $ . venv/bin/activate
- $ make test
當然,不方便的地方在于,你的每一個 shell 窗口都必須手動激活虛擬環(huán)境。所以當你使用 tmux 激活一個新的終端窗口或者把 vim 放到后臺上去運行的時候,就很麻煩。
在 make 命令里面激活虛擬環(huán)境看起來是很難做到的,因為每一段代碼甚至每一個命令都會在它自己的 shell 里面運行。但是我們稍后看一個辦法繞過這個限制,比如說使用.ONESHELL標志,但這無法解決新開新的代碼片段運行在新 shell 的問題。
在 make 命令里面封裝虛擬環(huán)境的調(diào)用命令
第二個方法基本上解決了在 make 命令里面激活虛擬環(huán)境的問題。這個辦法是從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 跟剛才的差不多,但是代碼看起來更復雜了。所以我們現(xiàn)在一行一行來看看它是怎么實現(xiàn)的。
如果虛擬環(huán)境已經(jīng)激活,或者pytest, flake8這些包已經(jīng)安裝到了系統(tǒng) Python 環(huán)境里面,那么我們直接調(diào)用他們就可以了。但是現(xiàn)在,在新的 Makefile 文件中,我們顯式地使用虛擬環(huán)境中的絕對路徑來調(diào)用他們。為了確保虛擬環(huán)境存在,每一段代碼都依賴于$(VENV)這一項。這一項確保了當前有一個最新的虛擬環(huán)境可用。
這種方案有效,是因為當我們執(zhí)行. venv/bin/activate的時候,本來虛擬環(huán)境就是把它自己的絕對路徑放到了環(huán)境變量里面。因此每一次調(diào)用 Python 或者其他包的時候,都是使用虛擬環(huán)境中安裝的。
雖然 Makefile 文件變得有點復雜了,但是我們要測試代碼的時候,還是僅僅需要簡單地執(zhí)行一下命令:
- $ make test
就可以了,我們不需要再去關心虛擬環(huán)境是不是已經(jīng)安裝了之類的問題。如果你不需要支持 Windows,甚至可以從 Makefile 里面移除Windows 相關的部分。這樣一來,這個 Makefile 文件即使對于不怎么用的人來說也不難理解。
哪一種更好?
我覺得第二種方案更方便。雖然第一種方法我已經(jīng)快樂地用了幾年了,而第二種方法是最近才學到的。之前我確實沒有注意到這種方法。但我注意到幾乎所有使用 Makefile的 Python 項目都用的第一種方法,我也想知道為什么。
Kingname 點評
我在Python 項目和Golang 項目里面經(jīng)常使用Makefile,其中,Python 項目我主要用來刪除__pycache__,而 Golang 項目中,由于我使用的是 VSCode 來開發(fā),它的 lint 有點問題,所以代碼寫完以后,我會使用 Makefile 來執(zhí)行一段gofmt命令,把所有.go文件都格式化。
但 Makefile 有一個非常智障的地方——它里面的縮進必須使用制表符,不能使用空格。所以要寫Makefile 的時候,我還必須用 vim 來寫。因為我的 PyCharm 已經(jīng)調(diào)成把所有制表符換成空格的設置了。而如果在 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
本文轉載自微信公眾號「未聞Code」,可以通過以下二維碼關注。轉載本文請聯(lián)系未聞Code公眾號。