Skip to content

Fix --trust_calibration_data being mutually exclusive with calibration data paths#1540

Open
adityasingh2400 wants to merge 1 commit into
NVIDIA:mainfrom
adityasingh2400:fix/onnx-quant-trust-calibration-data
Open

Fix --trust_calibration_data being mutually exclusive with calibration data paths#1540
adityasingh2400 wants to merge 1 commit into
NVIDIA:mainfrom
adityasingh2400:fix/onnx-quant-trust-calibration-data

Conversation

@adityasingh2400
Copy link
Copy Markdown

@adityasingh2400 adityasingh2400 commented May 24, 2026

What does this PR do?

Type of change: Bug fix

In python -m modelopt.onnx.quantization, the --trust_calibration_data flag is declared inside the same add_mutually_exclusive_group() as --calibration_data_path and --calibration_cache_path. That flag only controls allow_pickle when loading the data those paths point to, so it is meant to be combined with them rather than used as an alternative.

Because of the grouping, the secure pickle opt-in that the loader points users to is unreachable from the CLI. main() raises:

Calibration data file contains pickled objects which pose a security risk. For trusted sources, you may enable pickle deserialization by setting the --trust_calibration_data flag.

but --calibration_data_path X --trust_calibration_data is rejected by argparse before that code runs:

error: argument --trust_calibration_data: not allowed with argument --calibration_data_path

This moves --trust_calibration_data out of the mutually exclusive group so it is an independent flag, while --calibration_data_path and --calibration_cache_path stay mutually exclusive. The flag was added in #626.

Usage

# Previously errored at argparse; now works:
python -m modelopt.onnx.quantization \
    --onnx_path model.onnx \
    --calibration_data_path calib.npy \
    --trust_calibration_data

Testing

Added unit tests in tests/unit/onnx/quantization/test_autotune_quantization_integration.py that assert --trust_calibration_data parses together with either calibration path, that it defaults to False, and that --calibration_data_path / --calibration_cache_path remain mutually exclusive. Verified the new combine test fails on the pre-fix parser and passes after the change. ruff, mypy, and bandit pre-commit hooks pass on the changed files.

Before your PR is "Ready for review"

  • Is this change backward compatible?: ✅ (only loosens an argparse constraint; no previously accepted invocation is rejected)
  • If you copied code from any other sources or added a new PIP dependency, did you follow guidance in CONTRIBUTING.md: N/A
  • Did you write any new necessary tests?: ✅
  • Did you update Changelog?: ✅

Additional Information

The change is confined to the CLI argument grouping; the existing allow_pickle security logic in main() is unchanged.

Summary by CodeRabbit

Bug Fixes

  • Fixed ONNX quantization CLI to allow --trust_calibration_data flag to be used simultaneously with calibration data path or cache path options. Previously, a mutually-exclusive argument group incorrectly rejected this valid command combination.

Review Change Stack

…n data paths

The ONNX PTQ CLI added --trust_calibration_data to the same mutually
exclusive group as --calibration_data_path and --calibration_cache_path.
That flag only gates allow_pickle when loading the data those paths point
to, so it is meant to be combined with them, not used as an alternative.

With the current grouping, the secure opt-in the loader's error message
points users to ("enable pickle deserialization by setting the
--trust_calibration_data flag") is unreachable: argparse rejects
--calibration_data_path X --trust_calibration_data before main() runs.

Move --trust_calibration_data out of the group so it is an independent
flag while --calibration_data_path and --calibration_cache_path stay
mutually exclusive. Add regression tests for both behaviors.

Signed-off-by: Aditya Singh <adisin650@gmail.com>
@adityasingh2400 adityasingh2400 requested a review from a team as a code owner May 24, 2026 15:41
@adityasingh2400 adityasingh2400 requested a review from cjluo-nv May 24, 2026 15:41
@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot Bot commented May 24, 2026

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 24, 2026

📝 Walkthrough

Walkthrough

This PR fixes a CLI argument mutual-exclusion bug in the ONNX quantization module. The --trust_calibration_data flag was incorrectly placed in a mutually-exclusive group with calibration options, preventing users from using them together. The fix moves the flag out of the group to allow combination with --calibration_data_path and --calibration_cache_path, adds integration tests, and documents the fix.

Changes

ONNX Quantization CLI Flag Fix

Layer / File(s) Summary
CLI argument reorganization
modelopt/onnx/quantization/__main__.py
--trust_calibration_data is re-registered as a normal argument instead of a mutually-exclusive group member, allowing it to combine with calibration path options.
Test integration and coverage
tests/unit/onnx/quantization/test_autotune_quantization_integration.py
Module imports the parser, adjusts the existing TensorRT-free test, and adds tests verifying that --trust_calibration_data works with calibration options and that calibration-data and cache-path remain mutually exclusive.
Changelog documentation
CHANGELOG.rst
New bug-fix entry for version 0.45 documents the mutual-exclusion issue and notes the unreachable error path.

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: moving --trust_calibration_data out of a mutually exclusive group to fix its incompatibility with calibration data paths.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Anti-Patterns ✅ Passed PR securely implements allow_pickle=args.trust_calibration_data (defaults False), configurable via CLI flag, with security comment. No # nosec, eval/exec, or other violations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unit/onnx/quantization/test_autotune_quantization_integration.py`:
- Line 21: The test imports get_parser at module scope so re-import inside
test_quantization_cli_parser_imports_without_tensorrt doesn't see the
monkeypatched absence of TensorRT; modify the test to remove or reload
modelopt.onnx.quantization.__main__ after monkeypatching sys.modules["tensorrt"]
(e.g., pop sys.modules["modelopt.onnx.quantization.__main__"] or use
importlib.reload) and then import or call get_parser so the module-level
import-time behavior in __main__ runs under the patched tensorrt context.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 408776cf-db90-410d-80cb-cafd670138a0

📥 Commits

Reviewing files that changed from the base of the PR and between 16a0130 and 11eea19.

📒 Files selected for processing (3)
  • CHANGELOG.rst
  • modelopt/onnx/quantization/__main__.py
  • tests/unit/onnx/quantization/test_autotune_quantization_integration.py


import pytest

from modelopt.onnx.quantization.__main__ import get_parser
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the test currently reloads autotune but not __main__, while __main__.get_parser is imported at module scope.
rg -n -C2 'from modelopt\.onnx\.quantization\.__main__ import get_parser$|get_parser as get_parser_no_trt|importlib\.reload\(modelopt\.onnx\.quantization\.autotune\)|importlib\.reload\(.*__main__' tests/unit/onnx/quantization/test_autotune_quantization_integration.py

Repository: NVIDIA/Model-Optimizer

Length of output: 422


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="tests/unit/onnx/quantization/test_autotune_quantization_integration.py"

# Show relevant ranges around the module import and the no-TensorRT test.
sed -n '1,120p' "$FILE" | nl -ba | sed -n '1,120p'

Repository: NVIDIA/Model-Optimizer

Length of output: 109


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="tests/unit/onnx/quantization/test_autotune_quantization_integration.py"

wc -l "$FILE"
echo "----"
cat -n "$FILE" | sed -n '1,140p'

Repository: NVIDIA/Model-Optimizer

Length of output: 4014


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and print the __main__.py implementation of get_parser and any tensorrt-related code.
FILE="$(fd -t f "__main__.py" modelopt/onnx/quantization -a 2>/dev/null | head -n 5 | head -n 1)"
echo "Using: $FILE"
wc -l "$FILE"
echo "---- get_parser excerpt ----"
python3 - <<'PY'
import re,sys
path=sys.argv[1]
with open(path,'r',encoding='utf-8') as f:
    s=f.read().splitlines()
# print around def get_parser
for i,line in enumerate(s,1):
    if re.match(r"\s*def\s+get_parser\s*\(", line):
        start=max(1,i-20); end=min(len(s), i+120)
        for j in range(start,end+1):
            print(f"{j:4d}\t{s[j-1]}")
        break
else:
    print("get_parser not found")
PY "$FILE"

echo "---- tensorrt references in __main__.py ----"
rg -n "tensorrt" "$FILE" || true

echo "---- get_parser callers imports ----"
rg -n "get_parser" "$FILE" || true

Repository: NVIDIA/Model-Optimizer

Length of output: 416


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="$(fd -t f "__main__.py" modelopt/onnx/quantization -a 2>/dev/null | head -n 1)"
echo "Using: $FILE"
wc -l "$FILE"

echo "---- get_parser excerpt ----"
python3 - "$FILE" <<'PY'
import re,sys
path=sys.argv[1]
with open(path,'r',encoding='utf-8') as f:
    s=f.read().splitlines()
for i,line in enumerate(s,1):
    if re.match(r"\s*def\s+get_parser\s*\(", line):
        start=max(1,i-20); end=min(len(s), i+120)
        for j in range(start,end+1):
            print(f"{j:4d}\t{s[j-1]}")
        break
else:
    print("get_parser not found")
PY

echo "---- tensorrt references in __main__.py ----"
rg -n "tensorrt" "$FILE" || true

echo "---- get_parser callers imports in __main__.py ----"
rg -n "get_parser" "$FILE" || true

Repository: NVIDIA/Model-Optimizer

Length of output: 6122


🏁 Script executed:

#!/bin/bash
set -euo pipefail

MAIN="modelopt/onnx/quantization/__main__.py"
echo "== Top of __main__.py =="
cat -n "$MAIN" | sed -n '1,140p'

echo
echo "== References to autotune in __main__.py =="
rg -n "autotune" "$MAIN" || true

echo
echo "== TensorRT references in autotune module/package =="
# autotune may be a module or a package; search both by locating files under its path.
AUTOTUNE_DIR="$(dirname "$(fd -t f "autotune.py" modelopt/onnx/quantization -a 2>/dev/null | head -n 1)")/autotune" || true
# Fallback: just search under modelopt/onnx/quantization/autotune
if [ -d "modelopt/onnx/quantization/autotune" ]; then
  rg -n "tensorrt" "modelopt/onnx/quantization/autotune" || true
else
  echo "No directory modelopt/onnx/quantization/autotune found; searching any autotune.py"
  fd -t f "autotune*.py" modelopt/onnx/quantization -a 2>/dev/null | xargs -r rg -n "tensorrt" || true
fi

echo
echo "== Does importing __main__ pull in autotune at module scope? =="
# Show first 80 lines of imports in __main__.py (already printed), plus any non-local imports.
rg -n "^from |^import " "$MAIN" | sed -n '1,120p'

Repository: NVIDIA/Model-Optimizer

Length of output: 10289


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "tensorrt|TRT" modelopt/onnx/quantization/autotune/utils* modelopt/onnx/quantization/quantize* || true

echo "---- autotune/utils.py excerpt (imports) ----"
UTILS="$(fd -t f "utils.py" modelopt/onnx/quantization/autotune -a 2>/dev/null | head -n 1)"
echo "Using $UTILS"
wc -l "$UTILS"
sed -n '1,120p' "$UTILS" | cat -n

echo "---- quantize.py excerpt (imports) ----"
QUANT="$(fd -t f "quantize.py" modelopt/onnx/quantization -a 2>/dev/null | head -n 1)"
echo "Using $QUANT"
wc -l "$QUANT"
sed -n '1,160p' "$QUANT" | cat -n

Repository: NVIDIA/Model-Optimizer

Length of output: 13735


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== tensorrt references in modelopt/onnx/trt_utils.py =="
TRT_UTIL="$(fd -t f "trt_utils.py" modelopt/onnx -a 2>/dev/null | head -n 1)"
echo "Using: $TRT_UTIL"
wc -l "$TRT_UTIL"
rg -n "tensorrt|TRT_AVAILABLE" "$TRT_UTIL" || true
echo
echo "== Top of trt_utils.py =="
sed -n '1,220p' "$TRT_UTIL" | cat -n

echo
echo "== tensorrt references in modelopt/onnx/quantization/ort_utils.py =="
ORT_UTIL="$(fd -t f "ort_utils.py" modelopt/onnx/quantization -a 2>/dev/null | head -n 1)"
echo "Using: $ORT_UTIL"
wc -l "$ORT_UTIL"
rg -n "tensorrt|TRT_AVAILABLE" "$ORT_UTIL" || true
echo
echo "== Top of ort_utils.py =="
sed -n '1,220p' "$ORT_UTIL" | cat -n

echo
echo "== What does reloading autotune do at package import time? =="
AUTOTUNE_INIT="$(fd -t f "__init__.py" modelopt/onnx/quantization/autotune -a 2>/dev/null | head -n 1)"
echo "Using: $AUTOTUNE_INIT"
wc -l "$AUTOTUNE_INIT"
sed -n '1,260p' "$AUTOTUNE_INIT" | cat -n

Repository: NVIDIA/Model-Optimizer

Length of output: 23680


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="modelopt/onnx/trt_utils.py"
echo "== trt_utils.py lines 1-70 (imports/try-except) =="
sed -n '1,90p' "$FILE" | cat -n

echo
echo "== trt_utils.py around infer_types_shapes_tensorrt (260-420) =="
sed -n '240,430p' "$FILE" | cat -n

Repository: NVIDIA/Model-Optimizer

Length of output: 12888


Fix no-TensorRT CLI parser test to reload __main__ under the monkeypatched tensorrt context

get_parser is imported at module scope (line 21), so inside test_quantization_cli_parser_imports_without_tensorrt (lines 35-37) the re-import can reuse the already-loaded modelopt.onnx.quantization.__main__ from before sys.modules["tensorrt"] is patched. Reload __main__ after the monkeypatch so the import-time behavior actually reflects “no TensorRT”.

Suggested fix
-        from modelopt.onnx.quantization.__main__ import get_parser as get_parser_no_trt
-
-        parser = get_parser_no_trt()
+        import modelopt.onnx.quantization.__main__ as quant_main
+        importlib.reload(quant_main)
+        parser = quant_main.get_parser()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/onnx/quantization/test_autotune_quantization_integration.py` at
line 21, The test imports get_parser at module scope so re-import inside
test_quantization_cli_parser_imports_without_tensorrt doesn't see the
monkeypatched absence of TensorRT; modify the test to remove or reload
modelopt.onnx.quantization.__main__ after monkeypatching sys.modules["tensorrt"]
(e.g., pop sys.modules["modelopt.onnx.quantization.__main__"] or use
importlib.reload) and then import or call get_parser so the module-level
import-time behavior in __main__ runs under the patched tensorrt context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant