我在此对这篇教程进行一下补充,这部分内容其实只适用于单文件项目。而在编写当初的项目时,单文件模式其实就已经开始难以管理了,main.py 看起来非常的乱,完全不知道什么函数在哪里。我之后又使用 Typer 重构了项目,分离了工具函数与命令函数,并令其拥有了用户级的配置文件,在未来还会将业务逻辑与命令函数分离,具体的做法可以看我的项目,以及这篇教程。
随着我对LAMMPS数据后处理要求和后处理项目的增多,之前使用的Python脚本有点难以管理(每次都要从其他项目中复制脚本并修改输出文件名有点蠢)。将我之前的Python脚本功能集合开发成一个Python CLI应用的必要性越来越大。由于我是一个半路出家的和尚(不管从Python还是MD来说),我是通过Google Gemini学习的Python项目开发方式。为了防止遗忘,同时也为了强化记忆,我将从Google Gemini问来的如何使用uv进行项目管理的方式记载此处,同时剔除了AI幻觉便于学习。
uv是一个用 Rust 编写的极速 Python 包和项目管理工具,它使用 pyproject.toml 来组织Python项目。项目开发 | uv 中文文档描述了使用uv来进行Python项目开发的相应功能,我将叙述一个使用uv开发CLI应用的流程。
使用uv来初始化和管理项目
创建新项目
使用 uv init 来创建一个新的Python项目:
uv init my-cli-projectcd my-cli-project也可以在当前已经开发中的项目中使用uv来重新组织,在当前的工作目录中初始化一个项目:
mkdir my-cli-project && cd $_uv init上述命令会在项目目录中直接创建以下文件:
.├── .python-version├── README.md├── main.py└── pyproject.toml项目结构
这里uv默认构建的Python项目是一个平面的脚本项目, main.py 文件直接放在了主目录下。要开发一个CLI应用,你应该将项目调整为以下结构,Gemini推荐使用 src 的结构布局:
my-cli-project/├── .venv/ # 虚拟环境 (uv venv 创建, 应被Git忽略)|├── src/ # 源代码的根目录│ └── my_cli_project/ # 这才是你实际的Python包 (包名通常用下划线)│ ├── __init__.py # 包初始化文件, 可以为空│ └── main.py # CLI入口和核心逻辑│├── tests/ # 存放自动化测试代码的目录,可以没有│ ├── __init__.py # 标记为包│ └── test_main.py # 针对 main.py 的测试文件│├── .gitignore # Git忽略文件配置├── pyproject.toml # 项目配置文件└── README.md # 项目说明文档每个部分的职责
-
my_cli_project/(项目根目录)- 这是你项目的“容器”,所有与项目相关的文件都在这里。你的版本控制(如Git)也应该在这里初始化。
-
.venv/- 由
uv venv创建的虚拟环境目录。关键:一定要将它添加到.gitignore文件中,永远不要提交到版本控制里。
- 由
-
src/(源代码目录)- 这是一个专门存放你可安装源代码的目录。这是
src布局的核心。它的存在是为了将你的源代码与其他项目文件(如测试、文档、配置文件)清晰地隔离开。
- 这是一个专门存放你可安装源代码的目录。这是
-
src/my_cli_project/(Python包目录)-
这才是你真正的Python包。当你(或其他人)通过
pip或uv安装你的项目时,只有这个目录里的内容会被安装到Python的site-packages中。 -
它的名字 (
my_cli_project) 就是包名,也是你在代码中import时使用的名字。 -
__init__.py: 一个空文件,用于告诉Python这个目录是一个包。 -
main.py: 你的CLI应用主文件,里面包含Typer或argparse的代码。随着项目变大,你可以在这里添加更多的.py文件,如utils.py,core.py等。
-
-
tests/-
存放所有测试代码的地方。使用像
pytest这样的框架时,它会自动发现并运行这个目录下的测试。将测试与源代码分开是一种非常好的实践。 -
如果不使用
pytest这样的测试工具,或你的项目比较小的时候,可以在main.py文件的if __name__ == "__main__":中测试你的函数。
-
-
.gitignore- 告诉Git哪些文件或目录不应该被追踪。uv会自动将其自动生成的结构中不应该被Git所控制的内容包含在
.gitignore中,你只需要关心自己创建的文件。
- 告诉Git哪些文件或目录不应该被追踪。uv会自动将其自动生成的结构中不应该被Git所控制的内容包含在
-
pyproject.toml- 项目的中央配置文件。它定义了项目名称、版本、依赖、构建系统以及我们为CLI设置的
[project.scripts]入口点。
- 项目的中央配置文件。它定义了项目名称、版本、依赖、构建系统以及我们为CLI设置的
-
README.md- 项目的门面。用Markdown格式编写,介绍你的项目是做什么的、如何安装、如何使用。
正式开始开发
main.py 文件的结构
开发CLI应用需要使用 argprase 或 Typer 这样的命令行解析工具。 Typer 是一个第三方模块,它是一个用于构建CLI的常用框架,并且依赖 rich 库,但由于我的CLI应用有点简单,我选择了使用内置的 argparse 这个内置模块。我所开发的 main.py 结构如下所示:
import module
# 实现功能的函数def do_hello(name,flag): if flag: print("Hello World!") else if: print(f"Hello World! from {name}")
# 主函数def main(): # 1. 创建主解析器 parser = argparse.ArgumentParser(description="A cool CLI application built with argparse.")
# 2. 创建子命令解析器 # 这是实现 git <command> 这种模式的关键 subparsers = parser.add_subparsers(dest="command", help="Available commands", required=True)
# 3. 创建 'hello' 命令的解析器 parser_greet = subparsers.add_parser("hello", help="Greets someone.") parser_greet.add_argument( "--name", # 传参的全称, 实现类似功能: my-cli hello --name lancer_soul "-n", # 传参的简称,实现类似功能: my-cli hello -n laner_soul type=str, # 规定传参的类型,默认为str,其他类型需要特殊指定 default="World", # 规定传参默认值 help="The name to greet." ) parser_greet.add_argument( "-ignore", "-i", action="store_true", # 代表-i是一个flag,指定该选项为True, 不指定为False, 传入值时会报错 help="ignore --name to print 'Hello World!'" )
# 5. 解析来自终端的参数 args = parser.parse_args()
# 6. 根据解析出的命令,调用对应的函数,如果不传入命令,argparse会默认显示帮助 if args.command == "greet": do_hello(name=args.name, flag=args.ignore)
if __name__ == "__main__": main()配置 pyproject.toml
上述文件其实就是平常所使用的脚本文件,但要构建CLI应用,我们还需要配置 pyproject.toml 文件,除uv初始自动生成的内容外和 uv add module 生成的 dependencies 外,我们还需要填入以下内容:
[build-system] # 可以没有,不指定就默认使用setputoolsrequires = ["setuptools"]build-backend = "setuptools.build_meta"[project.scripts]my-cli = "my_cli_project.main:main" # 定义CLI入口,my-cli会使用my_cli_project包main.py中的main函数uv能够自动解析依赖并安装,我们不需要手动 uv add ,直接 uv run main.py 就可以安装文件中声明的依赖。但使用 uv add module 手动安装依赖并进入venv可以让nvim lsp提供补全。
测试应用
当你完成上述编辑与配置后,我们可以开始测试这个应用,测试有两种方式:
-
一种是在当前的虚拟环境中以“可编辑模式”安装这个应用并测试,可编辑模式意味着你任何对源码的修改都会立即生效,非常适合开发环境。
-
另一种是全新安装软件包并测试,这意味着你与其他用户的环境一致,用于最后测试。
可编辑模式
在你的项目根目录下,运行以下代码在当前虚拟环境以可编辑模式安装应用:
uv pip install -e .接下来测试你的应用:
my-cli hello -n lancer_soul全新安装
全新安装应用使用了uv提供的 uvx 命令,该命令会临时创建一个虚拟环境并安装指定的包,结束后立即销毁创建的环境。
uvx --from . my-cli hello -n lancer-soul安装自己的应用
只为你自己安装应用
我不想把我的应用提交到PyPI,并且我也不想在我的LAMMPS环境中安装venv,因此我选择将我的包安装为uv的一个tool。只需要在项目根目录的虚拟环境外运行:
uv tool install --from .这个命令会为你的包创建一个全新的虚拟环境并将其安装到环境中,之后你在任何地方只需要运行以下命令就查看已安装的包并使用CLI应用:
uv tool list # 查看目前已安装的包uv tool run my-cli hello -n lancer_soul # 运行你的包将应用提交到PyPI并使用
在将应用提交到PyPI前你需要先构建自己的应用,uv的构建目录默认为dist/:
uv build发布包的命令为:
uv publish通过 --token 或 UV_PUBLISH_TOKEN 设置PyPI令牌来设置自己的账号发布你的包。
结语
到此,你已经成功开发并安装了一个自己开发的Python应用。恭喜!