More work on pluggy.
This commit is contained in:
parent
084d4fe85f
commit
dfe20fca30
@ -1018,6 +1018,8 @@ class Cthulhu(GObject.Object):
|
|||||||
self.APIHelper = APIHelper(self)
|
self.APIHelper = APIHelper(self)
|
||||||
self.createCompatAPI()
|
self.createCompatAPI()
|
||||||
self.pluginSystemManager = plugin_system_manager.PluginSystemManager(self)
|
self.pluginSystemManager = plugin_system_manager.PluginSystemManager(self)
|
||||||
|
# Scan for available plugins at startup
|
||||||
|
self.pluginSystemManager.rescanPlugins()
|
||||||
def getAPIHelper(self):
|
def getAPIHelper(self):
|
||||||
return self.APIHelper
|
return self.APIHelper
|
||||||
def getPluginSystemManager(self):
|
def getPluginSystemManager(self):
|
||||||
|
@ -16,6 +16,7 @@ try:
|
|||||||
import pluggy
|
import pluggy
|
||||||
cthulhu_hookimpl = pluggy.HookimplMarker("cthulhu")
|
cthulhu_hookimpl = pluggy.HookimplMarker("cthulhu")
|
||||||
PLUGGY_AVAILABLE = True
|
PLUGGY_AVAILABLE = True
|
||||||
|
logging.getLogger(__name__).info("Successfully imported pluggy")
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# Fallback if pluggy is not available
|
# Fallback if pluggy is not available
|
||||||
def cthulhu_hookimpl(func=None, **kwargs):
|
def cthulhu_hookimpl(func=None, **kwargs):
|
||||||
@ -29,7 +30,9 @@ except ImportError:
|
|||||||
return lambda f: f
|
return lambda f: f
|
||||||
return func
|
return func
|
||||||
PLUGGY_AVAILABLE = False
|
PLUGGY_AVAILABLE = False
|
||||||
logging.getLogger(__name__).info("Pluggy not available, plugins will be disabled")
|
logging.getLogger(__name__).warning("Pluggy not available, plugins will be disabled")
|
||||||
|
import traceback
|
||||||
|
logging.getLogger(__name__).debug(traceback.format_exc())
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -22,7 +22,12 @@ except ImportError:
|
|||||||
PLUGGY_AVAILABLE = False
|
PLUGGY_AVAILABLE = False
|
||||||
logging.getLogger(__name__).info("Pluggy not available, plugins will be disabled")
|
logging.getLogger(__name__).info("Pluggy not available, plugins will be disabled")
|
||||||
|
|
||||||
|
# Set to True for more detailed plugin loading debug info
|
||||||
|
PLUGIN_DEBUG = True
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
if PLUGIN_DEBUG:
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
class PluginType(IntEnum):
|
class PluginType(IntEnum):
|
||||||
"""Types of plugins we support."""
|
"""Types of plugins we support."""
|
||||||
@ -74,9 +79,11 @@ class PluginSystemManager:
|
|||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
self.app = app
|
self.app = app
|
||||||
|
logger.info("Initializing PluginSystemManager")
|
||||||
|
|
||||||
# Initialize plugin manager
|
# Initialize plugin manager
|
||||||
if PLUGGY_AVAILABLE:
|
if PLUGGY_AVAILABLE:
|
||||||
|
logger.info("Pluggy is available, setting up plugin manager")
|
||||||
self.plugin_manager = pluggy.PluginManager("cthulhu")
|
self.plugin_manager = pluggy.PluginManager("cthulhu")
|
||||||
|
|
||||||
# Define hook specifications
|
# Define hook specifications
|
||||||
@ -93,8 +100,10 @@ class PluginSystemManager:
|
|||||||
"""Called when the plugin is deactivated."""
|
"""Called when the plugin is deactivated."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
logger.info("Adding hook specifications to plugin manager")
|
||||||
self.plugin_manager.add_hookspecs(CthulhuHookSpecs)
|
self.plugin_manager.add_hookspecs(CthulhuHookSpecs)
|
||||||
else:
|
else:
|
||||||
|
logger.warning("Pluggy is not available, plugins will be disabled")
|
||||||
self.plugin_manager = None
|
self.plugin_manager = None
|
||||||
|
|
||||||
# Plugin storage
|
# Plugin storage
|
||||||
@ -103,6 +112,10 @@ class PluginSystemManager:
|
|||||||
|
|
||||||
# Create plugin directories
|
# Create plugin directories
|
||||||
self._setup_plugin_dirs()
|
self._setup_plugin_dirs()
|
||||||
|
|
||||||
|
# Log available plugins directory paths
|
||||||
|
logger.info(f"System plugins directory: {PluginType.SYSTEM.get_root_dir()}")
|
||||||
|
logger.info(f"User plugins directory: {PluginType.USER.get_root_dir()}")
|
||||||
|
|
||||||
def _setup_plugin_dirs(self):
|
def _setup_plugin_dirs(self):
|
||||||
"""Ensure plugin directories exist."""
|
"""Ensure plugin directories exist."""
|
||||||
@ -190,7 +203,25 @@ class PluginSystemManager:
|
|||||||
|
|
||||||
def setActivePlugins(self, activePlugins):
|
def setActivePlugins(self, activePlugins):
|
||||||
"""Set active plugins and sync their state."""
|
"""Set active plugins and sync their state."""
|
||||||
|
logger.info(f"Setting active plugins: {activePlugins}")
|
||||||
|
|
||||||
|
# Make sure we have scanned for plugins first
|
||||||
|
if not self._plugins:
|
||||||
|
logger.info("No plugins found, rescanning...")
|
||||||
|
self.rescanPlugins()
|
||||||
|
|
||||||
self._active_plugins = activePlugins
|
self._active_plugins = activePlugins
|
||||||
|
|
||||||
|
# Log active vs available plugins
|
||||||
|
available_plugins = [p.get_module_name() for p in self.plugins]
|
||||||
|
logger.info(f"Available plugins: {available_plugins}")
|
||||||
|
logger.info(f"Active plugins: {self._active_plugins}")
|
||||||
|
|
||||||
|
# Find missing plugins
|
||||||
|
missing_plugins = [p for p in self._active_plugins if p not in available_plugins]
|
||||||
|
if missing_plugins:
|
||||||
|
logger.warning(f"Active plugins not found: {missing_plugins}")
|
||||||
|
|
||||||
self.syncAllPluginsActive()
|
self.syncAllPluginsActive()
|
||||||
|
|
||||||
def setPluginActive(self, pluginInfo, active):
|
def setPluginActive(self, pluginInfo, active):
|
||||||
@ -211,25 +242,52 @@ class PluginSystemManager:
|
|||||||
|
|
||||||
def isPluginActive(self, pluginInfo):
|
def isPluginActive(self, pluginInfo):
|
||||||
"""Check if a plugin is active."""
|
"""Check if a plugin is active."""
|
||||||
|
module_name = pluginInfo.get_module_name()
|
||||||
|
|
||||||
|
# Builtin plugins are always active
|
||||||
if pluginInfo.builtin:
|
if pluginInfo.builtin:
|
||||||
|
logger.debug(f"Plugin {module_name} is builtin, active by default")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# If a plugin is already loaded, it's active
|
||||||
if pluginInfo.loaded:
|
if pluginInfo.loaded:
|
||||||
|
logger.debug(f"Plugin {module_name} is already loaded, considered active")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return pluginInfo.get_module_name() in self.getActivePlugins()
|
# Check if plugin is in the active plugins list
|
||||||
|
is_active = module_name in self.getActivePlugins()
|
||||||
|
logger.debug(f"Plugin {module_name} active status: {is_active}")
|
||||||
|
return is_active
|
||||||
|
|
||||||
def syncAllPluginsActive(self):
|
def syncAllPluginsActive(self):
|
||||||
"""Sync the active state of all plugins."""
|
"""Sync the active state of all plugins."""
|
||||||
|
logger.info("Syncing active state of all plugins")
|
||||||
|
|
||||||
|
# Log plugin status before syncing
|
||||||
|
if PLUGIN_DEBUG:
|
||||||
|
for pluginInfo in self.plugins:
|
||||||
|
is_active = self.isPluginActive(pluginInfo)
|
||||||
|
is_loaded = pluginInfo.loaded
|
||||||
|
logger.debug(f"Plugin {pluginInfo.get_module_name()}: active={is_active}, loaded={is_loaded}")
|
||||||
|
|
||||||
# First unload inactive plugins
|
# First unload inactive plugins
|
||||||
for pluginInfo in self.plugins:
|
for pluginInfo in self.plugins:
|
||||||
if not self.isPluginActive(pluginInfo) and pluginInfo.loaded:
|
if not self.isPluginActive(pluginInfo) and pluginInfo.loaded:
|
||||||
|
logger.info(f"Unloading inactive plugin: {pluginInfo.get_module_name()}")
|
||||||
self.unloadPlugin(pluginInfo)
|
self.unloadPlugin(pluginInfo)
|
||||||
|
|
||||||
# Then load active plugins
|
# Then load active plugins
|
||||||
for pluginInfo in self.plugins:
|
for pluginInfo in self.plugins:
|
||||||
if self.isPluginActive(pluginInfo) and not pluginInfo.loaded:
|
if self.isPluginActive(pluginInfo) and not pluginInfo.loaded:
|
||||||
self.loadPlugin(pluginInfo)
|
logger.info(f"Loading active plugin: {pluginInfo.get_module_name()}")
|
||||||
|
result = self.loadPlugin(pluginInfo)
|
||||||
|
logger.info(f"Plugin {pluginInfo.get_module_name()} load result: {result}")
|
||||||
|
|
||||||
|
# Log final plugin status
|
||||||
|
active_plugins = [p.get_module_name() for p in self.plugins if p.loaded]
|
||||||
|
logger.info(f"Active plugins after sync: {active_plugins}")
|
||||||
|
inactive_plugins = [p.get_module_name() for p in self.plugins if not p.loaded]
|
||||||
|
logger.info(f"Inactive plugins after sync: {inactive_plugins}")
|
||||||
|
|
||||||
def loadPlugin(self, pluginInfo):
|
def loadPlugin(self, pluginInfo):
|
||||||
"""Load a plugin."""
|
"""Load a plugin."""
|
||||||
@ -239,15 +297,26 @@ class PluginSystemManager:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
module_name = pluginInfo.get_module_name()
|
module_name = pluginInfo.get_module_name()
|
||||||
|
logger.info(f"Attempting to load plugin: {module_name}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Already loaded?
|
# Already loaded?
|
||||||
if pluginInfo.loaded:
|
if pluginInfo.loaded:
|
||||||
|
logger.info(f"Plugin {module_name} already loaded, skipping")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Import plugin module
|
# Import plugin module
|
||||||
plugin_file = os.path.join(pluginInfo.get_module_dir(), "plugin.py")
|
plugin_file = os.path.join(pluginInfo.get_module_dir(), "plugin.py")
|
||||||
|
if not os.path.exists(plugin_file):
|
||||||
|
logger.error(f"Plugin file not found: {plugin_file}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info(f"Loading plugin from: {plugin_file}")
|
||||||
spec = importlib.util.spec_from_file_location(module_name, plugin_file)
|
spec = importlib.util.spec_from_file_location(module_name, plugin_file)
|
||||||
|
if spec is None:
|
||||||
|
logger.error(f"Failed to create spec for plugin: {module_name}")
|
||||||
|
return False
|
||||||
|
|
||||||
module = importlib.util.module_from_spec(spec)
|
module = importlib.util.module_from_spec(spec)
|
||||||
spec.loader.exec_module(module)
|
spec.loader.exec_module(module)
|
||||||
pluginInfo.module = module
|
pluginInfo.module = module
|
||||||
@ -260,6 +329,7 @@ class PluginSystemManager:
|
|||||||
attr.__module__ == module.__name__ and
|
attr.__module__ == module.__name__ and
|
||||||
hasattr(attr, 'activate')):
|
hasattr(attr, 'activate')):
|
||||||
plugin_class = attr
|
plugin_class = attr
|
||||||
|
logger.info(f"Found plugin class: {attr.__name__} in {module_name}")
|
||||||
break
|
break
|
||||||
|
|
||||||
if not plugin_class:
|
if not plugin_class:
|
||||||
@ -267,32 +337,47 @@ class PluginSystemManager:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Create and initialize plugin instance
|
# Create and initialize plugin instance
|
||||||
|
logger.info(f"Creating instance of plugin class: {plugin_class.__name__}")
|
||||||
plugin_instance = plugin_class()
|
plugin_instance = plugin_class()
|
||||||
pluginInfo.instance = plugin_instance
|
pluginInfo.instance = plugin_instance
|
||||||
|
|
||||||
# Ensure plugins have a reference to the app
|
# Ensure plugins have a reference to the app
|
||||||
plugin_instance.app = self.getApp()
|
plugin_instance.app = self.getApp()
|
||||||
|
logger.info(f"Set app reference for plugin: {module_name}")
|
||||||
|
|
||||||
if hasattr(plugin_instance, 'set_app'):
|
if hasattr(plugin_instance, 'set_app'):
|
||||||
plugin_instance.set_app(self.getApp())
|
plugin_instance.set_app(self.getApp())
|
||||||
|
logger.info(f"Called set_app() for plugin: {module_name}")
|
||||||
|
|
||||||
if hasattr(plugin_instance, 'set_plugin_info'):
|
if hasattr(plugin_instance, 'set_plugin_info'):
|
||||||
plugin_instance.set_plugin_info(pluginInfo)
|
plugin_instance.set_plugin_info(pluginInfo)
|
||||||
|
logger.info(f"Called set_plugin_info() for plugin: {module_name}")
|
||||||
|
|
||||||
# Register with pluggy and activate
|
# Register with pluggy and activate
|
||||||
|
if self.plugin_manager is None:
|
||||||
|
logger.error(f"Plugin manager is None when loading {module_name}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info(f"Registering plugin with pluggy: {module_name}")
|
||||||
self.plugin_manager.register(plugin_instance)
|
self.plugin_manager.register(plugin_instance)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
logger.info(f"Activating plugin: {module_name}")
|
||||||
self.plugin_manager.hook.activate(plugin=plugin_instance)
|
self.plugin_manager.hook.activate(plugin=plugin_instance)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error activating plugin {module_name}: {e}")
|
logger.error(f"Error activating plugin {module_name}: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
return False
|
return False
|
||||||
|
|
||||||
pluginInfo.loaded = True
|
pluginInfo.loaded = True
|
||||||
logger.info(f"Loaded plugin: {module_name}")
|
logger.info(f"Successfully loaded plugin: {module_name}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to load plugin {module_name}: {e}")
|
logger.error(f"Failed to load plugin {module_name}: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def unloadPlugin(self, pluginInfo):
|
def unloadPlugin(self, pluginInfo):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user