main
py 137 lines 4.36 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 import sys
4 from unittest.mock import MagicMock, patch
5
6 import pytest
7
8 from vllm.utils.import_utils import PlaceholderModule, _has_module, import_plugin
9
10
11 def _raises_module_not_found():
12 return pytest.raises(ModuleNotFoundError, match="No module named")
13
14
15 def test_placeholder_module_error_handling():
16 placeholder = PlaceholderModule("placeholder_1234")
17
18 with _raises_module_not_found():
19 int(placeholder)
20
21 with _raises_module_not_found():
22 placeholder()
23
24 with _raises_module_not_found():
25 _ = placeholder.some_attr
26
27 with _raises_module_not_found():
28 # Test conflict with internal __name attribute
29 _ = placeholder.name
30
31 # OK to print the placeholder or use it in a f-string
32 _ = repr(placeholder)
33 _ = str(placeholder)
34
35 # No error yet; only error when it is used downstream
36 placeholder_attr = placeholder.placeholder_attr("attr")
37
38 with _raises_module_not_found():
39 int(placeholder_attr)
40
41 with _raises_module_not_found():
42 placeholder_attr()
43
44 with _raises_module_not_found():
45 _ = placeholder_attr.some_attr
46
47 with _raises_module_not_found():
48 # Test conflict with internal __module attribute
49 _ = placeholder_attr.module
50
51
52 class TestHasModule:
53 """Tests for _has_module with trial import verification."""
54
55 def setup_method(self):
56 # Clear the @cache between tests so each test gets a fresh call
57 _has_module.cache_clear()
58
59 def test_returns_true_for_importable_stdlib_module(self):
60 assert _has_module("json") is True
61
62 def test_returns_false_for_nonexistent_module(self):
63 assert _has_module("nonexistent_module_xyz_12345") is False
64
65 def test_returns_false_when_find_spec_succeeds_but_import_fails(self):
66 """Simulate a native extension whose shared library is missing.
67
68 ``find_spec`` finds the package on disk, but the actual import
69 raises ``ImportError`` (e.g. missing ``libcudart.so``).
70 """
71 fake_spec = MagicMock()
72
73 with (
74 patch(
75 "vllm.utils.import_utils.importlib.util.find_spec",
76 return_value=fake_spec,
77 ),
78 patch(
79 "vllm.utils.import_utils.importlib.import_module",
80 side_effect=ImportError(
81 "libcudart.so.12: cannot open shared object file"
82 ),
83 ),
84 ):
85 assert _has_module("fake_native_ext") is False
86
87 def test_returns_false_when_find_spec_raises(self):
88 """``find_spec`` itself can raise for dotted names whose parent package
89 fails to import. This should be treated as the module being unavailable.
90 """
91 with patch(
92 "vllm.utils.import_utils.importlib.util.find_spec",
93 side_effect=ModuleNotFoundError("No module named 'fake_parent'"),
94 ):
95 assert _has_module("fake_parent.child") is False
96
97 def test_result_is_cached(self):
98 """Verify the @cache decorator prevents repeated imports."""
99 _has_module("json") # prime the cache
100
101 with patch("vllm.utils.import_utils.importlib.util.find_spec") as mock_spec:
102 result = _has_module("json") # should hit cache
103 mock_spec.assert_not_called()
104 assert result is True
105
106
107 class TestImportPlugin:
108 def test_importing_from_site_packages(self):
109 import json
110
111 result = import_plugin("json")
112 assert result is json
113
114 def test_importing_from_file(self, tmp_path):
115 plugin_file = tmp_path / "my_test_plugin.py"
116 plugin_file.write_text("VALUE = 42\n")
117
118 try:
119 result = import_plugin(str(plugin_file))
120 assert result is not None
121 assert result.VALUE == 42
122 finally:
123 sys.modules.pop("my_test_plugin", None)
124
125 def test_returns_none_when_both_attempts_fail(self):
126 with (
127 patch(
128 "vllm.utils.import_utils.import_from_path",
129 side_effect=FileNotFoundError("no such file"),
130 ),
131 patch(
132 "vllm.utils.import_utils.importlib.import_module",
133 side_effect=ModuleNotFoundError("no such module"),
134 ),
135 ):
136 result = import_plugin("nonexistent_plugin_xyz")
137 assert result is None