高级用法

自动化测试

使用 Tox 作为运行器

Tox 是一个很棒的工具,用于在多个 Python 版本或依赖集上进行测试。您可以配置一个 tox.ini,如下所示,以将您的测试与 PDM 集成:

  1. [tox]
  2. env_list = py{36,37,38},lint
  3. [testenv]
  4. setenv =
  5. PDM_IGNORE_SAVED_PYTHON="1"
  6. deps = pdm
  7. commands =
  8. pdm install --dev
  9. pytest tests
  10. [testenv:lint]
  11. deps = pdm
  12. commands =
  13. pdm install -G lint
  14. flake8 src/

要使用 Tox 创建的虚拟环境,您应该确保已设置 pdm config python.use_venv true。然后 PDM 将从 pdm lock 将依赖项安装到虚拟环境中。在专用的 venv 中,您可以直接运行工具,例如 pytest tests/,而不是 pdm run pytest tests/

您还应确保在测试命令中不要运行 pdm add/pdm remove/pdm update/pdm lock,否则 pdm lock 文件将被意外修改。可以使用 deps 配置提供额外的依赖项。此外,isolated_buildpassenv 配置应按上述示例设置,以使 PDM 正常工作。

为了摆脱这些限制,有一个 Tox 插件 tox-pdm 可以简化使用。您可以通过以下方式安装它:

  1. pip install tox-pdm

或者,

  1. pdm add --dev tox-pdm

然后,您可以使 tox.ini 更加整洁,如下所示:

  1. [tox]
  2. env_list = py{36,37,38},lint
  3. [testenv]
  4. groups = dev
  5. commands =
  6. pytest tests
  7. [testenv:lint]
  8. groups = lint
  9. commands =
  10. flake8 src/

查看 项目的 README 以获取详细指南。

使用 Nox 作为运行器

Nox 是另一个用于自动化测试的优秀工具。与 tox 不同,Nox 使用标准的 Python 文件进行配置。

在 Nox 中使用 PDM 更容易,以下是 noxfile.py 的示例:

```python hl_lines=”4” import os import nox

os.environ.update({“PDM_IGNORE_SAVED_PYTHON”: “1”})

@nox.session def tests(session): session.run_always(‘pdm’, ‘install’, ‘-G’, ‘test’, external=True) session.run(‘pytest’)

@nox.session def lint(session): session.run_always(‘pdm’, ‘install’, ‘-G’, ‘lint’, external=True) session.run(‘flake8’, ‘—import-order-style’, ‘google’)

  1. 请注意,应设置 `PDM_IGNORE_SAVED_PYTHON`,以便 PDM 可以正确地在虚拟环境中获取 Python。还要确保 `pdm` `PATH` 中可用。在运行 nox 之前,您还应确保配置项 `python.use_venv` true,以启用 venv 重用。
  2. ### 关于 PEP 582 `__pypackages__` 目录
  3. 默认情况下,如果您通过 [`pdm run`](../reference/cli.md#run) 运行工具,`__pypackages__` 将被程序及其创建的所有子进程看到。这意味着由这些工具创建的虚拟环境也了解 `__pypackages__` 内的包,这在某些情况下会导致意外行为。
  4. 对于 `nox`,您可以通过在 `noxfile.py` 中添加一行来避免这个问题:
  5. ```python
  6. os.environ.pop("PYTHONPATH", None)

对于 toxPYTHONPATH 不会传递给测试会话,所以这不会成为问题。此外,建议让 noxtox 位于它们自己的 pipx 环境中,这样您就不需要为每个项目安装。在这种情况下,PEP 582 包也不会成为问题。

在持续集成中使用 PDM

只需记住一件事 —— PDM 无法在 Python < 3.7 上安装,所以如果您的项目需要在这些 Python 版本上进行测试,您必须确保 PDM 已在正确的 Python 版本上安装,这可能与特定作业/任务运行的目标 Python 版本不同。

幸运的是,如果您正在使用 GitHub Action,有 pdm-project/setup-pdm 可以让这个过程变得更容易。以下是一个 GitHub Actions 的示例工作流程,您可以将其适应于其他 CI 平台。

  1. Testing:
  2. runs-on: ${{ matrix.os }}
  3. strategy:
  4. matrix:
  5. python-version: [3.7, 3.8, 3.9, '3.10', '3.11']
  6. os: [ubuntu-latest, macOS-latest, windows-latest]
  7. steps:
  8. - uses: actions/checkout@v3
  9. - name: Set up PDM
  10. uses: pdm-project/setup-pdm@v3
  11. with:
  12. python-version: ${{ matrix.python-version }}
  13. - name: Install dependencies
  14. run: |
  15. pdm sync -d -G testing
  16. - name: Run Tests
  17. run: |
  18. pdm run -v pytest tests

!!! 重要 “提示” 对于 GitHub Action 用户,Ubuntu 虚拟环境上存在一个已知的兼容性问题。 如果在那台机器上 PDM 并行安装失败,您应该将 parallel_install 设置为 false,或者设置环境变量 LD_PRELOAD=/lib/x86_64-linux-gnu/libgcc_s.so.1pdm-project/setup-pdm 操作已经处理了这个问题。

!!! 注意 如果您的 CI 脚本在没有正确设置用户的情况下运行,当 PDM 尝试创建其缓存目录时,您可能会遇到权限错误。 要解决这个问题,您可以自己设置 HOME 环境变量,到一个可写的目录,例如:

  1. ```bash
  2. export HOME=/tmp/home
  3. ```

在多阶段 Dockerfile 中使用 PDM

可以在多阶段 Dockerfile 中使用 PDM,首先将项目和依赖项安装到 __pypackages__ 中,然后将该文件夹复制到最终阶段,并添加到 PYTHONPATH

  1. ARG PYTHON_BASE=3.10-slim
  2. # 构建阶段
  3. FROM python:$PYTHON_BASE AS builder
  4. # 安装 PDM
  5. RUN pip install -U pdm
  6. # 禁用更新检查
  7. ENV PDM_CHECK_UPDATE=false
  8. # 复制文件
  9. COPY pyproject.toml pdm.lock README.md /project/
  10. COPY src/ /project/src
  11. # 将依赖项和项目安装到本地包目录
  12. WORKDIR /project
  13. RUN pdm install --check --prod --no-editable
  14. # 运行阶段
  15. FROM python:$PYTHON_BASE
  16. # 从构建阶段检索包
  17. COPY --from=builder /project/.venv/ /project/.venv
  18. ENV PATH="/project/.venv/bin:$PATH"
  19. # 设置命令/入口点,适应您的需求
  20. COPY src /project/src
  21. CMD ["python", "src/__main__.py"]

使用 PDM 管理单体仓库

使用 PDM,您可以在单个项目中拥有多个子包,每个子包都有自己的 pyproject.toml 文件。并且您可以创建只有一个 pdm.lock 文件来锁定所有依赖项。子包可以相互作为它们的依赖项。要实现这一点,请按照以下步骤操作:

project/pyproject.toml

  1. [tool.pdm.dev-dependencies]
  2. dev = [
  3. "-e file:///${PROJECT_ROOT}/packages/foo-core",
  4. "-e file:///${PROJECT_ROOT}/packages/foo-cli",
  5. "-e file:///${PROJECT_ROOT}/packages/foo-app",
  6. ]

packages/foo-cli/pyproject.toml

  1. [project]
  2. dependencies = ["foo-core"]

packages/foo-app/pyproject.toml

  1. [project]
  2. dependencies = ["foo-core"]

现在,在项目根目录下运行 pdm install,您将得到一个锁定所有依赖项的 pdm.lock。所有子包将以可编辑模式安装。

查看 🚀 示例仓库 了解更多详情。

pre-commit 的钩子

pre-commit 是一个强大的框架,用于集中管理 git 钩子。PDM 已经使用 pre-commit 钩子 进行其内部质量检查。PDM 还公开了几个可以本地或在 CI 管道中运行的钩子。

导出 requirements.txt

此钩子包装了命令 pdm export 以及任何有效的参数。它可以用作钩子(例如,用于 CI),以确保您将要签入代码库的 requirements.txt 反映了 pdm lock 的实际内容。

  1. # 导出 Python 需求
  2. - repo: https://github.com/pdm-project/pdm
  3. rev: 2.x.y # 公开钩子的 PDM 版本
  4. hooks:
  5. - id: pdm-export
  6. # 命令参数,例如:
  7. args: ['-o', 'requirements.txt', '--without-hashes']
  8. files: ^pdm.lock$

检查 pdm.lock 是否与 pyproject.toml 保持最新

此钩子包装了命令 pdm lock --check 以及任何有效的参数。它可以用作钩子(例如,用于 CI),以确保每当 pyproject.toml 有依赖项被添加/更改/删除时,pdm.lock 也是最新的。

  1. - repo: https://github.com/pdm-project/pdm
  2. rev: 2.x.y # 公开钩子的 PDM 版本
  3. hooks:
  4. - id: pdm-lock-check

将当前工作集与 pdm.lock 同步

此钩子包装了命令 pdm sync 以及任何有效的参数。它可以用作钩子,以确保每当您检出或合并一个分支时,您的当前工作集与 pdm.lock 同步。如果您想要使用系统的凭证存储,请在 additional_dependencies 中添加 keyring

  1. - repo: https://github.com/pdm-project/pdm
  2. rev: 2.x.y # 公开钩子的 PDM 版本
  3. hooks:
  4. - id: pdm-sync
  5. additional_dependencies:
  6. - keyring