fix(plugins): put a (re)loading plugin's directory first on sys.path (#663)

Plugins import their own files by bare name (`from sports import ...`),
which resolves to the first directory on sys.path that has the file. The
loader added a plugin's directory only if it was missing, so on a reload --
a live re-enable from the web UI -- the plugin's directory stayed behind
every plugin loaded since, and its bare imports found their files first.

Seen on ledpi: re-enabling UFC with hockey running failed with "cannot
import name '_status_is_final' from 'sports'" (it got hockey's sports.py).
A loading plugin's directory is now always moved to the front. Every
scoreboard ships its own sports.py, so any of them was exposed on reload.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-28 15:02:01 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent b8c01c69fb
commit 8557eff88a
3 changed files with 75 additions and 4 deletions
+14 -4
View File
@@ -592,11 +592,21 @@ class PluginLoader:
raise PluginError(error_msg, plugin_id=plugin_id, context={'entry_file': str(entry_file)})
with self._module_load_lock:
# Add plugin directory to sys.path if not already there
# Put this plugin's directory first on sys.path -- moving it there
# if it is already present. Plugins import their own modules by
# bare name (``from sports import ...``), and those resolve to the
# first directory that has the file. A directory added on an
# earlier load stays where it was, so reloading a plugin (a live
# re-enable from the web UI) after another scoreboard had loaded
# found that one's sports.py first and failed on a name only its
# own copy has.
plugin_dir_str = str(plugin_dir)
if plugin_dir_str not in sys.path:
sys.path.insert(0, plugin_dir_str)
self.logger.debug("Added plugin %s's directory to sys.path", plugin_id)
try:
sys.path.remove(plugin_dir_str)
except ValueError:
pass
sys.path.insert(0, plugin_dir_str)
self.logger.debug("Put plugin %s's directory first on sys.path", plugin_id)
# Import the plugin module
module_name = f"plugin_{plugin_id.replace('-', '_')}"