Skip to content

Commit 6a84edf

Browse files
committed
chore: add script to check for lazy imports
Signed-off-by: Aaron Pham <[email protected]>
1 parent 08e70e6 commit 6a84edf

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

.pre-commit-config.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ repos:
115115
entry: python tools/check_spdx_header.py
116116
language: python
117117
types: [python]
118+
- id: check-root-lazy-imports
119+
name: Check root lazy imports
120+
entry: python tools/check_init_lazy_imports.py
121+
language: python
122+
types: [python]
118123
- id: check-filenames
119124
name: Check for spaces in all filenames
120125
entry: bash

tools/check_init_lazy_imports.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3+
"""Ensure we perform lazy loading in vllm/__init__.py.
4+
i.e: appears only within the ``if typing.TYPE_CHECKING:`` guard,
5+
**except** for a short whitelist.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import ast
11+
import pathlib
12+
import sys
13+
from collections.abc import Iterable
14+
from typing import Final
15+
16+
REPO_ROOT: Final = pathlib.Path(__file__).resolve().parent.parent
17+
INIT_PATH: Final = REPO_ROOT / "vllm" / "__init__.py"
18+
19+
# If you need to add items to whitelist, do it here.
20+
ALLOWED_IMPORTS: Final[frozenset[str]] = frozenset({
21+
"vllm.env_override",
22+
})
23+
ALLOWED_FROM_MODULES: Final[frozenset[str]] = frozenset({
24+
".version",
25+
})
26+
27+
28+
def _is_internal(name: str | None, *, level: int = 0) -> bool:
29+
if level > 0:
30+
return True
31+
if name is None:
32+
return False
33+
return name.startswith("vllm.") or name == "vllm"
34+
35+
36+
def _fail(violations: Iterable[tuple[int, str]]) -> None:
37+
print("ERROR: Disallowed eager imports in vllm/__init__.py:\n",
38+
file=sys.stderr)
39+
for lineno, msg in violations:
40+
print(f" Line {lineno}: {msg}", file=sys.stderr)
41+
sys.exit(1)
42+
43+
44+
def main() -> None:
45+
source = INIT_PATH.read_text(encoding="utf-8")
46+
tree = ast.parse(source, filename=str(INIT_PATH))
47+
48+
violations: list[tuple[int, str]] = []
49+
50+
class Visitor(ast.NodeVisitor):
51+
52+
def __init__(self) -> None:
53+
super().__init__()
54+
self._in_type_checking = False
55+
56+
def visit_If(self, node: ast.If) -> None:
57+
guard_is_type_checking = False
58+
test = node.test
59+
if isinstance(test, ast.Attribute) and isinstance(
60+
test.value, ast.Name):
61+
guard_is_type_checking = (test.value.id == "typing"
62+
and test.attr == "TYPE_CHECKING")
63+
elif isinstance(test, ast.Name):
64+
guard_is_type_checking = test.id == "TYPE_CHECKING"
65+
66+
if guard_is_type_checking:
67+
prev = self._in_type_checking
68+
self._in_type_checking = True
69+
for child in node.body:
70+
self.visit(child)
71+
self._in_type_checking = prev
72+
for child in node.orelse:
73+
self.visit(child)
74+
else:
75+
self.generic_visit(node)
76+
77+
def visit_Import(self, node: ast.Import) -> None:
78+
if self._in_type_checking:
79+
return
80+
for alias in node.names:
81+
module_name = alias.name
82+
if _is_internal(
83+
module_name) and module_name not in ALLOWED_IMPORTS:
84+
violations.append((
85+
node.lineno,
86+
f"import '{module_name}' must be inside typing.TYPE_CHECKING", # noqa: E501
87+
))
88+
89+
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
90+
if self._in_type_checking:
91+
return
92+
module_as_written = ("." * node.level) + (node.module or "")
93+
if _is_internal(
94+
node.module, level=node.level
95+
) and module_as_written not in ALLOWED_FROM_MODULES:
96+
violations.append((
97+
node.lineno,
98+
f"from '{module_as_written}' import ... must be inside typing.TYPE_CHECKING", # noqa: E501
99+
))
100+
101+
Visitor().visit(tree)
102+
103+
if violations:
104+
_fail(violations)
105+
106+
107+
if __name__ == "__main__":
108+
main()

0 commit comments

Comments
 (0)