Python Multi-Version Test Template#
This template triggers test executions across multiple Python versions using parallel jobs. Useful for libraries or projects that must ensure compatibility between different Python versions.
Usage#
In your project’s .gitlab-ci.yml:
include:
- project: "bytecode-solutions/core/core-gitlab-templates"
file: "tests/run_tests.yml"
Advanced Usage with Custom Command#
You can customize the test command to run:
run-tests:
extends: .run_tests
variables:
cmd: "pytest tests/ -v --cov=mypackage"
Installing Additional Extras#
If your project requires optional dependency groups beyond [dev], pass them
via the extras input:
include:
- project: "bytecode-solutions/core/core-gitlab-templates"
file: "templates/tests/run_tests.yml"
ref: "release"
inputs:
extras: "postgres,mongo"
This installs .[postgres,mongo] on top of the base .[dev] install before
running the test command.
Custom Python Versions#
Override the default Python versions:
run-tests:
extends: .run_tests
variables:
versions:
- PYTHON_IMAGE: "python:3.11"
- PYTHON_IMAGE: "python:3.12"
- PYTHON_IMAGE: "python:3.13"
Parameters#
The template defines the following inputs:
branchOptional: Restrict execution to a specific branch. If empty, the tests will run on all branches.
Default:
""(runs on all branches)stageThe pipeline stage in which the test job runs.
Default:
testscmdCommand to execute to run the tests. This allows you to customize how tests are executed for your specific project.
Default:
python manager.py run-coverageextrasOptional: additional pip extras to install before running the tests. Accepts a comma-separated string of extra names (e.g.
all,postgres,mongo). Installed aspip install ".[extras]"after the base[dev]install. When empty, no extra install step is run.Default:
""(no extras installed)versionsPython versions to test. Array of image specifications that determines which Python versions will be tested in parallel.
- Default:
python:3.9python:3.10python:3.11python:3.12python:3.13python:3.14pypy:3.11
Behavior#
The job performs the following steps:
PyPy Support: Automatically detects PyPy images and installs Rust toolchain (required for packages with Rust dependencies)
Environment Setup: Creates a virtual environment for isolated testing
Package Installation: Attempts to install the package with
[dev]extras. If the[dev]extra doesn’t exist, falls back to installing the base packageExtras Installation: If the
extrasinput is non-empty, installs the specified extras (e.g.pip install ".[postgres,mongo]")Test Execution: Runs the specified test command
Parallel Execution: Executes tests simultaneously across all specified Python versions using GitLab’s matrix feature
Special Features#
PyPy Support#
The template automatically detects when running on PyPy and installs the Rust
toolchain. This is essential for packages that have dependencies with Rust
extensions (like cryptography):
if [[ "$PYTHON_IMAGE" == "pypy:3.11" ]]; then
apt install -y curl build-essential
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
fi
Flexible Installation#
The template gracefully handles projects that may or may not define development extras:
pip install ".[dev]" || pip install .
This ensures the job doesn’t fail if your pyproject.toml or setup.py
doesn’t define a [dev] extra.
Extras Installation#
After the base install, the template conditionally installs any additional
extras declared via the extras input:
if [[ -n "postgres,mongo" ]]; then
pip install ".[postgres,mongo]"
fi
When extras is empty (the default), this step is skipped entirely.
Parallel Execution#
Tests run simultaneously across all Python versions using GitLab’s matrix feature, significantly reducing total pipeline execution time.
Pip Caching#
The template caches pip packages in .pip_cache to speed up subsequent runs.
Tip
Consider reducing the number of Python versions tested on feature branches and running the full matrix only on main branches or tags to save CI resources.
Example: Targeted Testing#
# Test only latest versions on feature branches
run-tests-quick:
extends: .run_tests
variables:
versions:
- PYTHON_IMAGE: "python:3.13"
only:
- branches
except:
- main
# Full test matrix on main branch
run-tests-full:
extends: .run_tests
only:
- main
- tags