Skip to content

Commit 7084f4f

Browse files
Replace tox with nox for all test, lint, docs, and wheel sessions
- Add noxfile.py with GPU (venv_backend=none), CPU unit (parametrized over torch/transformers versions via 2-level @nox.parametrize), partial install, pre-commit, docs, and build_wheel sessions - Remove tox.ini; replace tox references in CI workflows, CLAUDE.md, CONTRIBUTING.md, CODEOWNERS, and conftest.py - GPU sessions use python -m pip/pytest to avoid PATH mismatches in containers (e.g. NeMo has pip→/usr/local/bin/pip, python→/opt/venv) - Add uv as default venv backend for CPU sessions; envdir=/tmp/.nox to avoid permission errors in mounted container directories - Unit sessions now addressable as unit-3.12(torch_211-tf_latest), etc. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com>
1 parent 4cd873f commit 7084f4f

12 files changed

Lines changed: 229 additions & 186 deletions

File tree

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ LICENSE @NVIDIA/modelopt-setup-codeowners
99
LICENSE_HEADER @NVIDIA/modelopt-setup-codeowners
1010
pyproject.toml @NVIDIA/modelopt-setup-codeowners
1111
SECURITY.md @NVIDIA/modelopt-setup-codeowners
12-
tox.ini @NVIDIA/modelopt-setup-codeowners
12+
noxfile.py @NVIDIA/modelopt-setup-codeowners
1313
uv.lock @NVIDIA/modelopt-setup-codeowners
1414

1515
# Library

.github/workflows/code_quality.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ jobs:
2424
with:
2525
extra_args: --results=verified,unknown
2626
- name: Run code quality checks
27-
run: pip install tox && tox -e pre-commit-all
27+
run: pip install nox uv && nox -s pre_commit_all

.github/workflows/gpu_tests.yml

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,16 @@ jobs:
4141
files: |
4242
.github/workflows/gpu_tests.yml
4343
modelopt/**
44+
noxfile.py
45+
pyproject.toml
4446
tests/gpu/**
4547
tests/gpu_megatron/**
46-
tests/gpu_regression/**
4748
tests/gpu_trtllm/**
49+
tests/gpu_regression/**
4850
examples/speculative_decoding/**
4951
examples/dataset/**
5052
modelopt_recipes/general/speculative_decoding/**
5153
tools/launcher/**
52-
pyproject.toml
53-
tox.ini
5454
fail_on_initial_diff_error: true
5555
wait-checks:
5656
needs: [check-file-changes]
@@ -101,14 +101,8 @@ jobs:
101101
COVERAGE_PROCESS_START: ${{ github.workspace }}/pyproject.toml
102102
COVERAGE_FILE: ${{ github.workspace }}/.coverage
103103
run: |
104-
# nemo containers use uv venvs which is not compatible with tox-current-env, so run tests directly
105-
if [[ "${{ matrix.example }}" == "gpu_megatron" ]]; then
106-
python -m pip install -e .[hf,dev-test]
107-
python -m pytest tests/gpu_megatron --cov
108-
else
109-
python -m pip install tox tox-current-env
110-
COV_ARGS="--cov" python -m tox -e cuda13-${{ matrix.example }} --current-env
111-
fi
104+
pip install nox
105+
nox -s ${{ matrix.example }}
112106
- name: Upload GPU coverage to Codecov
113107
uses: codecov/codecov-action@v5
114108
with:

.github/workflows/pages.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
- uses: actions/checkout@v6
2828
- uses: ./.github/actions/ubuntu-setup
2929
- name: Build docs
30-
run: pip install tox && tox -e build-docs
30+
run: pip install nox uv && nox -s docs
3131
- name: Upload docs artifact
3232
if: github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
3333
uses: actions/upload-artifact@v4
@@ -47,7 +47,7 @@ jobs:
4747
- uses: ./.github/actions/ubuntu-setup
4848
- name: Build docs
4949
if: github.event.action != 'closed'
50-
run: pip install tox && tox -e build-docs
50+
run: pip install nox uv && nox -s docs
5151
- name: Deploy / remove PR preview
5252
uses: rossjrw/pr-preview-action@v1
5353
with:

.github/workflows/release.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@ jobs:
1313
steps:
1414
- uses: actions/checkout@v6
1515
- uses: ./.github/actions/ubuntu-setup
16-
- name: Install dependencies
17-
run: pip install tox
1816
- name: Run basic unit tests
19-
run: tox -e py312-torch29-tf_latest-unit
17+
run: pip install nox uv && nox -s "unit-3.12(torch_211-tf_latest)"
2018
- name: Build Wheel
2119
run: |
22-
tox -e build-wheel
20+
nox -s build_wheel
2321
echo "WHEEL_PATH=$(find dist -name "*.whl" | head -n 1)" >> $GITHUB_ENV
2422
- name: Upload GitHub Release Artifact
2523
env:

.github/workflows/unit_tests.yml

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ on:
99
paths:
1010
- ".github/workflows/unit_tests.yml"
1111
- "modelopt/**"
12-
- "tests/unit/**"
12+
- "noxfile.py"
1313
- "pyproject.toml"
14-
- "tox.ini"
14+
- "tests/unit/**"
1515
- "tools/launcher/**"
1616
schedule:
1717
- cron: "0 0 * * *" # Nightly
@@ -38,7 +38,10 @@ jobs:
3838
- uses: actions/checkout@v6
3939
- uses: ./.github/actions/ubuntu-setup
4040
- name: Run unit tests
41-
run: pip install tox && COV_ARGS="--cov" tox -e py312-torch211-tf_latest-unit
41+
env:
42+
COVERAGE_PROCESS_START: ${{ github.workspace }}/pyproject.toml
43+
COVERAGE_FILE: ${{ github.workspace }}/.coverage
44+
run: pip install nox uv && nox -s "unit-3.12(torch_211-tf_latest)"
4245
- name: Upload coverage reports to Codecov
4346
uses: codecov/codecov-action@v5
4447
with:
@@ -57,8 +60,7 @@ jobs:
5760
with:
5861
python-version: "3.12"
5962
- name: Run unit tests (without coverage)
60-
# Some issues with torch 2.10 on Windows, so using 2.9 for now
61-
run: pip install tox && tox -e py312-torch29-tf_latest-unit
63+
run: pip install nox uv && nox -s "unit-3.12(torch_211-tf_latest)"
6264
multi-py:
6365
if: github.event_name == 'pull_request'
6466
needs: [linux]
@@ -74,7 +76,7 @@ jobs:
7476
with:
7577
python-version: "3.${{ matrix.py }}"
7678
- name: Run unit tests
77-
run: pip install tox && tox -e py3${{ matrix.py }}-torch211-tf_latest-unit
79+
run: pip install nox uv && nox -s "unit-3.${{ matrix.py }}(torch_211-tf_latest)"
7880
multi-torch:
7981
if: github.event_name == 'pull_request'
8082
needs: [linux]
@@ -88,7 +90,7 @@ jobs:
8890
- uses: actions/checkout@v6
8991
- uses: ./.github/actions/ubuntu-setup
9092
- name: Run unit tests
91-
run: pip install tox && tox -e py312-torch${{ matrix.torch }}-tf_latest-unit
93+
run: pip install nox uv && nox -s "unit-3.12(torch_${{ matrix.torch }}-tf_latest)"
9294
multi-transformers:
9395
if: github.event_name == 'pull_request'
9496
needs: [linux]
@@ -102,7 +104,7 @@ jobs:
102104
- uses: actions/checkout@v6
103105
- uses: ./.github/actions/ubuntu-setup
104106
- name: Run unit tests
105-
run: pip install tox && tox -e py312-torch211-tf_${{ matrix.tf }}-unit
107+
run: pip install nox uv && nox -s "unit-3.12(torch_211-tf_min)"
106108
launcher:
107109
if: github.event_name == 'pull_request'
108110
needs: [linux]
@@ -133,7 +135,7 @@ jobs:
133135
- uses: actions/checkout@v6
134136
- uses: ./.github/actions/ubuntu-setup
135137
- name: Run unit tests
136-
run: pip install tox && tox -e py312-partial-unit-${{ matrix.test-env }}
138+
run: pip install nox uv && nox -s "partial_unit(subset='${{ matrix.test-env }}')"
137139
unit-pr-required-check:
138140
# Run even if some jobs are skipped
139141
if: ${{ github.event_name == 'pull_request' && always() }}

CLAUDE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ Primarily Python codebase with optional C++/CUDA extensions supporting PyTorch,
3737
| Pattern match | `pytest tests/unit -k "test_quantize"` |
3838
| Lint + format (all files) | `pre-commit run --all-files` |
3939
| Lint (diff only) | `pre-commit run --from-ref origin/main --to-ref HEAD` |
40-
| Run via tox (CPU unit) | `tox -e py312-torch210-tf_latest-unit` |
41-
| Build docs | `tox -e build-docs` |
42-
| Build wheel | `tox -e build-wheel` |
40+
| Run via nox (CPU unit) | `nox -s "unit-3.12(torch_211-tf_latest)"` |
41+
| Build docs | `nox -s docs` |
42+
| Build wheel | `nox -s build_wheel` |
4343

4444
## Architecture
4545

@@ -104,7 +104,7 @@ A **recipe** is a declarative YAML specification of an optimization configuratio
104104
| `modelopt_recipes/general/ptq/` | Built-in PTQ recipe YAML files |
105105
| `pyproject.toml` | Optional dependency groups (`[onnx]`, `[hf]`, `[all]`, `[dev]`); ruff, mypy, pytest, bandit, and coverage config |
106106
| `.pre-commit-config.yaml` | Pre-commit hooks (ruff, mypy, clang-format, license headers) |
107-
| `tox.ini` | Test environment definitions |
107+
| `noxfile.py` | Test session definitions |
108108

109109
## Design Patterns
110110

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ We use [pytest](https://docs.pytest.org/) for all tests. For any new features /
8989
- `tests/gpu_trtllm`: Fast GPU-based unit tests for the core ModelOpt library for TensorRT-LLM features. In most cases, they should not take more than a few seconds to run.
9090
- `tests/examples`: Integration tests for ModelOpt examples. They should not take more than a few minutes to run. Please refer to [example test README](./tests/examples/README.md) for more details.
9191

92-
Please refer to [tox.ini](./tox.ini) for more details on how to run the tests and their dependencies.
92+
Please refer to [noxfile.py](./noxfile.py) for more details on how to run the tests and their dependencies.
9393

9494
## ✍️ Signing your work
9595

noxfile.py

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
17+
"""Nox session definitions for testing, linting, docs, and wheel builds.
18+
19+
Usage:
20+
python -m pip install nox uv # install nox and uv (once)
21+
nox -l # list all sessions
22+
nox -s gpu_megatron # run a GPU session (inside container)
23+
nox -s "unit-3.12(torch_211-tf_latest)" # run a specific unit test combination
24+
nox -s "unit-3.12(torch_211-tf_latest)" --no-venv # skip venv creation (reuse current env)
25+
"""
26+
27+
import glob
28+
import os
29+
import shutil
30+
31+
import nox
32+
33+
nox.options.default_venv_backend = "uv"
34+
nox.options.envdir = "/tmp/.nox"
35+
36+
TORCH_VERSIONS = {
37+
"torch_28": "torchvision~=0.23.0",
38+
"torch_29": "torchvision~=0.24.0",
39+
"torch_210": "torchvision~=0.25.0",
40+
"torch_211": "torchvision~=0.26.0",
41+
}
42+
43+
TRANSFORMERS_VERSIONS = {
44+
"tf_latest": None,
45+
"tf_min": "transformers~=4.56.0",
46+
}
47+
48+
49+
def _cov_args():
50+
"""Return --cov when COVERAGE_PROCESS_START is set (CI only)."""
51+
return ["--cov"] if os.environ.get("COVERAGE_PROCESS_START") else []
52+
53+
54+
# ─── CPU unit tests ───────────────────────────────────────────────────────────
55+
@nox.session(python=["3.10", "3.11", "3.12", "3.13"])
56+
@nox.parametrize("tf_ver", [nox.param(k, id=k) for k in TRANSFORMERS_VERSIONS])
57+
@nox.parametrize("torch_ver", [nox.param(k, id=k) for k in TORCH_VERSIONS])
58+
def unit(session, torch_ver, tf_ver):
59+
"""Unit tests — parametrized over torch and transformers versions."""
60+
session.install(TORCH_VERSIONS[torch_ver], "-e", ".[all,dev-test]")
61+
tf_pin = TRANSFORMERS_VERSIONS[tf_ver]
62+
if tf_pin:
63+
session.install(tf_pin)
64+
session.run("python", "-m", "pytest", "tests/unit", *_cov_args())
65+
66+
67+
@nox.session(python="3.12")
68+
@nox.parametrize("subset", ["onnx", "torch", "torch_deploy"])
69+
def partial_unit(session, subset):
70+
"""Unit tests with partial installs."""
71+
if subset == "onnx":
72+
session.install("torchvision~=0.26.0", ".[onnx,dev-test]")
73+
session.run("python", "-m", "pytest", "tests/unit/onnx")
74+
elif subset == "torch":
75+
session.install("megatron-core", ".[dev-test]")
76+
session.run(
77+
"python",
78+
"-m",
79+
"pytest",
80+
"tests/unit/torch",
81+
"--ignore=tests/unit/torch/deploy",
82+
"--ignore=tests/unit/torch/puzzletron",
83+
)
84+
else: # torch_deploy
85+
session.install(".[onnx,dev-test]")
86+
session.run("python", "-m", "pytest", "tests/unit/torch/deploy")
87+
88+
89+
# ─── GPU sessions (run inside containers — no new venv) ──────────────────────
90+
# `venv_backend="none"` skips creating a new venv so the session runs directly in the container's
91+
# existing Python environment (e.g. /opt/venv in NeMo) instead of an isolated one.
92+
# Use `python -m pip/pytest` to ensure the container's active venv Python is used,
93+
# not a stale PATH entry (e.g. NeMo container has pip → /usr/local/bin/pip but python → /opt/venv/bin/python).
94+
# Container: nvcr.io/nvidia/pytorch:26.01-py3 or later
95+
@nox.session(venv_backend="none")
96+
def gpu(session):
97+
# tests/gpu/_extensions/test_onnx_extensions.py fails for newer containers
98+
# until https://github.com/tbenthompson/cppimport/pull/98
99+
session.run(
100+
"python",
101+
"-m",
102+
"pip",
103+
"install",
104+
"--no-build-isolation",
105+
"git+https://github.com/Dao-AILab/fast-hadamard-transform.git",
106+
)
107+
session.run("python", "-m", "pip", "install", "-e", ".[all,dev-test]")
108+
session.run("python", "-m", "pip", "uninstall", "-y", "cupy-cuda12x")
109+
session.run("python", "-m", "pip", "install", "cupy-cuda13x")
110+
session.run(
111+
"python",
112+
"-m",
113+
"pip",
114+
"install",
115+
"--no-build-isolation",
116+
"git+https://github.com/state-spaces/mamba.git",
117+
"git+https://github.com/Dao-AILab/causal-conv1d.git",
118+
)
119+
session.run("python", "-m", "pytest", "tests/gpu", *_cov_args())
120+
121+
122+
# Container: nvcr.io/nvidia/nemo:26.02 or later
123+
@nox.session(venv_backend="none")
124+
def gpu_megatron(session):
125+
session.run("python", "-m", "pip", "install", "-e", ".[hf,dev-test]")
126+
session.run("python", "-m", "pytest", "tests/gpu_megatron", *_cov_args())
127+
128+
129+
# Container: nvcr.io/nvidia/pytorch:26.01-py3 or later
130+
@nox.session(venv_backend="none")
131+
def gpu_regression(session):
132+
session.run("python", "-m", "pip", "install", "-e", ".[hf,dev-test]")
133+
session.run("python", "-m", "pytest", "tests/gpu_regression", *_cov_args())
134+
135+
136+
# Container: nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc10 or later
137+
@nox.session(venv_backend="none")
138+
def gpu_trtllm(session):
139+
session.run("python", "-m", "pip", "install", "-e", ".[hf,dev-test]")
140+
session.run("python", "-m", "pytest", "tests/gpu_trtllm", *_cov_args())
141+
142+
143+
# ─── Code quality ─────────────────────────────────────────────────────────────
144+
@nox.session
145+
def pre_commit_all(session):
146+
session.install("-e", ".[all,dev-lint]")
147+
session.run("pre-commit", "run", "--all-files", "--show-diff-on-failure")
148+
149+
150+
@nox.session
151+
def pre_commit_diff(session):
152+
session.install("-e", ".[all,dev-lint]")
153+
session.run("pre-commit", "run", "--from-ref", "origin/main", "--to-ref", "HEAD")
154+
155+
156+
# ─── Docs ─────────────────────────────────────────────────────────────────────
157+
@nox.session
158+
def docs(session):
159+
session.install("-e", ".[all,dev-docs]")
160+
shutil.rmtree("docs/build", ignore_errors=True)
161+
shutil.rmtree("docs/source/reference/generated", ignore_errors=True)
162+
with session.chdir("docs"):
163+
session.run(
164+
"sphinx-build",
165+
"source",
166+
"build/html",
167+
"--fail-on-warning",
168+
"--show-traceback",
169+
"--keep-going",
170+
)
171+
172+
173+
@nox.session
174+
def docs_debug(session):
175+
session.install("-e", ".[all,dev-docs]")
176+
shutil.rmtree("docs/build", ignore_errors=True)
177+
shutil.rmtree("docs/source/reference/generated", ignore_errors=True)
178+
with session.chdir("docs"):
179+
session.run("sphinx-autobuild", "source", "build/html", "--host", "0.0.0.0")
180+
181+
182+
# ─── Wheel build ──────────────────────────────────────────────────────────────
183+
@nox.session
184+
def build_wheel(session):
185+
shutil.rmtree("build", ignore_errors=True)
186+
session.install("twine")
187+
session.run("pip", "wheel", "--no-deps", "--wheel-dir=dist", ".")
188+
wheels = glob.glob("dist/*.whl")
189+
session.run("twine", "check", *wheels)
190+
(modelopt_wheel,) = glob.glob("dist/nvidia_modelopt-*.whl")
191+
session.install(modelopt_wheel, "-f", "dist")
192+
with session.chdir("dist"):
193+
session.run("python", "-c", "import modelopt; print(modelopt.__version__)")

0 commit comments

Comments
 (0)