引言

将Python编写的桌面和命令行程序发布到PyPI是一个简单直接的分发方式,PyPI上存放着成千上万的第三方程序包。这些程序包里很多都带有可以直接运行的脚本,但要使用它们,你得对Python的生态圈有一定的了解。有了pipx这个工具,你可以在不影响全局Python解释器的情况下,安全地安装和运行这些应用程序。

通过本教程,你将掌握以下技能:

  • 把Python包索引(PyPI)打造成一个应用商店
  • 不通过Python直接调用就能运行已安装的应用程序
  • 防止不同应用间的依赖性冲突
  • 在临时环境中尝试试用应用程序
  • 管理已安装的应用及其运行环境

为了更好地利用本教程,你需要对命令行操作有一定的熟悉度。特别是,掌握如何在项目中管理Python版本、创建虚拟环境和安装第三方模块,这些技能将对你大有裨益。

开始使用 pipx

pipx 在功能上与 pip 类似,因为它同样支持从 PyPI 或其他包索引安装 Python 包。不过,pipx 与众不同之处在于,它不会将包安装到全局 Python 解释器或激活的虚拟环境中,而是自动创建并管理虚拟环境,以确保每个安装包的依赖项相互隔离

pipx 还会为安装包提供的每个命令行脚本在 PATH 环境变量中创建符号链接。这样,你就能直接在命令行中运行这些脚本,无需通过 Python 解释器。

可以把 pipx 看作是 JavaScript 生态中的 npx。这两个工具都让你能在命令行中安装和运行第三方模块,就像它们是独立的应用程序。但并非所有模块都生而平等。

大体上,通过 PyPI 分发的代码可以分为以下三类:

  1. 导入型:这些可能是纯 Python 代码或编译共享对象的 Python 绑定,你希望在 Python 项目中导入它们。它们通常是像 Requests 或 Polars 这样的库,提供可复用的代码片段来解决常见问题。也可能是像 FastAPI 或 PyGame 这样的框架,你基于它们构建应用程序。
  2. 运行型:这些通常是像 black、isort 或 flake8 这样的命令行工具,它们在开发过程中提供帮助。也可能是像 bpython 或主要用 TypeScript 编写的 JupyterLab 环境这样的完整应用程序。
  3. 混合型:它们同时提供导入代码和可执行脚本。Flask 和 Django 就是典型例子,它们既提供实用脚本,又主要作为 Web 框架。

要使分发包成为可运行的或混合型的,需要在配置文件中定义一个或多个入口点。过去这可能是 setup.py 或 setup.cfg,但现代 Python 构建系统通常应依赖于 pyproject.toml 文件,并在 [project.scripts] TOML 表中定义入口点。

每个入口点都对应一个独立的可执行脚本,只需在命令行中输入其名称即可执行。比如,如果你之前运行过 django-admin 命令,实际上就是触发了 Django 框架的一个入口点。

一旦你找到了一个带有入口点的 Python 包,并且想要使用它,最佳做法是先创建并激活一个专门的虚拟环境。这样做可以避免你的系统中不同项目需要同一 Python 库的不同版本时出现的依赖冲突,同时你也不需要管理员权限来安装这个包。

创建虚拟环境并记住在运行相关脚本前激活它可能会让人觉得繁琐。pipx 可以自动完成这些步骤,并提供了更多功能,我们将在本教程中探讨。但首先,你需要让 pipx 运行起来。

pipx 体验

如果你对 pipx 是否适合你的需求还不确定,并且希望在深入测试工具之前不做出承诺,那么有个好消息!得益于一个自包含的可执行文件,你可以在不安装的情况下试用 pipx。

要获取这个可执行文件,你可以在浏览器中访问官方 GitHub 仓库的项目发布页面,并下载名为 pipx.pyz 的最新版本文件。.pyz 文件是可执行的 Python ZIP 应用,它们本质上是包含 Python 源代码和一些元数据的 ZIP 文件,与 Java 中的 JAR 文件类似。它们还可以包含你通常需要手动安装的第三方依赖。

接下来,你可以通过将下载的 pipx.pyz 文件路径传递给 Python 解释器来执行它,操作方式与运行普通的 Python 脚本相同:

$ python pipx.pyz --version
1.4.3

除此之外,在MacOS和Linux上,您还可以使用CHMOD使文件可执行文件(+X)直接运行,而无需指定Python命令:

$ chmod +x pipx.pyz
$ ./pipx.pyz --version
1.4.3

这种操作之所以可行,是因为 ZIP 文件开头有一个 shebang 行,告诉 shell 使用哪个解释器来执行文件。虽然它实际上是一个二进制文件,但 shell 在将文件传递给 Python 解释器之前会先识别这个 shebang 行。

你可以像操作已安装的 pipx 一样,给 pipx.pyz 传递命令行参数和选项。比如,如果你想把 IPython 安装到一个独立的环境中,可以使用如下命令:

$ python pipx.pyz install ipython
  installed package ipython 8.22.1, installed using Python 3.12.2
  These apps are now globally available
    - ipython
    - ipython3
  These manual pages are now globally available
    - man1/ipython.1
done! ✨ 🌟 ✨

选择哪种 Python 可执行文件来运行这个 ZIP 应用,pipx 就会基于该 Python 版本创建虚拟环境。

作为一个独立的 Python ZIP 应用运行 pipx 的好处是不会在你的计算机上留下痕迹。然而,这样做可能会逐渐变得繁琐,因为你每次都得输入完整的路径,或者找到 pipx.pyz 文件并切换到其父目录。如果你觉得这种操作太麻烦,那么可能就到了在系统上安装 pipx 的时候了。

安装 pipx

在计算机上安装 pipx 有几种选择,甚至包括用 pipx 自己来安装 pipx 的选项!不过,这并不推荐,因为这可能会导致未来出现一些预料之外的问题。

你更好的选择是使用 pip 来安装 pipx。毕竟,这个工具作为一个普通的 Python 包在 PyPI 上是可用的。这是最简单可靠的安装方法,也会给你带来最少的麻烦。但是,请只在你不介意全局 Python 解释器中多出一些依赖项,并且不打算与其他项目共享时使用这种方法:

$ python -m pip install pipx

这总能确保你使用的是工具的最新发布版本,通常可以直接上手使用。但由于 pipx 安装后是一个 Python 模块,所以你需要通过完整的 python -m pipx 命令来运行它。如果你想直接使用 pipx 命令,可以按照下一节的说明配置 Unix shell 的自动补全功能。

总的来说,官方推荐通过操作系统的包管理器来安装 pipx,比如 Windows 上的 Scoop、macOS 上的 Homebrew,或者基于 Debian 的 Linux 发行版上的 APT:

$ sudo apt install pipx

将 pipx 安装为系统级软件包后,你可以在文件系统的任何位置运行它作为一个独立的命令。不过,这样安装的 pipx 可能不是最新版本,而且可能会连带安装许多额外的依赖,比如另一个 Python 解释器。

需要明白的是,作为系统软件包安装的 pipx 会与特定的系统依赖中的 Python 解释器绑定,这个解释器可能已经过时。因此,当你之后用 pipx 安装应用程序时,pipx 会坚持使用它自带的 Python 解释器来创建新的虚拟环境。

配置 pipx

无论你是否已经安装了 pipx,在完全使用这个工具之前,你首先需要做的是将必要的文件夹路径添加到 PATH 环境变量中。如果你遗漏了这一步,pipx 在你第一次使用时会提醒你,并提供一条有用的提示信息:

$ pipx install ipython
  installed package ipython 8.22.1, installed using Python 3.12.2
  These apps are now globally available
    - ipython
    - ipython3
  These manual pages are now globally available
    - man1/ipython.1
⚠️  Note: '/home/user/.local/bin' is not on your PATH environment variable.
 ⮑  These apps will not be globally accessible until your PATH is updated.
 ⮑  Run `pipx ensurepath` to automatically add it, or manually modify your
 ⮑  PATH in your shell's config file (i.e. ~/.bashrc).
done! ✨ 🌟 ✨

安装完 IPython 之后,pipx 在对应的虚拟环境中为 ipython 和 ipython3 命令创建了两个符号链接。然而,由于这些链接所在的目录没有添加到 PATH 环境变量中,你目前无法在终端直接使用这些链接。

要修复这个问题,你可以执行上面输出信息中建议的命令,然后重新启动终端或者刷新你的 shell 配置:

$ pipx ensurepath
Success! Added /home/user/.local/bin to the PATH environment variable.

Consider adding shell completions for pipx.
⮑ Run 'pipx completions' for instructions.

You will need to open a new terminal or re-login for the PATH changes
⮑ to take effect.

Otherwise pipx is ready to go! ✨ 🌟 ✨

在 Windows 系统中,执行 pipx ensurepath 命令会更改你的用户 PATH 环境变量。它将 pipx 存放安装包和虚拟环境的路径添加到家目录中。

如果你是 macOS 或 Linux 用户,执行同样的命令会在你用户家目录下的 shell 配置文件(比如 .bashrc 或 .zshrc)末尾添加相应的条目。

# ...

# Created by `pipx` on 2024-02-22 13:08:43
export PATH="$PATH:/home/user/.local/bin"

这个操作的效果等同于在 Windows 上更改 PATH 环境变量。当你下次执行 pipx ensurepath 命令时,它会检查到你已经执行过这个操作,因此不会在你的 shell 配置文件中重复添加相同的条目。

如果你想查看 pipx 在你的电脑上使用的目录路径,可以通过执行环境子命令来实现:

$ pipx environment
Environment variables (set by user):

PIPX_HOME=
PIPX_BIN_DIR=
PIPX_MAN_DIR=
PIPX_SHARED_LIBS=
PIPX_DEFAULT_PYTHON=
USE_EMOJI=

Derived values (computed by pipx):

PIPX_HOME=/home/user/.local/pipx
PIPX_BIN_DIR=/home/user/.local/bin
PIPX_MAN_DIR=/home/user/.local/share/man
PIPX_SHARED_LIBS=/home/user/.local/pipx/shared
PIPX_LOCAL_VENVS=/home/user/.local/pipx/venvs
PIPX_LOG_DIR=/home/user/.local/pipx/logs
PIPX_TRASH_DIR=/home/user/.local/pipx/.trash
PIPX_VENV_CACHEDIR=/home/user/.local/pipx/.cache
PIPX_DEFAULT_PYTHON=/home/user/.pyenv/versions/3.12.2/bin/python
USE_EMOJI=true

这个命令会显示针对你的操作系统定制的文件夹路径,你可以通过设置输出中顶部列出的一个或多个环境变量来修改这些路径。此外,你还可以更改默认的 Python 解释器路径。

还有一个额外的、完全可选的步骤,这个步骤仅适用于 macOS 和 Linux 系统,它在你的第一次运行 ensurepath 子命令时被提及。这个步骤建议你考虑为你的终端添加 pipx 的 shell 自动补全功能。执行 pipx completions 命令,你将获得适用于最流行 shell 类型的相关指南。

$ pipx completions
Add the appropriate command to your shell's config file
so that it is run on startup. You will likely have to restart
or re-login for the autocompletion to start working.

bash:
    eval "$(register-python-argcomplete pipx)"

(...)

例如,要在Bash中启用PIPX完成,您可以将突出显示的行添加到〜/.bashrc配置文件中并重新加载:

$ echo 'eval "$(register-python-argcomplete pipx)"' >> ~/.bashrc
$ source ~/.bashrc

这将为 pipx 在 Bash 环境下启用自动补全命令的功能。另外,如果你通过 pip 安装 pipx 作为一个 Python 包,它还会创建一个指向 pipx 可执行文件的符号链接,这样你可以直接运行 pipx,而不用每次都输入 python -m pipx

现在一切准备就绪,你可以开始使用 pipx 来在独立的环境中安装和运行 Python 应用了。

PyPI 应用商店

像苹果和谷歌这样的科技巨头通过 App Store 和 Google Play 等平台推广了移动应用的数字化分发。这一概念很快扩展到了其他领域,包括 Windows 的 Microsoft Store、浏览器扩展的 Chrome Web Store,以及像 Ubuntu 这样的开源操作系统中的 Ubuntu App Center。

与此相反,Python 包索引(PyPI)的传统重点一直是为开发者提供第三方 Python 库,而不是面向最终用户。有了 pipx 的帮助,你可以将 PyPI 转变为一个类似 Python 应用商店的平台。

你可以通过两种方式利用 pipx 实现这一目的,现在我们来探讨它们。

一次性应用

有时你会遇到一个看起来很有潜力的 Python 包,想要尝试一下。比如,你可能听说过 Ruff,这个新出现的工具。它几乎可以完全替代 Python 的传统代码检查和格式化工具,但由于是用 Rust 编写的,在性能上远超它们。此外,Ruff 将多种工具集成在一个多功能工具中。

通常情况下,你需要按照常规步骤操作,创建虚拟环境,使用 pip 安装包,并找出如何运行其入口点。完成对该包的使用后,你可能还想删除它或相关的虚拟环境以清理。听起来很繁琐!

幸运的是,pipx 让你轻松尝试可执行的 Python 脚本。特别是,你不必将 Ruff 安装到现有项目中,就能直接运行这个工具来检查你的代码库:

$ pipx run ruff check .
main.py:1:8: F401 [*] `math` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.

使用 pipx run 命令,可以从 PyPI 下载所需包的最新版本,并默默地将其安装到一个临时的虚拟环境中。接着,命令会在该虚拟环境中执行特定的 ruff check . 命令,整个过程不会影响你项目中的任何依赖。这个命令会对当前目录下的所有文件执行代码检查。

为了提升效率,pipx 会将这些临时虚拟环境保存在一个缓存位置,该位置会在两周后自动失效。当你下次再次运行同一应用程序时,pipx 会尝试使用已缓存的环境,而不是重新下载包。

这里,包名和它的入口点名称是一致的。但如果入口点名称不同,或者一个 Python 包有多个入口点怎么办呢?在这种情况下,你可以使用 --spec 选项,将包名作为要求说明符来指定:

$ pipx run --spec httpie http --body ifconfig.co/country
Poland

这段内容描述了如何安装 HTTPie 包,并使用其 http 入口点来获取 ifconfig.co/country 的 HTTP 响应消息体。这个在线服务可以用来验证你的 VPN 是否运行正常。

当你需要执行某个特定版本的 Python 包时,要求说明符同样能派上用场。比如,你正在学习 Real Python 上关于使用 Poetry 进行依赖管理的教程,并希望运行教程中提到的特定版本的 Poetry 以便跟随教程操作。为了复现相同的环境,你可以提供一个更具体的说明符:

$ pipx run --spec 'poetry==1.1.11' poetry new your-project

这篇最初的教程是基于我们重写文本之前的某个特定版本的 Poetry 编写的。由于这个旧版本不再兼容最新的 Python 解释器,你可能需要修改默认设置,比如通过设置环境变量 PIPX_DEFAULT_PYTHON 来实现。稍后你将了解到更多如何进行这些操作的方法。

能够指定包的特定版本在你需要同时运行同一工具的不同版本以管理多个项目时非常有用。然而,要求说明符更加灵活,它允许你添加额外的依赖,或者直接从一个远程的 Git 仓库或包含所需 Python 包的 ZIP 文件中执行命令:

$ pipx run --spec git+https://github.com/realpython/reader.git realpython

$ pipx run --spec \
  https://github.com/realpython/reader/archive/refs/tags/1.1.2.zip \
  realpython

这两个命令用于运行 Real Python 的内容订阅器,它能够获取最新的教程、视频课程和播客的摘要信息。第一个命令直接从 GitHub 仓库的默认分支执行 realpython 脚本,第二个命令则是从指定的发布存档中提取包。如果需要,你还可以在选择的 Git URL 后添加 at 符号 (@) 和特定的分支名称或提交哈希。

实际上,只要文件是 .py 格式,你就可以利用 pipx 从任何远程链接或本地文件执行 Python 脚本:

$ echo 'print("Hello, World!")' > hello.py

$ pipx run hello.py
Hello, World!

$ python -m http.server &>/dev/null &
$ pipx run http://0.0.0.0:8000/hello.py
Hello, World!

编写完 "Hello, World!" 程序后,你可以通过 pipx run 命令来运行它。接着,你可以利用 Python 自带的 HTTP 服务器来托管你的脚本,并允许 pipx 在执行前下载 Python 源代码。

如果你的脚本依赖于第三方库,你可以在文件顶部的一个特别格式化的注释中声明这些依赖,这个注释必须遵循内联脚本元数据的语法规范(PEP 723):

# /// script
# dependencies = [
#   "rich==13.7.0",
# ]
# ///

from rich import print

print("[b]Hello, World![/b]")

当你利用 pipx 执行这个脚本时,它会创建一个临时虚拟环境并安装所需的依赖。因此,打印出来的文字将以加粗格式显示。

当你需要偶尔运行某个命令而不想处理虚拟环境时,使用 pipx run 是个不错的选择。然而,如果你预计会频繁使用某个命令,你可能更希望以一种更持久的方式安装相关的 Python 包,并让 pipx 在你的 shell 中创建便捷的别名。

全局安装

假设你对 Ruff 很满意,并决定将其设为你的默认 Python 静态代码分析工具。为了在不影响系统解释器的情况下全局安装相应的 Python 包,你可以按照以下方式使用 pipx:

$ pipx install ruff
  installed package ruff 0.2.2, installed using Python 3.12.2
  These apps are now globally available
    - ruff
done! ✨ 🌟 ✨

pipx install 命令会创建一个独立的虚拟环境,并将最新版本的指定包安装进去。这可能听起来和之前提到的运行包的操作差不多,但实际上有两个关键的不同点。

第一,pipx 在一个不同的位置创建了一个持久的虚拟环境,这样做是为了避免被缓存机制清除。如果你想查看这个环境,可以使用 pipx 环境查看功能或加上 --verbose 参数。这个虚拟环境的命名依据是安装的包名,而不是随机的十六进制数字序列。

第二,pipx 会为安装包中找到的每个入口点创建符号链接。这意味着你可以直接在终端通过输入工具的名字来运行它们。例如,在从 PyPI 安装了最新版本的 ruff 之后,pipx 会在你的系统中注册一个新的全局命令 ruff。

$ ruff --version
ruff 0.2.2

$ which ruff
/home/user/.local/bin/ruff

只要输出中显示的父目录包含在你的 PATH 环境变量里,你就可以在终端的任何位置直接运行 ruff 命令。如果不在,那么请确保按照之前的说明配置 pipx。

如之前提到的,许多 Python 包定义了多个与不同操作相关的入口点。在这种情况下,pipx 会为每个入口点创建一个独立的符号链接。例如,mypy 包不仅包含了 mypy(一个广受欢迎的 Python 静态类型检查器),还包括了 mypyc(一个 C 扩展模块编译器)等其他几个工具:

$ pipx install mypy
  installed package mypy 1.8.0, installed using Python 3.12.2
  These apps are now globally available
    - dmypy
    - mypy
    - mypyc
    - stubgen
    - stubtest
done! ✨ 🌟 ✨

安装了 mypy 之后,通过 pipx,你的终端现在可以访问这个包提供的五个全局命令。

另一方面,有些 Python 包则完全没有定义任何入口点。可以说,这类包在 PyPI 上占据了大多数。正如你所记得的,pipx 不会安装这类包,并会显示以下消息:

$ pipx install polars

No apps associated with package polars or its dependencies.
⮑ If you are attempting to install a library, pipx should
⮑ not be used. Consider using pip or a similar tool instead.

然而,在某些情况下,一个包的直接或间接依赖中可能包含入口点。例如 pandas 包本身没有定义任何入口点,但是它依赖的 NumPy 包中定义了入口点:

$ pipx install pandas
Note: Dependent package 'numpy' contains 1 apps
  - f2py

No apps associated with package pandas. Try again with '--include-deps'
⮑ to include apps of dependent packages, which are listed above.
⮑ If you are attempting to install a library, pipx should
⮑ not be used. Consider using pip or a similar tool instead.

NumPy 提供了一个名为 f2py 的命令,它是一个将 Fortran 代码转换为 Python 接口的工具。如果你希望 pipx 能够安装依赖包中的这类应用程序,那么你应该使用 --include-deps 选项:

$ pipx install --include-deps pandas
  installed package pandas 2.2.1, installed using Python 3.12.2
  These apps are now globally available
    - f2py
done! ✨ 🌟 ✨

你已经在包含 pandas 的虚拟环境中安装了 f2py 应用程序。

pipx install 命令不像 pipx run 那样有一个 --spec 参数,后者允许你指定需求说明符。取而代之的是,在安装包时,你可以直接将所需的版本限制作为包名的一部分来提供,就像使用普通的 pip 命令一样:

$ pipx install poetry==1.1.11

请留意,对于每个包,你只能有一个虚拟环境,因为 pipx 会根据相应的 Python 包来命名虚拟环境。如果你想区分同一个包的不同版本,可以使用 --suffix 选项来提供一个自定义后缀,这个功能还在试验阶段。或者,你也可以通过 --force 标志来用新版本覆盖现有的环境。

使用 pipx 运行或安装应用时要注意的是,可能你的系统中已经安装了这些应用。例如,它们可能已经安装在全局 Python 解释器中、当前激活的虚拟环境中,或者作为系统级软件包等。这种情况下,pipx 会发出警告,但操作仍会继续执行:

$ pipx run poetry --version
⚠️  poetry is already on your PATH and installed
⮑ at /home/user/.local/bin/poetry.
⮑ Downloading and running anyway.
Poetry (version 1.8.0)

$ pipx install poetry
⚠️  Note: poetry was already on your PATH at /home/user/poetry
  installed package poetry 1.8.0, installed using Python 3.12.2
  These apps are now globally available
    - poetry
done! ✨ 🌟 ✨

在开始之前检查是否有任何 shell 别名、符号链接或 PATH 环境变量中的二进制文件可能会与 pipx 发生冲突,这是一个明智的做法。

现在你已经了解了如何使用 pipx 安装命令行 Python 应用,你可能会好奇如何卸载应用或清理相关的虚拟环境。接下来,你将学习如何妥善管理通过 pipx 安装的应用。


未完待续,欢迎关注!

动动您发财的小手点个赞吧!欢迎转发!

本文由mdnice多平台发布


科学冷冻工厂
29 声望3 粉丝