Skip to content

Commit 5c03c3f

Browse files
authored
fix null pointer errors in MetadataPluginFinder (#21)
1 parent 2a7c1a4 commit 5c03c3f

File tree

3 files changed

+78
-2
lines changed

3 files changed

+78
-2
lines changed

plux/runtime/resolve.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ def find_plugins(self) -> t.List[PluginSpec]:
3535
specs = []
3636
finds = self.entry_points_resolver.get_entry_points().get(self.namespace, [])
3737
for ep in finds:
38-
specs.append(self.to_plugin_spec(ep))
38+
spec = self.to_plugin_spec(ep)
39+
if spec:
40+
specs.append(spec)
3941
return specs
4042

4143
def to_plugin_spec(self, entry_point: EntryPoint) -> PluginSpec:
@@ -51,4 +53,5 @@ def to_plugin_spec(self, entry_point: EntryPoint) -> PluginSpec:
5153
"error resolving PluginSpec for plugin %s.%s", self.namespace, entry_point.name
5254
)
5355

54-
self.on_resolve_exception_callback(self.namespace, entry_point, e)
56+
if self.on_resolve_exception_callback:
57+
self.on_resolve_exception_callback(self.namespace, entry_point, e)

tests/plugins/invalid_module.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
# this module fails when importing to test the fault tolerance of the plugin discovery mechanism
2+
from plux import Plugin
23

34

45
def fail():
56
raise ValueError("this is an expected exception")
67

78

9+
class CannotBeLoadedPlugin(Plugin):
10+
namespace = "namespace_2"
11+
name = "cannot-be-loaded"
12+
13+
814
fail()

tests/runtime/test_resolve.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import typing as t
2+
from importlib import metadata
3+
from importlib.metadata import EntryPoint
4+
from unittest.mock import MagicMock
5+
6+
import pytest
7+
8+
from plux.runtime.metadata import EntryPointsResolver, build_entry_point_index
9+
from plux.runtime.resolve import MetadataPluginFinder
10+
11+
12+
class DummyEntryPointsResolver(EntryPointsResolver):
13+
entry_points: t.Dict[str, t.List[metadata.EntryPoint]]
14+
15+
def __init__(self, entry_points: t.List[metadata.EntryPoint]):
16+
self.entry_points = build_entry_point_index(entry_points)
17+
18+
def get_entry_points(self) -> t.Dict[str, t.List[metadata.EntryPoint]]:
19+
return self.entry_points
20+
21+
22+
@pytest.fixture
23+
def dummy_entry_point_resolver():
24+
return DummyEntryPointsResolver(
25+
[
26+
EntryPoint(
27+
group="namespace_1",
28+
name="plugin_1",
29+
value="tests.plugins.sample_plugins:plugin_spec_1",
30+
),
31+
EntryPoint(
32+
group="namespace_1",
33+
name="plugin_2",
34+
value="tests.plugins.sample_plugins:plugin_spec_2",
35+
),
36+
EntryPoint(
37+
group="namespace_2",
38+
name="cannot-be-loaded",
39+
value="tests.plugins.invalid_module:CannotBeLoadedPlugin",
40+
),
41+
EntryPoint(
42+
group="namespace_2",
43+
name="simple",
44+
value="tests.plugins.sample_plugins:SimplePlugin",
45+
),
46+
]
47+
)
48+
49+
50+
def test_resolve_error(dummy_entry_point_resolver):
51+
mock = MagicMock()
52+
finder = MetadataPluginFinder(
53+
"namespace_2",
54+
on_resolve_exception_callback=mock,
55+
entry_points_resolver=dummy_entry_point_resolver,
56+
)
57+
plugins = finder.find_plugins()
58+
assert len(plugins) == 1
59+
assert plugins[0].name == "simple"
60+
assert mock.call_count == 1
61+
assert mock.call_args[0][0] == "namespace_2"
62+
assert mock.call_args[0][1] == EntryPoint(
63+
"cannot-be-loaded",
64+
"tests.plugins.invalid_module:CannotBeLoadedPlugin",
65+
"namespace_2",
66+
)
67+
assert str(mock.call_args[0][2]) == "this is an expected exception"

0 commit comments

Comments
 (0)