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,12 +29,23 @@ class PluginManager:
|
||||||
self.plugin_classes: Dict[str, Type[BasePlugin]] = {}
|
self.plugin_classes: Dict[str, Type[BasePlugin]] = {}
|
||||||
self.config = self._load_config()
|
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:
|
for plugin_dir in self.PLUGIN_DIRS:
|
||||||
path = Path(plugin_dir).absolute()
|
path = Path(plugin_dir).absolute()
|
||||||
if path.exists() and str(path) not in sys.path:
|
if path.exists() and str(path) not in sys.path:
|
||||||
sys.path.insert(0, str(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:
|
def _load_config(self) -> dict:
|
||||||
"""Load plugin configuration."""
|
"""Load plugin configuration."""
|
||||||
config_path = Path("config/plugins.json")
|
config_path = Path("config/plugins.json")
|
||||||
|
|
@ -90,6 +101,9 @@ class PluginManager:
|
||||||
"""Discover all available plugin classes with error handling."""
|
"""Discover all available plugin classes with error handling."""
|
||||||
discovered = []
|
discovered = []
|
||||||
|
|
||||||
|
# First, ensure plugins package is loaded
|
||||||
|
self._ensure_plugins_package()
|
||||||
|
|
||||||
for plugin_dir in self.PLUGIN_DIRS:
|
for plugin_dir in self.PLUGIN_DIRS:
|
||||||
base_path = Path(plugin_dir)
|
base_path = Path(plugin_dir)
|
||||||
if not base_path.exists():
|
if not base_path.exists():
|
||||||
|
|
@ -110,20 +124,26 @@ class PluginManager:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
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"
|
module_name = f"{plugin_dir}.{item.name}.plugin"
|
||||||
|
|
||||||
if module_name in sys.modules:
|
if module_name in sys.modules:
|
||||||
module = sys.modules[module_name]
|
module = sys.modules[module_name]
|
||||||
else:
|
else:
|
||||||
spec = importlib.util.spec_from_file_location(
|
# Try normal import first
|
||||||
module_name, plugin_file
|
try:
|
||||||
)
|
module = importlib.import_module(module_name)
|
||||||
if not spec or not spec.loader:
|
except ImportError:
|
||||||
continue
|
# Fall back to spec loading
|
||||||
module = importlib.util.module_from_spec(spec)
|
spec = importlib.util.spec_from_file_location(
|
||||||
sys.modules[module_name] = module
|
module_name, plugin_file
|
||||||
spec.loader.exec_module(module)
|
)
|
||||||
|
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
|
# Find plugin class
|
||||||
for attr_name in dir(module):
|
for attr_name in dir(module):
|
||||||
|
|
@ -137,10 +157,24 @@ class PluginManager:
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[PluginManager] Failed to discover {item.name}: {e}")
|
print(f"[PluginManager] Failed to discover {item.name}: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return discovered
|
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:
|
def load_plugin(self, plugin_class: Type[BasePlugin]) -> bool:
|
||||||
"""Instantiate and initialize a plugin with error handling."""
|
"""Instantiate and initialize a plugin with error handling."""
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue