diff --git a/.github/actions/install-deps/action.yml b/.github/actions/install-deps/action.yml new file mode 100644 index 00000000..fadc7f4a --- /dev/null +++ b/.github/actions/install-deps/action.yml @@ -0,0 +1,43 @@ +name: Install Dependencies +description: Install package dependencies using uv + +inputs: + options: + # We can't directly provide the empty string for no options because it would be mapped to the + # default value thanks to github's weird logic (https://github.com/actions/toolkit/issues/940) + description: 'Package options to install (space-separated, e.g., "full"). Use "none" for no options.' + required: false + default: 'full' + groups: + description: 'Dependency groups to install (space-separated, e.g., "test doc")' + required: false + default: '' + +runs: + using: composite + steps: + - name: Install dependencies (options=[${{ inputs.options }}], groups=[${{ inputs.groups }}]) + shell: bash + run: | + # Map "none" to the empty string + options_input="${{ inputs.options }}" + if [ "$options_input" = "none" ]; then + options_input="" + fi + + # Build the install command + cmd="uv pip install -e" + + # Convert space-separated options to comma-separated for brackets + # E.g. "abc def" => '.[abc,def]' + options=$(echo "$options_input" | tr ' ' ',') + cmd="$cmd '.[$options]'" + + # Add groups + for group in ${{ inputs.groups }}; do + cmd="$cmd --group $group" + done + + # Print and execute the command + echo $cmd + eval $cmd diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 891e42f5..337b143b 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -8,7 +8,6 @@ on: env: UV_NO_SYNC: 1 - PYTHON_VERSION: '3.14' jobs: build-deploy-doc: @@ -24,10 +23,11 @@ jobs: - name: Set up uv uses: astral-sh/setup-uv@v5 with: - python-version: ${{ env.PYTHON_VERSION }} + python-version: '3.14' - - name: Install dependencies (default with full options & doc) - run: uv pip install --python-version=${{ env.PYTHON_VERSION }} -e '.[full]' --group doc + - uses: ./.github/actions/install-deps + with: + groups: doc - name: Determine deployment folder id: deploy_folder diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 282ac671..02cc877d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,9 +4,6 @@ on: release: types: [published] -env: - PYTHON_VERSION: 3.14 - jobs: pypi-publish: name: Publish to PyPI @@ -22,7 +19,7 @@ jobs: - name: Set up uv uses: astral-sh/setup-uv@v5 with: - python-version: ${{ env.PYTHON_VERSION }} + python-version: '3.14' - name: Build run: uv build diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ee628ee3..6b604fa3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,72 +8,52 @@ on: env: UV_NO_SYNC: 1 - PYTHON_VERSION: 3.14 jobs: - tests-full-install: - name: Run tests with full install - runs-on: ${{ matrix.os }} + tests: + # Default config: py3.14, ubuntu-latest, float32, full options. + # The idea is to make each of those params vary one by one, to limit the number of tests to run. + name: Run tests (py${{ matrix.python-version || '3.14' }}, ${{ matrix.os || 'ubuntu-latest' }}, ${{ matrix.dtype || 'float32' }}, ${{ matrix.options || 'full' }}) + runs-on: ${{ matrix.os || 'ubuntu-latest' }} strategy: - fail-fast: false # Ensure matrix jobs keep running even if one fails + fail-fast: false matrix: - python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] - os: [ubuntu-latest, macOS-latest, windows-latest] + include: + # Python version variations + - python-version: '3.10' + - python-version: '3.11' + - python-version: '3.12' + - python-version: '3.13' + - python-version: '3.14' # To actually run the default config + # OS variations + - os: macOS-latest + - os: windows-latest + # dtype variations + - dtype: float64 + # Installation options variations + - options: 'none' steps: - - uses: actions/checkout@v4 - - name: Set up uv - uses: astral-sh/setup-uv@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Install default (with full options) and test dependencies - run: uv pip install --python-version=${{ matrix.python-version }} -e '.[full]' --group test - - name: Run unit and doc tests with coverage report - run: uv run pytest -W error tests/unit tests/doc --cov=src --cov-report=xml - - name: Upload results to Codecov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} + - name: Checkout repository + uses: actions/checkout@v4 - tests-default-install: - name: Run (most) tests with default install - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - name: Set up uv uses: astral-sh/setup-uv@v5 with: - python-version: ${{ env.PYTHON_VERSION }} - - name: Install default (without any option) and test dependencies - run: uv pip install --python-version=${{ env.PYTHON_VERSION }} -e . --group test - - name: Run unit and doc tests with coverage report - run: | - uv run pytest -W error tests/unit tests/doc \ - --ignore tests/unit/aggregation/test_cagrad.py \ - --ignore tests/unit/aggregation/test_nash_mtl.py \ - --ignore tests/doc/test_aggregation.py \ - --cov=src --cov-report=xml - - name: Upload results to Codecov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} + python-version: ${{ matrix.python-version || '3.14' }} - tests-float64: - name: Run tests on float64 dtype - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Set up uv - uses: astral-sh/setup-uv@v5 + - uses: ./.github/actions/install-deps with: - python-version: ${{ env.PYTHON_VERSION }} - - name: Install default (with full options) and test dependencies - run: uv pip install --python-version=${{ env.PYTHON_VERSION }} -e '.[full]' --group test - - name: Run unit and doc tests with coverage report + options: ${{ matrix.options || 'full' }} + groups: test + + - name: Run tests run: uv run pytest -W error tests/unit tests/doc --cov=src --cov-report=xml env: - PYTEST_TORCH_DTYPE: float64 - - name: Upload results to Codecov + PYTEST_TORCH_DTYPE: ${{ matrix.dtype || 'float32' }} + + - &upload-codecov + name: Upload results to Codecov uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} @@ -88,10 +68,11 @@ jobs: - name: Set up uv uses: astral-sh/setup-uv@v5 with: - python-version: ${{ env.PYTHON_VERSION }} + python-version: '3.14' - - name: Install dependencies (default with full options & doc) - run: uv pip install --python-version=${{ env.PYTHON_VERSION }} -e '.[full]' --group doc + - uses: ./.github/actions/install-deps + with: + groups: doc - name: Build Documentation working-directory: docs @@ -101,16 +82,17 @@ jobs: name: Run mypy runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 - - name: Set up uv - uses: astral-sh/setup-uv@v5 - with: - python-version: ${{ env.PYTHON_VERSION }} + - name: Set up uv + uses: astral-sh/setup-uv@v5 + with: + python-version: '3.14' - - name: Install dependencies (default with full options & check) - run: uv pip install --python-version=${{ env.PYTHON_VERSION }} -e '.[full]' --group check + - uses: ./.github/actions/install-deps + with: + groups: check - - name: Run mypy - run: uv run mypy src/torchjd + - name: Run mypy + run: uv run mypy src/torchjd diff --git a/tests/unit/aggregation/test_cagrad.py b/tests/unit/aggregation/test_cagrad.py index e604c087..e77cb729 100644 --- a/tests/unit/aggregation/test_cagrad.py +++ b/tests/unit/aggregation/test_cagrad.py @@ -5,7 +5,12 @@ from utils.contexts import ExceptionContext from utils.tensors import ones_ -from torchjd.aggregation import CAGrad +try: + from torchjd.aggregation import CAGrad +except ImportError: + import pytest + + pytest.skip("CAGrad dependencies not installed", allow_module_level=True) from ._asserts import assert_expected_structure, assert_non_conflicting, assert_non_differentiable from ._inputs import scaled_matrices, typical_matrices diff --git a/tests/unit/aggregation/test_nash_mtl.py b/tests/unit/aggregation/test_nash_mtl.py index ce343e5d..e4a47642 100644 --- a/tests/unit/aggregation/test_nash_mtl.py +++ b/tests/unit/aggregation/test_nash_mtl.py @@ -3,7 +3,12 @@ from torch.testing import assert_close from utils.tensors import ones_, randn_ -from torchjd.aggregation import NashMTL +try: + from torchjd.aggregation import NashMTL +except ImportError: + import pytest + + pytest.skip("NashMTL dependencies not installed", allow_module_level=True) from ._asserts import assert_expected_structure, assert_non_differentiable from ._inputs import nash_mtl_matrices