33import importlib
44import logging
55import sys
6+ import traceback
67from argparse import SUPPRESS , HelpFormatter
78from pathlib import Path
89from typing import Literal
1617ARGPARSE_DOC_DIR = ROOT_DIR / "docs/argparse"
1718
1819sys .path .insert (0 , str (ROOT_DIR ))
20+
21+
22+ # Mock custom op code
23+ class MockCustomOp :
24+ @staticmethod
25+ def register (name ):
26+ def decorator (cls ):
27+ return cls
28+
29+ return decorator
30+
31+
32+ noop = lambda * a , ** k : None
1933sys .modules ["vllm._C" ] = MagicMock ()
34+ sys .modules ["vllm.model_executor.custom_op" ] = MagicMock (CustomOp = MockCustomOp )
35+ sys .modules ["vllm.utils.torch_utils" ] = MagicMock (direct_register_custom_op = noop )
36+
37+ # Mock any version checks by reading from compiled CI requirements
38+ with open (ROOT_DIR / "requirements/test.txt" ) as f :
39+ VERSIONS = dict (line .strip ().split ("==" ) for line in f if "==" in line )
40+ importlib .metadata .version = lambda name : VERSIONS .get (name ) or "0.0.0"
41+
42+ # Make torch.nn.Parameter safe to inherit from
43+ sys .modules ["torch.nn" ] = MagicMock (Parameter = object )
2044
2145
2246class PydanticMagicMock (MagicMock ):
@@ -31,20 +55,17 @@ def __get_pydantic_core_schema__(self, source_type, handler):
3155 return core_schema .any_schema ()
3256
3357
34- def auto_mock (module , attr , max_mocks = 50 ):
58+ def auto_mock (module , attr , max_mocks = 100 ):
3559 """Function that automatically mocks missing modules during imports."""
3660 logger .info ("Importing %s from %s" , attr , module )
3761 for _ in range (max_mocks ):
3862 try :
3963 # First treat attr as an attr, then as a submodule
40- with patch ("importlib.metadata.version" , return_value = "0.0.0" ):
41- return getattr (
42- importlib .import_module (module ),
43- attr ,
44- importlib .import_module (f"{ module } .{ attr } " ),
45- )
46- except importlib .metadata .PackageNotFoundError as e :
47- raise e
64+ return getattr (
65+ importlib .import_module (module ),
66+ attr ,
67+ importlib .import_module (f"{ module } .{ attr } " ),
68+ )
4869 except ModuleNotFoundError as e :
4970 logger .info ("Mocking %s for argparse doc generation" , e .name )
5071 sys .modules [e .name ] = PydanticMagicMock (name = e .name )
@@ -139,10 +160,19 @@ def create_parser(add_cli_args, **kwargs) -> FlexibleArgumentParser:
139160 Returns:
140161 FlexibleArgumentParser: A parser with markdown formatting for the class.
141162 """
142- parser = FlexibleArgumentParser (add_json_tip = False )
143- parser .formatter_class = MarkdownFormatter
144- with patch ("vllm.config.DeviceConfig.__post_init__" ):
145- _parser = add_cli_args (parser , ** kwargs )
163+ try :
164+ parser = FlexibleArgumentParser (add_json_tip = False )
165+ parser .formatter_class = MarkdownFormatter
166+ with patch ("vllm.config.DeviceConfig.__post_init__" ):
167+ _parser = add_cli_args (parser , ** kwargs )
168+ except ModuleNotFoundError as e :
169+ # Auto-mock runtime imports
170+ if tb_list := traceback .extract_tb (e .__traceback__ ):
171+ path = Path (tb_list [- 1 ].filename ).relative_to (ROOT_DIR )
172+ auto_mock (module = "." .join (path .parent .parts ), attr = path .stem )
173+ return create_parser (add_cli_args , ** kwargs )
174+ else :
175+ raise e
146176 # add_cli_args might be in-place so return parser if _parser is None
147177 return _parser or parser
148178
@@ -184,3 +214,7 @@ def on_startup(command: Literal["build", "gh-deploy", "serve"], dirty: bool):
184214 with open (doc_path , "w" , encoding = "utf-8" ) as f :
185215 f .write (super (type (parser ), parser ).format_help ())
186216 logger .info ("Argparse generated: %s" , doc_path .relative_to (ROOT_DIR ))
217+
218+
219+ if __name__ == "__main__" :
220+ on_startup ("build" , False )
0 commit comments