Fix to stop the screen reader from hanging when plugins fail.
This commit is contained in:
parent
df7f4c5e62
commit
682d66e08f
@ -24,4 +24,4 @@
|
|||||||
# Original source: https://gitlab.gnome.org/GNOME/orca
|
# Original source: https://gitlab.gnome.org/GNOME/orca
|
||||||
|
|
||||||
version = "2025.04.03"
|
version = "2025.04.03"
|
||||||
codeName = "testing"
|
codeName = "plugins"
|
||||||
|
@ -12,6 +12,8 @@ import os
|
|||||||
import inspect
|
import inspect
|
||||||
import importlib.util
|
import importlib.util
|
||||||
import logging
|
import logging
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
|
|
||||||
# Import pluggy if available
|
# Import pluggy if available
|
||||||
@ -152,6 +154,8 @@ class PluginSystemManager:
|
|||||||
self._plugins[name].instance = old_info.instance
|
self._plugins[name].instance = old_info.instance
|
||||||
self._plugins[name].module = old_info.module
|
self._plugins[name].module = old_info.module
|
||||||
|
|
||||||
|
logger.info(f"Rescanned plugins, found {len(self._plugins)} plugins")
|
||||||
|
|
||||||
def _scan_plugins_in_directory(self, directory):
|
def _scan_plugins_in_directory(self, directory):
|
||||||
"""Scan for plugins in a directory."""
|
"""Scan for plugins in a directory."""
|
||||||
if not os.path.exists(directory) or not os.path.isdir(directory):
|
if not os.path.exists(directory) or not os.path.isdir(directory):
|
||||||
@ -159,6 +163,7 @@ class PluginSystemManager:
|
|||||||
return
|
return
|
||||||
|
|
||||||
logger.info(f"Scanning for plugins in directory: {directory}")
|
logger.info(f"Scanning for plugins in directory: {directory}")
|
||||||
|
try:
|
||||||
for item in os.listdir(directory):
|
for item in os.listdir(directory):
|
||||||
plugin_dir = os.path.join(directory, item)
|
plugin_dir = os.path.join(directory, item)
|
||||||
if not os.path.isdir(plugin_dir):
|
if not os.path.isdir(plugin_dir):
|
||||||
@ -197,6 +202,10 @@ class PluginSystemManager:
|
|||||||
self._plugins[module_name] = plugin_info
|
self._plugins[module_name] = plugin_info
|
||||||
else:
|
else:
|
||||||
logger.warning(f"No plugin file found in directory: {plugin_dir}")
|
logger.warning(f"No plugin file found in directory: {plugin_dir}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error scanning directory {directory}: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
def _load_plugin_metadata(self, metadata_file):
|
def _load_plugin_metadata(self, metadata_file):
|
||||||
"""Load plugin metadata from a file."""
|
"""Load plugin metadata from a file."""
|
||||||
@ -231,6 +240,10 @@ class PluginSystemManager:
|
|||||||
logger.info("No plugins found, rescanning...")
|
logger.info("No plugins found, rescanning...")
|
||||||
self.rescanPlugins()
|
self.rescanPlugins()
|
||||||
|
|
||||||
|
# Double-check we found plugins after rescanning
|
||||||
|
if not self._plugins and activePlugins:
|
||||||
|
logger.warning("No plugins found after rescanning but active plugins requested. Continuing anyway.")
|
||||||
|
|
||||||
self._active_plugins = activePlugins
|
self._active_plugins = activePlugins
|
||||||
|
|
||||||
# Log active vs available plugins
|
# Log active vs available plugins
|
||||||
@ -243,7 +256,12 @@ class PluginSystemManager:
|
|||||||
if missing_plugins:
|
if missing_plugins:
|
||||||
logger.warning(f"Active plugins not found: {missing_plugins}")
|
logger.warning(f"Active plugins not found: {missing_plugins}")
|
||||||
|
|
||||||
|
try:
|
||||||
self.syncAllPluginsActive()
|
self.syncAllPluginsActive()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error syncing plugins: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
def setPluginActive(self, pluginInfo, active):
|
def setPluginActive(self, pluginInfo, active):
|
||||||
"""Set the active state of a plugin."""
|
"""Set the active state of a plugin."""
|
||||||
@ -303,6 +321,11 @@ class PluginSystemManager:
|
|||||||
logger.warning("Pluggy not available, skipping plugin sync")
|
logger.warning("Pluggy not available, skipping plugin sync")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Skip if no plugins found - don't waste time trying to sync nothing
|
||||||
|
if not self._plugins:
|
||||||
|
logger.warning("No plugins found, skipping plugin sync")
|
||||||
|
return
|
||||||
|
|
||||||
# Log plugin status before syncing
|
# Log plugin status before syncing
|
||||||
if PLUGIN_DEBUG:
|
if PLUGIN_DEBUG:
|
||||||
for pluginInfo in self.plugins:
|
for pluginInfo in self.plugins:
|
||||||
@ -311,15 +334,17 @@ class PluginSystemManager:
|
|||||||
logger.debug(f"Plugin {pluginInfo.get_module_name()}: active={is_active}, loaded={is_loaded}")
|
logger.debug(f"Plugin {pluginInfo.get_module_name()}: active={is_active}, loaded={is_loaded}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Create plans first, then execute to avoid changing collection during iteration
|
||||||
|
to_unload = [p for p in self.plugins if not self.isPluginActive(p) and p.loaded]
|
||||||
|
to_load = [p for p in self.plugins if self.isPluginActive(p) and not p.loaded]
|
||||||
|
|
||||||
# First unload inactive plugins
|
# First unload inactive plugins
|
||||||
for pluginInfo in self.plugins:
|
for pluginInfo in to_unload:
|
||||||
if not self.isPluginActive(pluginInfo) and pluginInfo.loaded:
|
|
||||||
logger.info(f"Unloading inactive plugin: {pluginInfo.get_module_name()}")
|
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 to_load:
|
||||||
if self.isPluginActive(pluginInfo) and not pluginInfo.loaded:
|
|
||||||
logger.info(f"Loading active plugin: {pluginInfo.get_module_name()}")
|
logger.info(f"Loading active plugin: {pluginInfo.get_module_name()}")
|
||||||
result = self.loadPlugin(pluginInfo)
|
result = self.loadPlugin(pluginInfo)
|
||||||
logger.info(f"Plugin {pluginInfo.get_module_name()} load result: {result}")
|
logger.info(f"Plugin {pluginInfo.get_module_name()} load result: {result}")
|
||||||
@ -342,12 +367,25 @@ 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}")
|
logger.debug(f"===== PLUGIN LOADING START: {module_name} =====")
|
||||||
|
|
||||||
|
# Setup timeout for plugin loading
|
||||||
|
load_timeout = 5 # seconds
|
||||||
|
load_completed = False
|
||||||
|
|
||||||
|
def timeout_handler():
|
||||||
|
nonlocal load_completed
|
||||||
|
if not load_completed:
|
||||||
|
logger.error(f"Plugin loading timed out for {module_name}")
|
||||||
|
|
||||||
|
timer = threading.Timer(load_timeout, timeout_handler)
|
||||||
|
timer.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Already loaded?
|
# Already loaded?
|
||||||
if pluginInfo.loaded:
|
if pluginInfo.loaded:
|
||||||
logger.info(f"Plugin {module_name} already loaded, skipping")
|
logger.info(f"Plugin {module_name} already loaded, skipping")
|
||||||
|
load_completed = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Try to find the plugin file
|
# Try to find the plugin file
|
||||||
@ -366,12 +404,14 @@ class PluginSystemManager:
|
|||||||
|
|
||||||
if not os.path.exists(plugin_file):
|
if not os.path.exists(plugin_file):
|
||||||
logger.error(f"Plugin file not found: {plugin_file}")
|
logger.error(f"Plugin file not found: {plugin_file}")
|
||||||
|
load_completed = True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.info(f"Loading plugin from: {plugin_file}")
|
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:
|
if spec is None:
|
||||||
logger.error(f"Failed to create spec for plugin: {module_name}")
|
logger.error(f"Failed to create spec for plugin: {module_name}")
|
||||||
|
load_completed = True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
module = importlib.util.module_from_spec(spec)
|
module = importlib.util.module_from_spec(spec)
|
||||||
@ -391,6 +431,7 @@ class PluginSystemManager:
|
|||||||
|
|
||||||
if not plugin_class:
|
if not plugin_class:
|
||||||
logger.error(f"No plugin class found in {module_name}")
|
logger.error(f"No plugin class found in {module_name}")
|
||||||
|
load_completed = True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Create and initialize plugin instance
|
# Create and initialize plugin instance
|
||||||
@ -410,9 +451,15 @@ class PluginSystemManager:
|
|||||||
plugin_instance.set_plugin_info(pluginInfo)
|
plugin_instance.set_plugin_info(pluginInfo)
|
||||||
logger.info(f"Called set_plugin_info() for plugin: {module_name}")
|
logger.info(f"Called set_plugin_info() for plugin: {module_name}")
|
||||||
|
|
||||||
|
# Log plugin methods for debugging
|
||||||
|
if PLUGIN_DEBUG:
|
||||||
|
logger.debug(f"Plugin instance methods: {[m for m in dir(plugin_instance) if not m.startswith('_')]}")
|
||||||
|
logger.debug(f"Checking for activate method: {'activate' in dir(plugin_instance)}")
|
||||||
|
|
||||||
# Register with pluggy and activate
|
# Register with pluggy and activate
|
||||||
if self.plugin_manager is None:
|
if self.plugin_manager is None:
|
||||||
logger.error(f"Plugin manager is None when loading {module_name}")
|
logger.error(f"Plugin manager is None when loading {module_name}")
|
||||||
|
load_completed = True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.info(f"Registering plugin with pluggy: {module_name}")
|
logger.info(f"Registering plugin with pluggy: {module_name}")
|
||||||
@ -425,17 +472,23 @@ class PluginSystemManager:
|
|||||||
logger.error(f"Error activating plugin {module_name}: {e}")
|
logger.error(f"Error activating plugin {module_name}: {e}")
|
||||||
import traceback
|
import traceback
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
|
load_completed = True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
pluginInfo.loaded = True
|
pluginInfo.loaded = True
|
||||||
logger.info(f"Successfully loaded plugin: {module_name}")
|
logger.debug(f"===== PLUGIN LOADING SUCCESS: {module_name} =====")
|
||||||
|
load_completed = True
|
||||||
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
|
import traceback
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
|
logger.debug(f"===== PLUGIN LOADING FAILED: {module_name} =====")
|
||||||
|
load_completed = True
|
||||||
return False
|
return False
|
||||||
|
finally:
|
||||||
|
timer.cancel()
|
||||||
|
|
||||||
def unloadPlugin(self, pluginInfo):
|
def unloadPlugin(self, pluginInfo):
|
||||||
"""Unload a plugin."""
|
"""Unload a plugin."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user