jmri_core.plugins
Entry-point-based plugin discovery for connections and extensions.
JMRI's Java code uses Reflections to scan the classpath for Manager
implementations and ConnectionConfig classes. In Python we do this
declaratively via importlib.metadata entry points:
[project.entry-points."jmri.connection"]
loconet = "jmri_loconet:LocoNetConnection"
nce = "jmri_nce:NceConnection"
internal = "jmri_core.hal.internal:InternalConnection"
Each entry point name is the connection prefix string the user sees in
panels and rosters. The loaded value must be a subclass of
jmri_core.hal.connection.SystemConnection.
1"""Entry-point-based plugin discovery for connections and extensions. 2 3JMRI's Java code uses Reflections to scan the classpath for ``Manager`` 4implementations and ``ConnectionConfig`` classes. In Python we do this 5declaratively via ``importlib.metadata`` entry points: 6 7 [project.entry-points."jmri.connection"] 8 loconet = "jmri_loconet:LocoNetConnection" 9 nce = "jmri_nce:NceConnection" 10 internal = "jmri_core.hal.internal:InternalConnection" 11 12Each entry point name is the connection prefix string the user sees in 13panels and rosters. The loaded value must be a subclass of 14:class:`jmri_core.hal.connection.SystemConnection`. 15""" 16from __future__ import annotations 17 18from importlib.metadata import entry_points 19from typing import TYPE_CHECKING 20 21if TYPE_CHECKING: 22 from jmri_core.hal.connection import SystemConnection 23 24CONNECTION_GROUP = "jmri.connection" 25 26 27def load_connections() -> dict[str, type[SystemConnection]]: 28 """Return ``{prefix: connection_class}`` for every registered driver. 29 30 Failures to load a single entry point are silently dropped — a broken 31 driver should never crash the core's discovery pass. Use 32 :func:`load_connections_strict` if you want failures to surface. 33 """ 34 result: dict[str, type[SystemConnection]] = {} 35 for ep in entry_points(group=CONNECTION_GROUP): 36 try: 37 cls = ep.load() 38 except Exception: # noqa: BLE001 — third-party plugin code 39 continue 40 result[ep.name] = cls 41 return result 42 43 44def load_connections_strict() -> dict[str, type[SystemConnection]]: 45 """Like :func:`load_connections` but raises on the first failed entry.""" 46 return {ep.name: ep.load() for ep in entry_points(group=CONNECTION_GROUP)}
CONNECTION_GROUP =
'jmri.connection'
28def load_connections() -> dict[str, type[SystemConnection]]: 29 """Return ``{prefix: connection_class}`` for every registered driver. 30 31 Failures to load a single entry point are silently dropped — a broken 32 driver should never crash the core's discovery pass. Use 33 :func:`load_connections_strict` if you want failures to surface. 34 """ 35 result: dict[str, type[SystemConnection]] = {} 36 for ep in entry_points(group=CONNECTION_GROUP): 37 try: 38 cls = ep.load() 39 except Exception: # noqa: BLE001 — third-party plugin code 40 continue 41 result[ep.name] = cls 42 return result
Return {prefix: connection_class} for every registered driver.
Failures to load a single entry point are silently dropped — a broken
driver should never crash the core's discovery pass. Use
load_connections_strict() if you want failures to surface.
45def load_connections_strict() -> dict[str, type[SystemConnection]]: 46 """Like :func:`load_connections` but raises on the first failed entry.""" 47 return {ep.name: ep.load() for ep in entry_points(group=CONNECTION_GROUP)}
Like load_connections() but raises on the first failed entry.