fix: Quantity repr/format crashes and wrong format-spec semantics #411
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # This workflow will install Python dependencies, run tests and lint with a variety of Python versions | |
| # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions | |
| name: Continuous Integration | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| permissions: | |
| contents: read # to fetch code | |
| actions: write # to cancel previous workflows | |
| # This is what will cancel the workflow | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| type_check: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: [ "3.13" ] | |
| steps: | |
| - name: Cancel Previous Runs | |
| uses: styfle/cancel-workflow-action@0.13.1 | |
| with: | |
| access_token: ${{ github.token }} | |
| - uses: actions/checkout@v6 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install dependencies | |
| run: | | |
| python -m pip cache purge | |
| python -m pip install --upgrade pip setuptools --no-cache-dir | |
| python -m pip install -r requirements-dev.txt --no-cache-dir | |
| python -m pip install 'mypy>=1.11,<3' --no-cache-dir | |
| pip install . --no-cache-dir | |
| - name: Run mypy | |
| run: | | |
| mypy saiunit/ | |
| test_no_jax: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: [ "3.13" ] | |
| steps: | |
| - name: Cancel Previous Runs | |
| uses: styfle/cancel-workflow-action@0.13.1 | |
| with: | |
| access_token: ${{ github.token }} | |
| - uses: actions/checkout@v6 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install dependencies (without JAX) | |
| run: | | |
| python -m pip cache purge | |
| python -m pip install --upgrade pip setuptools --no-cache-dir | |
| python -m pip install -r requirements.txt --no-cache-dir | |
| python -m pip install pytest --no-cache-dir | |
| pip install . --no-cache-dir | |
| - name: Verify JAX is not installed | |
| run: | | |
| python -c "import importlib.util, sys; assert importlib.util.find_spec('jax') is None, 'jax should not be installed'; print('OK: JAX is not installed')" | |
| - name: Verify top-level import works | |
| run: | | |
| python -c "import saiunit as u; print('saiunit', u.__version__); q = u.Quantity(3.0, u.meter); print('Quantity:', q)" | |
| - name: Verify JAX-only submodules raise BackendError | |
| run: | | |
| python -c "import saiunit as u; from saiunit._exceptions import BackendError | |
| for name in ('autograd', 'lax', 'sparse'): | |
| try: | |
| getattr(u, name) | |
| except BackendError as e: | |
| print(f'OK: u.{name} raised BackendError: {e}') | |
| else: | |
| raise SystemExit(f'FAIL: u.{name} should have raised BackendError')" | |
| - name: Run no-jax smoke tests | |
| run: | | |
| pytest saiunit/_no_jax_test.py -v | |
| # --------------------------------------------------------------------------- | |
| # Per-backend isolation jobs. | |
| # | |
| # Each job below installs exactly ONE array backend on top of the base | |
| # `requirements.txt` (numpy + array_api_compat) and runs the full test | |
| # suite with that backend set as the saiunit default via the | |
| # `SAIUNIT_DEFAULT_BACKEND` env var (read in conftest.py). | |
| # | |
| # Goal: prove that `pip install saiunit[<backend>]` works end-to-end and | |
| # that backend-specific kwargs / hidden cross-backend dependencies cannot | |
| # leak through saiunit's public API. | |
| # | |
| # The previous ``test_linux`` / ``test_macos`` / ``test_windows`` "install | |
| # everything" matrix was removed: ``test_pure_jax`` is the same workload on | |
| # Linux (saiunit's primary support target), and the per-backend isolation | |
| # jobs catch the cross-backend bugs the multi-backend matrix could not. | |
| # | |
| # cupy is intentionally not included: GitHub free runners have no GPU. | |
| # --------------------------------------------------------------------------- | |
| test_pure_numpy: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: [ "3.13" ] | |
| env: | |
| SAIUNIT_DEFAULT_BACKEND: numpy | |
| steps: | |
| - name: Cancel Previous Runs | |
| uses: styfle/cancel-workflow-action@0.13.1 | |
| with: | |
| access_token: ${{ github.token }} | |
| - uses: actions/checkout@v6 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install (numpy backend only) | |
| run: | | |
| python -m pip cache purge | |
| python -m pip install --upgrade pip setuptools --no-cache-dir | |
| python -m pip install -r requirements.txt --no-cache-dir | |
| python -m pip install pytest --no-cache-dir | |
| python -m pip install matplotlib --no-cache-dir | |
| pip install . --no-cache-dir | |
| - name: Verify only numpy backend is installed | |
| run: | | |
| python -c "import importlib.util as u | |
| for name in ('jax', 'torch', 'cupy', 'dask', 'ndonnx'): | |
| assert u.find_spec(name) is None, f'{name} should not be installed in pure-numpy job' | |
| print('OK: only numpy backend installed')" | |
| - name: Test with pytest | |
| run: | | |
| pytest saiunit/ | |
| test_pure_jax: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: [ "3.13" ] | |
| env: | |
| SAIUNIT_DEFAULT_BACKEND: jax | |
| steps: | |
| - name: Cancel Previous Runs | |
| uses: styfle/cancel-workflow-action@0.13.1 | |
| with: | |
| access_token: ${{ github.token }} | |
| - uses: actions/checkout@v6 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install (jax backend only) | |
| run: | | |
| python -m pip cache purge | |
| python -m pip install --upgrade pip setuptools --no-cache-dir | |
| python -m pip install -r requirements.txt --no-cache-dir | |
| python -m pip install pytest --no-cache-dir | |
| python -m pip install jax --no-cache-dir | |
| # Test-only deps used by the JAX-touching test files | |
| # (saiunit/fft/, saiunit/lax/, saiunit/linalg/, saiunit/math/, | |
| # saiunit/sparse/, custom_array_test.py). Neither pulls in | |
| # another array backend. | |
| python -m pip install absl-py brainstate --no-cache-dir | |
| pip install . --no-cache-dir | |
| # Replace the brainstate-pulled PyPI brainunit with the local one, | |
| # so it tracks the in-tree saiunit (PyPI brainunit may import names | |
| # this PR has removed). | |
| python make_brainunit_setup.py | |
| pip install ./brainunit --force-reinstall --no-deps --no-cache-dir | |
| - name: Verify only jax backend is installed | |
| run: | | |
| python -c "import importlib.util as u | |
| for name in ('torch', 'cupy', 'dask', 'ndonnx'): | |
| assert u.find_spec(name) is None, f'{name} should not be installed in pure-jax job' | |
| assert u.find_spec('jax') is not None, 'jax must be installed' | |
| print('OK: only jax backend installed')" | |
| - name: Test with pytest | |
| run: | | |
| pytest saiunit/ | |
| test_pure_torch: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: [ "3.13" ] | |
| env: | |
| SAIUNIT_DEFAULT_BACKEND: torch | |
| steps: | |
| - name: Cancel Previous Runs | |
| uses: styfle/cancel-workflow-action@0.13.1 | |
| with: | |
| access_token: ${{ github.token }} | |
| - uses: actions/checkout@v6 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install (torch backend only, CPU wheel) | |
| run: | | |
| python -m pip cache purge | |
| python -m pip install --upgrade pip setuptools --no-cache-dir | |
| python -m pip install -r requirements.txt --no-cache-dir | |
| python -m pip install pytest --no-cache-dir | |
| pip install torch --index-url https://download.pytorch.org/whl/cpu --no-cache-dir | |
| pip install . --no-cache-dir | |
| - name: Verify only torch backend is installed | |
| run: | | |
| python -c "import importlib.util as u | |
| for name in ('jax', 'cupy', 'dask', 'ndonnx'): | |
| assert u.find_spec(name) is None, f'{name} should not be installed in pure-torch job' | |
| assert u.find_spec('torch') is not None, 'torch must be installed' | |
| print('OK: only torch backend installed')" | |
| - name: Test with pytest | |
| run: | | |
| pytest saiunit/ | |
| test_pure_dask: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: [ "3.13" ] | |
| env: | |
| SAIUNIT_DEFAULT_BACKEND: dask | |
| steps: | |
| - name: Cancel Previous Runs | |
| uses: styfle/cancel-workflow-action@0.13.1 | |
| with: | |
| access_token: ${{ github.token }} | |
| - uses: actions/checkout@v6 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install (dask backend only) | |
| run: | | |
| python -m pip cache purge | |
| python -m pip install --upgrade pip setuptools --no-cache-dir | |
| python -m pip install -r requirements.txt --no-cache-dir | |
| python -m pip install pytest --no-cache-dir | |
| pip install 'dask[array]' --no-cache-dir | |
| pip install . --no-cache-dir | |
| - name: Verify only dask backend is installed | |
| run: | | |
| python -c "import importlib.util as u | |
| for name in ('jax', 'torch', 'cupy', 'ndonnx'): | |
| assert u.find_spec(name) is None, f'{name} should not be installed in pure-dask job' | |
| assert u.find_spec('dask.array') is not None, 'dask.array must be installed' | |
| print('OK: only dask backend installed')" | |
| - name: Test with pytest | |
| run: | | |
| pytest saiunit/ | |
| test_pure_ndonnx: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: [ "3.13" ] | |
| env: | |
| SAIUNIT_DEFAULT_BACKEND: ndonnx | |
| steps: | |
| - name: Cancel Previous Runs | |
| uses: styfle/cancel-workflow-action@0.13.1 | |
| with: | |
| access_token: ${{ github.token }} | |
| - uses: actions/checkout@v6 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install (ndonnx backend only) | |
| run: | | |
| python -m pip cache purge | |
| python -m pip install --upgrade pip setuptools --no-cache-dir | |
| python -m pip install -r requirements.txt --no-cache-dir | |
| python -m pip install pytest --no-cache-dir | |
| pip install ndonnx --no-cache-dir | |
| pip install . --no-cache-dir | |
| - name: Verify only ndonnx backend is installed | |
| run: | | |
| python -c "import importlib.util as u | |
| for name in ('jax', 'torch', 'cupy', 'dask'): | |
| assert u.find_spec(name) is None, f'{name} should not be installed in pure-ndonnx job' | |
| assert u.find_spec('ndonnx') is not None, 'ndonnx must be installed' | |
| print('OK: only ndonnx backend installed')" | |
| - name: Test with pytest | |
| run: | | |
| pytest saiunit/ |