def __post_plugin_process(self, load_result=None, unload_result=None, reload_result=None): """ When returns, all events / operations are finished processing """ if load_result is None: load_result = SingleOperationResult() if unload_result is None: unload_result = SingleOperationResult() if reload_result is None: reload_result = SingleOperationResult() dependency_check_result = self.__check_plugin_dependencies() self.last_operation_result.record(load_result, unload_result, reload_result, dependency_check_result) # Expected plugin states: # success_list fail_list # load_result LOADED N/A # unload_result UNLOADING UNLOADING # reload_result READY UNLOADING # dep_chk_result LOADED / READY UNLOADING self.registry_storage.clear( ) # in case plugin invokes dispatch_event during on_load. dont let them trigger listeners newly_loaded_plugins = { *load_result.success_list, *reload_result.success_list } for plugin in dependency_check_result.success_list: if plugin in newly_loaded_plugins: plugin.ready() for plugin in dependency_check_result.success_list: if plugin in newly_loaded_plugins: if isinstance(plugin, RegularPlugin): plugin.receive_event(MCDRPluginEvents.PLUGIN_LOADED, (plugin.old_entry_module_instance, )) for plugin in unload_result.success_list + unload_result.failed_list + reload_result.failed_list + dependency_check_result.failed_list: plugin.assert_state({PluginState.UNLOADING}) # plugins might just be newly loaded but failed on dependency check, dont dispatch event to them if plugin not in newly_loaded_plugins: plugin.receive_event(MCDRPluginEvents.PLUGIN_UNLOADED, ()) # plugin.receive_event(MCDRPluginEvents.PLUGIN_REMOVED, ()) plugin.remove() # they should be for plugin in self.get_regular_plugins(): plugin.assert_state({PluginState.READY}) self.__update_registry() self.__sort_plugins_by_id()
def __reload_ready_plugins( self, filter: Callable[[RegularPlugin], bool], specific: Optional[RegularPlugin] = None) -> SingleOperationResult: result = SingleOperationResult() plugin_list = self.get_regular_plugins() if specific is None else [ specific ] for plugin in plugin_list: if plugin.in_states({PluginState.READY}) and filter(plugin): result.record(plugin, self.__reload_plugin(plugin)) return result
def __collect_and_remove_plugins( self, filter: Callable[[RegularPlugin], bool], specific: Optional[RegularPlugin] = None) -> SingleOperationResult: result = SingleOperationResult() plugin_list = self.get_regular_plugins() if specific is None else [ specific ] for plugin in plugin_list: if filter(plugin): result.record(plugin, self.__unload_plugin(plugin)) return result
def __post_plugin_process(self, load_result=None, unload_result=None, reload_result=None): if load_result is None: load_result = SingleOperationResult() if unload_result is None: unload_result = SingleOperationResult() if reload_result is None: reload_result = SingleOperationResult() dependency_check_result = self.check_plugin_dependencies() self.last_operation_result.record(load_result, unload_result, reload_result, dependency_check_result) # Expected plugin states: # success_list fail_list # load_result LOADED N/A # unload_result UNLOADING UNLOADING # reload_result READY UNLOADING # dep_chk_result LOADED / READY UNLOADING for plugin in load_result.success_list + reload_result.success_list: if plugin in dependency_check_result.success_list: plugin.ready() newly_loaded_plugins = { *load_result.success_list, *reload_result.success_list } for plugin in dependency_check_result.success_list: if plugin in newly_loaded_plugins: if isinstance(plugin, RegularPlugin): plugin.receive_event(MCDRPluginEvents.PLUGIN_LOAD, (plugin.old_module_instance, )) for plugin in unload_result.success_list + unload_result.failed_list + reload_result.failed_list + dependency_check_result.failed_list: plugin.assert_state({PluginState.UNLOADING}) # plugins might just be newly loaded but failed on dependency check, dont dispatch event to them if plugin not in load_result.success_list: plugin.receive_event(MCDRPluginEvents.PLUGIN_UNLOAD, ()) plugin.remove() # they should be for plugin in self.get_regular_plugins(): plugin.assert_state({PluginState.READY}) self.__update_registry()
def check_plugin_dependencies(self) -> SingleOperationResult: result = SingleOperationResult() walker = DependencyWalker(self) for item in walker.walk(): plugin = self.plugins.get(item.plugin_id) # should be not None result.record(plugin, item.success) if not item.success: self.logger.error( self.mcdr_server.tr( 'plugin_manager.check_plugin_dependencies.item_failed', plugin, item.reason)) self.__unload_plugin(plugin) self.logger.debug(self.mcdr_server.tr( 'plugin_manager.check_plugin_dependencies.topo_order'), option=DebugOption.PLUGIN) for plugin in result.success_list: self.logger.debug('- {}'.format(plugin), option=DebugOption.PLUGIN) # the success list order matches the dependency topo order return result
def __check_if_success(self, operation_result: SingleOperationResult, check_loaded: bool) -> bool: """ Check if there's any plugin inside the given operation result (load result / reload result etc.) Then check if the plugin passed the dependency check if param check_loaded is True """ success = operation_result.has_success() if success and check_loaded: plugin = operation_result.success_list[0] success = plugin in self.__mcdr_server.plugin_manager.last_operation_result.dependency_check_result.success_list return success
def __collect_and_process_new_plugins( self, filter: Callable[[str], bool]) -> SingleOperationResult: result = SingleOperationResult() for plugin_directory in self.plugin_directories: file_list = file_util.list_file_with_suffix( plugin_directory, constant.PLUGIN_FILE_SUFFIX) for file_path in file_list: if not self.contains_plugin_file(file_path) and filter( file_path): plugin = self.__load_plugin(file_path) if plugin is None: result.fail(file_path) else: result.succeed(plugin) return result
def __collect_and_process_new_plugins( self, filter: Callable[[str], bool]) -> SingleOperationResult: result = SingleOperationResult() for plugin_directory in self.plugin_directories: if os.path.isdir(plugin_directory): for file in os.listdir(plugin_directory): file_path = os.path.join(plugin_directory, file) if plugin_factory.maybe_plugin(file_path): if not self.contains_plugin_file(file_path) and filter( file_path): plugin = self.__load_plugin(file_path) if plugin is None: result.fail(file_path) else: result.succeed(plugin) else: self.logger.warning( 'Plugin directory "{}" not found'.format(plugin_directory)) return result
def __collect_and_process_new_plugins( self, filter: Callable[[str], bool], *, possible_paths: Optional[List[str]] = None ) -> SingleOperationResult: """ :param filter: A str predicate function for testing if the plugin file path is acceptable :param possible_paths: Optional. If you have already done self.__collect_possible_plugin_file_paths() before, you can pass the previous result as the argument to reuse that, so less time cost """ if possible_paths is None: possible_paths = self.__collect_possible_plugin_file_paths() result = SingleOperationResult() for file_path in possible_paths: if not self.contains_plugin_file(file_path) and filter(file_path): plugin = self.__load_plugin(file_path) if plugin is None: result.fail(file_path) else: result.succeed(plugin) return result