Skip to content

Commit 36064a1

Browse files
committed
feat: make TORCH_GROUP the single source of truth for torch pinning
setup_env.sh now reads TORCH_GROUP directly and folds it into every sync it runs, so make env/test/test-fast/docs/etc. all pin torch instead of floating on whatever pyproject.toml's plain bound resolves to. test-highest-pytorch/test-lowest-pytorch/env-highest-torch lock their group via an inline `export TORCH_GROUP=... &&` at the top of the recipe (rather than a hardcoded --with-torch_2_X flag), so their name stays a promise regardless of a command-line override — verified that GNU Make's own `override` directive achieves the same lock but trips up the mbake Makefile formatter's duplicate-target detector, which doesn't recognize that syntax. --with-torch_2_X stays as a guarded alternate path for direct, non-Make invocations of setup_env.sh; a guard now errors clearly if it disagrees with an explicitly-set TORCH_GROUP instead of surfacing a raw uv conflicting-groups resolver error.
1 parent 6eac37a commit 36064a1

2 files changed

Lines changed: 80 additions & 30 deletions

File tree

Makefile

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,15 @@ VENV_HIGHEST_TORCH ?= .venv-highest-torch
5353
VENV_LOWEST_TORCH ?= .venv-lowest-torch
5454
VENV_TUTORIAL ?= .venv-tutorial
5555

56-
# Torch dependency group (pyproject.toml [dependency-groups]) that
57-
# `test-smoke` installs; override to smoke test against a different pinned
58-
# torch minor version, e.g. `make test-smoke TORCH_GROUP=torch_2_8`.
56+
# Torch dependency group (pyproject.toml [dependency-groups]) that every
57+
# environment-building target (env, test, test-smoke, docs, ...) pins to.
58+
# Single source of truth for which torch/torchao/torchvision triple
59+
# setup_env.sh installs; override e.g. `make test-fast TORCH_GROUP=torch_2_8`.
60+
# test-highest-pytorch/test-lowest-pytorch/env-highest-torch lock this to a
61+
# specific group via `export TORCH_GROUP=... &&` at the top of their recipe,
62+
# so their name stays a promise regardless of a command-line value.
5963
TORCH_GROUP ?= torch_2_11
64+
export TORCH_GROUP
6065

6166
# Documentation directory. Defaults to $(MAKEFILE_DIR)docs so the same recipe
6267
# works in both contexts:
@@ -181,7 +186,8 @@ env: _maybe_patch_pyproject
181186

182187
# Set up development environment with latest supported PyTorch version
183188
env-highest-torch: _maybe_patch_pyproject
184-
@$(SETUP_ENV) --venv $(VENV_HIGHEST_TORCH) --python-version $(PYTHON_VERSION) --with-torch_2_11
189+
@export TORCH_GROUP=torch_2_11 && \
190+
$(SETUP_ENV) --venv $(VENV_HIGHEST_TORCH) --python-version $(PYTHON_VERSION)
185191
@$(call write_active_venv,$(VENV_HIGHEST_TORCH))
186192

187193
# Set up environment for running tutorials (quantization notebook)
@@ -250,20 +256,22 @@ test-smoke:
250256
# Run tests on lowest supported PyTorch version (pass PYTEST_ARGS for custom flags)
251257
test-lowest-pytorch:
252258
@echo "Running tests on lowest PyTorch version supported..."
253-
@$(call use_env,VENV_LOWEST_TORCH,--with-torch_2_8) && \
254-
echo "Testing with lowest supported PyTorch versions" && \
255-
uv run --no-sync --active python $(SCRIPTS)/make/log_versions.py && \
256-
$(RUN_TESTS) $(PYTEST_ARGS) && \
257-
echo "All tests passed!"
259+
@export TORCH_GROUP=torch_2_8 && \
260+
$(call use_env,VENV_LOWEST_TORCH) && \
261+
echo "Testing with lowest supported PyTorch versions" && \
262+
uv run --no-sync --active python $(SCRIPTS)/make/log_versions.py && \
263+
$(RUN_TESTS) $(PYTEST_ARGS) && \
264+
echo "All tests passed!"
258265

259266
# Run tests on highest supported PyTorch version (pass PYTEST_ARGS for custom flags)
260267
test-highest-pytorch:
261268
@echo "Running tests on highest PyTorch version supported..."
262-
@$(call use_env,VENV_HIGHEST_TORCH,--with-torch_2_11) && \
263-
echo "Testing with latest supported PyTorch versions" && \
264-
uv run --no-sync --active python $(SCRIPTS)/make/log_versions.py && \
265-
$(RUN_TESTS) $(PYTEST_ARGS) && \
266-
echo "All tests passed!"
269+
@export TORCH_GROUP=torch_2_11 && \
270+
$(call use_env,VENV_HIGHEST_TORCH) && \
271+
echo "Testing with latest supported PyTorch versions" && \
272+
uv run --no-sync --active python $(SCRIPTS)/make/log_versions.py && \
273+
$(RUN_TESTS) $(PYTEST_ARGS) && \
274+
echo "All tests passed!"
267275

268276
# Run tutorial notebook tests
269277
test-tutorials:

scripts/make/setup_env.sh

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ show_help() {
127127
echo ""
128128
echo "Environment variables:"
129129
echo " VENV Virtual environment name, overrides --venv (default: .venv)"
130+
echo " TORCH_GROUP Torch dependency group to pin; source of truth for torch pinning"
130131
echo ""
131132
echo "Available dependency groups: $AVAILABLE_GROUPS"
132133
echo "Conflicting groups (excluded from --all-groups): ${CONFLICTING_GROUPS[*]}"
@@ -135,8 +136,8 @@ show_help() {
135136
echo " $0 --python-version 3.11 # Setup with dev group only"
136137
echo " $0 --python-version 3.11 --with-docs # Setup with dev and docs groups"
137138
echo " $0 --python-version 3.11 --all-groups # Setup with all non-conflicting groups"
138-
echo " $0 --python-version 3.11 --all-groups --with-torch_2_11 # Setup with all groups and torch 2.11"
139-
echo " $0 --python-version 3.11 --all-groups --with-torch_2_8 # Setup with all groups and torch 2.8"
139+
echo " TORCH_GROUP=torch_2_11 $0 --python-version 3.11 --all-groups # Setup with all groups and torch 2.11"
140+
echo " TORCH_GROUP=torch_2_8 $0 --python-version 3.11 --all-groups # Setup with all groups and torch 2.8"
140141
echo " $0 --python-version 3.11 --venv .venv-exp # Setup with custom venv name"
141142
echo " $0 --python-version 3.11 --with-docs --venv .venv-exp # Setup with docs group and custom venv name"
142143
echo " $0 --python-version 3.12 # Setup with Python 3.12"
@@ -222,6 +223,7 @@ validate_groups() {
222223
# Validate dependency group names early
223224
[[ ${#EXTRA_GROUPS[@]} -gt 0 ]] && validate_groups "${EXTRA_GROUPS[@]}"
224225
[[ ${#EXCLUDE_GROUPS[@]} -gt 0 ]] && validate_groups "${EXCLUDE_GROUPS[@]}"
226+
[[ -n "${TORCH_GROUP:-}" ]] && validate_groups "$TORCH_GROUP"
225227

226228
# Check for conflicts between --with-<group> and --without-<group>
227229
if [[ ${#EXTRA_GROUPS[@]} -gt 0 && ${#EXCLUDE_GROUPS[@]} -gt 0 ]]; then
@@ -233,6 +235,21 @@ if [[ ${#EXTRA_GROUPS[@]} -gt 0 && ${#EXCLUDE_GROUPS[@]} -gt 0 ]]; then
233235
done
234236
fi
235237

238+
# TORCH_GROUP (env var) is the single source of truth for torch pinning; a
239+
# --with-<torch_2_*> flag that disagrees with it is almost certainly a
240+
# mistake (e.g. TORCH_GROUP left set from a prior `make` invocation) rather
241+
# than an intentional double-specification, so fail loudly instead of
242+
# leaving it to a raw `uv` conflicting-groups resolver error.
243+
if [[ -n "${TORCH_GROUP:-}" ]]; then
244+
for GROUP in "${EXTRA_GROUPS[@]:-}"; do
245+
if [[ " ${CONFLICTING_GROUPS[*]} " == *" ${GROUP} "* && "$GROUP" != "$TORCH_GROUP" ]]; then
246+
echo "Error: --with-$GROUP conflicts with TORCH_GROUP=$TORCH_GROUP." >&2
247+
echo "TORCH_GROUP is the source of truth; unset it or set it to '$GROUP' instead." >&2
248+
exit 1
249+
fi
250+
done
251+
fi
252+
236253
# Validate venv name contains only allowed characters
237254
if [[ ! "$VENV" =~ ^[a-zA-Z0-9._/-]+$ ]]; then
238255
echo "Error: Virtual environment name contains invalid characters"
@@ -267,24 +284,35 @@ if [[ "$ENSURE_MODE" == "true" ]] && [ -f "$VENV/bin/python" ]; then
267284
for GROUP in "${EXTRA_GROUPS[@]}"; do
268285
case "$GROUP" in
269286
docs) IMPORT_STMTS+="; import sphinx" ;;
270-
torch_2_8 | torch_2_9 | torch_2_10 | torch_2_11)
271-
IMPORT_STMTS+="; import torchao"
272-
EXPECTED_TORCH="$(group_torch_pin "$GROUP")"
273-
# These groups always pin torch, so an empty result means the
274-
# pyproject parse regressed — fail loudly instead of silently
275-
# skipping the version check (which would reintroduce the bug).
276-
if [[ -z "$EXPECTED_TORCH" ]]; then
277-
echo "Error: could not parse a torch pin for group '$GROUP' in $PYPROJECT_TOML" >&2
278-
exit 1
279-
fi
280-
IMPORT_STMTS+="; import torch; assert torch.__version__.split('+')[0] == '$EXPECTED_TORCH'"
281-
;;
282287
rio) IMPORT_STMTS+="; import turi_lightning" ;;
283288
tamm-export) IMPORT_STMTS+="; import tamm_export" ;;
284289
esac
285290
done
286291
fi
287292

293+
# TORCH_GROUP is the single source of truth for torch pinning; fall back
294+
# to a --with-torch_2_* flag for direct, non-Make invocations. Checked
295+
# unconditionally rather than only when present in EXTRA_GROUPS, since
296+
# TORCH_GROUP drives the sync regardless of what's in EXTRA_GROUPS.
297+
EXPECTED_GROUP="${TORCH_GROUP:-}"
298+
if [[ -z "$EXPECTED_GROUP" ]]; then
299+
for GROUP in "${EXTRA_GROUPS[@]:-}"; do
300+
[[ " ${CONFLICTING_GROUPS[*]} " == *" ${GROUP} "* ]] && EXPECTED_GROUP="$GROUP"
301+
done
302+
fi
303+
if [[ -n "$EXPECTED_GROUP" ]]; then
304+
IMPORT_STMTS+="; import torchao"
305+
EXPECTED_TORCH="$(group_torch_pin "$EXPECTED_GROUP")"
306+
# These groups always pin torch, so an empty result means the
307+
# pyproject parse regressed — fail loudly instead of silently
308+
# skipping the version check (which would reintroduce the bug).
309+
if [[ -z "$EXPECTED_TORCH" ]]; then
310+
echo "Error: could not parse a torch pin for '$EXPECTED_GROUP' in $PYPROJECT_TOML" >&2
311+
exit 1
312+
fi
313+
IMPORT_STMTS+="; import torch; assert torch.__version__.split('+')[0] == '$EXPECTED_TORCH'"
314+
fi
315+
288316
if "$VENV/bin/python" -c "$IMPORT_STMTS" 2>/dev/null; then
289317
exit 0
290318
fi
@@ -324,9 +352,14 @@ echo "[2/3] Installing dependencies..."
324352
SYNC_CMD=(uv sync --active)
325353
if [[ "$ALL_GROUPS" == "true" ]]; then
326354
SYNC_CMD+=(--all-groups)
327-
# Exclude conflicting groups unless explicitly requested via --with-*
355+
# Exclude conflicting torch groups other than the one TORCH_GROUP or
356+
# --with-<group> selects, so --all-groups doesn't trip the mutual
357+
# -conflicts declaration in pyproject.toml.
328358
for GROUP in "${CONFLICTING_GROUPS[@]}"; do
329-
if [[ ! " ${EXTRA_GROUPS[*]:-} " == *" ${GROUP} "* ]]; then
359+
GROUP_REQUESTED=false
360+
[[ "$GROUP" == "${TORCH_GROUP:-}" ]] && GROUP_REQUESTED=true
361+
[[ " ${EXTRA_GROUPS[*]:-} " == *" ${GROUP} "* ]] && GROUP_REQUESTED=true
362+
if [[ "$GROUP_REQUESTED" == "false" ]]; then
330363
SYNC_CMD+=(--no-group "$GROUP")
331364
fi
332365
done
@@ -335,6 +368,15 @@ elif [[ ${#EXTRA_GROUPS[@]} -gt 0 ]]; then
335368
SYNC_CMD+=(--group "$GROUP")
336369
done
337370
fi
371+
# TORCH_GROUP is the single source of truth for torch pinning: fold it in
372+
# unless --all-groups already swept it up or a --with-<group> above already
373+
# requested it explicitly (the earlier guard ensures no disagreement).
374+
TORCH_GROUP_ALREADY_SYNCED=false
375+
[[ "$ALL_GROUPS" == "true" ]] && TORCH_GROUP_ALREADY_SYNCED=true
376+
[[ " ${EXTRA_GROUPS[*]:-} " == *" ${TORCH_GROUP:-} "* ]] && TORCH_GROUP_ALREADY_SYNCED=true
377+
if [[ -n "${TORCH_GROUP:-}" && "$TORCH_GROUP_ALREADY_SYNCED" == "false" ]]; then
378+
SYNC_CMD+=(--group "$TORCH_GROUP")
379+
fi
338380
# Apply explicit group exclusions (e.g., --without-coreai)
339381
if [[ ${#EXCLUDE_GROUPS[@]} -gt 0 ]]; then
340382
for GROUP in "${EXCLUDE_GROUPS[@]}"; do

0 commit comments

Comments
 (0)