# 构建和测试Python

了解如何创建持续集成（CI）工作流来生成和测试Python项目。

## 简介

本指南介绍如何生成、测试和发布Python包。

GitHub 托管的运行器具有预安装了软件的工具缓存，包括 Python 和 PyPy。 您无需安装任何项目！ 有关最新软件以及预装的 Python 和 PyPy 版本的完整列表，请参阅 [GitHub 托管的运行程序](/zh/enterprise-cloud@latest/actions/using-github-hosted-runners/about-github-hosted-runners#supported-software)。

## 先决条件

你应该熟悉 YAML 和 GitHub Actions 的语法。 有关详细信息，请参阅“[撰写工作流程](/zh/enterprise-cloud@latest/actions/learn-github-actions)”。

我们建议你对 Python 和 pip 有基本的了解。 有关详细信息，请参阅：

* ```
          [Python 入门](https://www.python.org/about/gettingstarted/)
  ```
* ```
          [Pip 包管理器](https://pypi.org/project/pip/)
  ```

## 使用Python工作流模板

若要快速开始使用，请将工作流模板添加到存储库的 `.github/workflows` 目录。

如果你的存储库已包含至少一个 `.py` 文件，GitHub 提供的 Python 工作流模板应该适用。 本指南的后续部分提供了如何自定义此工作流模板的示例。

1. 在 GitHub 上，导航到存储库的主页面。1. 在仓库名称下，单击“<svg version="1.1" width="16" height="16" viewBox="0 0 16 16" class="octicon octicon-play" aria-label="play" role="img"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"></path></svg> Actions”\*\*\*\*。

   ![“github/docs”存储库的选项卡的屏幕截图。 “操作”选项卡以橙色边框突出显示。](/assets/images/help/repository/actions-tab-global-nav-update.png)1. 如果存储库中已有工作流，请单击“新建工作流”。

2. “选择工作流”页面显示一系列推荐的工作流模板。 搜索“Python应用程序”。

3. 在“Python应用程序”工作流中，单击**Configure**。

4. 根据需要编辑工作流。 例如，更改Python版本。

5. 单击“提交更改”。

工作流文件 `python-app.yml` 将添加到存储库的 `.github/workflows` 目录中。

## 指定Python版本

要在 GitHub 托管的运行器上使用 Python 或 PyPy 的预安装版本，请使用 `setup-python` 操作。 此操作会在每个运行器上的工具缓存中找到特定版本的 Python 或 PyPy，并将必要的二进制文件添加到 `PATH`，这将在作业的剩余时间内持续存在。 如果未在工具缓存中预安装特定版本的Python，`setup-python`作将从 [`python-versions`](https://github.com/actions/python-versions) 存储库下载并设置相应的版本。

使用 `setup-python` 是 Python 与 GitHub Actions 结合使用时的推荐方式，因为它能确保不同运行器和不同版本的 Python 行为一致。 如果使用自承载运行程序，则必须安装Python并将其添加到 `PATH`。 有关详细信息，请参阅 [`setup-python` 操作](https://github.com/marketplace/actions/setup-python)。

下表描述了每个 GitHub 托管的运行器中工具缓存的位置。

<div class="ghd-tool rowheaders">

|        | Ubuntu                   | Mac                               | Windows                        |
| ------ | ------------------------ | --------------------------------- | ------------------------------ |
| 工具缓存目录 | `/opt/hostedtoolcache/*` | `/Users/runner/hostedtoolcache/*` | `C:\hostedtoolcache\windows\*` |
|        |                          |                                   |                                |

```
          **Python工具缓存**|`/opt/hostedtoolcache/Python/*`|`/Users/runner/hostedtoolcache/Python/*`|`C:\hostedtoolcache\windows\Python\*`|
```

|PyPy 工具缓存|`/opt/hostedtoolcache/PyPy/*`|`/Users/runner/hostedtoolcache/PyPy/*`|`C:\hostedtoolcache\windows\PyPy\*`|

</div>

如果使用的是自托管运行器，则可以将运行器配置为使用 `setup-python` 操作来管理依赖项。 有关详细信息，请参阅 `setup-python` 自述文件中的[将 setup-python 与自托管运行器结合使用](https://github.com/actions/setup-python#using-setup-python-with-a-self-hosted-runner)。

GitHub 支持语义版本控制语法。 有关详细信息，请参阅“[使用语义版本控制](https://docs.npmjs.com/about-semantic-versioning#using-semantic-versioning-to-specify-update-types-your-package-can-accept)”和“[语义版本控制规范](https://semver.org/)”。

### 使用多个Python版本

以下示例使用任务的矩阵来设置多个Python版本。 有关详细信息，请参阅“[在工作流中运行作业变体](/zh/enterprise-cloud@latest/actions/using-jobs/using-a-matrix-for-your-jobs)”。

```yaml copy
name: Python package

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["pypy3.10", "3.9", "3.10", "3.11", "3.12", "3.13"]

    steps:
      - uses: actions/checkout@v5
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      # You can test your matrix by printing the current Python version
      - name: Display Python version
        run: python -c "import sys; print(sys.version)"
```

### 使用特定Python版本

可以配置特定版本的Python。 例如 3.12。 或者，您也可以使用语义版本语法来获得最新的次要版本。 此示例使用 Python 3 的最新次要版本。

```yaml copy
name: Python package

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5
      - name: Set up Python
        # This is the version of the action for setting up Python, not the Python version.
        uses: actions/setup-python@v5
        with:
          # Semantic version range syntax or exact version of a Python version
          python-version: '3.x'
          # Optional - x64 or x86 architecture, defaults to x64
          architecture: 'x64'
      # You can test your matrix by printing the current Python version
      - name: Display Python version
        run: python -c "import sys; print(sys.version)"
```

### 排除版本

如果指定不可用的Python版本，`setup-python`失败，并出现错误，例如：`##[error]Version 3.7 with arch x64 not found`。 错误消息包含可用的版本。

如果存在不希望运行的Python配置，还可以在工作流中使用 `exclude` 关键字。 有关详细信息，请参阅“[GitHub Actions 的工作流语法](/zh/enterprise-cloud@latest/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategy)”。

```yaml copy
name: Python package

on: [push]

jobs:
  build:

    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        python-version: ["3.9", "3.11", "3.13", "pypy3.10"]
        exclude:
          - os: macos-latest
            python-version: "3.11"
          - os: windows-latest
            python-version: "3.11"
```

### 使用默认Python版本

建议使用 `setup-python` 配置工作流中使用的Python版本，因为它有助于明确依赖项。 如果不使用 `setup-python`，则调用 `PATH` 时，`python` 中设置的默认Python版本将用于任何 shell。 GitHub 托管的运行器之间有不同的 Python 默认版本，这可能导致非预期的更改或使用的版本比预期更旧。

| GitHub 托管的运行器 | 说明                                                                                                                               |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Ubuntu        | Ubuntu 运行器在 `/usr/bin/python` 和 `/usr/bin/python3` 下安装了多个版本的系统 Python。 GitHub 除了安装在工具缓存中的版本，还有与 Ubuntu 一起打包的 Python 版本。          |
| Windows       | 不包括工具缓存中的Python版本，Windows不附带等效版本的系统Python。 为保持与其他运行器一致的行为，并允许 Python 在没有 `setup-python` 操作的情况下开箱即用，GitHub 将从工具缓存中添加几个版本到 `PATH`。 |
| macOS         | 除了作为工具缓存一部分的版本外，macOS 运行程序还安装了多个版本的系统Python。 系统Python版本位于 `/usr/local/Cellar/python/*` 目录中。                                      |

## 安装依赖

GitHub 托管运行器已安装 pip 软件包管理器。 在构建和测试代码之前，您可以使用 pip 从 PyPI 软件包注册表安装依赖项。 例如，下面的 YAML 安装或升级 `pip` 包安装程序以及 `setuptools` 和 `wheel` 包。

可以缓存依赖项来加快工作流运行。 有关详细信息，请参阅“[依赖项缓存参考](/zh/enterprise-cloud@latest/actions/using-workflows/caching-dependencies-to-speed-up-workflows)”。

```yaml copy
steps:
- uses: actions/checkout@v5
- name: Set up Python
  uses: actions/setup-python@v5
  with:
    python-version: '3.x'
- name: Install dependencies
  run: python -m pip install --upgrade pip setuptools wheel
```

### 需求文件

更新 `pip` 后，下一步通常是从 `requirements.txt` 安装依赖项。 有关详细信息，请参阅 [pip](https://pip.pypa.io/en/stable/cli/pip_install/#example-requirements-file)。

```yaml copy
steps:
- uses: actions/checkout@v5
- name: Set up Python
  uses: actions/setup-python@v5
  with:
    python-version: '3.x'
- name: Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt
```

### 缓存依赖项

可以使用 [`setup-python` 操作](https://github.com/actions/setup-python)缓存和还原依赖项。

以下示例缓存 pip 的依赖项。

```yaml copy
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    cache: 'pip'
- run: pip install -r requirements.txt
- run: pip test
```

默认情况下，`setup-python` 操作将在整个存储库中搜索依赖项文件（对于 pip，该文件为 `requirements.txt`；对于 pipenv，文件为 `Pipfile.lock`；对于 poetry，则为 `poetry.lock`）。 有关详细信息，请参阅自述文件中的“缓存软件包依赖项”部分。

如果有自定义要求或需要更精细的缓存控制，可以使用 [`cache` 操作](https://github.com/marketplace/actions/cache)。 Pip 根据运行器的操作系统将依赖项缓存在不同的位置。 您需要缓存的路径可能不同于上面的 Ubuntu 示例，具体取决于您使用的操作系统。 有关详细信息，请参阅[Python缓存示例](https://github.com/actions/cache/blob/main/examples.md#python---pip)在`cache`中。

## 测试代码

您可以使用与本地相同的命令来构建和测试代码。

### 使用 pytest 和 pytest-cov 测试

此示例安装或升级 `pytest` 和 `pytest-cov`。 然后进行测试并以 JUnit 格式输出，而代码覆盖结果则以 Cobertura 输出。 有关详细信息，请参阅 [JUnit](https://junit.org/junit5/) 和 [Cobertura](https://cobertura.github.io/cobertura/)。

```yaml copy
steps:
- uses: actions/checkout@v5
- name: Set up Python
  uses: actions/setup-python@v5
  with:
    python-version: '3.x'
- name: Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt
- name: Test with pytest
  run: |
    pip install pytest pytest-cov
    pytest tests.py --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html
```

### 使用 Ruff 进行代码检查 (lint) 和/或格式化

以下示例安装或升级 `ruff` 并使用它对所有文件执行 lint 操作。 有关详细信息，请参阅 [Ruff](https://docs.astral.sh/ruff)。

```yaml copy
steps:
- uses: actions/checkout@v5
- name: Set up Python
  uses: actions/setup-python@v5
  with:
    python-version: '3.x'
- name: Install the code linting and formatting tool Ruff
  run: pipx install ruff
- name: Lint code with Ruff
  run: ruff check --output-format=github --target-version=py39
- name: Check code formatting with Ruff
  run: ruff format --diff --target-version=py39
  continue-on-error: true
```

格式化步骤已设置 `continue-on-error: true`。 这可防止在格式设置步骤不成功时工作流失败。 解决所有格式设置错误后，可以删除此选项，以便工作流捕获新问题。

### 使用 tox 运行测试

通过 GitHub Actions，您可以使用 tox 运行测试并将工作分散到多个作业。 需要使用 `-e py` 选项调用 tox，以选择 `PATH` 中的 Python 版本，而不是指定特定版本。 有关详细信息，请参阅 [tox](https://tox.readthedocs.io/en/latest/)。

```yaml copy
name: Python package

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python: ["3.9", "3.11", "3.13"]

    steps:
      - uses: actions/checkout@v5
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python }}
      - name: Install tox and any other packages
        run: pip install tox
      - name: Run tox
        # Run tox using the version of Python in `PATH`
        run: tox -e py
```

## 将工作流数据打包为构件

您可以在工作流程完成后上传构件以查看。 例如，您可能需要保存日志文件、核心转储、测试结果或屏幕截图。 有关详细信息，请参阅“[使用工作流工件存储和共享数据](/zh/enterprise-cloud@latest/actions/using-workflows/storing-workflow-data-as-artifacts)”。

以下示例演示如何使用 `upload-artifact` 操作来存档运行 `pytest` 得到的测试结果。 有关详细信息，请参阅 [`upload-artifact` 操作](https://github.com/actions/upload-artifact)。

```yaml copy
name: Python package

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]

    steps:
      - uses: actions/checkout@v5
      - name: Setup Python # Set Python version
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      # Install pip and pytest
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install pytest
      - name: Test with pytest
        run: pytest tests.py --doctest-modules --junitxml=junit/test-results-${{ matrix.python-version }}.xml
      - name: Upload pytest test results
        uses: actions/upload-artifact@v4
        with:
          name: pytest-results-${{ matrix.python-version }}
          path: junit/test-results-${{ matrix.python-version }}.xml
        # Use always() to always run this step to publish test results when there are test failures
        if: ${{ always() }}
```

## 发布到 PyPI

可以将工作流配置为在 CI 测试通过后将Python包发布到 PyPI。 本部分演示如何在每次发布版本时使用 GitHub Actions 将包上传到 PyPI。 有关详细信息，请参阅“[管理存储库中的发行版](/zh/enterprise-cloud@latest/repositories/releasing-projects-on-github/managing-releases-in-a-repository)”。

下面的示例工作流使用“[受信任的发布](https://docs.pypi.org/trusted-publishers/)”对 PyPI 进行身份验证，无需手动配置的 API 令牌。

```yaml copy
# 此工作流使用未经 GitHub 认证的操作。
# 它们由第三方提供，并受
# 单独的服务条款、隐私政策和支持
# 文档。

# GitHub 建议将操作固定到提交 SHA。
# 若要获取较新版本，需要更新 SHA。
# 还可以引用标记或分支，但该操作可能会更改而不发出警告。

name: Upload Python Package

on:
  release:
    types: [published]

permissions:
  contents: read

jobs:
  release-build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5

      - uses: actions/setup-python@v5
        with:
          python-version: "3.x"

      - name: Build release distributions
        run: |
          # NOTE: put your own distribution build steps here.
          python -m pip install build
          python -m build

      - name: Upload distributions
        uses: actions/upload-artifact@v4
        with:
          name: release-dists
          path: dist/

  pypi-publish:
    runs-on: ubuntu-latest

    needs:
      - release-build

    permissions:
      # IMPORTANT: this permission is mandatory for trusted publishing
      id-token: write

    # Dedicated environments with protections for publishing are strongly recommended.
    environment:
      name: pypi
      # OPTIONAL: uncomment and update to include your PyPI project URL in the deployment status:
      # url: https://pypi.org/p/YOURPROJECT

    steps:
      - name: Retrieve release distributions
        uses: actions/download-artifact@v5
        with:
          name: release-dists
          path: dist/

      - name: Publish release distributions to PyPI
        uses: pypa/gh-action-pypi-publish@6f7e8d9c0b1a2c3d4e5f6a7b8c9d0e1f2a3b4c5d
```

有关此工作流的详细信息，包括所需的 PyPI 设置，请参阅 [在 PyPI 中配置 OpenID Connect](/zh/enterprise-cloud@latest/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-pypi)。