Skip to content

Centralize 'trtexec' subprocess runs in ONNX into a single function#1268

Open
gcunhase wants to merge 2 commits intoNVIDIA:mainfrom
gcunhase:dev/gcunhasergio/add_run_trtexec_func
Open

Centralize 'trtexec' subprocess runs in ONNX into a single function#1268
gcunhase wants to merge 2 commits intoNVIDIA:mainfrom
gcunhase:dev/gcunhasergio/add_run_trtexec_func

Conversation

@gcunhase
Copy link
Copy Markdown
Contributor

@gcunhase gcunhase commented Apr 15, 2026

What does this PR do?

Type of change: code improvement

Subprocess is needed to run trtexec commands but those require nosec approval. This PR centralizes it into a single function for the ONNX workflow to avoid future approval triggers. The torch workflow has that centralized in run_command.

Original request: #1259 (comment)

Usage

results = _run_trtexec(cmd)

Testing

N/A

Before your PR is "Ready for review"

  • Is this change backward compatible?: ✅
  • 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?: N/A
  • Did you update Changelog?: N/A

Summary by CodeRabbit

  • Refactor
    • Internal centralization of execution and process handling for benchmarking and engine build/profile flows; improves consistency and maintainability while preserving existing behavior, outputs, and error handling. No user-facing functional changes.

@gcunhase gcunhase requested a review from a team as a code owner April 15, 2026 21:06
@gcunhase gcunhase requested a review from cjluo-nv April 15, 2026 21:06
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 2026

📝 Walkthrough

Walkthrough

Centralized execution of the external trtexec command by adding _run_trtexec in modelopt/onnx/quantization/ort_utils.py; updated callers to delegate trtexec invocation to this helper (benchmark and engine builder), and removed redundant TRTEXEC/TRTEXEC_PATH constants from the TensorRT runtime constants module.

Changes

Cohort / File(s) Summary
Subprocess Execution Utility
modelopt/onnx/quantization/ort_utils.py
Added _run_trtexec(...) which builds/runs trtexec with captured stdout/stderr and optional timeout; refactored _check_for_trtexec to use this helper.
Benchmark Delegation
modelopt/onnx/quantization/autotune/benchmark.py
TrtExecBenchmark.run now delegates execution to _run_trtexec(cmd) instead of calling subprocess.run directly; logging, return-code handling, and latency parsing remain unchanged.
TensorRT Constants Removal
modelopt/torch/_deploy/_runtime/tensorrt/constants.py
Removed TRTEXEC / TRTEXEC_PATH constants (previously "trtexec").
Engine Builder Execution
modelopt/torch/_deploy/_runtime/tensorrt/engine_builder.py
Replaced generic _run_command with _run_trtexec_streamed that prefixes trtexec and accepts trtexec args; updated build_engine and profile_engine to stop embedding TRTEXEC_PATH in commands and to use the new helper.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Security Anti-Patterns ❌ Error The pull request violates security coding practices by using '# nosec' comments to bypass Bandit security checks and lacks timeout protection on subprocess.Popen.wait() calls. Remove '# nosec' comments, add timeout parameter to _run_trtexec_streamed function, and implement timeout exception handling for subprocess.wait() calls.
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 objective: centralizing 'trtexec' subprocess runs into a single function, which is the primary change across the modified files.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Review rate limit: 9/10 reviews remaining, refill in 6 minutes.

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

Comment thread modelopt/onnx/quantization/ort_utils.py Outdated
def _run_trtexec(cmd, timeout=None):
"""Run a 'trtexec' command via subprocess."""
# Ensure that this command is a trtexec run
assert any("trtexec" in c for c in cmd), "Subprocess can only execute 'trtexec' commands"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@kevalmorabia97 please let me know if this is enough to ensure only trtexec runs or if this is even necessary. Thanks.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we not pass trtexec in cmd and only pass arguments to trtexec so this function prepends trtexec to the cmd?

Please add type annotation also

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done, please check

@gcunhase gcunhase changed the title Centralize subprocess runs in ONNX into a single function Centralize 'trtexec' subprocess runs in ONNX into a single function Apr 15, 2026
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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@modelopt/onnx/quantization/ort_utils.py`:
- Around line 47-53: Replace the fragile assert and the banned "# nosec" in
_run_trtexec: validate that cmd is a non-empty sequence and that cmd[0] contains
"trtexec" (or equals "trtexec") using an explicit runtime check (e.g., if not
cmd or "trtexec" not in str(cmd[0]): raise ValueError("...")), then call
subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) without any
"# nosec" comment; keep the function name _run_trtexec and parameters (cmd,
timeout) and raise a clear exception (ValueError/RuntimeError) when validation
fails so Bandit checks are not bypassed.
🪄 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: Pro Plus

Run ID: 7e439fa2-8582-4851-9734-1eec0c8351c0

📥 Commits

Reviewing files that changed from the base of the PR and between 361f7e3 and 6f25809.

📒 Files selected for processing (2)
  • modelopt/onnx/quantization/autotune/benchmark.py
  • modelopt/onnx/quantization/ort_utils.py

Comment thread modelopt/onnx/quantization/ort_utils.py Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 15, 2026

Codecov Report

❌ Patch coverage is 83.33333% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 76.93%. Comparing base (3ad4f4f) to head (01cc557).

Files with missing lines Patch % Lines
modelopt/onnx/quantization/ort_utils.py 75.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #1268       +/-   ##
===========================================
+ Coverage   66.36%   76.93%   +10.57%     
===========================================
  Files         471      471               
  Lines       50504    50506        +2     
===========================================
+ Hits        33515    38855     +5340     
+ Misses      16989    11651     -5338     
Flag Coverage Δ
examples 41.57% <16.66%> (+0.71%) ⬆️
gpu 59.71% <83.33%> (+32.63%) ⬆️
unit 52.83% <33.33%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread modelopt/onnx/quantization/ort_utils.py
Signed-off-by: gcunhase <4861122+gcunhase@users.noreply.github.com>
@gcunhase gcunhase force-pushed the dev/gcunhasergio/add_run_trtexec_func branch from 6f25809 to 01cc557 Compare April 30, 2026 19:16
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.

♻️ Duplicate comments (1)
modelopt/onnx/quantization/ort_utils.py (1)

47-53: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Replace the assert/# nosec gate with explicit validation.

assert is not enforceable at runtime, and the # nosec B603 suppression keeps Bandit bypassed. Validate the executable from cmd[0] explicitly and remove the suppression so this helper actually enforces the trtexec-only contract. As per coding guidelines, Bandit security checks must pass without exceptions.

Proposed fix
-def _run_trtexec(cmd, timeout=None):
+def _run_trtexec(cmd: Sequence[str], timeout: float | None = None):
     """Run a 'trtexec' command via subprocess."""
-    # Ensure that this command is a trtexec run
-    assert any("trtexec" in c for c in cmd), "Subprocess can only execute 'trtexec' commands"
+    if not cmd:
+        raise ValueError("Empty command passed to _run_trtexec")
+    exe = os.path.basename(str(cmd[0])).lower()
+    if not exe.startswith("trtexec"):
+        raise ValueError("Subprocess can only execute 'trtexec' commands")
 
     # Run trtexec command
-    return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)  # nosec B603
+    return subprocess.run(list(cmd), capture_output=True, text=True, timeout=timeout)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@modelopt/onnx/quantization/ort_utils.py` around lines 47 - 53, The helper
_run_trtexec currently uses an assert and a "# nosec" suppression; replace that
with explicit validation: check cmd is a non-empty sequence and validate the
executable by inspecting os.path.basename(cmd[0]) (ensure it equals or contains
"trtexec"), and if the check fails raise a ValueError (or RuntimeError) with a
clear message; then call subprocess.run(cmd, capture_output=True, text=True,
timeout=timeout) without the "# nosec" comment so Bandit can validate it. Ensure
you import os at top if not present and keep function name _run_trtexec and the
subprocess.run usage unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@modelopt/onnx/quantization/ort_utils.py`:
- Around line 47-53: The helper _run_trtexec currently uses an assert and a "#
nosec" suppression; replace that with explicit validation: check cmd is a
non-empty sequence and validate the executable by inspecting
os.path.basename(cmd[0]) (ensure it equals or contains "trtexec"), and if the
check fails raise a ValueError (or RuntimeError) with a clear message; then call
subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) without the
"# nosec" comment so Bandit can validate it. Ensure you import os at top if not
present and keep function name _run_trtexec and the subprocess.run usage
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9f4ea3a4-c8aa-4f4a-985c-e760d68c2329

📥 Commits

Reviewing files that changed from the base of the PR and between 6f25809 and 01cc557.

📒 Files selected for processing (2)
  • modelopt/onnx/quantization/autotune/benchmark.py
  • modelopt/onnx/quantization/ort_utils.py

@gcunhase gcunhase requested a review from a team as a code owner May 1, 2026 16:24
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.

Actionable comments posted: 2

♻️ Duplicate comments (1)
modelopt/onnx/quantization/ort_utils.py (1)

47-60: ⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Remove the # nosec bypass and make the shared helper time-bounded by default.

This helper still introduces a new # nosec suppression, which is a pre-merge policy violation here, and timeout=None means every new caller can block forever on an external process.

Possible direction
+DEFAULT_TRTEXEC_TIMEOUT_S = 300.0
+
 def _run_trtexec(
-    args: list[str] | None = None, timeout: float | None = None
-) -> subprocess.CompletedProcess:
+    args: Sequence[str] | None = None,
+    timeout: float = DEFAULT_TRTEXEC_TIMEOUT_S,
+) -> subprocess.CompletedProcess[str]:
     """Run a 'trtexec' command via subprocess.
@@
     Returns:
         The completed subprocess result.
     """
     cmd = ["trtexec", *(args or [])]
-    return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)  # nosec B603
+    # `cmd[0]` is fixed to `trtexec`; callers may only supply argv items.
+    return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, check=False)

If Bandit still flags the safe wrapper, please route that through the required codeowner/security review instead of reintroducing a suppression.

As per coding guidelines, "Bandit security checks must pass without exceptions. # nosec comments are not allowed as a bypass for security checks," and subprocess execution should enforce limits "by enforcing limits (e.g., timeouts) for external execution."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@modelopt/onnx/quantization/ort_utils.py` around lines 47 - 60, Remove the "#
nosec" suppression and make _run_trtexec time-bounded by default: change the
function signature of _run_trtexec to use a sensible default timeout (e.g.,
timeout: float = 30.0) instead of None, remove the "# nosec B603" comment, and
call subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) so
callers cannot block indefinitely; keep the same cmd construction (cmd =
["trtexec", *(args or [])]) and if Bandit still flags this wrapper escalate to
the security/codeowner review rather than reintroducing a nosec.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@modelopt/onnx/quantization/autotune/benchmark.py`:
- Around line 44-45: The code in TrtExecBenchmark currently delegates to
_run_trtexec but ignores the caller-supplied trtexec_path; update the call sites
in TrtExecBenchmark (and any other callers noted) to pass the provided
trtexec_path into _run_trtexec (or into _check_for_trtexec if that helper
resolves the executable) so custom binary locations are honored; if _run_trtexec
does not accept an executable parameter, add one (e.g., executable or
trtexec_path) and use it when building the command, and ensure the
FileNotFoundError branch and logging still reflect the actual executable
attempted (reference TrtExecBenchmark, _run_trtexec, and _check_for_trtexec).

In `@modelopt/torch/_deploy/_runtime/tensorrt/engine_builder.py`:
- Around line 43-66: The _run_trtexec_streamed function uses p.wait() with no
timeout (and a "# nosec" bypass) which can hang; change it to enforce a timeout
and handle TimeoutExpired: either replace Popen + wait with subprocess.run(cmd,
stdout=log, stderr=log, cwd=..., timeout=TRTEXEC_TIMEOUT) or if using Popen keep
p.communicate(timeout=TRTEXEC_TIMEOUT) and on TimeoutExpired call p.kill(), read
remaining output, and return a failure code and the captured bytes; remove the
"# nosec" bypass and ensure you flush/rewind the temp file (NamedTemporaryFile)
before reading its contents to return combined stdout/stderr and log the output
on non-zero/timeout return.

---

Duplicate comments:
In `@modelopt/onnx/quantization/ort_utils.py`:
- Around line 47-60: Remove the "# nosec" suppression and make _run_trtexec
time-bounded by default: change the function signature of _run_trtexec to use a
sensible default timeout (e.g., timeout: float = 30.0) instead of None, remove
the "# nosec B603" comment, and call subprocess.run(cmd, capture_output=True,
text=True, timeout=timeout) so callers cannot block indefinitely; keep the same
cmd construction (cmd = ["trtexec", *(args or [])]) and if Bandit still flags
this wrapper escalate to the security/codeowner review rather than reintroducing
a nosec.
🪄 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: bc4f915d-eeb0-46f0-839e-1c966372cd61

📥 Commits

Reviewing files that changed from the base of the PR and between 01cc557 and 14ca64a.

📒 Files selected for processing (4)
  • modelopt/onnx/quantization/autotune/benchmark.py
  • modelopt/onnx/quantization/ort_utils.py
  • modelopt/torch/_deploy/_runtime/tensorrt/constants.py
  • modelopt/torch/_deploy/_runtime/tensorrt/engine_builder.py
💤 Files with no reviewable changes (1)
  • modelopt/torch/_deploy/_runtime/tensorrt/constants.py

Comment on lines +44 to 45
from modelopt.onnx.quantization.ort_utils import _check_for_trtexec, _run_trtexec

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 | 🟠 Major | ⚡ Quick win

Preserve the caller-supplied trtexec_path when delegating here.

TrtExecBenchmark still accepts and documents trtexec_path, but _run_trtexec() hardcodes "trtexec", so this call now ignores custom binary paths entirely. That breaks existing callers using a non-PATH install and makes the FileNotFoundError branch misleading.

Possible direction
- from modelopt.onnx.quantization.ort_utils import _check_for_trtexec, _run_trtexec
+ from modelopt.onnx.quantization.ort_utils import _check_for_trtexec, _run_trtexec
@@
-            result = _run_trtexec(cmd)
+            result = _run_trtexec(cmd, executable=self.trtexec_path)
def _run_trtexec(
    args: Sequence[str] | None = None,
    *,
    executable: str = "trtexec",
    timeout: float | None = None,
) -> subprocess.CompletedProcess[str]:
    cmd = [executable, *(args or [])]
    ...

Also applies to: 270-270

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@modelopt/onnx/quantization/autotune/benchmark.py` around lines 44 - 45, The
code in TrtExecBenchmark currently delegates to _run_trtexec but ignores the
caller-supplied trtexec_path; update the call sites in TrtExecBenchmark (and any
other callers noted) to pass the provided trtexec_path into _run_trtexec (or
into _check_for_trtexec if that helper resolves the executable) so custom binary
locations are honored; if _run_trtexec does not accept an executable parameter,
add one (e.g., executable or trtexec_path) and use it when building the command,
and ensure the FileNotFoundError branch and logging still reflect the actual
executable attempted (reference TrtExecBenchmark, _run_trtexec, and
_check_for_trtexec).

Comment on lines +43 to 66
def _run_trtexec_streamed(args: list[str], cwd: Path | None = None) -> tuple[int, bytes]:
"""Run a 'trtexec' command via subprocess, streaming stdout/stderr to a temp file.

This util will not direct stdout and stderr to console if the cmd succeeds.
The 'trtexec' binary is hardcoded as the executable; only its arguments may be supplied
by the caller. This restricts the function to trtexec invocations.

Output handling: stdout and stderr are captured to a temp file and returned as bytes.
On failure (non-zero returncode), the captured output is also logged at ERROR level;
on success, this function emits nothing to the console.

Args:
cmd: the command line list
cwd: current working directory
args: Arguments to pass to trtexec (without the 'trtexec' command itself).
cwd: Optional working directory for the subprocess.

Returns:
return code: 0 means successful, otherwise means failed
log_string: the stdout and stderr output as a string

A tuple of (returncode, output) where output is the combined stdout/stderr bytes.
"""
cmd = ["trtexec", *args]
logging.info(" ".join(cmd))
with NamedTemporaryFile("w+b") as log:
p = subprocess.Popen(cmd, stdout=log, stderr=log, cwd=str(cwd) if cwd else None) # nosec
p = subprocess.Popen( # nosec B603 - cmd[0] is hardcoded "trtexec"
cmd, stdout=log, stderr=log, cwd=str(cwd) if cwd else None
)
p.wait()
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 | 🔴 Critical | 🏗️ Heavy lift

This streamed wrapper still bypasses Bandit and can hang indefinitely.

p.wait() has no timeout, so a stuck trtexec process can block engine build/profile forever, and the new # nosec comment is blocked by repo policy.

Possible direction
-def _run_trtexec_streamed(args: list[str], cwd: Path | None = None) -> tuple[int, bytes]:
+def _run_trtexec_streamed(
+    args: list[str],
+    cwd: Path | None = None,
+    timeout: float = 300.0,
+) -> tuple[int, bytes]:
@@
-        p = subprocess.Popen(  # nosec B603 - cmd[0] is hardcoded "trtexec"
-            cmd, stdout=log, stderr=log, cwd=str(cwd) if cwd else None
-        )
-        p.wait()
+        p = subprocess.Popen(cmd, stdout=log, stderr=log, cwd=str(cwd) if cwd else None)
+        try:
+            p.wait(timeout=timeout)
+        except subprocess.TimeoutExpired:
+            p.kill()
+            p.wait()
+            raise

As per coding guidelines, "Bandit security checks must pass without exceptions. # nosec comments are not allowed as a bypass for security checks," and subprocess execution should enforce limits "by enforcing limits (e.g., timeouts) for external execution."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@modelopt/torch/_deploy/_runtime/tensorrt/engine_builder.py` around lines 43 -
66, The _run_trtexec_streamed function uses p.wait() with no timeout (and a "#
nosec" bypass) which can hang; change it to enforce a timeout and handle
TimeoutExpired: either replace Popen + wait with subprocess.run(cmd, stdout=log,
stderr=log, cwd=..., timeout=TRTEXEC_TIMEOUT) or if using Popen keep
p.communicate(timeout=TRTEXEC_TIMEOUT) and on TimeoutExpired call p.kill(), read
remaining output, and return a failure code and the captured bytes; remove the
"# nosec" bypass and ensure you flush/rewind the temp file (NamedTemporaryFile)
before reading its contents to return combined stdout/stderr and log the output
on non-zero/timeout return.

Comment thread modelopt/torch/_deploy/_runtime/tensorrt/engine_builder.py

import logging
import shutil
import subprocess # nosec
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can you move this import subprocess in the function also?

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.

2 participants