Skip to content

fix(sglang): give the lazily exported handlers real types - #13682

Open
ayaangazali wants to merge 1 commit into
ai-dynamo:mainfrom
ayaangazali:fix/sglang-handlers-getattr-typing
Open

ayaangazali wants to merge 1 commit into
ai-dynamo:mainfrom
ayaangazali:fix/sglang-handlers-getattr-typing

Conversation

@ayaangazali

@ayaangazali ayaangazali commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

dynamo/sglang/request_handlers/__init__.py resolves its eleven handler classes through a module-level __getattr__ so that importing the package does not pull in every backend submodule.

mypy treats a module defining __getattr__ as possibly having any attribute, and here that costs two things rather than one:

  • Every handler resolves as Any for all consumers, so nothing about their constructors or methods is checked at a call site.
  • A typo checks clean, in every module importing from this package.

The first is the bigger loss and is what makes this worth more than typo detection. DecodeWorkerHandler is constructed in the sglang worker paths, and until now mypy had nothing to check those calls against.

Declaring the eleven names under TYPE_CHECKING, mirroring _EXPORTS, and putting __getattr__ behind if not TYPE_CHECKING gives the checker the real classes back.

This completes the set. Same correction @GuanLuo asked for on #13626, already applied to the two dynamo/common packages (#13666), dynamo/vllm/envs.py (#13680) and planner/plugins/transport (#13681). One file and one owner area per PR on purpose; this is the last package in the repo with the pattern.

Validation

probe before after
reveal_type(DecodeWorkerHandler) Any def (engine: Any, config: dynamo.sglang.args.Config, publisher: ... , shutdown_event: ..., enable_frontend_decoding: bool =, first_token_source: ...) -> ...DecodeWorkerHandler
all eleven exported names 11 resolve as Any 0 resolve as Any
import DecodeWorkerHandlr no error has no attribute "DecodeWorkerHandlr"; maybe "DecodeWorkerHandler"? [attr-defined]

The "0 remaining Any" line is the check that matters, and it doubles as the completeness proof that the TYPE_CHECKING block covers _EXPORTS exactly: any name I had missed would still have revealed as Any.

Runtime behaviour unchanged, verified directly:

  • After import dynamo.sglang.request_handlers, sys.modules contains none of the handler submodules. Laziness is the reason this __getattr__ exists, so this was the thing worth proving.
  • _EXPORTS and __all__ are untouched at eleven names each, and every __all__ entry is still covered by _EXPORTS.
  • An unknown attribute still raises AttributeError: module 'dynamo.sglang.request_handlers' has no attribute 'DecodeWorkerHandlr'.

mypy clean on the file. pre-commit run --files initially failed on isort, which reordered the new imports; re-run after applying that, it passes with zero failing hooks.

Not verified here: I have no SGLang installed, so actually constructing a handler raises ModuleNotFoundError on this machine, on this branch and on main alike. The type-level checks above were run against the declarations rather than by instantiating anything.


Open in Devin Review

Summary by CodeRabbit

  • Refactor
    • Improved static type checking for exported request handlers.
    • Preserved existing runtime lazy-loading behavior and error handling.

@ayaangazali
ayaangazali requested a review from a team as a code owner August 22, 2026 23:03
Copilot AI lite review requested due to automatic review settings August 22, 2026 23:03
@copy-pr-bot

copy-pr-bot Bot commented Aug 22, 2026

Copy link
Copy Markdown

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.

@ayaangazali
ayaangazali temporarily deployed to external_collaborator August 22, 2026 23:03 — with GitHub Actions Inactive
@ayaangazali
ayaangazali temporarily deployed to external_collaborator August 22, 2026 23:03 — with GitHub Actions Inactive

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown
Contributor

👋 Hi ayaangazali! Thank you for contributing to ai-dynamo/dynamo.

Just a reminder: The NVIDIA Test Github Validation CI runs an essential subset of the testing framework to quickly catch errors.Your PR reviewers may elect to test the changes comprehensively before approving your changes.

🚀

@github-actions github-actions Bot added external-contribution Pull request is from an external contributor fix backend::sglang Relates to the sglang backend labels Aug 22, 2026

@devin-ai-integration devin-ai-integration 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.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@coderabbitai

coderabbitai Bot commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

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: 9503ec6b-0333-42b9-bab8-95b4ec683076

📥 Commits

Reviewing files that changed from the base of the PR and between d13d464 and 1ab00dd.

📒 Files selected for processing (1)
  • components/src/dynamo/sglang/request_handlers/__init__.py

Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.


Walkthrough

The request handler module adds TYPE_CHECKING declarations for lazily exported handler classes. Runtime __getattr__ remains responsible for validation, dynamic imports, caching, and errors outside type-checking execution.

Changes

Typed lazy handler exports

Layer / File(s) Summary
Handler export typing and runtime loading
components/src/dynamo/sglang/request_handlers/__init__.py
The module declares exported handler classes for static analysis without importing their modules at runtime. The runtime-only __getattr__ preserves existing lookup, import, caching, and error behavior.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 1ab00

This change restores static types for lazily exported SGLang handlers without changing runtime imports or behavior. No actionable merge-blocking risk remains after normal checks and review.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description explains the change and validation clearly, but it omits the required Related Issues section and the reviewer-start guidance. Add the required Related Issues section with either an issue link or the confirmed no-related-issue checkbox, and identify where reviewers should start.
✅ Passed checks (3 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly and concisely describes the main change: assigning real types to lazily exported SGLang handlers.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

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

@ayaangazali

Copy link
Copy Markdown
Contributor Author

The lychee red here is the action failing to install itself, not a broken link. No link was checked.

From the job log, the failure is in lychee-setup and the two steps that would do the actual work never ran:

##[start-action id=lychee.lychee-setup]
Downloading from: https://github.com/lycheeverse/lychee/releases/download/lychee-v0.24.2/
                  lychee-x86_64-unknown-linux-gnu.tar.gz
##[error]Process completed with exit code 35.
##[end-action id=lychee.lychee-setup;outcome=failure]

##[start-action display=Install lychee] -> outcome=skipped
##[start-action display=Run Lychee]     -> outcome=skipped

Exit 35 is a TLS connect failure fetching the release tarball. Three things put it outside this PR:

The asset is fine. That same URL returns 200 on retry, so the release was not removed and this is a transient network failure on the runner rather than a bad reference.

Run Lychee is marked skipped, so the link checker never executed. Nothing in the repository was examined, which means no diff could have caused this.

My four sibling PRs all pass lychee right now: #13666, #13646, #13680 and #13681. Two of those were opened within minutes of this one and are the same shape of change, a Python __init__.py with no links in it.

Not pushing anything. There is no code fix for a failed binary download, and a speculative commit would only add noise. This needs a re-run.

Worth separating from the other lychee failure I reported on #13626 last week, which was a genuine 404 on a link in the SGLang multimodal page and which #13651 fixed. That one was real and this one is not; the tell is whether Run Lychee executed or was skipped.

`dynamo/sglang/request_handlers/__init__.py` resolves its eleven handler
classes through a module-level `__getattr__` so that importing the package does
not pull in every backend submodule.

mypy treats a module that defines `__getattr__` as possibly having any
attribute, which costs two things here rather than one:

- every handler resolves as `Any` for all consumers, so nothing about their
  constructors or methods is checked at a call site
- a typo such as `DecodeWorkerHandlr` checks clean, in every module importing
  from this package

Declare the eleven names under `TYPE_CHECKING`, mirroring `_EXPORTS`, and put
`__getattr__` behind `if not TYPE_CHECKING`. The checker then sees the real
classes, so `DecodeWorkerHandler` resolves to its actual constructor signature
instead of `Any`, and an unknown name is reported with a suggestion.

No runtime change. Importing the package still pulls in none of the handler
submodules, `_EXPORTS` and `__all__` are untouched at eleven names each, and an
unknown attribute still raises `AttributeError`.

Signed-off-by: ayaangazali <ayaangazali.work@gmail.com>
@ayaangazali
ayaangazali force-pushed the fix/sglang-handlers-getattr-typing branch from 1ab00dd to f5b8864 Compare August 23, 2026 03:05
@ayaangazali
ayaangazali temporarily deployed to external_collaborator August 23, 2026 03:05 — with GitHub Actions Inactive
@ayaangazali

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (now f5b8864ea) purely to trigger a fresh CI run, since the lychee red could not clear on its own.

The diagnosis in my earlier comment is unchanged: the failure was in lychee-setup, downloading the action's own binary, with Install lychee and Run Lychee both marked skipped. Nothing in the repository was checked, so nothing in this diff could have caused it, and there is no code fix for a failed download.

Confirmed it was safe before force-pushing rather than assuming: no maintainer has posted /ok to test on this PR, so no vetted run is being discarded, the previous head was my own commit rather than a maintainer's, and nothing upstream has touched request_handlers/__init__.py since the branch point, so the diff is unchanged.

Re-verified the behaviour on the new base: importing the package still pulls in none of the handler submodules, and __all__ is still eleven names.

@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions Bot added the Stale label Sep 22, 2026

This branch was previously deployed

1 inactive deployment
external_collaborator — f5b8864e Deployed Aug 23, 2026 by ayaangazali via ok-to-test #11889
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend::sglang Relates to the sglang backend external-contribution Pull request is from an external contributor fix size/M Stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants