From 4dfeac63687628e041bef1bbae29e68b4bc3ca39 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Fri, 31 Jan 2025 12:09:11 +0100
Subject: [PATCH 1/4] add initial tox.ini
---
.gitignore | 3 ++
tox.ini | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 105 insertions(+)
create mode 100644 tox.ini
diff --git a/.gitignore b/.gitignore
index cad76cb460..e63a1a5556 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+# generated by cythonize
+tests/cython/test_cython.c
+
# In case somebody wants to restore the directory for local testing
notes-to-self/
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000000..0a06b3c0ea
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,102 @@
+[tox]
+envlist = py{39,310,311,312,313,314,py310}
+labels =
+ check = typing, gen_exports, type_completeness, pip_compile
+ cython = py39-cython2,py39-cython,py311-cython2,py313-cython
+
+# TODO:
+# coverage
+# ci.sh uses --verbose --durations=10
+# -p _trio_check_attrs_aliases
+# mypy cache
+# LSP
+# apport
+# use tox in CI
+# switch to nox?
+# move to pyproject.toml
+
+# protip: install uv-tox for faster venv generation
+
+[testenv]
+# use wheels instead of sdist, significantly faster install
+package = wheel
+wheel_build_env = .pkg
+deps =
+ hypothesis: hypothesis
+ -r test-requirements.txt
+set_env =
+ slow: TOX_RUN_SLOW = '--run-slow'
+commands =
+ pytest {env:TOX_RUN_SLOW:} {posargs}
+
+[testenv:no_test_requirements]
+commands =
+ pytest --skip-optional-imports {posargs}
+
+[testenv:docs]
+deps =
+ -r docs-requirements.txt
+# base_python synced with .readthedocs.yml
+base_python = 3.11
+commands =
+ sphinx-build {posargs:--fresh-env} docs/source docs/build
+
+[testenv:py39-cython2,py39-cython,py311-cython2,py313-cython]
+deps =
+ cython
+ cython2: cython<3
+ setuptools ; python_version >= '3.12'
+commands =
+ cythonize --inplace -X linetrace=True tests/cython/test_cython.pyx
+ python --version
+ cython --version
+ python -m tests.cython.run_test_cython
+
+# tox -m check should be equivalent to this
+[testenv:checksh]
+deps =
+ -r test-requirements.txt
+ exceptiongroup
+base_python = 3.13
+allowlist_externals = ./check.sh
+set_env =
+ PYRIGHT_PYTHON_IGNORE_WARNINGS=1
+commands =
+ ./check.sh
+
+[testenv:gen_exports]
+deps =
+ -r test-requirements.txt
+base_python = 3.13
+commands =
+ python ./src/trio/_tools/gen_exports.py --test
+
+[testenv:pip_compile]
+base_python = 3.13
+commands =
+ pre-commit run pip-compile --all-files
+
+[testenv:typing]
+deps =
+ -r test-requirements.txt
+ exceptiongroup
+base_python = 3.13
+set_env =
+ PYRIGHT_PYTHON_IGNORE_WARNINGS=1
+commands =
+ mypy --platform linux
+ mypy --platform darwin
+ mypy --platform win32
+
+ pyright src/trio/_tests/type_tests
+ pyright src/trio/_core/_tests/type_tests
+
+[testenv:type_completeness]
+deps =
+ -r test-requirements.txt
+ exceptiongroup
+base_python = 3.13
+set_env =
+ PYRIGHT_PYTHON_IGNORE_WARNINGS=1
+commands =
+ python src/trio/_tests/check_type_completeness.py
From a020836286c1a5b433d5d552c6e031a826874ee4 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Fri, 14 Feb 2025 16:56:08 +0100
Subject: [PATCH 2/4] remove check.sh, replace with tox invocation
---
.github/workflows/ci.yml | 7 +-
check.sh | 81 -----------------
ci.sh | 190 +++++++++++++++++++--------------------
tox.ini | 32 +++----
4 files changed, 109 insertions(+), 201 deletions(-)
delete mode 100755 check.sh
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a4489e609d..1d22dc89a3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -276,10 +276,15 @@ jobs:
python-version: ${{ fromJSON(format('["{0}", "{1}"]', format('{0}.0-alpha - {0}.X', matrix.python), matrix.python))[startsWith(matrix.python, 'pypy')] }}
cache: pip
cache-dependency-path: test-requirements.txt
+ - name: Check Formatting
+ if: matrix.check_formatting == '1'
+ run:
+ python -m pip install tox
+ tox -m check
- name: Run tests
+ if: matrix.check_formatting == '0'
run: ./ci.sh
env:
- CHECK_FORMATTING: '${{ matrix.check_formatting }}'
NO_TEST_REQUIREMENTS: '${{ matrix.no_test_requirements }}'
- if: >-
always()
diff --git a/check.sh b/check.sh
deleted file mode 100755
index 701db8ce61..0000000000
--- a/check.sh
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/bin/bash
-
-set -ex
-
-ON_GITHUB_CI=true
-EXIT_STATUS=0
-
-# If not running on Github's CI, discard the summaries
-if [ -z "${GITHUB_STEP_SUMMARY+x}" ]; then
- GITHUB_STEP_SUMMARY=/dev/null
- ON_GITHUB_CI=false
-fi
-
-# Test if the generated code is still up to date
-echo "::group::Generate Exports"
-python ./src/trio/_tools/gen_exports.py --test \
- || EXIT_STATUS=$?
-echo "::endgroup::"
-
-# Run mypy on all supported platforms
-# MYPY is set if any of them fail.
-MYPY=0
-echo "::group::Mypy"
-# Cleanup previous runs.
-rm -f mypy_annotate.dat
-# Pipefail makes these pipelines fail if mypy does, even if mypy_annotate.py succeeds.
-set -o pipefail
-mypy --show-error-end --platform linux | python ./src/trio/_tools/mypy_annotate.py --dumpfile mypy_annotate.dat --platform Linux \
- || { echo "* Mypy (Linux) found type errors." >> "$GITHUB_STEP_SUMMARY"; MYPY=1; }
-# Darwin tests FreeBSD too
-mypy --show-error-end --platform darwin | python ./src/trio/_tools/mypy_annotate.py --dumpfile mypy_annotate.dat --platform Mac \
- || { echo "* Mypy (Mac) found type errors." >> "$GITHUB_STEP_SUMMARY"; MYPY=1; }
-mypy --show-error-end --platform win32 | python ./src/trio/_tools/mypy_annotate.py --dumpfile mypy_annotate.dat --platform Windows \
- || { echo "* Mypy (Windows) found type errors." >> "$GITHUB_STEP_SUMMARY"; MYPY=1; }
-set +o pipefail
-# Re-display errors using Github's syntax, read out of mypy_annotate.dat
-python ./src/trio/_tools/mypy_annotate.py --dumpfile mypy_annotate.dat
-# Then discard.
-rm -f mypy_annotate.dat
-echo "::endgroup::"
-# Display a big error if we failed, outside the group so it can't be collapsed.
-if [ $MYPY -ne 0 ]; then
- echo "::error:: Mypy found type errors."
- EXIT_STATUS=1
-fi
-
-# Check pip compile is consistent
-echo "::group::Pip Compile - Tests & Docs"
-pre-commit run pip-compile --all-files \
- || EXIT_STATUS=$?
-echo "::endgroup::"
-
-echo "::group::Pyright interface tests"
-python src/trio/_tests/check_type_completeness.py || EXIT_STATUS=$?
-
-pyright src/trio/_tests/type_tests || EXIT_STATUS=$?
-pyright src/trio/_core/_tests/type_tests || EXIT_STATUS=$?
-echo "::endgroup::"
-
-# Finally, leave a really clear warning of any issues and exit
-if [ $EXIT_STATUS -ne 0 ]; then
- cat <> "$GITHUB_STEP_SUMMARY"
-exit 0
diff --git a/ci.sh b/ci.sh
index 83ec65748b..f414d94a2c 100755
--- a/ci.sh
+++ b/ci.sh
@@ -47,115 +47,109 @@ python -m build
wheel_package=$(ls dist/*.whl)
python -m uv pip install "trio @ $wheel_package" -c test-requirements.txt
-if [ "$CHECK_FORMATTING" = "1" ]; then
- python -m uv pip install -r test-requirements.txt exceptiongroup
- echo "::endgroup::"
- source check.sh
+# Actual tests
+# expands to 0 != 1 if NO_TEST_REQUIREMENTS is not set, if set the `-0` has no effect
+# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02
+if [ "${NO_TEST_REQUIREMENTS-0}" == 1 ]; then
+ python -m uv pip install pytest coverage -c test-requirements.txt
+ flags="--skip-optional-imports"
else
- # Actual tests
- # expands to 0 != 1 if NO_TEST_REQUIREMENTS is not set, if set the `-0` has no effect
- # https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02
- if [ "${NO_TEST_REQUIREMENTS-0}" == 1 ]; then
- python -m uv pip install pytest coverage -c test-requirements.txt
- flags="--skip-optional-imports"
- else
- python -m uv pip install -r test-requirements.txt
- flags=""
- fi
+ python -m uv pip install -r test-requirements.txt
+ flags=""
+fi
- # So we can run the test for our apport/excepthook interaction working
- if [ -e /etc/lsb-release ] && grep -q Ubuntu /etc/lsb-release; then
- sudo apt install -q python3-apport
- fi
+# So we can run the test for our apport/excepthook interaction working
+if [ -e /etc/lsb-release ] && grep -q Ubuntu /etc/lsb-release; then
+ sudo apt install -q python3-apport
+fi
- # If we're testing with a LSP installed, then it might break network
- # stuff, so wait until after we've finished setting everything else
- # up.
- if [ "$LSP" != "" ]; then
- echo "Installing LSP from ${LSP}"
- # We use --insecure because one of the LSP's has been observed to give
- # cert verification errors:
- #
- # https://github.com/python-trio/trio/issues/1478
- #
- # *Normally*, you should never ever use --insecure, especially when
- # fetching an executable! But *in this case*, we're intentionally
- # installing some untrustworthy quasi-malware onto into a sandboxed
- # machine for testing. So MITM attacks are really the least of our
- # worries.
- if [ "$LSP_EXTRACT_FILE" != "" ]; then
- # We host the Astrill VPN installer ourselves, and encrypt it
- # so as to decrease the chances of becoming an inadvertent
- # public redistributor.
- curl-harder -o lsp-installer.zip "$LSP"
- unzip -P "not very secret trio ci key" lsp-installer.zip "$LSP_EXTRACT_FILE"
- mv "$LSP_EXTRACT_FILE" lsp-installer.exe
- else
- curl-harder --insecure -o lsp-installer.exe "$LSP"
- fi
- # This is only needed for the Astrill LSP, but there's no harm in
- # doing it all the time. The cert was manually extracted by installing
- # the package in a VM, clicking "Always trust from this publisher"
- # when installing, and then running 'certmgr.msc' and exporting the
- # certificate. See:
- # http://www.migee.com/2010/09/24/solution-for-unattendedsilent-installs-and-would-you-like-to-install-this-device-software/
- certutil -addstore "TrustedPublisher" src/trio/_tests/astrill-codesigning-cert.cer
- # Double-slashes are how you tell windows-bash that you want a single
- # slash, and don't treat this as a unix-style filename that needs to
- # be replaced by a windows-style filename.
- # http://www.mingw.org/wiki/Posix_path_conversion
- ./lsp-installer.exe //silent //norestart
- echo "Waiting for LSP to appear in Winsock catalog"
- while ! netsh winsock show catalog | grep "Layered Chain Entry"; do
- sleep 1
- done
- netsh winsock show catalog
+# If we're testing with a LSP installed, then it might break network
+# stuff, so wait until after we've finished setting everything else
+# up.
+if [ "$LSP" != "" ]; then
+ echo "Installing LSP from ${LSP}"
+ # We use --insecure because one of the LSP's has been observed to give
+ # cert verification errors:
+ #
+ # https://github.com/python-trio/trio/issues/1478
+ #
+ # *Normally*, you should never ever use --insecure, especially when
+ # fetching an executable! But *in this case*, we're intentionally
+ # installing some untrustworthy quasi-malware onto into a sandboxed
+ # machine for testing. So MITM attacks are really the least of our
+ # worries.
+ if [ "$LSP_EXTRACT_FILE" != "" ]; then
+ # We host the Astrill VPN installer ourselves, and encrypt it
+ # so as to decrease the chances of becoming an inadvertent
+ # public redistributor.
+ curl-harder -o lsp-installer.zip "$LSP"
+ unzip -P "not very secret trio ci key" lsp-installer.zip "$LSP_EXTRACT_FILE"
+ mv "$LSP_EXTRACT_FILE" lsp-installer.exe
+ else
+ curl-harder --insecure -o lsp-installer.exe "$LSP"
fi
- echo "::endgroup::"
-
- echo "::group::Setup for tests"
+ # This is only needed for the Astrill LSP, but there's no harm in
+ # doing it all the time. The cert was manually extracted by installing
+ # the package in a VM, clicking "Always trust from this publisher"
+ # when installing, and then running 'certmgr.msc' and exporting the
+ # certificate. See:
+ # http://www.migee.com/2010/09/24/solution-for-unattendedsilent-installs-and-would-you-like-to-install-this-device-software/
+ certutil -addstore "TrustedPublisher" src/trio/_tests/astrill-codesigning-cert.cer
+ # Double-slashes are how you tell windows-bash that you want a single
+ # slash, and don't treat this as a unix-style filename that needs to
+ # be replaced by a windows-style filename.
+ # http://www.mingw.org/wiki/Posix_path_conversion
+ ./lsp-installer.exe //silent //norestart
+ echo "Waiting for LSP to appear in Winsock catalog"
+ while ! netsh winsock show catalog | grep "Layered Chain Entry"; do
+ sleep 1
+ done
+ netsh winsock show catalog
+fi
+echo "::endgroup::"
- # We run the tests from inside an empty directory, to make sure Python
- # doesn't pick up any .py files from our working dir. Might have already
- # been created by a previous run.
- mkdir empty || true
- cd empty
+echo "::group::Setup for tests"
- INSTALLDIR=$(python -c "import os, trio; print(os.path.dirname(trio.__file__))")
- cp ../pyproject.toml "$INSTALLDIR" # TODO: remove this
+# We run the tests from inside an empty directory, to make sure Python
+# doesn't pick up any .py files from our working dir. Might have already
+# been created by a previous run.
+mkdir empty || true
+cd empty
- # get mypy tests a nice cache
- MYPYPATH=".." mypy --config-file= --cache-dir=./.mypy_cache -c "import trio" >/dev/null 2>/dev/null || true
+INSTALLDIR=$(python -c "import os, trio; print(os.path.dirname(trio.__file__))")
+cp ../pyproject.toml "$INSTALLDIR" # TODO: remove this
- # support subprocess spawning with coverage.py
- echo "import coverage; coverage.process_startup()" | tee -a "$INSTALLDIR/../sitecustomize.py"
+# get mypy tests a nice cache
+MYPYPATH=".." mypy --config-file= --cache-dir=./.mypy_cache -c "import trio" >/dev/null 2>/dev/null || true
- perl -i -pe 's/-p trio\._tests\.pytest_plugin//' "$INSTALLDIR/pyproject.toml"
+# support subprocess spawning with coverage.py
+echo "import coverage; coverage.process_startup()" | tee -a "$INSTALLDIR/../sitecustomize.py"
- echo "::endgroup::"
- echo "::group:: Run Tests"
- if PYTHONPATH=../tests COVERAGE_PROCESS_START=$(pwd)/../pyproject.toml \
- coverage run --rcfile=../pyproject.toml -m \
- pytest -ra --junitxml=../test-results.xml \
- -p _trio_check_attrs_aliases --verbose --durations=10 \
- -p trio._tests.pytest_plugin --run-slow $flags "${INSTALLDIR}"; then
- PASSED=true
- else
- PASSED=false
- fi
- echo "::endgroup::"
- echo "::group::Coverage"
+perl -i -pe 's/-p trio\._tests\.pytest_plugin//' "$INSTALLDIR/pyproject.toml"
- coverage combine --rcfile ../pyproject.toml
- coverage report -m --rcfile ../pyproject.toml
- coverage xml --rcfile ../pyproject.toml
+echo "::endgroup::"
+echo "::group:: Run Tests"
+if PYTHONPATH=../tests COVERAGE_PROCESS_START=$(pwd)/../pyproject.toml \
+ coverage run --rcfile=../pyproject.toml -m \
+ pytest -ra --junitxml=../test-results.xml \
+ -p _trio_check_attrs_aliases --verbose --durations=10 \
+ -p trio._tests.pytest_plugin --run-slow $flags "${INSTALLDIR}"; then
+ PASSED=true
+else
+ PASSED=false
+fi
+echo "::endgroup::"
+echo "::group::Coverage"
- # Remove the LSP again; again we want to do this ASAP to avoid
- # accidentally breaking other stuff.
- if [ "$LSP" != "" ]; then
- netsh winsock reset
- fi
+coverage combine --rcfile ../pyproject.toml
+coverage report -m --rcfile ../pyproject.toml
+coverage xml --rcfile ../pyproject.toml
- echo "::endgroup::"
- $PASSED
+# Remove the LSP again; again we want to do this ASAP to avoid
+# accidentally breaking other stuff.
+if [ "$LSP" != "" ]; then
+ netsh winsock reset
fi
+
+echo "::endgroup::"
+$PASSED
diff --git a/tox.ini b/tox.ini
index 0a06b3c0ea..24c5ea99b1 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,15 +5,17 @@ labels =
cython = py39-cython2,py39-cython,py311-cython2,py313-cython
# TODO:
-# coverage
-# ci.sh uses --verbose --durations=10
-# -p _trio_check_attrs_aliases
-# mypy cache
-# LSP
-# apport
-# use tox in CI
-# switch to nox?
-# move to pyproject.toml
+# * environment to check coverage
+# * replace ci.sh
+# * --verbose --durations=10
+# * -p _trio_check_attrs_aliases
+# * mypy cache
+# * LSP
+# * apport
+# * use tox in CI
+# * switch to nox?
+# * move to pyproject.toml?
+# * this means conditional deps need to be replaced
# protip: install uv-tox for faster venv generation
@@ -52,18 +54,6 @@ commands =
cython --version
python -m tests.cython.run_test_cython
-# tox -m check should be equivalent to this
-[testenv:checksh]
-deps =
- -r test-requirements.txt
- exceptiongroup
-base_python = 3.13
-allowlist_externals = ./check.sh
-set_env =
- PYRIGHT_PYTHON_IGNORE_WARNINGS=1
-commands =
- ./check.sh
-
[testenv:gen_exports]
deps =
-r test-requirements.txt
From eeef5bd9766055ae97a241aed0194cd85209d8ae Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Fri, 14 Feb 2025 17:24:06 +0100
Subject: [PATCH 3/4] fix ci
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1d22dc89a3..a84a676ae1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -279,7 +279,7 @@ jobs:
- name: Check Formatting
if: matrix.check_formatting == '1'
run:
- python -m pip install tox
+ python -m pip install tox &&
tox -m check
- name: Run tests
if: matrix.check_formatting == '0'
From 6d2a0f569b7d1d41c2d3b91786a10a16611e77ab Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Mon, 24 Feb 2025 12:30:45 +0100
Subject: [PATCH 4/4] fixes after review
---
tox.ini | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/tox.ini b/tox.ini
index 24c5ea99b1..46b9fae2bc 100644
--- a/tox.ini
+++ b/tox.ini
@@ -20,6 +20,7 @@ labels =
# protip: install uv-tox for faster venv generation
[testenv]
+description = "Base environment for running tests depending on python version."
# use wheels instead of sdist, significantly faster install
package = wheel
wheel_build_env = .pkg
@@ -32,29 +33,37 @@ commands =
pytest {env:TOX_RUN_SLOW:} {posargs}
[testenv:no_test_requirements]
+description = "Run tests without optional test-requirements, to see we don't accidentally depend on a library not specified in depends."
+deps =
+ pytest
commands =
pytest --skip-optional-imports {posargs}
[testenv:docs]
+description = "Build documentation into docs/build."
deps =
-r docs-requirements.txt
# base_python synced with .readthedocs.yml
+# To avoid syncing we can make RTD call the tox environment
base_python = 3.11
commands =
sphinx-build {posargs:--fresh-env} docs/source docs/build
[testenv:py39-cython2,py39-cython,py311-cython2,py313-cython]
+description = "Run cython tests."
deps =
cython
cython2: cython<3
setuptools ; python_version >= '3.12'
-commands =
- cythonize --inplace -X linetrace=True tests/cython/test_cython.pyx
+commands_pre =
python --version
cython --version
+ cythonize --inplace -X linetrace=True tests/cython/test_cython.pyx
+commands =
python -m tests.cython.run_test_cython
[testenv:gen_exports]
+description = "Run gen_exports.py, regenerating code for public API wrappers."
deps =
-r test-requirements.txt
base_python = 3.13
@@ -62,11 +71,14 @@ commands =
python ./src/trio/_tools/gen_exports.py --test
[testenv:pip_compile]
+description = "Run pre-commit job pip-compile"
base_python = 3.13
commands =
pre-commit run pip-compile --all-files
+# TODO: allow specifying e.g. typing-3.11 to run with --python[-]version=3.11
[testenv:typing]
+description = "Run type checks: mypy on all platforms, and pyright on `src/trio[/_core]/_tests/type_tests/`."
deps =
-r test-requirements.txt
exceptiongroup
@@ -74,6 +86,7 @@ base_python = 3.13
set_env =
PYRIGHT_PYTHON_IGNORE_WARNINGS=1
commands =
+ # use mypy_annotate if running in CI? if not, should remove it
mypy --platform linux
mypy --platform darwin
mypy --platform win32
@@ -82,6 +95,7 @@ commands =
pyright src/trio/_core/_tests/type_tests
[testenv:type_completeness]
+description = "Check type completeness, using our wrapper around pyright --verifytypes."
deps =
-r test-requirements.txt
exceptiongroup