Resolve duplicate logical plugin names by priority. Multiple installed distributions may expose the same plugin capability (for example CE and EE variants of the Dream plugin). In that case we keep only the highest-priority implementation and skip the rest. If the h
(
candidates: list[tuple[str, MemOSPlugin]],
)
| 50 | |
| 51 | @staticmethod |
| 52 | def _select_plugin_winners( |
| 53 | candidates: list[tuple[str, MemOSPlugin]], |
| 54 | ) -> dict[str, MemOSPlugin]: |
| 55 | """Resolve duplicate logical plugin names by priority. |
| 56 | |
| 57 | Multiple installed distributions may expose the same plugin capability |
| 58 | (for example CE and EE variants of the Dream plugin). In that case we |
| 59 | keep only the highest-priority implementation and skip the rest. |
| 60 | |
| 61 | If the highest priority is shared by more than one plugin implementation, |
| 62 | startup should fail loudly because plugin activation would be ambiguous. |
| 63 | """ |
| 64 | |
| 65 | grouped: dict[str, list[tuple[str, MemOSPlugin]]] = {} |
| 66 | for entry_point_name, plugin in candidates: |
| 67 | grouped.setdefault(plugin.name, []).append((entry_point_name, plugin)) |
| 68 | |
| 69 | winners: dict[str, MemOSPlugin] = {} |
| 70 | for plugin_name, group in grouped.items(): |
| 71 | group.sort(key=lambda item: item[1].priority, reverse=True) |
| 72 | winner_ep_name, winner = group[0] |
| 73 | tied = [item for item in group if item[1].priority == winner.priority] |
| 74 | if len(tied) > 1: |
| 75 | tied_names = ", ".join( |
| 76 | f"{entry_point_name}({plugin.__class__.__name__})" |
| 77 | for entry_point_name, plugin in tied |
| 78 | ) |
| 79 | raise RuntimeError( |
| 80 | "Multiple plugins share the same logical name and highest priority: " |
| 81 | f"name='{plugin_name}', priority={winner.priority}, providers=[{tied_names}]" |
| 82 | ) |
| 83 | |
| 84 | for loser_ep_name, loser in group[1:]: |
| 85 | logger.info( |
| 86 | "Plugin implementation skipped due to lower priority: name=%s, " |
| 87 | "winner=%s(%s, priority=%s), skipped=%s(%s, priority=%s)", |
| 88 | plugin_name, |
| 89 | winner_ep_name, |
| 90 | winner.__class__.__name__, |
| 91 | winner.priority, |
| 92 | loser_ep_name, |
| 93 | loser.__class__.__name__, |
| 94 | loser.priority, |
| 95 | ) |
| 96 | |
| 97 | winners[plugin_name] = winner |
| 98 | return winners |
| 99 | |
| 100 | def discover(self) -> None: |
| 101 | """Discover and load all installed plugins via entry_points.""" |
no test coverage detected