Skip to content

[None][infra] Run mypy type check in the build stage without pre-commit - #17560

Merged
brnguyen2 merged 5 commits into
NVIDIA:mainfrom
brnguyen2:type-check-in-build-stage
Aug 13, 2026
Merged

[None][infra] Run mypy type check in the build stage without pre-commit#17560
brnguyen2 merged 5 commits into
NVIDIA:mainfrom
brnguyen2:type-check-in-build-stage

Conversation

@brnguyen2

@brnguyen2 brnguyen2 commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Dev Engineer Review

  • Moves mypy from GPU test nodes to the x86_64 vanilla build.
  • Runs scripts/run_mypy.sh after compiled bindings are produced.
  • Uses MYPY_REQUIRE_BINDINGS=1 to retain binding import checks.
  • Supports driverless build pods with a CUDA toolkit stub library.
  • Distinguishes missing bindings from unloadable bindings and reports tracebacks.
  • Removes obsolete type-check plumbing from jenkins/L0_Test.groovy.
  • Defines the shared mypy file set in pyproject.toml.
  • Updates the pre-commit hook to use the shared file set without remote hook fetches.
  • Restores type-check coverage for the current sampler modules.
  • No test-list files changed.

QA Engineer Review

No test changes.

Description

The Run type check stage invoked mypy through python3 -m pre_commit run type-check --all-files on a GPU test node (PackageSanityCheck). Before running
the selected hook, pre-commit clones every remote hook repo listed in
.pre-commit-config.yaml (it reads each repo's manifest), so a single failed
git fetch to github.com aborts the whole stage — even though type-check
itself is a language: script hook that runs a local, network-free mypy wrapper
(scripts/run_mypy.sh). Test nodes without reliable github access hit this
intermittently:

[INFO] Initializing environment for https://github.com/pycqa/isort.
CalledProcessError: ('git', 'fetch', 'origin', '--tags') return code: 128
fatal: could not read Username for 'https://github.com': No such device or address

This is transient infra, not a real type error.

What this PR does:

  1. Moves the type check into the build stage (jenkins/Build.groovy), right
    after build_wheel.py produces the compiled bindings in place, gated to run
    once on the x86_64 vanilla config. Importing tensorrt_llm.bindings does not
    require a physical GPU — the existing CPU-* unit-test stages already import
    the package with system_gpu_count=0; it only needs the driver/runtime
    libraries, which the build image provides. The stage is dropped from
    jenkins/L0_Test.groovy along with the now-unused typeCheck plumbing.

  2. Invokes mypy directly via scripts/run_mypy.sh instead of through
    pre-commit, so the step does zero network. mypy and its dependencies come from
    requirements-dev.txt, which the build node installs from the internal PyPI
    mirror. MYPY_REQUIRE_BINDINGS=1 keeps the hard-fail behavior if bindings
    can't be imported.

  3. Single-sources the checked file set in pyproject.toml under
    [tool.mypy] files. The pre-commit hook now runs mypy with no explicit paths
    (pass_filenames: false) so it reads the same list; its files: pattern only
    gates when the hook triggers. This removes the duplicated list that lived in a
    .pre-commit-config.yaml anchor.

  4. Restores sampler type-check coverage that was silently dropped when
    sampling_utils.py was split into per-feature modules ([TRTLLM-14736][chore] Split the sampler package into per-feature modules #16981): the old anchor
    still referenced sampling_utils.py and ops/interface.py (both removed),
    which a regex silently ignores but an explicit list cannot. They are replaced
    with the current sampler modules (sampler_strategy.py, sampler_common.py,
    logprobs.py, finish_reasons.py, top_p_decay.py, penalties.py).

Test Coverage

  • The relocated Run type check stage exercises the same scripts/run_mypy.sh
    full (compiled-bindings) mypy pass over the sampler source and test modules
    listed in pyproject.toml [tool.mypy] files.
  • Locally verified: .pre-commit-config.yaml passes pre-commit validate-config;
    mypy with no explicit paths correctly targets the [tool.mypy] files set; the
    lightweight pass over the newly-covered sampler modules is clean. The
    authoritative full strict + bindings check runs in this PR's build stage.

PR Checklist

  • Please check this after reviewing the above items as appropriate for this PR.

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7756653f-f032-4da4-8ff8-e6e911ac6986

📥 Commits

Reviewing files that changed from the base of the PR and between c82fb81 and 4cc7602.

📒 Files selected for processing (1)
  • scripts/run_mypy.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/run_mypy.sh

Walkthrough

The PR centralizes mypy file discovery in pyproject.toml, enables binding-aware type checks in the x86_64 vanilla build, adds CUDA driver-stub handling, and removes type-check execution from platform test flows.

Changes

Sampler type-checking pipeline

Layer / File(s) Summary
Centralize mypy file discovery
.pre-commit-config.yaml, pyproject.toml
The mypy hook uses the sampler file set defined in pyproject.toml and triggers on sampler source or test changes.
Run mypy after wheel compilation
jenkins/Build.groovy, scripts/run_mypy.sh
The x86_64 vanilla build runs mypy after wheel compilation. The script requires compiled bindings, supports CUDA driver stubs, and reports binding import diagnostics.
Remove type checking from platform tests
jenkins/L0_Test.groovy
Platform test wrappers and callers no longer pass or execute the removed type-check control.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: dpitman-nvda

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description check ✅ Passed The description explains the issue, solution, test coverage, and includes the required PR checklist section.
Title check ✅ Passed The title follows the required format and clearly identifies the infrastructure change to run mypy during the build stage.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🤖 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 `@jenkins/Build.groovy`:
- Around line 453-457: Ensure binding-aware mypy validation runs when typeCheck
is enabled even if buildOrCache returns a reused artifact before runLLMBuild.
Add the check after cached bindings are made importable in the cache-hit path,
or disable artifact reuse for that selected type-check build, while preserving
the existing runLLMBuild validation for fresh builds.

In `@pyproject.toml`:
- Around line 888-909: Add the standard NVIDIA copyright header at the beginning
of pyproject.toml, using 2026 as the latest meaningful modification year, while
preserving the existing configuration content unchanged.
- Around line 888-893: Update the mypy configuration comment in pyproject.toml
to remove the obsolete static-analysis-files anchor reference and explain that
pre-commit sets pass_filenames: false, so both CI and pre-commit use the
[tool.mypy].files list.
🪄 Autofix

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: 06369e90-c705-4b67-bf69-46a30ccf5235

📥 Commits

Reviewing files that changed from the base of the PR and between 6deb48c and e8f9f4e.

📒 Files selected for processing (4)
  • .pre-commit-config.yaml
  • jenkins/Build.groovy
  • jenkins/L0_Test.groovy
  • pyproject.toml

Comment thread jenkins/Build.groovy
Comment thread pyproject.toml Outdated
Comment thread pyproject.toml Outdated
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65592 [ run ] triggered by Bot. Commit: e8f9f4e Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65592 [ run ] completed with state FAILURE. Commit: e8f9f4e
/LLM/main/L0_MergeRequest_PR pipeline #53324 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

Comment thread jenkins/Build.groovy
@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run --skip-test

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🤖 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 `@scripts/run_mypy.sh`:
- Around line 24-29: Update the NVIDIA copyright header in scripts/run_mypy.sh
to use 2026, reflecting the latest meaningful modification; leave the script
behavior unchanged.
- Around line 53-56: Update the CUDA stub discovery logic around the stub
assignment to iterate through the candidate paths and select the first one
passing [[ -f "$candidate" ]], rather than piping ls output. Ensure unmatched
globs do not trigger set -e/pipefail termination, and retain the existing
empty-stub return behavior when no candidate exists.
- Around line 60-63: Update the CUDA stub setup in the script to discover a
matching stub without allowing a failed ls/head pipeline to trigger set -e,
while preserving the fallback behavior when none exists. Replace the fixed
TMPDIR-based stub_dir with an owner-only mktemp -d directory created per
invocation, link libcuda.so.1 there, and ensure the directory is removed after
mypy exits by avoiding exec mypy or arranging cleanup in a parent process.
🪄 Autofix

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: 8de5d25d-1d96-4cde-89eb-c64c4f491fc5

📥 Commits

Reviewing files that changed from the base of the PR and between 2f240cd and a5d0fbe.

📒 Files selected for processing (1)
  • scripts/run_mypy.sh

Comment thread scripts/run_mypy.sh
Comment thread scripts/run_mypy.sh Outdated
Comment thread scripts/run_mypy.sh Outdated
@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run --skip-test

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65607 [ run ] triggered by Bot. Commit: c82fb81 Link to invocation

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

CodeRabbit review — addressed in 4cc7602:

Fixed (both were real, thank you):

  • run_mypy.sh:56 — handle an absent CUDA stub without exiting. Correct, and the failure mode was worse than "returns nonzero": under set -o pipefail the unmatched glob made stub=$(ls ... | head -1) nonzero, so set -e aborted the script and the [[ -z "$stub" ]] fallback right below it was unreachable. A machine with no CUDA toolkit got a bare exit instead of the actionable import error. Now iterates the candidate paths with [[ -f ]], which also settles SC2012.
  • run_mypy.sh:70 — owner-only temporary directory. Also correct. Everything on LD_LIBRARY_PATH is loaded into this process, so a fixed path under a shared /tmp would let any local user pre-create it and plant their own libcuda.so.1. Now mktemp -d per invocation (0700) removed via an EXIT trap; mypy is consequently invoked without exec so the trap runs, and the script still exits with mypy's status.

Not applicable:

  • run_mypy.sh:29 — update the copyright year to 2026. Line 2 already reads Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
  • pyproject.toml:893 — correct the pre-commit documentation. Already done in 2f240cd; the comment no longer mentions static-analysis-files and states that the hook sets pass_filenames: false so both paths read [tool.mypy] files.
  • pyproject.toml:909 — add the NVIDIA copyright header. pyproject.toml has never carried one on main, and no other build-config file in the repo does. Adding one here would be inconsistent rather than compliant.

Open question for a human, not addressed:

  • Build.groovy:457 — mypy on reused build artifacts. The observation is accurate: buildOrCache returns early when reuseArtifactPath is set, so runLLMBuild and the type check do not run on a cache hit. I have deliberately not changed this, because artifact reuse is keyed to an unchanged commit, and mypy is deterministic over the same source — the pipeline that produced those artifacts ran the check for that commit. The residual risk is a chain where the producing pipeline itself reused artifacts. Forcing the vanilla x86_64 build to skip reuse would close it at the cost of a full build on every run, which seems the wrong trade for a deterministic static check. Happy to be overruled.

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65637 [ run ] triggered by Bot. Commit: 4cc7602 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65607 [ run ] completed with state ABORTED. Commit: c82fb81

Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65637 [ run ] completed with state SUCCESS. Commit: 4cc7602
/LLM/main/L0_MergeRequest_PR pipeline #53359 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65687 [ run ] triggered by Bot. Commit: 4cc7602 Link to invocation

@brnguyen2
brnguyen2 requested a review from BowenFu August 12, 2026 21:37
Comment thread scripts/run_mypy.sh

@tburt-nv tburt-nv left a comment

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.

Only reviewing pyproject.toml, approved.

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65687 [ run ] completed with state FAILURE. Commit: 4cc7602
/LLM/main/L0_MergeRequest_PR pipeline #53406 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot skip --comment "Infra-only PR (jenkins groovy, pre-commit, mypy config, run_mypy.sh); no product code. Pipeline 53406 on tip 4cc7602: both build stages green (validates the relocated build-stage mypy check on x86_64+SBSA), SBSA tests 11/11, 162/168 x86 single-GPU green. The 3 failing stages are known main-side flakes with documented cross-user history: A100X test_llm_pytorch part0 (0.8%), DGX_H100 _torch/multimodal ucx-reset (6.8%), RTXPro6000D test_ptp_quickstart_bert (4.9%). None relate to type-check/build/orchestration."

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65757 [ skip ] triggered by Bot. Commit: 4cc7602 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65757 [ skip ] completed with state SUCCESS. Commit: 4cc7602
Skipping testing for commit 4cc7602

Link to invocation

The "Run type check" stage ran mypy through `pre_commit run type-check`
on a GPU test node. pre-commit clones every remote hook repo in
.pre-commit-config.yaml before running the selected hook, so a single
failed `git fetch` to github.com aborts the stage even though the
type-check hook itself is a local, network-free mypy wrapper. Test nodes
without reliable github access hit this intermittently.

Move the type check into the build stage, right after build_wheel.py
produces the compiled bindings in place, and invoke scripts/run_mypy.sh
directly instead of through pre-commit. This drops the github hook-repo
clone entirely; mypy and its dependencies come from requirements-dev.txt,
which the build node installs from the internal PyPI mirror. Importing
tensorrt_llm.bindings does not require a GPU (the CPU unit-test stages
already import the package with no GPU), only the driver/runtime
libraries that the build image provides.

Single-source the checked file set in pyproject.toml [tool.mypy] `files`.
The pre-commit hook now runs mypy with no explicit paths (pass_filenames:
false) so it reads the same list; its `files:` pattern only gates when
the hook triggers. This removes the duplicated list that previously lived
in a .pre-commit-config.yaml anchor.

Restore type-check coverage of the sampler modules that was dropped when
sampling_utils.py was split into per-feature modules: the anchor still
listed sampling_utils.py and ops/interface.py, both removed, which the
regex silently ignored. Replace them with the current sampler modules.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
… is present

The relocated type-check stage fails on the CPU build pod: it has no
libcuda.so.1, so `import tensorrt_llm.bindings` cannot even be loaded and
MYPY_REQUIRE_BINDINGS=1 hard-fails the build.

The PR description justified the move by noting the CPU-* unit-test stages
already import the package with system_gpu_count=0. That is true but conflates
two different things. bindings.*.so links against the CUDA driver API -- the
cuMem* virtual-memory and cuMulticast* NVLink-multicast paths, which have no
CUDA runtime-API equivalent -- so it carries a DT_NEEDED on libcuda.so.1.
DT_NEEDED is resolved when the loader maps the library, before any of its code
runs, so the import dies in dlopen regardless of whether a device is present.
The CPU test stages have no GPU but do have the driver libraries; the build pod
has neither. What the import needs is the driver library, not a GPU.

Measured on a driverless node (no libcuda.so.1 in ldconfig, no /dev/nvidia*)
against a clean build: without libcuda.so.1 the load fails in create_module
with "libcuda.so.1: cannot open shared object file"; with the CUDA toolkit's
stub libcuda.so exposed as libcuda.so.1 the module loads and initializes
cleanly, while torch reports device_count 0 and warns that the driver is a stub
library. So nothing on the import path calls a driver function or needs a
device -- only symbol resolution does, which is exactly what the toolkit ships
the stub for.

Detect the missing driver and put the stub on LD_LIBRARY_PATH under its SONAME
before attempting the import. A machine with a real driver is left untouched.
The stub directory has a fixed path so repeated runs reuse it.

Also split the failure message, which previously reported "no compiled bindings
found" for what was really a load failure -- the bindings were built, they just
could not be mapped. It now points at the traceback and distinguishes a missing
build from an unloadable one.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
… gate

Review fixes:

- Stub discovery used `ls ... | head -n 1`. Under `set -o pipefail` an unmatched
  glob makes the whole pipeline nonzero, so `set -e` aborted the script before
  the "no stub found" branch could run -- that fallback was unreachable, and a
  machine with no CUDA toolkit at all got a bare exit instead of the actionable
  import error. Iterate the candidate paths and test each with `[[ -f ]]`.
  Also settles shellcheck SC2012.
- The stub directory was a fixed path under $TMPDIR. Everything on
  LD_LIBRARY_PATH is loaded into this process, so a predictable directory on a
  shared /tmp lets any local user pre-create it and plant their own
  libcuda.so.1. Use `mktemp -d` (owner-only) per invocation and remove it via an
  EXIT trap. mypy is consequently invoked without `exec`, so the trap runs; the
  script still exits with mypy's status.

Also document what the import gate does and does not establish. It stands in
for three conditions that fail independently -- the extension was built, the
compiled artifacts agree with each other (a bindings.*.so built against a
different libth_common.so fails here with an undefined symbol, which is why
this is an import and not a file test), and the runtime dependencies are
installed so mypy reads real inline types rather than Any. It establishes
nothing about the .pyi stubs, which are what mypy actually consumes; those are
generated at build time and not checked in, so a stub-generation regression
would leave the gate green. Asserting stub presence is left as a follow-up
rather than folded in, since the two catch disjoint failures.

Adds TODO(TRTLLM-15310) to evaluate a stronger type checker than mypy.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run

@brnguyen2
brnguyen2 force-pushed the type-check-in-build-stage branch from 4cc7602 to 3cf56cd Compare August 13, 2026 03:29
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65775 [ run ] triggered by Bot. Commit: 3cf56cd Link to invocation

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot skip --comment "already skipped"

@brnguyen2
brnguyen2 enabled auto-merge (squash) August 13, 2026 03:58
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65780 [ skip ] triggered by Bot. Commit: 3cf56cd Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65775 [ run ] completed with state ABORTED. Commit: 3cf56cd

Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #65780 [ skip ] completed with state SUCCESS. Commit: 3cf56cd
Skipping testing for commit 3cf56cd

Link to invocation

@brnguyen2
brnguyen2 merged commit 0232413 into NVIDIA:main Aug 13, 2026
8 checks passed
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.

5 participants