Skip to content

sharrow testing

sharrow testing #379

Workflow file for this run

name: sharrow testing
on:
push:
branches: [ main, develop ]
tags:
- 'v[0-9]+.[0-9]+**'
pull_request:
branches: [ main, develop ]
workflow_dispatch:
inputs:
deploy_docs:
description: Build and deploy the documentation without publishing to PyPI
required: true
default: false
type: boolean
jobs:
fmt:
name: formatting quality
runs-on: ubuntu-latest
defaults:
run:
shell: bash -l {0}
steps:
- uses: actions/checkout@v4
- name: Code Quality Check with Ruff
# code quality check, stop the build for any errors
uses: astral-sh/ruff-action@v3
with:
version: "latest"
args: "check . --show-fixes --exit-non-zero-on-fix"
test-minimal:
needs: fmt
name: minimal tests
runs-on: ubuntu-latest
defaults:
run:
shell: bash -l {0}
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v7
with:
version: "0.9.6"
enable-cache: true
cache-dependency-glob: "uv.lock"
python-version: '3.11'
- name: Initial simple tests
# tests that sharrow can be imported and that categorical tests can be run
run: |
uv run pytest sharrow/tests/test_categorical.py
- name: Dataset tests
# tests that the datasets can be read and that the tests can be run
run: |
uv run pytest sharrow/tests/test_datasets.py
- name: More complete test with pytest
run: |
uv run pytest -v --disable-warnings sharrow/tests
test:
needs: fmt
name: ${{ matrix.os }} py${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: ["ubuntu-latest", "windows-latest"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
fail-fast: false
defaults:
run:
shell: bash -l {0}
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v7
with:
version: "0.9.6"
python-version: ${{ matrix.python-version }}
- name: File contents
run: |
cat sharrow/example_data.py
- name: UV sync
run: |
uv self version
uv cache clean
uv sync --locked
- name: Syntax Check with Ruff
uses: astral-sh/ruff-action@v3
with:
args: "check . --select=E9,F63,F7,F82 --no-fix"
- name: Code Quality Check with Ruff
uses: astral-sh/ruff-action@v3
with:
args: "check . --show-fixes --exit-non-zero-on-fix"
- name: Test with pytest
run: |
uv run --locked pytest
deploy-docs:
needs: test
# Run for releases or when explicitly requested from the Actions UI.
if: >-
startsWith(github.ref, 'refs/tags/v') ||
(github.event_name == 'workflow_dispatch' && inputs.deploy_docs)
runs-on: ubuntu-latest
environment: asim
defaults:
run:
shell: bash -l {0}
steps:
- uses: actions/checkout@v4
- name: Install uv and set Python 3.10
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
version: "0.12.2"
python-version: "3.10"
enable-cache: true
- name: Install dependencies
run: |
uv sync --locked --dev
- name: UV checkup
run: |
uv --version
uv run --locked python --version
uv pip list
- name: Build wheel
run: |
uv build
- name: Build the docs
run: |
uv run --locked --with-requirements docs/requirements.txt python docs/_script/run_all.py
uv run --locked --with-requirements docs/requirements.txt jupyter-book build ./docs
- name: Push to GitHub Pages
uses: peaceiris/actions-gh-pages@v3.5.9
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# Token is created automatically by Github Actions, no other config needed
publish_dir: ./docs/_build/html
# now send to PyPI
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
if: >-
github.event_name == 'push' &&
github.repository_owner == 'ActivitySim' &&
startsWith(github.ref, 'refs/tags/v')
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
activitysim-examples:
# test that updates to sharrow will not break the activitysim canonical examples
needs: fmt
env:
python-version: "3.10"
label: linux-64
# limit glibc malloc arenas, which can otherwise retain large amounts
# of memory freed during numba compilation
MALLOC_ARENA_MAX: "2"
# standard GitHub-hosted ubuntu runners have 4 cores
NUMBA_NUM_THREADS: "4"
strategy:
matrix:
include:
- region: ActivitySim 1-Zone Example (MTC)
region-org: ActivitySim
region-repo: activitysim-prototype-mtc
region-branch: extended
- region: ActivitySim 2-Zone Example (SANDAG)
region-org: ActivitySim
region-repo: sandag-abm3-example
region-branch: main
fail-fast: false
defaults:
run:
shell: bash -l {0}
name: ${{ matrix.region }}
runs-on: ubuntu-latest
timeout-minutes: 720 # Sets the timeout to 12 hours
steps:
- name: Checkout Sharrow
uses: actions/checkout@v4
with:
path: 'sharrow'
- name: Checkout ActivitySim
uses: actions/checkout@v4
with:
repository: 'ActivitySim/activitysim'
ref: 'main'
path: 'activitysim'
- name: Setup UV
uses: astral-sh/setup-uv@v7
with:
version: "0.9.6"
enable-cache: true
cache-dependency-glob: "uv.lock"
python-version: ${{ env.python-version }}
- name: Set cache date for year and month
run: echo "DATE=$(date +'%Y%m')" >> $GITHUB_ENV
- name: Add swap space
# numba compilation of sharrow flows has a large transient memory
# peak; extra swap absorbs the spike instead of the runner being
# terminated by the host when it exhausts physical memory
run: |
sudo swapon --show || true
df -h /mnt
sudo fallocate -l 8G /mnt/extra-swapfile
sudo chmod 600 /mnt/extra-swapfile
sudo mkswap /mnt/extra-swapfile
sudo swapon /mnt/extra-swapfile
free -h
- name: Restore sharrow flow cache
# restores previously compiled sharrow flows (including numba on-disk
# cache files), which greatly reduces both the time and the peak
# memory needed to run the examples
uses: actions/cache/restore@v4
with:
path: ~/.cache/ActivitySim
key: sharrow-flows-${{ matrix.region-repo }}-${{ env.DATE }}-${{ github.run_id }}
restore-keys: |
sharrow-flows-${{ matrix.region-repo }}-${{ env.DATE }}-
sharrow-flows-${{ matrix.region-repo }}-
- name: Create Virtual Env
run: |
uv venv
source .venv/bin/activate
uv pip install "black==22.12.0" "coveralls>=4" \
"cytoolz==0.12.2" "dask>=2026" "isort>=5.12.0" \
"multimethod<2.0" "nbmake>=1.4.6" "numba>=0.57" \
"numpy>=2,<3" "openmatrix==0.3.5.0" \
"pandera>=0.30" "pandas>=2,<3" "platformdirs>=3.2" \
"psutil>=5.9" "pyarrow>=11.0" "pydantic>=2.6,<3" "pypyr>=5.8" \
"tables>=3.9" "pytest>=7.2" "pytest-cov" "pytest-regressions" \
"pyyaml>=6" "requests>=2.28" "ruff" "scikit-learn>=1.2" \
"simwrapper>1.7" "sparse" "xarray>=2025" \
"zarr>=2,<3" "zstandard" \
./sharrow ./activitysim
- name: UV checkup
run: |
source .venv/bin/activate
uv pip list
- name: Checkout Example
uses: actions/checkout@v4
with:
repository: '${{ matrix.region-org }}/${{ matrix.region-repo }}'
ref: '${{ matrix.region-branch }}'
path: '${{ matrix.region-repo }}'
- name: Test ${{ matrix.region }}
# Each collected test runs in its own process, so memory is fully
# released between tests. A watchdog stops a test gracefully when
# system memory (incl. swap) is nearly exhausted: a controlled
# failure (unlike a runner shutdown) lets later steps still run, in
# particular saving the sharrow flow cache so a subsequent attempt
# resumes from the already-compiled flows instead of starting over.
run: |
source .venv/bin/activate
cd ${{ matrix.region-repo }}
mem_line() {
free -m | awk '/^Mem:/ {u=$3; a=$7} /^Swap:/ {s=$4} END {print "used_MB=" u " avail_MB=" a " swap_free_MB=" s}'
}
free_total() {
free -m | awk '/^Mem:/ {a=$7} /^Swap:/ {s=$4} END {print a + s}'
}
# stream memory samples into the live log so the data survives
# even if the runner is terminated abruptly
( while true; do echo "[mem] $(date +%H:%M:%S) $(mem_line)" | tee -a /tmp/mem-monitor.log; sleep 30; done ) &
monitor_pid=$!
readarray -t tests < <(python -m pytest ./test --collect-only -q | grep '::')
echo "collected ${#tests[@]} tests"
rc=0
for t in "${tests[@]}"; do
echo "::group::pytest $t"
python -m pytest "$t" &
test_pid=$!
while kill -0 "$test_pid" 2>/dev/null; do
if [ "$(free_total)" -lt 1024 ]; then
echo "[watchdog] $(date +%H:%M:%S) memory nearly exhausted ($(mem_line)); stopping test"
kill "$test_pid" 2>/dev/null || true
sleep 30
fi
sleep 5
done
if wait "$test_pid"; then
echo "[ok] $t"
else
echo "[failed] $t (exit code $?)"
rc=1
fi
echo "::endgroup::"
done
kill "$monitor_pid" 2>/dev/null || true
exit $rc
- name: Save sharrow flow cache
# saved even when the tests fail, so that flows compiled during a
# failed run still warm the cache for subsequent runs
if: always()
uses: actions/cache/save@v4
with:
path: ~/.cache/ActivitySim
key: sharrow-flows-${{ matrix.region-repo }}-${{ env.DATE }}-${{ github.run_id }}
- name: Memory monitor report
if: always()
run: |
echo "== peak system memory usage =="
awk '{ for (i=1; i<=NF; i++) if ($i ~ /^used_MB=/) { v=substr($i,9)+0; if (v>max) max=v } }
END { print "peak used_MB=" max }' /tmp/mem-monitor.log || true