From f5b8864ea7c6e3f2636896123eec87d99107785d Mon Sep 17 00:00:00 2001 From: ayaangazali Date: Sat, 22 Aug 2026 16:03:00 -0700 Subject: [PATCH] fix(sglang): give the lazily exported handlers real types `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 --- .../sglang/request_handlers/__init__.py | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/components/src/dynamo/sglang/request_handlers/__init__.py b/components/src/dynamo/sglang/request_handlers/__init__.py index 0c1fde3e2bc8..790b38c00341 100644 --- a/components/src/dynamo/sglang/request_handlers/__init__.py +++ b/components/src/dynamo/sglang/request_handlers/__init__.py @@ -2,7 +2,23 @@ # SPDX-License-Identifier: Apache-2.0 from importlib import import_module -from typing import Any +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + # Declared, not imported at runtime. These mirror `_EXPORTS` below; without + # them the module-level `__getattr__` is the only thing that resolves the + # handlers, and hiding it from the type checker would make them all look + # missing. + from .embedding import EmbeddingWorkerHandler + from .handler_base import BaseGenerativeHandler, BaseWorkerHandler + from .image_diffusion import ImageDiffusionWorkerHandler + from .llm import DecodeWorkerHandler, DiffusionWorkerHandler, PrefillWorkerHandler + from .multimodal import ( + MultimodalEncodeWorkerHandler, + MultimodalPrefillWorkerHandler, + MultimodalWorkerHandler, + ) + from .video_generation import VideoGenerationWorkerHandler _EXPORTS = { # Base handlers @@ -25,14 +41,22 @@ } -def __getattr__(name: str) -> Any: - if name not in _EXPORTS: - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") +# Hidden from the type checker on purpose. A module-level `__getattr__` tells +# mypy this package may have any attribute, so a typo such as +# `from ... request_handlers import DecodeWorkerHandlr` would check clean in +# every module that imports from here, and the real handlers resolve as `Any`. +# The `TYPE_CHECKING` block above declares them, so the checker gains their +# actual types and loses nothing. +if not TYPE_CHECKING: + + def __getattr__(name: str) -> Any: + if name not in _EXPORTS: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") - module = import_module(_EXPORTS[name], __name__) - value = getattr(module, name) - globals()[name] = value - return value + module = import_module(_EXPORTS[name], __name__) + value = getattr(module, name) + globals()[name] = value + return value __all__ = [