fix: Fix plugin import path for installed plugins

The issue was that installed plugins (calculator, clock_widget, etc.)
could not import 'plugins.base_plugin' because the plugins package
wasn't properly in Python's module path when loading via
spec_from_file_location.

FIX:
1. Added project root to sys.path so 'plugins' package is findable
2. Added _ensure_plugins_package() method to preload plugins module
3. Modified discover_plugins() to try normal import first
4. Added proper error logging with traceback for debugging

This ensures installed plugins can properly import BasePlugin
from plugins.base_plugin as expected.
This commit is contained in:
LemonNexus 2026-02-15 16:06:45 +00:00
parent 2dd9392694
commit 7a3aa9b7f1
1 changed files with 44 additions and 10 deletions

View File

@ -29,12 +29,23 @@ class PluginManager:
self.plugin_classes: Dict[str, Type[BasePlugin]] = {}
self.config = self._load_config()
# Add plugin dirs to path
# Add plugin dirs and project root to path
# Project root needed for 'plugins.base_plugin' imports
project_root = Path(__file__).parent.parent.absolute()
if str(project_root) not in sys.path:
sys.path.insert(0, str(project_root))
for plugin_dir in self.PLUGIN_DIRS:
path = Path(plugin_dir).absolute()
if path.exists() and str(path) not in sys.path:
sys.path.insert(0, str(path))
# Ensure plugins package is loaded
try:
import plugins
except ImportError:
pass # plugins package will be loaded when needed
def _load_config(self) -> dict:
"""Load plugin configuration."""
config_path = Path("config/plugins.json")
@ -90,6 +101,9 @@ class PluginManager:
"""Discover all available plugin classes with error handling."""
discovered = []
# First, ensure plugins package is loaded
self._ensure_plugins_package()
for plugin_dir in self.PLUGIN_DIRS:
base_path = Path(plugin_dir)
if not base_path.exists():
@ -110,12 +124,18 @@ class PluginManager:
continue
try:
# Load the plugin module
# Load the plugin module using normal import
# This ensures proper package resolution
module_name = f"{plugin_dir}.{item.name}.plugin"
if module_name in sys.modules:
module = sys.modules[module_name]
else:
# Try normal import first
try:
module = importlib.import_module(module_name)
except ImportError:
# Fall back to spec loading
spec = importlib.util.spec_from_file_location(
module_name, plugin_file
)
@ -137,10 +157,24 @@ class PluginManager:
except Exception as e:
print(f"[PluginManager] Failed to discover {item.name}: {e}")
import traceback
traceback.print_exc()
continue
return discovered
def _ensure_plugins_package(self):
"""Ensure the plugins package is loaded in sys.modules."""
if 'plugins' not in sys.modules:
try:
import plugins
except ImportError:
# Create a minimal plugins module if it doesn't exist
import types
plugins_module = types.ModuleType('plugins')
plugins_module.__path__ = [str(Path('plugins').absolute())]
sys.modules['plugins'] = plugins_module
def load_plugin(self, plugin_class: Type[BasePlugin]) -> bool:
"""Instantiate and initialize a plugin with error handling."""
try: