diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47ea859c5fefbb..6e3f930cf110a1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -496,6 +496,12 @@ jobs: name: hypothesis-example-db path: ${{ env.CPYTHON_BUILDDIR }}/.hypothesis/examples/ + test-lazy-imports-all: + name: 'Lazy imports enabled' + needs: build-context + if: fromJSON(needs.build-context.outputs.run-tests) + uses: ./.github/workflows/reusable-test-lazy-imports-all.yml + build-asan: name: 'Address sanitizer' runs-on: ${{ matrix.os }} @@ -669,6 +675,7 @@ jobs: - build-emscripten - build-wasi - test-hypothesis + - test-lazy-imports-all - build-asan - build-san - cross-build-linux diff --git a/.github/workflows/reusable-test-lazy-imports-all.yml b/.github/workflows/reusable-test-lazy-imports-all.yml new file mode 100644 index 00000000000000..97413be691a2ff --- /dev/null +++ b/.github/workflows/reusable-test-lazy-imports-all.yml @@ -0,0 +1,80 @@ +name: Reusable Lazy Imports Tests + +# Run the CPython test suite with global lazy imports forced on +# (``-X lazy_imports=all``). +# +# Modules that are known to fail under lazy imports are listed in +# Lib/test/lazy_imports_all_exclude.txt and skipped here. Remove entries from +# that file as the modules are fixed so this workflow starts guarding them +# against regressions. Excluded modules are also checked separately so the +# workflow fails when one starts passing and its exclusion should be removed. + +on: + workflow_call: + +permissions: + contents: read + +env: + FORCE_COLOR: 1 + +jobs: + test-lazy-imports-all: + name: 'Run Tests with lazy_imports=all' + runs-on: ubuntu-24.04 + timeout-minutes: 60 + env: + EXCLUDE_FILE: Lib/test/lazy_imports_all_exclude.txt + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Register gcc problem matcher + run: echo "::add-matcher::.github/problem-matchers/gcc.json" + - name: Install dependencies + run: sudo ./.github/workflows/posix-deps-apt.sh + - name: Configure CPython + run: ./configure --config-cache --with-pydebug + - name: Build CPython + run: make -j4 + - name: Display build info + run: make pythoninfo + - name: Verify lazy imports are fully enabled + run: ./python -X lazy_imports=all -c "import sys; assert sys.flags.lazy_imports == 1, sys.flags.lazy_imports; print('lazy imports all enabled')" + - name: Build test list (all tests minus the known-failing exclusions) + run: | + set -euo pipefail + ./python -m test --list-tests > all_tests.txt + # Strip comments/blank lines from the exclusion file, then drop those + # exact test names (whole-line, fixed-string match) from the run list. + grep -vE '^\s*(#.*)?$' "$EXCLUDE_FILE" > exclude_tests.txt + grep -vxF -f exclude_tests.txt all_tests.txt > run_tests.txt + # Fail loudly if any exclusion entry matched nothing: a stale or + # mistyped name (or a change in `--list-tests` output) would otherwise + # silently stop excluding a module and let it fail the run. + stale=$(comm -23 <(sort -u exclude_tests.txt) <(sort -u all_tests.txt)) + if [ -n "$stale" ]; then + echo "::error::Stale entries in $EXCLUDE_FILE (no longer match 'python -m test --list-tests'); remove or fix them:" + echo "$stale" + exit 1 + fi + echo "Excluding $(wc -l < exclude_tests.txt) module(s); running $(wc -l < run_tests.txt) of $(wc -l < all_tests.txt)." + - name: Run tests with lazy imports + run: xvfb-run xargs ./python -X lazy_imports=all -m test --fast-ci --timeout=900 < run_tests.txt + - name: Verify excluded tests still need exclusion + if: success() + run: | + set -euo pipefail + unexpected_passes=() + while IFS= read -r test_name; do + [ -n "$test_name" ] || continue + echo "Checking excluded test: $test_name" + if xvfb-run ./python -X lazy_imports=all -m test --fast-ci --timeout=900 "$test_name"; then + unexpected_passes+=("$test_name") + fi + done < exclude_tests.txt + if [ "${#unexpected_passes[@]}" -ne 0 ]; then + echo "::error::These tests still appear in $EXCLUDE_FILE but now pass with -X lazy_imports=all. Remove them from the exclude file:" + printf '%s\n' "${unexpected_passes[@]}" + exit 1 + fi diff --git a/Lib/test/lazy_imports_all_exclude.txt b/Lib/test/lazy_imports_all_exclude.txt new file mode 100644 index 00000000000000..4a169bc089819a --- /dev/null +++ b/Lib/test/lazy_imports_all_exclude.txt @@ -0,0 +1,42 @@ +# Test modules that currently FAIL under global lazy imports +# (``-X lazy_imports=all`` / ``PYTHON_LAZY_IMPORTS=all``). +# +# The "Lazy Imports All" CI workflow +# (.github/workflows/reusable-test-lazy-imports-all.yml) runs the whole test +# suite with lazy_imports=all, skipping every module listed here. Exclusion is +# whole-module: a listed module is skipped entirely, so any passing tests it +# contains are not covered until its line is removed. As each module is fixed, +# delete its line so the workflow starts guarding it against regressions. The +# workflow also checks listed modules separately and fails if one now passes, +# so accidental fixes prompt cleanup of this file. +# +# Format: one test name per line, exactly as printed by +# ``python -m test --list-tests``. Lines starting with ``#`` and blank lines +# are ignored. Note that split test packages use a dotted path +# (e.g. test.test_future_stmt.test_future) while ordinary modules use the bare +# name (e.g. test_builtin). + +test.test_inspect.test_inspect +test___all__ +test__interpreters +test_builtin +test_clinic +test_crossinterp +test_datetime +test_generated_cases +test_heapq +test_idle +test_import +test_importlib +test_json +test_pkg +test_profile +test_profiling +test_pyrepl +test_subprocess +test_symtable +test_tools +test_trace +test_type_annotations +test_unittest +test_zoneinfo