如何优雅地用 Python 为项目安装和创建 Python 包依赖?

在接手他人项目的时候,或者创建一个项目依赖时:

  • 如果项目用 anaconda,项目就需要导出一个 environment.yaml ,存在下面问题:

    • 文件过大:Anaconda 的 environment.yaml 往往包含了大量的包和依赖,其中一些可能与项目不直接相关。这不仅增加了环境搭建的复杂性,而且可能引入不必要的依赖。
    • 平台依赖性:environment.yaml 文件可能包含特定于操作系统或平台的包,这会在不同系统间迁移项目时造成问题。
    • 最大的问题是:environment.yaml 文件无法复现用 whl 包或者 gz 包等这些本地包安装的库。也无法安装第三方源安装的包,比如 pytorch。
  • 如果项目用 pip,项目就需要导出一个 requirements.txt 文件,但这种方法也有其局限性:

    • 不完整的依赖管理:requirements.txt 文件通常只列出了项目直接依赖的包,而不包括这些包的依赖(即二级依赖)。这可能导致缺失依赖或版本冲突。
    • 版本冲突问题:在没有明确指定依赖版本的情况下,pip 可能会安装最新版本的包,这可能与项目的实际兼容性不符。
    • 安装顺序问题:pip 不保证按照 requirements.txt 中的顺序安装包,这在某些包有特定安装顺序要求时可能导致问题。

相信平常接手过其他人项目的人应该很清楚其中的血与泪,因此我们更推荐 Poetry,它有以下的优点:

  1. 更简洁的依赖文件:Poetry 使用 pyproject.toml 来管理依赖,这使得依赖声明更为简洁和直观。同时,Poetry 通过 poetry.lock 文件锁定具体版本,确保环境的一致性。
  2. 完整的依赖解析:Poetry 能够解析和管理项目的全部依赖(包括子依赖),减少了版本冲突的可能性。
  3. 虚拟环境管理:Poetry 自动管理虚拟环境,无需手动创建和激活,简化了环境配置的步骤。
  4. 更好的跨平台兼容性:Poetry 在处理依赖时考虑了跨平台的问题,使得在不同操作系统间迁移和设置项目变得更加容易。
  5. 支持本地包和多源包:Poetry 支持从本地路径、私有仓库或多个源安装包,提供了比 pip 更灵活的包管理选项。

Poetry 类似于 pip,能帮助你进行包管理(dependency management),但它比 pip 强大得多,因为它还包含了以下 pip 没有的功能:

  • 虚拟环境管理
  • 包依赖管理
  • 包的打包与发布

虚拟环境管理

指的是使用内置的 venv 或 virtualenv 包来创建和管理 Python 的虚拟环境,不同的虚拟环境之间是相互独立的,也就是说,它们对应的路径各不相同。

包管理

包管理(dependency management) 指的是使用像 pip 这类的包管理器来管理 Python 环境(不一定是虚拟环境),即管理环境中安装的所有包(package、dependency)及其版本。

依赖性管理

在这个语境下,dependency 基本上就是你安装的包(package)。

包的“依赖性管理”(重要) 这个有点难以定义,因为它不是一个有广泛共识的术语,在英文中也难以找到对应的单词。这里指的是管理包之间的“依赖关系”和“版本冲突”这两件事:

  • 依赖关系指的是,当一个包被安装时,它所依赖的包也必须一并安装(这个比较简单)。相反,当一个包被移除时,它所依赖的包也必须一并移除——除非这些包还被其他包所依赖(这就复杂了)。
  • 而版本冲突,指的是单一包被两个以上的包所依赖,但不同的包对依赖的包有不同的最低或最高版本要求,若两者要求的范围“没有交集”,则会发生冲突,导致包失效或无法安装。

这两大问题,都是 pip 无法解决的,也是 Python 开发上的两大痛点。

How? Conda + Poetry

由于我们大多数人都有 Conda,用 Conda 创建 Python 版本,然后用 Poetry 创建虚拟环境,然后管理包依赖。

Conda 创建预期 Python

conda 在这里用于创建特定的 Python 版本。

  • 如果是接手项目,参考 pyproject.toml 中内容(如下),如果发现是 Python 3.8,则创建一个环境 conda create -n python_env_name python=3.8

    [tool.poetry.dependencies]
    python = "^3.8"
  • 如果是自己创建项目,只需要用 conda 创建自己想要的环境。

安装 Poetry

pip install poetry==1.7.1 -i https://pypi.tuna.tsinghua.edu.cn/simple

下面的指令都需要在包含 pyproject.toml 文件的项目目录中运行:

  1. 打开终端或命令提示符:首先,确保你可以访问命令行界面。
  2. 定位到你的项目目录:使用 cd 命令切换到包含 pyproject.toml 文件的项目目录。

    cd path/to/your/project

创建项目虚拟环境

  1. 确认pyproject.toml存在:
  • 若存在 pyproject.toml,则运行

    conda activate python_env_name    # 激活对应 Python 环境
    poetry env use python # 使用当前 conda

    使用 Poetry 创建项目的虚拟环境是一个自动化过程,这可以确保你的项目依赖被正确地隔离。

  • 若不存在pyproject.toml,则运行下一节 初始化项目的 pyproject.toml

  1. 初始化虚拟环境:在项目目录中,运行以下命令来使用 Poetry 创建虚拟环境。
poetry env use python   #   使用当前环境的 Python
poetry shell    #   进入虚拟环境

这个命令会创建一个新的虚拟环境(如果尚未创建),并激活它。如果虚拟环境已经存在,它会被重新激活。

  1. 检查虚拟环境信息:要确认虚拟环境已经被正确创建并激活,可以使用以下命令:
poetry env info

这个命令会显示关于当前激活的虚拟环境的信息,包括它的位置和使用的 Python 版本。

初始化项目的 pyproject.toml

如果是创建项目则需要用 poetry init 初始化项目文件,它会引导你创建一个 pyproject.toml 文件,这个文件是 Poetry 用来管理项目的依赖和各种配置的主要文件。以下是如何使用 poetry init 的步骤:

  1. 运行 poetry init 命令:在项目目录中,运行以下命令:

    poetry init
  2. 跟随提示进行操作:执行该命令后,Poetry 会引导你通过一系列问题来创建 pyproject.toml 文件。这包括:

    • 项目的基本信息:例如项目名称、版本、描述、作者等。
    • 定义依赖:Poetry 会询问是否要交互式地定义项目依赖。如果选择是,它会提示你搜索并添加包。你也可以跳过这一步,稍后手动添加依赖到 pyproject.toml 文件中。
    • 开发依赖:类似地,Poetry 也会询问是否要添加开发时依赖(例如,用于测试或构建的包)。
  3. 完成并查看 pyproject.toml:完成上述步骤后,Poetry 会创建 pyproject.toml 文件。打开这个文件,确认里面的内容是否符合你的预期。
  4. (可选)手动编辑 pyproject.toml:如果需要,你可以直接编辑这个文件来添加或修改依赖、修改项目信息等。如果你想要用第三方源(比如清华源),请看下下节【添加镜像源】。

安装项目依赖

  • 如果是接手项目,直接 poetry install就可以,它会自动根据依赖使用最佳的安装方式。poetry install 命令是 Poetry 中用于安装项目依赖的主要命令。当你在项目中运行此命令时,它会根据 pyproject.toml 文件和 poetry.lock 文件(如果存在)安装所有必要的依赖。以下是使用 poetry install 的基本步骤:

    1. 运行 poetry install 命令:在项目目录中,运行以下命令:

      poetry install

      这个命令将会:

      • 检查 pyproject.toml 文件中列出的依赖。
      • 如果存在 poetry.lock 文件,Poetry 会根据该文件中锁定的版本来安装依赖。这确保了环境的一致性。
      • 如果不存在 poetry.lock 文件,Poetry 会解析 pyproject.toml 中的依赖并生成一个新的 poetry.lock 文件。
      • 安装所有必要的依赖到项目的虚拟环境中。如果项目的虚拟环境还未创建,Poetry 会先创建它。
    2. 验证安装:安装完成后,你可以通过运行项目或其测试来验证依赖是否正确安装。

poetry init 是一个非常方便的命令,特别是当你开始一个新项目而还未确定所有依赖时。它不仅帮助你创建了项目的基本结构,还使得后续的依赖管理变得简单。此外,即使在项目已经开始的情况下,poetry init 也可以用于引入 Poetry 作为依赖管理工具。在这种情况下,它将帮助你创建 pyproject.toml 文件,而不会影响现有的项目文件。

添加镜像源

poetry source add 用于向 Poetry 项目添加一个新的包源(repository)。这在你需要从不是默认的 PyPI 源安装包时非常有用,比如你可能需要从私有仓库或其他第三方仓库安装包。以下是如何使用 poetry source add 的步骤:

  1. 添加新的包源:使用 poetry source add 命令添加一个新的源。这个命令通常需要三个主要参数:源的名称、源的 URL 和源的类型(默认为 legacy)。

    poetry source add   [--type ]

    例如,如果你想添加一个名为 my-private-repo 的私有仓库,你可以运行:

    poetry source add my-private-repo https://private-repo.example.com/simple/

    如果该仓库是一个 PyPI 兼容仓库(例如,使用 PyPI 格式的私有 Artifactory 仓库),你可能不需要指定 --type 参数,因为默认值 legacy 适用于标准的 PyPI 格式仓库。

  2. 验证源是否已添加:完成添加源后,你可以通过查看 pyproject.toml 文件来验证新源是否已被正确添加。该文件现在应该包含一个 [tool.poetry.source] 部分,其中列出了刚刚添加的源信息。
  3. 安装或更新依赖:添加新的源之后,你可以通过 poetry addpoetry update 命令来安装或更新依赖,Poetry 将会考虑这些新添加的源。

使用 poetry source add 命令可以扩展你的项目以从多个源获取依赖,这在使用私有包或特定于项目的包时特别有用。

例子,添加清华源作为默认源:

poetry source add tsinghua https://pypi.tuna.tsinghua.edu.cn/simple --default

增加依赖包

poetry add 命令是用于向 Poetry 管理的项目中添加依赖的。当你执行这个命令时,Poetry 会将指定的包添加到你的 pyproject.toml 文件中,并自动更新 poetry.lock 文件,以确保依赖的一致性。以下是使用 poetry add 的基本步骤:

  1. 添加一个依赖:使用 poetry add <package-name> 命令添加一个依赖。例如,如果你想添加 requests 包,你应该运行:

    poetry add requests

    这会将 requests 包添加到你的项目中,并自动选择一个合适的版本。

  2. 指定依赖版本:如果你需要指定依赖的特定版本或版本范围,你可以在包名后加上版本号。例如:

    poetry add requests@^2.25.1 
    poetry add requests==2.25.1 # 或者像 pip 一样。

    这将会安装 requests 包的 2.25.1 版本或更高的兼容版本(但不会升级到 3.x 版本)。

  3. 添加开发依赖:如果你想添加一个仅在开发过程中使用的包(如测试框架),可以使用 -D--dev 选项。例如,添加测试库 pytest 作为开发依赖:

    poetry add pytest --dev
  4. 等待命令执行完成:Poetry 会解析和安装依赖,并更新 pyproject.tomlpoetry.lock 文件。
  5. 验证依赖:添加完依赖后,可以通过运行项目来验证新添加的依赖是否按预期工作。

poetry add 命令简化了依赖管理过程,它不仅添加了依赖,还考虑了版本兼容性和项目的整体依赖树。通过自动更新 pyproject.tomlpoetry.lock 文件,它确保了项目依赖的一致性和项目团队成员之间的依赖同步。

移除依赖包

在 Poetry 中删除一个包,即从项目的依赖中移除它,使用 poetry remove 命令实现。命令会从 pyproject.toml 文件中删除指定的包,并更新 poetry.lock 文件以反映这一变化。以下是详细步骤:

  1. 删除一个依赖包:使用 poetry remove <package-name> 命令删除一个依赖。例如,如果你想从项目中移除 requests 包,你应该运行:

    poetry remove requests

    这会从你的项目依赖中移除 requests 包,并更新 pyproject.tomlpoetry.lock 文件。

  2. 删除开发依赖:如果要删除的包是一个开发依赖(即原本是使用 poetry add --dev 添加的),你同样使用 poetry remove 命令,Poetry 会自动识别它是开发依赖并进行处理。

    poetry remove 
  3. 等待命令执行完成:Poetry 会处理依赖的移除,并更新相关文件。
  4. 验证依赖已被移除:完成命令执行后,你可以检查 pyproject.toml 文件以确认该依赖已被移除。同时,确保项目在移除了该依赖后仍然按预期运行。

通过使用 poetry remove 命令,你可以轻松地管理项目的依赖列表,确保它们保持最新且与项目需求一致。

如何发布

  • 编写 README 文件:一个清晰的 README 文件对于你的包的可用性至关重要。确保它包含了项目描述、安装指南、使用示例和任何必要的文档链接。

  • 检查版本号:确保 pyproject.toml 文件中的版本号是正确的,并且遵循语义化版本控制规则。如果是首次发布,通常版本号会是 0.1.01.0.0

  • 运行 poetry lock 命令poetry lock 命令在 Poetry 中用于创建或更新 poetry.lock 文件,这个文件精确记录了项目所有依赖(包括子依赖)的具体版本。通过使用这个命令,你可以确保在不同环境中重现相同的依赖安装,这对于项目的一致性和稳定性至关重要。以下是使用 poetry lock 命令的步骤:

    1. 在项目目录中,运行以下命令:

      poetry lock

      这个命令会执行以下操作:

      • 解析 pyproject.toml 文件中指定的依赖,包括版本范围。
      • 生成或更新 poetry.lock 文件,其中包含了所有依赖的精确版本号和其他必要信息,以确保在任何环境中安装相同的依赖集。
    2. 检查 poetry.lock 文件:执行命令后,检查项目目录中的 poetry.lock 文件。这个文件应该被更新,反映了当前依赖的精确状态。
    3. 提交 poetry.lock 文件到版本控制系统:为了保持团队或部署环境间的一致性,确保将更新后的 poetry.lock 文件提交到版本控制系统(如 Git)。

    使用 poetry lock 命令的场景包括:

    • 当你添加、删除或修改 pyproject.toml 中的依赖时。
    • 当你希望更新依赖到可用的最新版本,但又不想更改 pyproject.toml 中的版本规范时。
    • 当你需要确保项目依赖的一致性和稳定性时。

    记住,poetry.lock 文件应该与 pyproject.toml 文件一起被版本控制,以保证项目在不同开发和部署环境中的一致性。(注意:poetry add 运行时会自动运行 poetry lock

  • Git 发布:通过 git 命令 push 到远程仓库。

进阶用法

配置 Poetry 和 PyPI 仓库

为了更方便地管理和发布包,你可以配置 Poetry 以使用不同的 PyPI 仓库。

  1. 添加自定义仓库:如果你想使用除了官方 PyPI 以外的仓库,可以添加一个新的仓库配置:

    poetry config repositories.custom_repo_name https://custom-repo-url.com

    这里的 custom_repo_name 是你为仓库设置的名字,https://custom-repo-url.com 是仓库的 URL。

  2. 发布包到自定义仓库:如果你开发了自己的包并想发布到配置的仓库,可以使用:

    poetry publish --repository custom_repo_name

    确保你已经正确设置了认证信息,如需要的 API token。

使用 Poetry 进行版本控制

Poetry 还可以帮助你管理项目的版本。

  1. 版本号:查看当前项目的版本号,可以使用:

    poetry version
  2. 版本更新:要更新项目版本(如进行小的修改或发布新的版本),可以使用:

    poetry version patch  # 小更新
    poetry version minor  # 次要更新
    poetry version major  # 主要更新

    这将更新 pyproject.toml 中的版本号。

常见问题

HTTPResponse Error

一般来说,这是因为你在安装某个安装包的时候,影响了 poetry 库所依赖的库,如果你使用了 poetry 自带的虚拟环境,就不会出现这种问题。比如下面的 urllib3:

在遇到 'HTTPResponse' object has no attribute 'strict' 问题时,应该降级 urllib3,一般这种情况是因为 Poetry 本身不兼容更高版本的 urllib3

'HTTPResponse' object has no attribute 'strict' · Issue #7936 · python-poetry/poetry

Poetry has urllib3 pinned below 2.0, so you had to mess something up on your end (for example by installing your project and poetry into the same environment). In any case, this is not Poetry’s fault.

运行:

poetry add urllib3==1.26.15

安装 Pytorch 的例子

pytorch 官方源

Previous PyTorch Versions | PyTorch 从这里找地址,缺点是下载速度慢,容易超时失败。

建议设置环境变量 POETRY_REQUESTS_TIMEOUT 作为超时时长,单位秒。

poetry source add -p explicit pytorch https://download.pytorch.org/whl/cu118
poetry add --source pytorch torch torchvision

腾讯云源

缺点是这个源只支持 cuda 12.1 的 torch,不过一般情况下建议使用这个,能兼容我们的设备:

poetry source add -p explicit pytorch-tencent https://mirrors.cloud.tencent.com/pypi/simple/
poetry add --source pytorch-tencent torch torchvision

本地源

Previous PyTorch Versions | PyTorch 下载,比如从 https://download.pytorch.org/whl/cu118下载符合你系统环境的版本,建议安装本地包的时候上传到 dvc,加快下载速度:Tech / Kensho / mchat_x_LMA · GitLab 就是这样。

  • pyproject.toml 中添加内容就好了,例如:
[tool.poetry.dependencies]
python = "^3.8"
loguru = "^0.7.2"
dvc = "2.51.0"
dvc-webhdfs = "^3.1.0"
lit = { file = "model/torch2.0.1_cu118/lit-15.0.7.tar.gz" }
torch = { file = "model/torch2.0.1_cu118/torch-2.0.1+cu118-cp38-cp38-linux_x86_64.whl" }

添加之后运行 poetry install

  • 直接运行
poetry add model/torch2.0.1_cu118/torch-2.0.1+cu118-cp38-cp38-linux_x86_64.whl

尾声

进阶请查看官方文档:Introduction | Documentation | Poetry – Python dependency management and packaging made easy

本文说明了使用 Poetry 的动机、基本使用说明,以及在特定场景下的使用。poetry是什么poetry 中文poetry 中文poetry 中文poetry 中文poetry 中文poetry 中文poetry 中文poetry 中文poetry 中文poetry 中文poetry 中文poetry 中文poetry 中文poetry installpoetry install

订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论