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:
parent
2dd9392694
commit
7a3aa9b7f1
|
|
@ -29,11 +29,22 @@ 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."""
|
||||
|
|
@ -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,20 +124,26 @@ 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:
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
module_name, plugin_file
|
||||
)
|
||||
if not spec or not spec.loader:
|
||||
continue
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[module_name] = module
|
||||
spec.loader.exec_module(module)
|
||||
# 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
|
||||
)
|
||||
if not spec or not spec.loader:
|
||||
continue
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[module_name] = module
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
# Find plugin class
|
||||
for attr_name in dir(module):
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Reference in New Issue