Python项目中Makefile的应用与入门指南

本文详细介绍了如何在Python项目中使用Makefile来统一管理开发工作流,包括环境配置、代码格式化、测试运行等自动化任务,通过具体代码示例展示Makefile的编写方法和最佳实践。

引言

想象一下:你正在开发一个Python项目,每次运行测试都需要输入python3 -m pytest tests/ --verbose --cov=src。格式化代码时要运行black . && isort .。进行代码检查时又要执行flake8 src tests。很快你就会发现自己需要记住十几个不同的命令,而你的团队成员也在用略有不同的方式执行相同的操作。

这时Makefile就能派上用场。虽然最初用于C和C++项目,但Makefile在Python开发中同样非常有用,可以作为一种简单的方法来标准化和自动化常见任务。你可以把Makefile看作是一个集中定义所有重复操作快捷方式的地方。

为什么在Python项目中使用Makefile?

团队协作的一致性

当团队中的每个人都使用make test而不是记住带有所有参数的确切pytest命令时,你就能消除"在我机器上能运行"的问题。新团队成员可以快速上手,立即知道如何运行测试、格式化代码或部署应用。

真正有效的文档

简化复杂工作流

某些任务需要多个步骤。你可能需要安装依赖、运行迁移、填充测试数据,然后启动开发服务器。使用Makefile后,这只需要一个make dev命令就能完成。

创建第一个Python Makefile

让我们逐步构建一个实用的Makefile。在项目根目录创建一个名为Makefile(无扩展名)的文件。

基本结构和帮助命令

这段代码为Makefile创建了一个自动帮助系统,显示所有可用命令及其描述:

1
2
3
4
5
6
.PHONY: help
help:  ## 显示帮助信息
	@echo "可用命令:"
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2}'

.DEFAULT_GOAL := help

.PHONY: help告诉Make"help"不是一个真实文件,而是一个要运行的命令。当你输入make help时,它会先打印"可用命令:",然后使用grep和awk的组合扫描Makefile本身,找到所有包含命令名称后跟##描述的行,并将它们格式化为带有命令名称和解释的可读列表。

环境设置

这段代码创建了三个环境管理命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
.PHONY: install
install:  ## 安装依赖
	pip install -r requirements.txt
	pip install -r requirements-dev.txt

.PHONY: venv
venv:  ## 创建虚拟环境
	python3 -m venv venv
	@echo "使用以下命令激活: source venv/bin/activate"

.PHONY: clean
clean:  ## 清理缓存文件和构建产物
	find . -type f -name "*.pyc" -delete
	find . -type d -name "__pycache__" -delete
	find . -type d -name "*.egg-info" -exec rm -rf {} +
	rm -rf build/ dist/ .coverage htmlcov/ .pytest_cache/

install命令运行pip两次,从requirements文件安装主要依赖和开发工具。venv命令创建一个名为"venv"的Python虚拟环境文件夹,并打印激活说明。

clean命令删除Python在开发过程中创建的所有杂乱文件。它会删除编译的Python文件(.pyc)、缓存文件夹(pycache)、包信息目录以及构建产物如覆盖率报告和测试缓存。

代码质量和测试

这段代码创建了代码质量命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
.PHONY: format
format:  ## 使用black和isort格式化代码
	black .
	isort .

.PHONY: lint
lint:  ## 运行代码检查
	flake8 src tests
	black --check .
	isort --check-only .

.PHONY: test
test:  ## 运行测试
	python -m pytest tests/ --verbose

.PHONY: test-cov
test-cov:  ## 运行带覆盖率的测试
	python -m pytest tests/ --verbose --cov=src --cov-report=html --cov-report=term

.PHONY: check
check: lint test  ## 运行所有检查(代码检查+测试)

format命令使用black进行格式化和isort进行导入组织,自动修复代码样式。

lint命令在不更改任何内容的情况下检查代码是否遵循样式规则。flake8查找样式违规,而black和isort以仅检查模式运行,查看是否需要格式化。

test命令运行测试套件。test-cov运行测试并测量代码覆盖率,生成报告。check命令通过依赖lint和test命令,同时运行代码检查和测试。

开发工作流

这段代码创建了开发工作流命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.PHONY: dev
dev: install  ## 设置开发环境
	@echo "开发环境准备就绪!"
	@echo "运行'make serve'启动开发服务器"

.PHONY: serve
serve:  ## 启动开发服务器
	python3 -m flask run --debug

.PHONY: shell
shell:  ## 启动带有应用上下文的Python shell
	python3 -c "from src.app import create_app; app=create_app(); app.app_context().push(); import IPython; IPython.start_ipython()"

dev命令首先运行install命令设置依赖,然后打印成功消息和后续步骤。serve命令以调试模式启动Flask开发服务器。

shell命令启动一个已经连接到Flask应用上下文的IPython shell,这样你就可以交互式地测试数据库查询和应用功能,而无需手动导入所有内容。

更多Makefile技巧

使用变量

你可以定义变量来避免重复:

1
2
3
4
5
6
7
PYTHON := python3
TEST_PATH := tests/
SRC_PATH := src/

.PHONY: test
test:  ## 运行测试
	$(PYTHON) -m pytest $(TEST_PATH) --verbose

条件命令

有时你需要根据环境有不同的行为:

1
2
3
4
5
6
7
8
9
.PHONY: deploy
deploy:  ## 部署应用
ifeq ($(ENV),production)
	@echo "部署到生产环境..."
	# 生产环境部署命令
else
	@echo "部署到预发布环境..."
	# 预发布环境部署命令
endif

文件依赖

你可以让目标依赖于文件,这样它们只在需要时运行:

1
2
3
4
5
6
requirements.txt: pyproject.toml
	pip-compile pyproject.toml

.PHONY: sync-deps
sync-deps: requirements.txt  ## 同步依赖
	pip-sync requirements.txt

🔗 这是一个完整的Flask Web应用Makefile示例。

最佳实践和技巧

以下是编写Makefile时应遵循的一些最佳实践:

  • 不要过度复杂化你的Makefile。如果任务变得复杂,考虑将逻辑移到单独的脚本中,并从Make调用它。
  • 选择能清楚表明其功能的命令名称。make testmake t更好,make dev-setupmake setup更清晰。
  • 对于不创建文件的命令,始终将它们声明为.PHONY。这可以防止有人创建与你的命令同名的文件时出现问题。
  • 组织你的Makefile,将相关功能分组在一起。
  • 确保所有命令都能在全新克隆的仓库中工作。没有什么比一个损坏的设置过程更让新贡献者沮丧了。

结论

Makefile可能看起来像是一个老式工具,但它们在Python项目中非常有效。它们为常见任务提供一致的接口,并帮助新贡献者快速上手。

首先创建一个只包含install、test和help命令的基本Makefile。随着项目的增长和工作流变得更加复杂,你可以根据需要添加更多目标和依赖。

记住,目标不是创建最聪明或最复杂的Makefile,而是让你的日常开发任务更容易、更可靠。保持简单,保持实用,让你的Makefile成为命令中心,为Python项目的混乱带来秩序。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计