文章目录
- mkdir my-uv-demo cd my-uv-demo 目录结构如下: my-uv-demo/├── pyproject.toml└── src/ └── my_uv_demo/ ├── __init__.py └── mycli.py
- def main(): print(“Hello from mycli! ✨”) if __name__ == “__main__”: main()
- [project] name = “my-uv-demo” # PyPI 上的包名(需唯一) version = “0.1.0” description = “A simple CLI tool built with uv” authors = [{name = “Your Name”, email = “you@example.com”}] requires-python = “>=3.12” license = {text = “MIT”} readme = “README.md” # 定义命令行入口:mycli -> 调用 my_uv_demo.xccli:main [project.scripts] xccli = “my_uv_demo.mycli:main” # 指定源码在 src/ 下 [tool.hatch.build.targets.wheel] packages = [“src/my_uv_demo”] [build-system] requires = [“hatchling”] build-backend = “hatchling.build”
- 在项目根目录运行: uvx –from . mycli 输出: Hello from mycli! ✨ 这说明你的 CLI 工具已能正常工作!
- 一旦发布成功,为什么uvx xccli 直接执行就可以运行?它是怎么找到这个xccli工具的? 一句话回答:uvx xccli 能直接运行,是因为 uvx 会自动在 PyPI 上查找哪个已发布的 Python 包提供了名为 xccli 的可执行脚本(entry point),然后下载、安装并运行它。 这依赖于两个核心机制: PyPI 包的元数据中声明了 console_scripts(即 [project.scripts]) uvx 能解析这些元数据,并按需构建临时环境来执行命令
- 第一步:你的包在 PyPI 上“注册”了 mycli 命令 当你在 pyproject.toml 中写: [project.scripts] mycli = “my_uv_demo.mycli:main” 打包工具(如 hatchling)会在生成的 wheel(.whl 文件)中,将这条信息写入 entry_points.txt 元数据文件,内容类似: [console_scripts] mycli = my_uv_demo.mycli:main 当你发布到 PyPI 后,这个元数据就公开可查。 这个机制最早来自 setuptools 的 console_scripts,现在是标准(PEP 621)。 第二步:用户运行 uvx xccli 当用户输入: uvx mycli uvx 会执行以下步骤: 1.查询 PyPI:哪个包提供了 xccli? uvx 会向 PyPI 的 API 发起请求(或使用本地缓存),查找所有已发布包中,console_scripts 包含 xccli 的包。 例如,它发现: 包 my-uv-demo(你发布的)声明了 xccli = … 没有其他包冲突(如果有多个,会报错或让用户指定) 注意:uvx 不是靠包名找命令,而是靠 命令名 → 反查提供者。 2.下载并安装该包到隔离环境 uvx 会: 创建一个临时虚拟环境(或复用缓存) 用 uv pip install my-uv-demo 安装你的包(及其依赖) 安装过程中,pip/uv 会根据 console_scripts 自动生成一个可执行文件(如 xccli 或 xccli.exe)并放入环境的 bin/ 目录 3.直接执行该命令 最后,uvx 直接调用这个 xccli 可执行文件,等价于: /path/to/temp-env/bin/mycli 但对用户完全透明! 验证:手动查看 PyPI 包的 entry points 你可以用 pip show 查看任意已安装包的脚本: pip install my-uv-demo # 假设你已发布 pip show -f my-uv-demo | grep mycli 或者用 uv: uv pip show -f my-uv-demo 你会看到类似: console-scripts: xccli = my_uv_demo.xccli:main
目录
- 为什么选择 uv + pyproject.toml
- 开发一个简单的命令行工具:mycli
- 1. 创建项目结构
- 2. 编写核心逻辑:src/my_uv_demo/mycli.py
- 3. 配置 pyproject.toml
- 4. 本地测试(使用 uvx)
- 用户如何使用你的工具
- 详解原理
- 详细原理分解
- 常见问题 & 注意事项
- 总结
在现代 Python 开发生态中,命令行工具(CLI)是自动化、脚本化和工具链集成的核心。借助 uv(超快的 Python 包管理器)和 PyPI,我们可以快速开发、打包、发布一个 CLI 工具,并让用户无需安装 Python 环境就能通过 uvx 直接运行!
本文将带你完成整个流程:
- 开发一个最简 CLI 工具
- 使用标准 src/ 项目结构
- 打包并发布到 PyPI
- 用户通过 uvx xccli 一键使用
- uv 是由 Astral 开发的超快 Python 包安装器(比 pip 快 10–100 倍)
- uvx 可以直接运行 PyPI 上的 CLI 工具,无需 pip install
- pyproject.toml 是现代 Python 项目的标准配置文件(取代 setup.py)
- src/ 布局 避免开发时意外导入本地模块,更安全
我们要创建的工具叫 mycli,运行后输出一句问候。
mkdir my-uv-demo
cd my-uv-demo
目录结构如下:
my-uv-demo/
├── pyproject.toml
└── src/
└── my_uv_demo/
├── __init__.py
└── mycli.py
def main():
print("Hello from mycli! ✨")
if __name__ == "__main__":
main()
[project]
name = "my-uv-demo" # PyPI 上的包名(需唯一)
version = "0.1.0"
description = "A simple CLI tool built with uv"
authors = [{name = "Your Name", email = "you@example.com"}]
requires-python = ">=3.12"
license = {text = "MIT"}
readme = "README.md"
# 定义命令行入口:mycli -> 调用 my_uv_demo.xccli:main
[project.scripts]
xccli = "my_uv_demo.mycli:main"
# 指定源码在 src/ 下
[tool.hatch.build.targets.wheel]
packages = ["src/my_uv_demo"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
在项目根目录运行:
uvx --from . mycli
输出:
Hello from mycli! ✨
这说明你的 CLI 工具已能正常工作!
一旦发布成功,任何安装了 uv 的用户都可以直接运行:
uvx xccli
无需:
- 安装 Python(如果系统已有)
- 创建虚拟环境
- 手动 pip install
uvx 会自动:
- 从 PyPI 下载你的包
- 解析依赖(本例无依赖)
- 在隔离环境中运行 xccli 命令
输出依然是:
Hello from mycli! ✨
这就是现代 Python 工具分发的极致体验!
一旦发布成功,为什么uvx xccli 直接执行就可以运行?它是怎么找到这个xccli工具的?
一句话回答:uvx xccli 能直接运行,是因为 uvx 会自动在 PyPI 上查找哪个已发布的 Python 包提供了名为 xccli 的可执行脚本(entry point),然后下载、安装并运行它。
这依赖于两个核心机制:
- PyPI 包的元数据中声明了 console_scripts(即 [project.scripts])
- uvx 能解析这些元数据,并按需构建临时环境来执行命令
第一步:你的包在 PyPI 上“注册”了 mycli 命令
当你在 pyproject.toml 中写:
[project.scripts] mycli = "my_uv_demo.mycli:main"
打包工具(如 hatchling)会在生成的 wheel(.whl 文件)中,将这条信息写入 entry_points.txt 元数据文件,内容类似:
[console_scripts] mycli = my_uv_demo.mycli:main
当你发布到 PyPI 后,这个元数据就公开可查。
这个机制最早来自 setuptools 的 console_scripts,现在是标准(PEP 621)。
第二步:用户运行 uvx xccli
当用户输入:
uvx mycli
uvx 会执行以下步骤:
1.查询 PyPI:哪个包提供了 xccli?
uvx 会向 PyPI 的 API 发起请求(或使用本地缓存),查找所有已发布包中,console_scripts 包含 xccli 的包。
例如,它发现:
- 包 my-uv-demo(你发布的)声明了 xccli = …
- 没有其他包冲突(如果有多个,会报错或让用户指定)
注意:uvx 不是靠包名找命令,而是靠 命令名 → 反查提供者。
2.下载并安装该包到隔离环境
uvx 会:
- 创建一个临时虚拟环境(或复用缓存)
- 用 uv pip install my-uv-demo 安装你的包(及其依赖)
- 安装过程中,pip/uv 会根据 console_scripts 自动生成一个可执行文件(如 xccli 或 xccli.exe)并放入环境的 bin/ 目录
3.直接执行该命令
最后,uvx 直接调用这个 xccli 可执行文件,等价于:
/path/to/temp-env/bin/mycli
但对用户完全透明!
验证:手动查看 PyPI 包的 entry points
你可以用 pip show 查看任意已安装包的脚本:
pip install my-uv-demo # 假设你已发布 pip show -f my-uv-demo | grep mycli
或者用 uv:
uv pip show -f my-uv-demo
你会看到类似:
console-scripts: xccli = my_uv_demo.xccli:main
Q1:如果两个包都定义了 xccli 怎么办?
A:uvx 会报错,提示冲突。因此命令名最好具有唯一性或品牌标识(如 black、ruff、poetry)。
Q2:为什么不能直接用包名运行?比如 uvx my-uv-demo?
A:因为包名 ≠ 命令名。一个包可以提供多个命令(如 git 提供 git, git-receive-pack 等),所以必须通过命令名定位。
Q3:uvx 会不会每次都下载?
A:不会!uvx 会缓存已安装的工具(默认在 ~/.cache/uv/tools/),下次运行极快。
| 步骤 | 命令/操作 |
|---|---|
| 初始化项目 | 创建 src/ 结构 + pyproject.toml |
| 本地测试 | uvx –from . mycli |
| 打包 | uv build |
| 发布 | uv publish(需 PyPI Token) |
| 用户使用 | uvx mycli |
通过这套流程,你不仅开发了一个 CLI 工具,还让它像 npm 全局命令一样简单易用,但速度更快、依赖更干净。
到此这篇关于从零开始带你使用Python开发一个命令行工具的文章就介绍到这了,更多相关Python命令行工具内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!
您可能感兴趣的文章:
- Python的CLI(命令行界面)工具的使用
- Python打造自己的专属命令行工具
- 基于Python实现png转webp的命令行工具
- Python httpstat命令行工具功能使用探索
- Python库docopt命令行参数解析工具
- Python命令行解析工具argparse模块
- python开发简单的命令行工具简介
- Python 命令行解析工具 argparse基本用法