Skip to content

Python 项目的程序入口与脚本管理

在采用现代化的 src 布局后,一个常见的问题是:"我应该如何运行我的代码?程序的入口在哪里?" 这个问题触及了 Python 与 JavaScript 在项目执行理念上的一个核心差异。

参考资料:


1. 核心理念:从"运行文件"到"运行包"

  • JavaScript (Node.js): 通常有一个明确的入口文件(如 index.jsmain.js),你可以通过 node index.js 来启动整个应用。
  • Python: 现代 Python 鼓励将你的项目视为一个可安装的包。直接运行包内的某个文件(如 python src/my_package/app.py)是一种反模式,因为它会破坏 Python 的导入系统,导致 ImportError

正确的做法是,首先以可编辑模式安装你的包,然后通过模块或定义的入口点来运行它。

bash
# 在你的项目根目录 (包含 pyproject.toml 的地方)
# 这会将你的项目链接到当前的虚拟环境中,让解释器能找到它
pip install -e .

安装之后,你就有两种"Pythonic"的方式来运行你的代码。


2. 定义程序入口 (Entry Points)

方式一:使用模块执行器 (python -m)

python -m <module_name> 命令告诉 Python 解释器去作为模块查找并执行 module_name,而不是作为普通脚本。这会自动处理好路径问题,让包内的相对导入正常工作。

假设你的入口逻辑在 src/my_package/app.py 文件中,并被 if __name__ == "__main__" 保护:

python
# src/my_package/app.py
from . import helper # 这是一个有效的相对导入

def main():
    print("应用主逻辑开始...")
    helper.do_something()

if __name__ == "__main__":
    main()

在安装了包之后,你可以这样运行它:

bash
python -m my_package.app

这会正确地执行 app.py 中的代码块,并且 from . import helper 也能正常工作。

方式二(推荐):使用 [project.scripts]

这是最现代、最强大的方式,它能让你的 Python 应用像一个真正的原生命令行工具一样被调用。你可以在 pyproject.toml 文件中定义一个或多个"脚本入口"。

1. 准备你的函数 首先,确保你的入口逻辑被封装在一个无参数的函数中,例如上面例子中的 main() 函数。

2. 在 pyproject.toml 中配置[project] 表下,添加一个新的 scripts 表:

toml
# pyproject.toml

[project]
name = "my-awesome-app"
# ... 其他元数据 ...

[project.scripts]
my-awesome-app = "my_package.app:main"
another-tool = "my_package.tools:run_tool"
  • 格式: 命令名称 = "包名.模块名:函数名"
  • my-awesome-app: 这是用户安装你的包后,可以在终端直接输入的命令。
  • "my_package.app:main": 这指向了当用户运行该命令时,实际应该被调用的函数。

3. 重新安装 每次修改 [project.scripts] 后,你需要重新运行 pip install -e . 来让这些脚本生效。

4. 运行 现在,你或你的用户可以在终端的任何地方直接运行:

bash
my-awesome-app

这会直接调用 my_package.app 模块里的 main 函数。这种方式不仅方便,而且完全隐藏了项目的内部文件结构,提供了非常专业的命令行体验。


3. 类似 npm scripts 的任务运行器

[project.scripts] 解决了"程序入口"的问题,但对于开发过程中的其他任务(如测试、linting、构建),Python 社区并没有一个像 npm scripts 那样内置于核心配置文件中的统一标准。不过,有几种非常流行的替代方案:

Makefile (通用且简单)

Makefile 是一个非常强大的通用任务运行器,不限于任何语言。你可以用它来定义一系列开发任务的快捷方式。

makefile
# Makefile
.PHONY: install test lint run

# 安装所有依赖,包括开发依赖
install:
	pip install -e ".[dev]"

# 运行测试
test:
	pytest

# 运行 linter
lint:
	ruff check .

# 通过入口点运行主程序
run:
	my-awesome-app

开发者只需要运行 make test, make run 等简单命令即可。

现代构建工具的脚本功能

一些现代的 Python 项目管理工具正在向 npm scripts 的体验靠拢,它们允许你在 pyproject.toml 的特定 [tool.*.scripts] 表中定义任务。

Poetry 示例:

toml
# pyproject.toml (using Poetry)
[tool.poetry.scripts]
test = "pytest"
lint = "ruff check ."
run = "my-awesome-app"

然后你可以通过 poetry run test, poetry run lint 来执行这些任务。

Hatch 示例:

toml
# pyproject.toml (using Hatch)
[tool.hatch.scripts]
test = "pytest"
lint = "ruff check ."
run = "my-awesome-app"

然后你可以通过 hatch run test, hatch run lint 来执行。

结论: 虽然 Python 没有与 npm scripts 完全一一对应的原生机制,但通过结合 [project.scripts] (用于程序入口) 和任务运行器 (如 Makefile 或工具内置脚本),你可以构建出一个同样强大、清晰且易于维护的开发工作流。