Skip to content

Commit 3edca52

Browse files
committed
Replace ManualPluginLoader with ListPluginLoader
1 parent 0239156 commit 3edca52

File tree

5 files changed

+192
-154
lines changed

5 files changed

+192
-154
lines changed

tests/conftest.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
from collections.abc import Generator
2+
13
import pytest
24

35
from variantlib.plugins.loader import BasePluginLoader
4-
from variantlib.plugins.loader import ManualPluginLoader
6+
from variantlib.plugins.loader import ListPluginLoader
57

68

79
@pytest.fixture(scope="session")
@@ -16,8 +18,6 @@ def mocked_plugin_apis() -> list[str]:
1618
@pytest.fixture(scope="session")
1719
def mocked_plugin_loader(
1820
mocked_plugin_apis: list[str],
19-
) -> BasePluginLoader:
20-
loader = ManualPluginLoader()
21-
for plugin_api in mocked_plugin_apis:
22-
loader.load_plugin(plugin_api)
23-
return loader
21+
) -> Generator[BasePluginLoader]:
22+
with ListPluginLoader(mocked_plugin_apis) as loader:
23+
yield loader

tests/test_api.py

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
from variantlib.models.configuration import VariantConfiguration as VConfigurationModel
4444
from variantlib.models.metadata import ProviderInfo
4545
from variantlib.models.metadata import VariantMetadata
46-
from variantlib.plugins.loader import ManualPluginLoader
46+
from variantlib.plugins.loader import ListPluginLoader
4747
from variantlib.pyproject_toml import VariantPyProjectToml
4848
from variantlib.variants_json import VariantsJson
4949

@@ -236,26 +236,24 @@ def test_validation_result_properties():
236236

237237

238238
def test_validate_variant(mocked_plugin_apis: list[str]):
239-
plugin_loader = ManualPluginLoader()
240-
for plugin_api in mocked_plugin_apis:
241-
plugin_loader.load_plugin(plugin_api)
242-
# Verify whether the variant properties are valid
243-
res = validate_variant(
244-
VariantDescription(
245-
[
246-
VariantProperty("test_namespace", "name1", "val1d"),
247-
VariantProperty("test_namespace", "name2", "val2d"),
248-
VariantProperty("test_namespace", "name3", "val3a"),
249-
VariantProperty("second_namespace", "name3", "val3a"),
250-
VariantProperty("incompatible_namespace", "flag1", "on"),
251-
VariantProperty("incompatible_namespace", "flag2", "off"),
252-
VariantProperty("incompatible_namespace", "flag5", "on"),
253-
VariantProperty("missing_namespace", "name", "val"),
254-
VariantProperty("private", "build_type", "debug"),
255-
]
256-
),
257-
plugin_loader=plugin_loader,
258-
)
239+
with ListPluginLoader(mocked_plugin_apis) as plugin_loader:
240+
# Verify whether the variant properties are valid
241+
res = validate_variant(
242+
VariantDescription(
243+
[
244+
VariantProperty("test_namespace", "name1", "val1d"),
245+
VariantProperty("test_namespace", "name2", "val2d"),
246+
VariantProperty("test_namespace", "name3", "val3a"),
247+
VariantProperty("second_namespace", "name3", "val3a"),
248+
VariantProperty("incompatible_namespace", "flag1", "on"),
249+
VariantProperty("incompatible_namespace", "flag2", "off"),
250+
VariantProperty("incompatible_namespace", "flag5", "on"),
251+
VariantProperty("missing_namespace", "name", "val"),
252+
VariantProperty("private", "build_type", "debug"),
253+
]
254+
),
255+
plugin_loader=plugin_loader,
256+
)
259257

260258
assert res == VariantValidationResult(
261259
{

tests/test_plugins.py

Lines changed: 101 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import json
4+
import logging
45
import re
56
import sys
67
from abc import ABC
@@ -23,7 +24,7 @@
2324
from variantlib.models.variant import VariantProperty
2425
from variantlib.plugins.loader import BasePluginLoader
2526
from variantlib.plugins.loader import EntryPointPluginLoader
26-
from variantlib.plugins.loader import ManualPluginLoader
27+
from variantlib.plugins.loader import ListPluginLoader
2728
from variantlib.plugins.loader import PluginLoader
2829
from variantlib.protocols import PluginType
2930
from variantlib.protocols import VariantFeatureConfigType
@@ -113,26 +114,18 @@ def test_get_supported_configs(
113114
}
114115

115116

116-
def test_manual_loading(mocked_plugin_apis: list[str]):
117-
loader = ManualPluginLoader()
118-
for plugin_api in mocked_plugin_apis:
119-
loader.load_plugin(plugin_api)
120-
121-
assert list(loader.get_supported_configs().keys()) == [
122-
"test_namespace",
123-
"second_namespace",
124-
]
125-
126-
127117
def test_namespace_clash():
128-
loader = ManualPluginLoader()
129-
loader.load_plugin("tests.mocked_plugins:MockedPluginA")
130-
with pytest.raises(
131-
RuntimeError,
132-
match="Two plugins found using the same namespace test_namespace. Refusing to "
133-
"proceed.",
118+
with (
119+
pytest.raises(
120+
RuntimeError,
121+
match="Two plugins found using the same namespace test_namespace. Refusing "
122+
"to proceed.",
123+
),
124+
ListPluginLoader(
125+
["tests.mocked_plugins:MockedPluginA", "tests.test_plugins:ClashingPlugin"]
126+
),
134127
):
135-
loader.load_plugin("tests.test_plugins:ClashingPlugin")
128+
pass
136129

137130

138131
class IncorrectListTypePlugin(ExceptionPluginBase):
@@ -144,16 +137,16 @@ class IncorrectListTypePlugin(ExceptionPluginBase):
144137

145138
@pytest.mark.parametrize("method", ["get_all_configs", "get_supported_configs"])
146139
def test_get_configs_incorrect_list_type(method: str):
147-
loader = ManualPluginLoader()
148-
loader.load_plugin("tests.test_plugins:IncorrectListTypePlugin")
149-
150-
with pytest.raises(
151-
PluginError,
152-
match=r".*"
153-
+ re.escape(
154-
f"Provider exception_test, {method}() method returned incorrect type. "
155-
"Expected list[variantlib.protocols.VariantFeatureConfigType], "
156-
"got <class 'tuple'>"
140+
with (
141+
ListPluginLoader(["tests.test_plugins:IncorrectListTypePlugin"]) as loader,
142+
pytest.raises(
143+
PluginError,
144+
match=r".*"
145+
+ re.escape(
146+
f"Provider exception_test, {method}() method returned incorrect type. "
147+
"Expected list[variantlib.protocols.VariantFeatureConfigType], "
148+
"got <class 'tuple'>"
149+
),
157150
),
158151
):
159152
getattr(loader, method)()
@@ -164,13 +157,13 @@ class IncorrectListLengthPlugin(ExceptionPluginBase):
164157

165158

166159
def test_get_all_configs_incorrect_list_length():
167-
loader = ManualPluginLoader()
168-
loader.load_plugin("tests.test_plugins:IncorrectListLengthPlugin")
169-
170-
with pytest.raises(
171-
ValueError,
172-
match=r"Provider exception_test, get_all_configs\(\) method returned no valid "
173-
r"configs",
160+
with (
161+
ListPluginLoader(["tests.test_plugins:IncorrectListLengthPlugin"]) as loader,
162+
pytest.raises(
163+
ValueError,
164+
match=r"Provider exception_test, get_all_configs\(\) method returned "
165+
r"no valid configs",
166+
),
174167
):
175168
loader.get_all_configs()
176169

@@ -181,38 +174,44 @@ class IncorrectListMemberTypePlugin(ExceptionPluginBase):
181174

182175
@pytest.mark.parametrize("method", ["get_all_configs", "get_supported_configs"])
183176
def test_get_configs_incorrect_list_member_type(method: str):
184-
loader = ManualPluginLoader()
185-
loader.load_plugin("tests.test_plugins:IncorrectListMemberTypePlugin")
186-
187-
with pytest.raises(
188-
PluginError,
189-
match=r".*"
190-
+ re.escape(
191-
f"Provider exception_test, {method}() method returned incorrect type. "
192-
"Expected list[variantlib.protocols.VariantFeatureConfigType], "
193-
"got list[typing.Union[variantlib.protocols.VariantFeatureConfigType, "
194-
)
195-
+ r"(dict, int|int, dict)",
177+
with (
178+
ListPluginLoader(
179+
["tests.test_plugins:IncorrectListMemberTypePlugin"]
180+
) as loader,
181+
pytest.raises(
182+
PluginError,
183+
match=r".*"
184+
+ re.escape(
185+
f"Provider exception_test, {method}() method returned incorrect type. "
186+
"Expected list[variantlib.protocols.VariantFeatureConfigType], "
187+
"got list[typing.Union[variantlib.protocols.VariantFeatureConfigType, "
188+
)
189+
+ r"(dict, int|int, dict)",
190+
),
196191
):
197192
getattr(loader, method)()
198193

199194

200-
def test_namespace_missing_module():
201-
with pytest.raises(
202-
PluginError,
203-
match=r"Loading the plugin from 'tests.no_such_module:foo' failed: "
204-
r"No module named 'tests.no_such_module'",
205-
):
206-
ManualPluginLoader().load_plugin("tests.no_such_module:foo")
195+
def test_namespace_missing_module(caplog: pytest.CapLogFixture):
196+
caplog.set_level(logging.DEBUG)
197+
with ListPluginLoader(["tests.no_such_module:foo"]):
198+
pass
199+
assert caplog.records[-1].exc_info[0] == PluginError
200+
assert (
201+
"Loading the plugin from 'tests.no_such_module:foo' failed: "
202+
"No module named 'tests.no_such_module'"
203+
) in str(caplog.records[-1].exc_info[1])
207204

208205

209-
def test_namespace_incorrect_name():
210-
with pytest.raises(
211-
PluginError,
212-
match=r"Loading the plugin from 'tests.test_plugins:no_such_name' "
213-
r"failed: module 'tests.test_plugins' has no attribute 'no_such_name'",
214-
):
215-
ManualPluginLoader().load_plugin("tests.test_plugins:no_such_name")
206+
def test_namespace_incorrect_name(caplog: pytest.CapLogFixture):
207+
caplog.set_level(logging.DEBUG)
208+
with ListPluginLoader([("tests.test_plugins:no_such_name")]):
209+
pass
210+
assert caplog.records[-1].exc_info[0] == PluginError
211+
assert (
212+
"Loading the plugin from 'tests.test_plugins:no_such_name' "
213+
"failed: module 'tests.test_plugins' has no attribute 'no_such_name'"
214+
) in str(caplog.records[-1].exc_info[1])
216215

217216

218217
class IncompletePlugin:
@@ -222,13 +221,14 @@ def get_supported_configs(self) -> list[VariantFeatureConfigType]:
222221
return []
223222

224223

225-
def test_namespace_incorrect_type():
226-
with pytest.raises(
227-
PluginError,
228-
match=r"'tests.test_plugins:RANDOM_STUFF' points at a value that "
229-
r"is not callable: 123",
230-
):
231-
ManualPluginLoader().load_plugin("tests.test_plugins:RANDOM_STUFF")
224+
def test_namespace_incorrect_type(caplog: pytest.CapLogFixture):
225+
caplog.set_level(logging.DEBUG)
226+
with ListPluginLoader(["tests.test_plugins:RANDOM_STUFF"]):
227+
pass
228+
assert caplog.records[-1].exc_info[0] == PluginError
229+
assert (
230+
"'tests.test_plugins:RANDOM_STUFF' points at a value that is not callable: 123"
231+
) in str(caplog.records[-1].exc_info[1])
232232

233233

234234
class RaisingInstantiationPlugin:
@@ -244,16 +244,15 @@ def get_supported_configs(self) -> list[VariantFeatureConfigType]:
244244
return []
245245

246246

247-
def test_namespace_instantiation_raises():
248-
with pytest.raises(
249-
PluginError,
250-
match=r"Instantiating the plugin from "
251-
r"'tests.test_plugins:RaisingInstantiationPlugin' failed: "
252-
r"I failed to initialize",
253-
):
254-
ManualPluginLoader().load_plugin(
255-
"tests.test_plugins:RaisingInstantiationPlugin"
256-
)
247+
def test_namespace_instantiation_raises(caplog: pytest.CapLogFixture):
248+
caplog.set_level(logging.DEBUG)
249+
with ListPluginLoader(["tests.test_plugins:RaisingInstantiationPlugin"]):
250+
pass
251+
assert (
252+
"Instantiating the plugin from "
253+
"'tests.test_plugins:RaisingInstantiationPlugin' failed: "
254+
"I failed to initialize"
255+
) in str(caplog.records[-1].exc_info[1])
257256

258257

259258
class CrossTypeInstantiationPlugin:
@@ -270,15 +269,20 @@ def get_supported_configs(self) -> list[VariantFeatureConfigType]:
270269

271270

272271
@pytest.mark.parametrize("cls", ["IncompletePlugin", "CrossTypeInstantiationPlugin"])
273-
def test_namespace_instantiation_returns_incorrect_type(cls: type):
274-
with pytest.raises(
275-
PluginError,
276-
match=rf"Instantiating the plugin from 'tests.test_plugins:{cls}' "
277-
r"returned an object that does not meet the PluginType prototype: "
278-
r"<tests.test_plugins.IncompletePlugin object at .*> "
279-
r"\(missing attributes: get_all_configs\)",
280-
):
281-
ManualPluginLoader().load_plugin(f"tests.test_plugins:{cls}")
272+
def test_namespace_instantiation_returns_incorrect_type(
273+
cls: type, caplog: pytest.CapLogFixture
274+
):
275+
caplog.set_level(logging.DEBUG)
276+
with ListPluginLoader([f"tests.test_plugins:{cls}"]):
277+
pass
278+
assert (
279+
f"Instantiating the plugin from 'tests.test_plugins:{cls}' "
280+
"returned an object that does not meet the PluginType prototype: "
281+
"<tests.test_plugins.IncompletePlugin object at "
282+
) in str(caplog.records[-1].exc_info[1])
283+
assert ("(missing attributes: get_all_configs)") in str(
284+
caplog.records[-1].exc_info[1]
285+
)
282286

283287

284288
def test_get_build_setup(
@@ -327,29 +331,19 @@ def test_namespaces(
327331
]
328332

329333

330-
def test_load_plugin():
331-
loader = ManualPluginLoader()
332-
loader.load_plugin("tests.mocked_plugins:IndirectPath.MoreIndirection.plugin_a")
333-
assert loader.namespaces == ["test_namespace"]
334-
335-
loader.load_plugin("tests.mocked_plugins:IndirectPath.MoreIndirection.plugin_b")
336-
assert loader.namespaces == ["test_namespace", "second_namespace"]
337-
338-
339-
def test_manual_plugin_loader_as_context_manager():
340-
with ManualPluginLoader() as loader:
341-
loader.load_plugin("tests.mocked_plugins:IndirectPath.MoreIndirection.plugin_a")
342-
assert loader.namespaces == ["test_namespace"]
343-
344-
loader.load_plugin("tests.mocked_plugins:IndirectPath.MoreIndirection.plugin_b")
334+
def test_non_class_attrs():
335+
with ListPluginLoader(
336+
[
337+
"tests.mocked_plugins:IndirectPath.MoreIndirection.plugin_a",
338+
"tests.mocked_plugins:IndirectPath.MoreIndirection.plugin_b",
339+
]
340+
) as loader:
345341
assert loader.namespaces == ["test_namespace", "second_namespace"]
346342

347-
assert not loader.namespaces
348-
349343

350344
def test_load_plugin_invalid_arg():
351-
with pytest.raises(ValidationError):
352-
ManualPluginLoader().load_plugin("tests.mocked_plugins")
345+
with pytest.raises(ValidationError), ListPluginLoader(["tests.mocked_plugins"]):
346+
pass
353347

354348

355349
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)