Skip to content

docs

docs #100

Workflow file for this run

name: Quality
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
push:
branches:
- main
- release/*
workflow_call:
inputs:
static_analysis_only:
description: Skip the pytest matrix after static analysis.
required: false
type: boolean
default: false
coverage:
description: Run the Python 3.12 tests under coverage and publish coverage results.
required: false
type: boolean
default: false
env:
X2PY_GFORTRAN_BINARY: gfortran-13
X2PY_GFORTRAN_PACKAGE: gfortran-13
X2PY_REAL_LIBRARY_NATIVE_CACHE_PATH: .pytest_cache/x2py/real-library-native
jobs:
static-analysis:
name: Static Analysis
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install QA dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[qa]"
- name: Verify static-analysis versions
run: python tools/check_static_analysis_versions.py
- name: Ruff lint
run: python -m ruff check .
- name: Ruff format
run: python -m ruff format --check .
- name: Bandit security scan
run: python -m bandit -c pyproject.toml -r c_parser fortran_parser semantics x2py --severity-level medium --confidence-level medium
- name: Vulture dead-code scan
run: python -m vulture
- name: Radon complexity policy
env:
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PUSH_BEFORE_SHA: ${{ github.event.before }}
run: python tools/check_radon_policy.py --base-ref auto
- name: Radon complexity report
continue-on-error: true
run: python -m radon cc c_parser fortran_parser semantics x2py -n C -s --total-average
- name: Radon maintainability report
continue-on-error: true
run: python -m radon mi c_parser fortran_parser semantics x2py -s
real-library-native-cache:
name: Real-Library Native Cache
if: ${{ !inputs.static_analysis_only }}
needs: static-analysis
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install test dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[qa]"
- name: Install pinned GFortran
shell: bash
run: |
if ! command -v "$X2PY_GFORTRAN_BINARY" >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install --yes "$X2PY_GFORTRAN_PACKAGE"
fi
compiler_dir="$RUNNER_TEMP/x2py-gfortran"
mkdir -p "$compiler_dir"
ln -sf "$(command -v "$X2PY_GFORTRAN_BINARY")" "$compiler_dir/gfortran"
echo "$compiler_dir" >> "$GITHUB_PATH"
"$compiler_dir/gfortran" --version
- name: Capture native cache key facts
id: native-cache-facts
shell: bash
run: |
echo "gfortran_hash=$(gfortran --version | head -n 1 | sha256sum | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- name: Restore real-library native cache
id: restore-real-library-native-cache
uses: actions/cache/restore@v4
with:
path: ${{ env.X2PY_REAL_LIBRARY_NATIVE_CACHE_PATH }}
key: real-library-native-${{ runner.os }}-${{ runner.arch }}-${{ steps.native-cache-facts.outputs.gfortran_hash }}-${{ hashFiles('tests/data/fortran/blas/**', 'tests/data/fortran/lapack/**', 'tests/wrapper/fortran/real_libraries/test_real_blas_lapack.py', 'tools/warm_real_library_native_cache.py') }}
restore-keys: |
real-library-native-${{ runner.os }}-${{ runner.arch }}-${{ steps.native-cache-facts.outputs.gfortran_hash }}-
- name: Warm real-library native cache
env:
PYTHONPATH: .
X2PY_REAL_LIBRARY_NATIVE_CACHE_DIR: ${{ github.workspace }}/${{ env.X2PY_REAL_LIBRARY_NATIVE_CACHE_PATH }}
run: python tools/warm_real_library_native_cache.py
- name: Save real-library native cache
if: steps.restore-real-library-native-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ env.X2PY_REAL_LIBRARY_NATIVE_CACHE_PATH }}
key: real-library-native-${{ runner.os }}-${{ runner.arch }}-${{ steps.native-cache-facts.outputs.gfortran_hash }}-${{ hashFiles('tests/data/fortran/blas/**', 'tests/data/fortran/lapack/**', 'tests/wrapper/fortran/real_libraries/test_real_blas_lapack.py', 'tools/warm_real_library_native_cache.py') }}
test:
name: Tests (Python ${{ matrix.python-version }})
if: ${{ !inputs.static_analysis_only }}
needs: [static-analysis, real-library-native-cache]
runs-on: ubuntu-24.04
permissions:
contents: read
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install test dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[qa]"
- name: Install pinned GFortran
shell: bash
run: |
if ! command -v "$X2PY_GFORTRAN_BINARY" >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install --yes "$X2PY_GFORTRAN_PACKAGE"
fi
compiler_dir="$RUNNER_TEMP/x2py-gfortran"
mkdir -p "$compiler_dir"
ln -sf "$(command -v "$X2PY_GFORTRAN_BINARY")" "$compiler_dir/gfortran"
echo "$compiler_dir" >> "$GITHUB_PATH"
"$compiler_dir/gfortran" --version
- name: Capture native cache key facts
id: native-cache-facts
shell: bash
run: |
echo "gfortran_hash=$(gfortran --version | head -n 1 | sha256sum | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- name: Restore real-library native cache
uses: actions/cache/restore@v4
with:
path: ${{ env.X2PY_REAL_LIBRARY_NATIVE_CACHE_PATH }}
key: real-library-native-${{ runner.os }}-${{ runner.arch }}-${{ steps.native-cache-facts.outputs.gfortran_hash }}-${{ hashFiles('tests/data/fortran/blas/**', 'tests/data/fortran/lapack/**', 'tests/wrapper/fortran/real_libraries/test_real_blas_lapack.py', 'tools/warm_real_library_native_cache.py') }}
restore-keys: |
real-library-native-${{ runner.os }}-${{ runner.arch }}-${{ steps.native-cache-facts.outputs.gfortran_hash }}-
- name: Run tests
shell: bash
env:
PYTHONPATH: .
HYPOTHESIS_PROFILE: ci
X2PY_COVERAGE_REQUESTED: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/main') || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'run-coverage')) || (github.event_name == 'workflow_call' && inputs.coverage) }}
X2PY_REAL_LIBRARY_NATIVE_CACHE_DIR: ${{ github.workspace }}/${{ env.X2PY_REAL_LIBRARY_NATIVE_CACHE_PATH }}
run: |
if [[ "${{ matrix.python-version }}" == "3.12" ]]; then
test_paths=(tests)
else
test_paths=(
tests/parser
tests/property
tests/semantics
tests/pyi
tests/tools
tests/benchmarks
tests/test_naming_policy.py
tests/wrapper/fortran/arrays
tests/wrapper/fortran/build_from_pyi
tests/wrapper/fortran/build_from_source
tests/wrapper/fortran/callbacks
tests/wrapper/fortran/derived_types
tests/wrapper/fortran/edit_pyi_contracts
tests/wrapper/fortran/external_routines
tests/wrapper/fortran/function_calls
tests/wrapper/fortran/layout_rules
tests/wrapper/fortran/module_state
tests/wrapper/fortran/multiple_files
tests/wrapper/fortran/naming
tests/wrapper/fortran/runtime_behavior
tests/wrapper/fortran/scalars
tests/wrapper/fortran/strings
"tests/wrapper/fortran/real_libraries/test_real_blas_lapack.py::test_full_library_wrapper_imports_every_root_procedure_from_cached_shared_library[blas]"
tests/wrapper/fortran/real_libraries/test_stage7_native_bundles.py
)
fi
if [[ "${{ matrix.python-version }}" == "3.12" && "$X2PY_COVERAGE_REQUESTED" == "true" ]]; then
COVERAGE_PROCESS_START="${{ github.workspace }}/pyproject.toml" \
python -m coverage run -m pytest -q --randomly-seed=1 "${test_paths[@]}"
else
python -m pytest -q --randomly-seed=1 "${test_paths[@]}"
fi
- name: Upload coverage data
if: matrix.python-version == '3.12' && ((github.event_name == 'push' && github.ref == 'refs/heads/main') || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'run-coverage')) || (github.event_name == 'workflow_call' && inputs.coverage))
uses: actions/upload-artifact@v4
with:
name: coverage-py312
path: .coverage.*
include-hidden-files: true
if-no-files-found: error
coverage-report:
name: Coverage Report
if: ${{ !inputs.static_analysis_only && ((github.event_name == 'push' && github.ref == 'refs/heads/main') || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'run-coverage')) || (github.event_name == 'workflow_call' && inputs.coverage)) }}
needs: test
runs-on: ubuntu-24.04
permissions:
contents: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install coverage dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[qa]"
- name: Download coverage data
uses: actions/download-artifact@v4
with:
pattern: coverage-*
path: coverage-artifacts
merge-multiple: true
- name: Combine coverage data
run: python -m coverage combine coverage-artifacts
- name: Report coverage
run: python -m coverage report
- name: Emit coverage XML
run: python -m coverage xml -o coverage.xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v6
with:
files: ./coverage.xml
flags: py312
use_oidc: true
fail_ci_if_error: true
# Benchmark workflow is intentionally parked for later activation.
# benchmark:
# if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
# runs-on: ubuntu-latest
# timeout-minutes: 15
# permissions:
# contents: read
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4
# - name: Set up Python
# uses: actions/setup-python@v5
# with:
# python-version: "3.12"
# - name: Install QA dependencies
# run: |
# python -m pip install --upgrade pip
# python -m pip install -e ".[qa]"
# - name: Run parser benchmarks
# env:
# PYTHONPATH: .
# run: python -m pytest -q tests/benchmarks -m benchmark --benchmark-only --benchmark-json=benchmark.json
# - name: Upload benchmark report
# uses: actions/upload-artifact@v4
# with:
# name: parser-benchmark-${{ github.run_id }}
# path: benchmark.json
# retention-days: 90