Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions plux/runtime/resolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ def find_plugins(self) -> t.List[PluginSpec]:
specs = []
finds = self.entry_points_resolver.get_entry_points().get(self.namespace, [])
for ep in finds:
specs.append(self.to_plugin_spec(ep))
spec = self.to_plugin_spec(ep)
if spec:
specs.append(spec)
return specs

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

self.on_resolve_exception_callback(self.namespace, entry_point, e)
if self.on_resolve_exception_callback:
self.on_resolve_exception_callback(self.namespace, entry_point, e)
6 changes: 6 additions & 0 deletions tests/plugins/invalid_module.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# this module fails when importing to test the fault tolerance of the plugin discovery mechanism
from plux import Plugin


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


class CannotBeLoadedPlugin(Plugin):
namespace = "namespace_2"
name = "cannot-be-loaded"


fail()
67 changes: 67 additions & 0 deletions tests/runtime/test_resolve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import typing as t
from importlib import metadata
from importlib.metadata import EntryPoint
from unittest.mock import MagicMock

import pytest

from plux.runtime.metadata import EntryPointsResolver, build_entry_point_index
from plux.runtime.resolve import MetadataPluginFinder


class DummyEntryPointsResolver(EntryPointsResolver):
entry_points: t.Dict[str, t.List[metadata.EntryPoint]]

def __init__(self, entry_points: t.List[metadata.EntryPoint]):
self.entry_points = build_entry_point_index(entry_points)

def get_entry_points(self) -> t.Dict[str, t.List[metadata.EntryPoint]]:
return self.entry_points


@pytest.fixture
def dummy_entry_point_resolver():
return DummyEntryPointsResolver(
[
EntryPoint(
group="namespace_1",
name="plugin_1",
value="tests.plugins.sample_plugins:plugin_spec_1",
),
EntryPoint(
group="namespace_1",
name="plugin_2",
value="tests.plugins.sample_plugins:plugin_spec_2",
),
EntryPoint(
group="namespace_2",
name="cannot-be-loaded",
value="tests.plugins.invalid_module:CannotBeLoadedPlugin",
),
EntryPoint(
group="namespace_2",
name="simple",
value="tests.plugins.sample_plugins:SimplePlugin",
),
]
)


def test_resolve_error(dummy_entry_point_resolver):
mock = MagicMock()
finder = MetadataPluginFinder(
"namespace_2",
on_resolve_exception_callback=mock,
entry_points_resolver=dummy_entry_point_resolver,
)
plugins = finder.find_plugins()
assert len(plugins) == 1
assert plugins[0].name == "simple"
assert mock.call_count == 1
assert mock.call_args[0][0] == "namespace_2"
assert mock.call_args[0][1] == EntryPoint(
"cannot-be-loaded",
"tests.plugins.invalid_module:CannotBeLoadedPlugin",
"namespace_2",
)
assert str(mock.call_args[0][2]) == "this is an expected exception"