From 30a47f47f074142088874b3efbdef7b429202aec Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Tue, 24 Mar 2026 19:02:59 +0100 Subject: [PATCH 1/7] test: terminate wheel test background jobs reliably --- test-wheel.bash | 115 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 18 deletions(-) diff --git a/test-wheel.bash b/test-wheel.bash index f68e3d2..c00d39d 100755 --- a/test-wheel.bash +++ b/test-wheel.bash @@ -1,6 +1,6 @@ #!/usr/bin/env bash -set -e +set -euo pipefail venv_python="${PYTHON_WHEEL_TEST_EXECUTABLE:-}" if [[ -z "$venv_python" ]]; then @@ -29,33 +29,110 @@ python -m pip install -U pip pip install pytest cleanup=true +background_pids=() +failed_pids=() +cleanup_done=false -trap ' - failed_pids=() - for pid in $(jobs -p); do - if kill -0 $pid >/dev/null 2>&1; then - # Background process is still running - kill it. - kill $pid +is_windows_shell=false +case "${OSTYPE:-}" in + msys*|cygwin*) + is_windows_shell=true + ;; +esac +if [[ "${OS:-}" == "Windows_NT" ]]; then + is_windows_shell=true +fi + +terminate_pid_tree() { + local pid="$1" + + if ! kill -0 "$pid" >/dev/null 2>&1; then + return 0 + fi + + if [[ "$is_windows_shell" == true ]] && command -v taskkill >/dev/null 2>&1; then + taskkill //PID "$pid" //T //F >/dev/null 2>&1 || true + else + kill -TERM "-$pid" >/dev/null 2>&1 || kill "$pid" >/dev/null 2>&1 || true + fi + + local deadline=$((SECONDS + 20)) + while kill -0 "$pid" >/dev/null 2>&1; do + if (( SECONDS >= deadline )); then + if [[ "$is_windows_shell" == true ]] && command -v taskkill >/dev/null 2>&1; then + taskkill //PID "$pid" //T //F >/dev/null 2>&1 || true + else + kill -KILL "-$pid" >/dev/null 2>&1 || kill -9 "$pid" >/dev/null 2>&1 || true + fi + break + fi + sleep 1 + done + + wait "$pid" 2>/dev/null || true +} + +run_command() { + local cmd="$1" + if [[ "$cmd" == *.py ]] && [[ "$cmd" != *[[:space:]]* ]]; then + python "$cmd" + else + bash -lc "$cmd" + fi +} + +launch_background_command() { + local cmd="$1" + if [[ "$is_windows_shell" == true ]]; then + bash -lc "$cmd" & + elif command -v setsid >/dev/null 2>&1; then + setsid bash -lc "$cmd" & + else + bash -lc "$cmd" & + fi +} + +cleanup_background_jobs() { + local pid="" + for pid in "${background_pids[@]}"; do + if kill -0 "$pid" >/dev/null 2>&1; then + terminate_pid_tree "$pid" else - exit_status=$? - if [[ $exit_status -eq 0 ]]; then + if wait "$pid"; then echo "Background task $pid already exited with zero status." else + local exit_status=$? echo "Background task $pid exited with nonzero status ($exit_status)." failed_pids+=("$pid") fi fi done +} +cleanup_virtualenv() { if [[ "$cleanup" == "true" ]]; then - echo "→ Removing $venv"; rm -rf "$venv" + echo "→ Removing $venv" + rm -rf "$venv" fi +} + +on_exit() { + if [[ "$cleanup_done" == "true" ]]; then + return 0 + fi + cleanup_done=true + set +e + cleanup_background_jobs + cleanup_virtualenv if [[ ${#failed_pids[@]} -gt 0 ]]; then echo "The following background processes exited with nonzero status: ${failed_pids[@]}" - exit 1 + return 1 fi - ' EXIT + return 0 +} + +trap on_exit EXIT while [[ $# -gt 0 ]]; do case $1 in @@ -67,7 +144,8 @@ while [[ $# -gt 0 ]]; do ;; -b|--background) echo "→ Launching background task: $2" - $2 & + launch_background_command "$2" + background_pids+=("$!") echo "... started with PID: $!" sleep 5 shift @@ -75,11 +153,7 @@ while [[ $# -gt 0 ]]; do ;; -f|--foreground) echo "→ Starting foreground task: $2" - if [[ "$2" == *.py ]] && [[ "$2" != *[[:space:]]* ]]; then - python "$2" - else - $2 - fi + run_command "$2" shift shift ;; @@ -91,3 +165,8 @@ while [[ $# -gt 0 ]]; do ;; esac done + +cleanup_status=0 +on_exit || cleanup_status=$? +trap - EXIT +exit "$cleanup_status" From 358e836c764baec56edd2193223a4cc5ce08e50a Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Tue, 24 Mar 2026 19:39:23 +0100 Subject: [PATCH 2/7] test: preserve deferred args in wheel harness --- test-wheel.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test-wheel.bash b/test-wheel.bash index c00d39d..a1d1b66 100755 --- a/test-wheel.bash +++ b/test-wheel.bash @@ -77,18 +77,18 @@ run_command() { if [[ "$cmd" == *.py ]] && [[ "$cmd" != *[[:space:]]* ]]; then python "$cmd" else - bash -lc "$cmd" + $cmd fi } launch_background_command() { local cmd="$1" if [[ "$is_windows_shell" == true ]]; then - bash -lc "$cmd" & + $cmd & elif command -v setsid >/dev/null 2>&1; then - setsid bash -lc "$cmd" & + setsid $cmd & else - bash -lc "$cmd" & + $cmd & fi } From dd51812616afbfd8563849576138d36cc8c28fc5 Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Tue, 24 Mar 2026 19:53:36 +0100 Subject: [PATCH 3/7] bash: support empty background jobs on macOS --- test-wheel.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-wheel.bash b/test-wheel.bash index a1d1b66..d7a60e2 100755 --- a/test-wheel.bash +++ b/test-wheel.bash @@ -94,7 +94,7 @@ launch_background_command() { cleanup_background_jobs() { local pid="" - for pid in "${background_pids[@]}"; do + for pid in "${background_pids[@]:+${background_pids[@]}}"; do if kill -0 "$pid" >/dev/null 2>&1; then terminate_pid_tree "$pid" else @@ -126,7 +126,7 @@ on_exit() { cleanup_virtualenv if [[ ${#failed_pids[@]} -gt 0 ]]; then - echo "The following background processes exited with nonzero status: ${failed_pids[@]}" + echo "The following background processes exited with nonzero status: ${failed_pids[@]:+${failed_pids[@]}}" return 1 fi return 0 From 4d908f2e2f6dd4931eae9847ed6d6aacaa98d452 Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Tue, 24 Mar 2026 20:58:00 +0100 Subject: [PATCH 4/7] bash: avoid waiting on taskkilled jobs --- test-wheel.bash | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/test-wheel.bash b/test-wheel.bash index d7a60e2..b519764 100755 --- a/test-wheel.bash +++ b/test-wheel.bash @@ -52,18 +52,15 @@ terminate_pid_tree() { if [[ "$is_windows_shell" == true ]] && command -v taskkill >/dev/null 2>&1; then taskkill //PID "$pid" //T //F >/dev/null 2>&1 || true - else - kill -TERM "-$pid" >/dev/null 2>&1 || kill "$pid" >/dev/null 2>&1 || true + return 0 fi + kill -TERM "-$pid" >/dev/null 2>&1 || kill "$pid" >/dev/null 2>&1 || true + local deadline=$((SECONDS + 20)) while kill -0 "$pid" >/dev/null 2>&1; do if (( SECONDS >= deadline )); then - if [[ "$is_windows_shell" == true ]] && command -v taskkill >/dev/null 2>&1; then - taskkill //PID "$pid" //T //F >/dev/null 2>&1 || true - else - kill -KILL "-$pid" >/dev/null 2>&1 || kill -9 "$pid" >/dev/null 2>&1 || true - fi + kill -KILL "-$pid" >/dev/null 2>&1 || kill -9 "$pid" >/dev/null 2>&1 || true break fi sleep 1 @@ -95,6 +92,15 @@ launch_background_command() { cleanup_background_jobs() { local pid="" for pid in "${background_pids[@]:+${background_pids[@]}}"; do + if [[ "$is_windows_shell" == true ]]; then + if kill -0 "$pid" >/dev/null 2>&1; then + terminate_pid_tree "$pid" + else + wait "$pid" 2>/dev/null || true + fi + continue + fi + if kill -0 "$pid" >/dev/null 2>&1; then terminate_pid_tree "$pid" else From f1a038cbd4044f0b24019ee2493d00d09ae010af Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Tue, 24 Mar 2026 22:01:45 +0100 Subject: [PATCH 5/7] bash: skip blocking venv cleanup on windows --- test-wheel.bash | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/test-wheel.bash b/test-wheel.bash index b519764..5063a5e 100755 --- a/test-wheel.bash +++ b/test-wheel.bash @@ -116,10 +116,21 @@ cleanup_background_jobs() { } cleanup_virtualenv() { - if [[ "$cleanup" == "true" ]]; then - echo "→ Removing $venv" - rm -rf "$venv" + if [[ "$cleanup" != "true" ]]; then + return 0 + fi + + if [[ "$is_windows_shell" == true ]]; then + # GitHub's Windows runners clean the workspace after each job anyway. + # Avoid synchronously deleting the temporary venv here: Git-Bash/MSYS can + # spend minutes tearing down a Python tree after the background services + # were killed, which turns integration tests into apparent hangs. + echo "→ Skipping synchronous removal of $venv on Windows" + return 0 fi + + echo "→ Removing $venv" + rm -rf "$venv" } on_exit() { From 03b832b35ca84958365b33fb366a0d4e7f09c9e7 Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Tue, 24 Mar 2026 22:26:18 +0100 Subject: [PATCH 6/7] bash: use shell pids for windows cleanup --- test-wheel.bash | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/test-wheel.bash b/test-wheel.bash index 5063a5e..24fd7c9 100755 --- a/test-wheel.bash +++ b/test-wheel.bash @@ -50,8 +50,22 @@ terminate_pid_tree() { return 0 fi - if [[ "$is_windows_shell" == true ]] && command -v taskkill >/dev/null 2>&1; then - taskkill //PID "$pid" //T //F >/dev/null 2>&1 || true + if [[ "$is_windows_shell" == true ]]; then + # Git-Bash/MSYS background jobs are tracked via shell PIDs. Use POSIX + # signals against that PID namespace instead of `taskkill`, which expects + # native Windows PIDs and can silently miss the spawned service. + kill -TERM "$pid" >/dev/null 2>&1 || true + + local deadline=$((SECONDS + 20)) + while kill -0 "$pid" >/dev/null 2>&1; do + if (( SECONDS >= deadline )); then + kill -KILL "$pid" >/dev/null 2>&1 || true + break + fi + sleep 1 + done + + wait "$pid" 2>/dev/null || true return 0 fi @@ -92,15 +106,6 @@ launch_background_command() { cleanup_background_jobs() { local pid="" for pid in "${background_pids[@]:+${background_pids[@]}}"; do - if [[ "$is_windows_shell" == true ]]; then - if kill -0 "$pid" >/dev/null 2>&1; then - terminate_pid_tree "$pid" - else - wait "$pid" 2>/dev/null || true - fi - continue - fi - if kill -0 "$pid" >/dev/null 2>&1; then terminate_pid_tree "$pid" else From b9f7254585fc9e7c937e5240c68caabde127698d Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Tue, 24 Mar 2026 22:39:06 +0100 Subject: [PATCH 7/7] bash: keep windows cleanup non-blocking --- test-wheel.bash | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/test-wheel.bash b/test-wheel.bash index 24fd7c9..593b55a 100755 --- a/test-wheel.bash +++ b/test-wheel.bash @@ -54,18 +54,11 @@ terminate_pid_tree() { # Git-Bash/MSYS background jobs are tracked via shell PIDs. Use POSIX # signals against that PID namespace instead of `taskkill`, which expects # native Windows PIDs and can silently miss the spawned service. + # + # Keep this non-blocking on Windows: waiting/spinning in the MSYS shell can + # itself fail with `fork: Resource temporarily unavailable` during test + # teardown. kill -TERM "$pid" >/dev/null 2>&1 || true - - local deadline=$((SECONDS + 20)) - while kill -0 "$pid" >/dev/null 2>&1; do - if (( SECONDS >= deadline )); then - kill -KILL "$pid" >/dev/null 2>&1 || true - break - fi - sleep 1 - done - - wait "$pid" 2>/dev/null || true return 0 fi @@ -106,6 +99,15 @@ launch_background_command() { cleanup_background_jobs() { local pid="" for pid in "${background_pids[@]:+${background_pids[@]}}"; do + if [[ "$is_windows_shell" == true ]]; then + if kill -0 "$pid" >/dev/null 2>&1; then + terminate_pid_tree "$pid" + else + wait "$pid" 2>/dev/null || true + fi + continue + fi + if kill -0 "$pid" >/dev/null 2>&1; then terminate_pid_tree "$pid" else