[Docs] Mock all imports for docs (#27873)

Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>
This commit is contained in:
Harry Mellor 2025-11-01 10:02:23 +00:00 committed by GitHub
parent 2c0c7c39bd
commit 799ce45cc1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 49 additions and 23 deletions

View File

@ -3,6 +3,7 @@
import importlib import importlib
import logging import logging
import sys import sys
import traceback
from argparse import SUPPRESS, HelpFormatter from argparse import SUPPRESS, HelpFormatter
from pathlib import Path from pathlib import Path
from typing import Literal from typing import Literal
@ -16,7 +17,30 @@ ROOT_DIR = Path(__file__).parent.parent.parent.parent
ARGPARSE_DOC_DIR = ROOT_DIR / "docs/argparse" ARGPARSE_DOC_DIR = ROOT_DIR / "docs/argparse"
sys.path.insert(0, str(ROOT_DIR)) sys.path.insert(0, str(ROOT_DIR))
# Mock custom op code
class MockCustomOp:
@staticmethod
def register(name):
def decorator(cls):
return cls
return decorator
noop = lambda *a, **k: None
sys.modules["vllm._C"] = MagicMock() sys.modules["vllm._C"] = MagicMock()
sys.modules["vllm.model_executor.custom_op"] = MagicMock(CustomOp=MockCustomOp)
sys.modules["vllm.utils.torch_utils"] = MagicMock(direct_register_custom_op=noop)
# Mock any version checks by reading from compiled CI requirements
with open(ROOT_DIR / "requirements/test.txt") as f:
VERSIONS = dict(line.strip().split("==") for line in f if "==" in line)
importlib.metadata.version = lambda name: VERSIONS.get(name) or "0.0.0"
# Make torch.nn.Parameter safe to inherit from
sys.modules["torch.nn"] = MagicMock(Parameter=object)
class PydanticMagicMock(MagicMock): class PydanticMagicMock(MagicMock):
@ -31,20 +55,17 @@ class PydanticMagicMock(MagicMock):
return core_schema.any_schema() return core_schema.any_schema()
def auto_mock(module, attr, max_mocks=50): def auto_mock(module, attr, max_mocks=100):
"""Function that automatically mocks missing modules during imports.""" """Function that automatically mocks missing modules during imports."""
logger.info("Importing %s from %s", attr, module) logger.info("Importing %s from %s", attr, module)
for _ in range(max_mocks): for _ in range(max_mocks):
try: try:
# First treat attr as an attr, then as a submodule # First treat attr as an attr, then as a submodule
with patch("importlib.metadata.version", return_value="0.0.0"): return getattr(
return getattr( importlib.import_module(module),
importlib.import_module(module), attr,
attr, importlib.import_module(f"{module}.{attr}"),
importlib.import_module(f"{module}.{attr}"), )
)
except importlib.metadata.PackageNotFoundError as e:
raise e
except ModuleNotFoundError as e: except ModuleNotFoundError as e:
logger.info("Mocking %s for argparse doc generation", e.name) logger.info("Mocking %s for argparse doc generation", e.name)
sys.modules[e.name] = PydanticMagicMock(name=e.name) sys.modules[e.name] = PydanticMagicMock(name=e.name)
@ -139,10 +160,19 @@ def create_parser(add_cli_args, **kwargs) -> FlexibleArgumentParser:
Returns: Returns:
FlexibleArgumentParser: A parser with markdown formatting for the class. FlexibleArgumentParser: A parser with markdown formatting for the class.
""" """
parser = FlexibleArgumentParser(add_json_tip=False) try:
parser.formatter_class = MarkdownFormatter parser = FlexibleArgumentParser(add_json_tip=False)
with patch("vllm.config.DeviceConfig.__post_init__"): parser.formatter_class = MarkdownFormatter
_parser = add_cli_args(parser, **kwargs) with patch("vllm.config.DeviceConfig.__post_init__"):
_parser = add_cli_args(parser, **kwargs)
except ModuleNotFoundError as e:
# Auto-mock runtime imports
if tb_list := traceback.extract_tb(e.__traceback__):
path = Path(tb_list[-1].filename).relative_to(ROOT_DIR)
auto_mock(module=".".join(path.parent.parts), attr=path.stem)
return create_parser(add_cli_args, **kwargs)
else:
raise e
# add_cli_args might be in-place so return parser if _parser is None # add_cli_args might be in-place so return parser if _parser is None
return _parser or parser return _parser or parser
@ -184,3 +214,7 @@ def on_startup(command: Literal["build", "gh-deploy", "serve"], dirty: bool):
with open(doc_path, "w", encoding="utf-8") as f: with open(doc_path, "w", encoding="utf-8") as f:
f.write(super(type(parser), parser).format_help()) f.write(super(type(parser), parser).format_help())
logger.info("Argparse generated: %s", doc_path.relative_to(ROOT_DIR)) logger.info("Argparse generated: %s", doc_path.relative_to(ROOT_DIR))
if __name__ == "__main__":
on_startup("build", False)

View File

@ -9,12 +9,4 @@ mkdocs-git-revision-date-localized-plugin
mkdocs-minify-plugin mkdocs-minify-plugin
regex regex
ruff ruff
# Required for argparse hook only
-f https://download.pytorch.org/whl/cpu
cachetools
cloudpickle
py-cpuinfo
msgspec
pydantic pydantic
torch

View File

@ -3,7 +3,7 @@
from collections import UserDict from collections import UserDict
from collections.abc import Callable, Hashable, Iterator, KeysView, Mapping from collections.abc import Callable, Hashable, Iterator, KeysView, Mapping
from types import MappingProxyType from types import MappingProxyType
from typing import Generic, NamedTuple, TypeVar, cast, overload from typing import NamedTuple, TypeVar, cast, overload
import cachetools import cachetools
@ -48,7 +48,7 @@ class CacheInfo(NamedTuple):
) )
class LRUCache(cachetools.LRUCache[_K, _V], Generic[_K, _V]): class LRUCache(cachetools.LRUCache[_K, _V]):
def __init__(self, capacity: float, getsizeof: Callable[[_V], float] | None = None): def __init__(self, capacity: float, getsizeof: Callable[[_V], float] | None = None):
super().__init__(capacity, getsizeof) super().__init__(capacity, getsizeof)