#12388 Remove TestCase.defer{SetUp, TestMethod, TearDown, RunCleanups… #56
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Try to get a short workflow name and a job name that start with Python | |
# version to make it easier to check the status inside GitHub UI. | |
# | |
# When using external actions check that the external repos are permitted via | |
# the GitHub configuration at https://github.com/twisted/twisted/settings/actions | |
# | |
name: CI | |
on: | |
push: | |
branches: | |
- trunk | |
- release-* | |
tags: | |
- twisted-* | |
pull_request: | |
branches: [ trunk ] | |
workflow_dispatch: | |
inputs: | |
debug_enabled: | |
type: boolean | |
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' | |
required: false | |
default: false | |
permissions: | |
contents: read | |
# Only have a run a single parallel for each branch. | |
# Runs for trunk are queues. | |
# Older runs for non-trunk branches are cancelled and the jobs are executed | |
# only for the latest push to the branch. | |
concurrency: | |
group: ${{ github.ref }} | |
cancel-in-progress: ${{ github.ref != 'refs/heads/trunk' }} | |
defaults: | |
run: | |
shell: bash | |
env: | |
# Set to 'yes' to open a tunnel to GitHub's VMs through tmate on failures. | |
# You can also trigger it via manual workflow trigger. | |
# https://github.com/mxschmitt/action-tmate#manually-triggered-debug | |
TMATE_DEBUG: 'no' | |
# The default values in the job generated by the matrix. | |
DEFAULT_PYTHON_VERSION: '3.11' | |
# Dump Python tracebacks on segfault: | |
PYTHONFAULTHANDLER: '1' | |
jobs: | |
testing: | |
# We can't use `env.*` in the job name, only in the steps. | |
name: ${{ matrix.python-version }}-${{ matrix.job-name || 'default-tests' }} | |
# We have Ubuntu as the base for running agains multiple Python versions. | |
runs-on: "${{ matrix.runs-on || 'ubuntu-22.04' }}" | |
env: | |
# By default we run all tests with all deps with coverage. | |
TOXENV: "${{ matrix.tox-env || 'alldeps-withcov-posix' }}" | |
# As of April 2021 GHA VM have 2 CPUs - Azure Standard_DS2_v2 | |
# Trial distributed jobs enabled to speed up the CI jobs. | |
# On Windows, we don't yet enable distributed tests as is not yet | |
# supported. | |
TRIAL_ARGS: "${{ matrix.trial-args || '-j 4' }}" | |
strategy: | |
fail-fast: false | |
# The matrix is designed to not expand into any job. | |
# It is used to document the test environement variations. | |
# The actual job enviroment are defined in the `include` section. | |
matrix: | |
# The Python version on which the job is executed. | |
# We need at least one value here, so we go with latest Python version | |
# that we support.. | |
python-version: ['3.12'] | |
# Just use the default OS. | |
runs-on: [''] | |
# Human readable short description for this job. | |
job-name: [''] | |
# This is the main tox target. | |
# It is later extended with more jobs that should run in a specific | |
# OS + Python version configuration. | |
tox-env: [''] | |
# We just go with the default arguments. | |
trial-args: [''] | |
# By default, tests are executed without disabling IPv6. | |
noipv6: [''] | |
# By default, tests are executed without extra platform dependencies. | |
platform-deps: [''] | |
# By default, tests are executed directly via tox. | |
tox-wrapper: [''] | |
# Tests are executed with the default target which is the full test suite. | |
trial-target: [''] | |
# By default the coverage is not skipped. | |
skip-coverage: [''] | |
# Here the matrix is extended with variations of the default | |
# value. | |
include: | |
# `nodeps` is created to make sure we don't have import errors in the | |
# runtime and production code. | |
# The minimum supported Python version should be used to maximize | |
# coverage of code that otherwise depends on backports. | |
# Distributed test run is disabled here so that we also have | |
# end to end functional test usage for non-distributed trial runs. | |
# When updating the minimum Python version here, also update the | |
# `python_requires` from `setup.cfg`. | |
- python-version: '3.8.0' | |
tox-env: 'nodeps-withcov-posix' | |
job-name: 'nodeps-test' | |
# Older pyhon is only on 20.04. | |
runs-on: 'ubuntu-20.04' | |
# `noipv6` is created to make sure all is OK on an OS which doesn't | |
# have IPv6 available. | |
# Any supported Python version is OK for this job. | |
# We go with latest micro release for the smallest minor release | |
# that we support. | |
# This is also used to run tests with minimum dependency versions. | |
- python-version: '3.8' | |
noipv6: '-noipv6' | |
tox-env: 'mindeps-withcov-posix' | |
job-name: 'minimum-deps-noipv6' | |
# Just Python 3.8 with default settings. | |
- python-version: '3.8' | |
# Just Python 3.9 with default settings. | |
- python-version: '3.9' | |
# Just Python 3.10 with default settings. | |
- python-version: '3.10' | |
# Just Python 3.11 with default settings. | |
- python-version: '3.11' | |
# Just Python 3.13 with default settings. | |
- python-version: '3.13' | |
# Newest Intel macOS and oldest Python (major) supported versions. | |
- python-version: '3.8' | |
runs-on: 'macos-13' | |
job-name: 'macos-13-default-tests' | |
tox-env: 'macos-withcov-alldeps' | |
# Newest ARM macOS and newest Python supported versions. | |
- python-version: '3.13' | |
runs-on: 'macos-14' | |
job-name: 'macos-14-default-tests' | |
tox-env: 'macos-withcov-alldeps' | |
# Windows, minimum Python version with select reactor. | |
- python-version: '3.8' | |
runs-on: 'windows-2022' | |
tox-env: 'alldeps-withcov-windows' | |
job-name: 'win-default-tests-select' | |
# Distributed trial is not yet suported on Windows so we overwrite | |
# the default trial-args to remove concurrent runs and to | |
# select a reactor. | |
trial-args: '--reactor=select' | |
# Windows, newest Python supported version with iocp reactor. | |
- python-version: '3.13' | |
runs-on: 'windows-2022' | |
tox-env: 'alldeps-withcov-windows' | |
job-name: 'win-default-tests-iocp' | |
# Distributed trial is not yet suported on Windows. | |
trial-args: '--reactor=iocp' | |
# On PYPY coverage is very slow (5min vs 30min) as there is no C | |
# extension. | |
# There is very little PYPY specific code so there is not much to | |
# gain from reporting coverage. | |
- python-version: 'pypy3.10-v7.3.14' | |
tox-env: 'alldeps-nocov-posix' | |
job-name: 'no-coverage' | |
skip-coverage: yes | |
# We still run some tests with coverage, | |
# as there are test with specific code branches for pypy. | |
- python-version: 'pypy3.10-v7.3.13' | |
trial-target: 'twisted.test.test_compat twisted.test.test_defer twisted.internet.test.test_socket twisted.trial.test.test_tests' | |
job-name: 'with-coverage' | |
# We run the full test suite with the GI reactor against an X virtual | |
# framebuffer server. This covers our integration with the version | |
# of Gtk packaged within our selected version of Linux above. | |
- python-version: '3.8' | |
tox-env: 'alldeps-gtk-withcov-posix' | |
platform-deps: 'gtk-platform' | |
tox-wrapper: 'xvfb-run -a' | |
job-name: 'gtk-tests' | |
trial-args: '--reactor=gi -j 4' | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Set up Python | |
uses: actions/setup-python@v5 | |
with: | |
python-version: ${{ matrix.python-version }} | |
allow-prereleases: true | |
check-latest: true | |
- name: Get pip cache dir | |
id: pip-cache | |
run: | | |
echo "cache-dir=$(pip cache dir)" >> $GITHUB_OUTPUT | |
- name: pip cache | |
uses: actions/cache@v4 | |
with: | |
path: ${{ steps.pip-cache.outputs.cache-dir }} | |
key: | |
${{ runner.os }}-pip-${{ hashFiles('pyproject.toml', 'setup.py', | |
'setup.cfg', 'tox.ini') }} | |
restore-keys: | | |
${{ runner.os }}-pip- | |
# Make sure the matrix is defined in such a way that this is triggered | |
# only on Linux. | |
- name: Disable IPv6 | |
if: matrix.noipv6 | |
run: | | |
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1 | |
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1 | |
sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1 | |
ip a | |
- uses: twisted/python-info-action@v1 | |
- name: Install dependencies | |
run: | | |
openssl version | |
python -m pip install --upgrade pip tox~=3.0 coverage | |
# Make sure the matrix is defined in such a way that this is triggered | |
# only on Linux. | |
- name: Install GTK system deps | |
if: matrix.platform-deps == 'gtk-platform' | |
run: | | |
# *-dev dependencies are for pygobject | |
# https://gitlab.gnome.org/GNOME/pygobject/-/blob/3.42.0/setup.py#L129-L134 | |
sudo apt-get update | |
sudo apt-get install -y \ | |
libgirepository1.0-dev \ | |
libglib2.0-dev \ | |
libcairo2-dev \ | |
libffi-dev \ | |
gir1.2-gtk-3.0 \ | |
xvfb | |
- name: Test | |
run: | | |
${{ matrix.tox-wrapper }} tox ${{ matrix.trial-target }} | |
# If one of the above steps fails, fire up tmate for remote debugging. | |
# This is fired for manual trigger or via the environment variable. | |
- name: Tmate debug session | |
if: ${{ failure() && (env.TMATE_DEBUG == 'yes' || github.event_name == 'workflow_dispatch' && inputs.debug_enabled ) }} | |
uses: mxschmitt/action-tmate@v3 | |
with: | |
limit-access-to-actor: true | |
- name: Prepare coverage results | |
if: ${{ !cancelled() && !matrix.skip-coverage }} | |
run: | | |
# sub-process coverage are generated in separate files so we combine them | |
# to get an unified coverage for the local run. | |
python -m coverage combine | |
python -m coverage xml -o coverage.xml -i | |
# It is important to keep the unique names for the coverage files | |
# so that when uploaded to the same artifact, they don't overlap. | |
# We don't combine the files yet. | |
# The files from all jobs will be combined later. | |
# We still do a initial combine for each job as otherwise | |
# we wil end up with many files and it will take a long time | |
# to upload/download/combine them. | |
mv .coverage .coverage-job-${{ matrix.python-version || env.DEFAULT_PYTHON_VERSION }}-${{ matrix.job-name || 'default-tests' }} | |
- name: Store coverage file | |
if: ${{ !cancelled() && !matrix.skip-coverage }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: coverage-${{ matrix.python-version || env.DEFAULT_PYTHON_VERSION }}-${{ matrix.job-name || 'default-tests' }} | |
include-hidden-files: true | |
if-no-files-found: error # 'warn' or 'ignore', defaults to `warn` | |
path: .coverage-job-* | |
coverage-report: | |
name: Coverage report | |
runs-on: ubuntu-latest | |
if: always() # Always run the coverage report, even when the tests fail. | |
needs: | |
- testing # Code coverage only needs to wait for test jobs. | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: Install dependencies | |
run: | | |
python -m pip install --upgrade pip | |
python -m pip install --upgrade coverage[toml] diff_cover | |
- name: Download coverage reports | |
uses: actions/download-artifact@v4 | |
with: | |
pattern: coverage-* | |
merge-multiple: true | |
path: . | |
- name: Prepare coverage | |
run: | | |
coverage combine .coverage-job-* | |
# XML is needed for the diff-cover. | |
coverage xml | |
- name: Report coverage | |
run: | | |
# Report for the job log. | |
diff-cover --markdown-report coverage-report.md --compare-branch origin/trunk coverage.xml | |
cat coverage-report.md >> $GITHUB_STEP_SUMMARY | |
{ | |
echo '# Coverage Report' | |
echo '```' | |
coverage report --skip-covered --skip-empty --show-missing | |
echo '```' | |
} >> $GITHUB_STEP_SUMMARY | |
- name: Enforce diff coverage | |
run: | | |
diff-cover --fail-under=100 --compare-branch origin/trunk coverage.xml | |
- name: Generate HTML report on failure | |
if: ${{ failure() }} | |
run: | | |
coverage html --skip-covered --skip-empty | |
- name: Upload HTML report on failure | |
if: ${{ failure() }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: html-report | |
path: htmlcov | |
- name: Publish to codecov.io | |
uses: codecov/codecov-action@v4 | |
if: ${{ !cancelled() }} | |
with: | |
files: coverage.xml | |
name: tests-combined | |
fail_ci_if_error: true | |
benchmarks: | |
runs-on: ubuntu-22.04 | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Set up Python | |
uses: actions/setup-python@v5 | |
with: | |
# When you will need to change this to run the benchmarks | |
# on a different Python version, | |
# it will also change the benchmark results. | |
# This is expected and unavoidable. | |
# This is why it's best not to configure the default | |
# Python version here, | |
# but rather have an explicit Python version. | |
# The PRs that change the Python version can | |
# be merged by ignoring the benchmark results. | |
python-version: '3.12' | |
- name: Install dependencies | |
run: | | |
python -m pip install --upgrade pip | |
python -m pip install '.[tls,conch,http2]' pytest pytest-codspeed pytest-cov pytest-benchmark | |
- name: Run benchmarks | |
uses: CodSpeedHQ/action@v3 | |
with: | |
token: ${{ secrets.CODSPEED_TOKEN }} | |
# codspeed runs this command under a CPU emulator, so it's super-slow, | |
# so we try to just do the benchmarks and nothing else. | |
run: python -m pytest --codspeed benchmarks/ | |
- name: Collect benchmarks coverage | |
run: | | |
# We try to run the benchmark tests only once. | |
# It should be enough to collect the coverage. | |
# The previous Codspeed run takes care of the actual benchmark | |
# measurements. | |
pytest \ | |
--benchmark-min-rounds 1 --benchmark-max-time 0.00001 \ | |
--benchmark-columns=rounds \ | |
--cov benchmarks --cov twisted --cov-config=.coveragerc \ | |
--cov-report=xml \ | |
benchmarks | |
- name: Publish benchmarks coverage | |
uses: codecov/codecov-action@v4 | |
if: ${{ !cancelled() }} | |
with: | |
files: coverage.xml | |
name: benchmarks | |
fail_ci_if_error: true | |
- name: Fail on missing benchmarks coverage | |
run: | | |
# This makes sure that the benchmark tests always have 100% coverage. | |
# Make sure the include expression is escaped to prevent shell | |
# expansion. | |
coverage report --fail-under=100 --include 'benchmarks/*' --show-missing | |
static-checks: | |
runs-on: ubuntu-22.04 | |
env: | |
TOX_PARALLEL_NO_SPINNER: 1 | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
# Need full history for various diff checks to work. | |
fetch-depth: 0 | |
- name: Set up Python | |
uses: actions/setup-python@v5 | |
with: | |
python-version: '${{ env.DEFAULT_PYTHON_VERSION }}' | |
- name: Install dependencies | |
run: | | |
python -m pip install --upgrade pip tox~=3.0 | |
- name: Run the checks | |
run: | | |
python --version | |
tox --parallel -e lint,mypy,newsfragment | |
# Used to validate the narrative and API docs. | |
# Checks for invalid syntax or broken links. | |
# This is only the validation and help highlight docs errors. | |
# The actual docs are built and published via Read The Docs. | |
apidocs: | |
name: API docs build | |
runs-on: ubuntu-22.04 | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Set up Python | |
uses: actions/setup-python@v5 | |
with: | |
python-version: ${{ env.DEFAULT_PYTHON_VERSION }} | |
- name: Install dependencies | |
run: | | |
python -m pip install --upgrade pip tox~=3.0 | |
- name: Build apidocs | |
run: | | |
tox -e apidocs | |
narrativedocs: | |
name: Narrative docs build | |
runs-on: ubuntu-22.04 | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Set up Python | |
uses: actions/setup-python@v5 | |
with: | |
python-version: ${{ env.DEFAULT_PYTHON_VERSION }} | |
- name: Install dependencies | |
run: | | |
python -m pip install --upgrade pip tox~=3.0 | |
- name: Run the checks | |
run: | | |
tox -e narrativedocs | |
# Used for various release automation, but not for publishing, as publishing needs extra permissions for the API token. | |
# So we isolate the actual publish step in a separate job. | |
# This is also executed for each PR to exercise the release as much | |
# as possible and reduce the possibility of finding bugs in the release | |
# process late in the release cycle, | |
# The files are published only when a tag is created using the next job, release-publish | |
release-build: | |
name: Build and check release, then upload artifact | |
runs-on: 'ubuntu-22.04' | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Set up Python | |
uses: actions/setup-python@v5 | |
with: | |
python-version: '${{ env.DEFAULT_PYTHON_VERSION }}' | |
- name: Test | |
run: | | |
python -m pip install --upgrade pip tox~=3.0 pep517 | |
rm -rf dist/* | |
tox -e release-prepare | |
- uses: twisted/python-info-action@v1 | |
- name: Files to be pushed to PyPi | |
run: | | |
ls -R dist/ | |
- uses: actions/upload-artifact@v4 | |
with: | |
name: dist | |
path: dist | |
- name: Check matched tag version and branch version - on tag | |
if: startsWith(github.ref, 'refs/tags/twisted-') | |
run: python admin/check_tag_version_match.py "${{ github.ref }}" | |
# Used exclusively for PyPi release publishing. | |
# This is only executed on tags and only does the publishing. | |
# The actual build is done by `release-build` job, | |
# which is executed for each PR to help detect release | |
# defects during the normal development cycle. | |
release-publish: | |
needs: | |
release-build | |
if: startsWith(github.ref, 'refs/tags/twisted-') | |
environment: | |
release | |
permissions: | |
# IMPORTANT: this permission is mandatory for trusted publishing | |
id-token: write | |
name: publish on twisted-* tag | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/download-artifact@v4 | |
with: | |
name: dist | |
path: dist | |
- name: Publish to PyPI - on tag | |
uses: pypa/gh-action-pypi-publish@v1.12.2 | |
# We have this job so that the PR can be blocked on a single job. | |
# In this way, each time a job is modified, | |
# we don't have to go to GitHub UI and reconfigure branch protection. | |
# See GitHub support answer for this hack. | |
# https://gist.github.com/altendky/2e3483a1f7e1ba21cc97de75db9b7d1c | |
all-successful: | |
# Is very important to force running this always, as otherwise it will be | |
# skipped by default. | |
if: always() | |
runs-on: ubuntu-latest | |
# Here should be the list of all the other jobs defined in this file. | |
needs: | |
- benchmarks | |
- testing | |
- narrativedocs | |
- apidocs | |
- static-checks | |
- release-build | |
- coverage-report | |
steps: | |
- name: Require all successes | |
shell: python | |
env: | |
RESULTS: ${{ toJSON(needs.*.result) }} | |
run: | | |
import json | |
import os | |
import sys | |
results = json.loads(os.environ["RESULTS"]) | |
sys.exit(0 if all(result == "success" for result in results) else 1) |