""" pyexcel_io.plugins ~~~~~~~~~~~~~~~~~~~ factory for getting readers and writers :copyright: (c) 2014-2022 by Onni Software Ltd. :license: New BSD License, see LICENSE for more details """ import pyexcel_io.utils as ioutils import pyexcel_io.manager as manager import pyexcel_io.constants as constants import pyexcel_io.exceptions as exceptions from lml.loader import scan_plugins_regex from lml.plugin import PluginInfo, PluginManager, PluginInfoChain ERROR_MESSAGE_FORMATTER = "one of these plugins for %s data in '%s': %s" UPGRADE_MESSAGE = "Please upgrade the plugin '%s' according to \ plugin compactibility table." READER_PLUGIN = "pyexcel-io reader" READER_PLUGIN_V2 = "pyexcel-io v2 reader" WRITER_PLUGIN = "pyexcel-io writer" WRITER_PLUGIN_V2 = "pyexcel-io v2 writer" class IOPluginInfo(PluginInfo): """Pyexcel-io plugin info description""" def tags(self): for file_type in self.file_types: yield file_type class IOPluginInfoChain(PluginInfoChain): """provide custom functions to add a reader and a writer""" def add_a_reader( self, relative_plugin_class_path=None, file_types=None, stream_type=None, ): """add pyexcle-io reader plugin info""" a_plugin_info = IOPluginInfo( READER_PLUGIN, self._get_abs_path(relative_plugin_class_path), file_types=file_types, stream_type=stream_type, ) return self.add_a_plugin_instance(a_plugin_info) def add_a_writer( self, relative_plugin_class_path=None, file_types=None, stream_type=None, ): """add pyexcle-io writer plugin info""" a_plugin_info = IOPluginInfo( WRITER_PLUGIN, self._get_abs_path(relative_plugin_class_path), file_types=file_types, stream_type=stream_type, ) return self.add_a_plugin_instance(a_plugin_info) class IOPluginInfoChainV2(PluginInfoChain): """provide custom functions to add a reader and a writer""" def add_a_reader( self, relative_plugin_class_path=None, locations=(), file_types=None, stream_type=None, ): """add pyexcle-io reader plugin info""" a_plugin_info = IOPluginInfo( READER_PLUGIN_V2, self._get_abs_path(relative_plugin_class_path), file_types=[ f"{location}-{file_type}" for file_type in file_types for location in locations ], stream_type=stream_type, ) return self.add_a_plugin_instance(a_plugin_info) def add_a_writer( self, relative_plugin_class_path=None, locations=(), file_types=(), stream_type=None, ): """add pyexcle-io writer plugin info""" a_plugin_info = IOPluginInfo( WRITER_PLUGIN_V2, self._get_abs_path(relative_plugin_class_path), file_types=[ f"{location}-{file_type}" for file_type in file_types for location in locations ], stream_type=stream_type, ) return self.add_a_plugin_instance(a_plugin_info) class IOManager(PluginManager): """Manage pyexcel-io plugins""" def __init__(self, plugin_type, known_list): PluginManager.__init__(self, plugin_type) self.known_plugins = known_list self.action = "read" if self.plugin_name == WRITER_PLUGIN: self.action = "write" def load_me_later(self, plugin_info): PluginManager.load_me_later(self, plugin_info) _do_additional_registration(plugin_info) def register_a_plugin(self, cls, plugin_info): """for dynamically loaded plugin""" PluginManager.register_a_plugin(self, cls, plugin_info) _do_additional_registration(plugin_info) def get_a_plugin(self, file_type=None, library=None, **keywords): __file_type = file_type.lower() try: plugin = self.load_me_now(__file_type, library=library) except Exception: self.raise_exception(__file_type) handler = plugin() handler.set_type(__file_type) return handler def raise_exception(self, file_type): plugins = self.known_plugins.get(file_type, None) if plugins: message = "Please install " if len(plugins) > 1: message += ERROR_MESSAGE_FORMATTER % ( self.action, file_type, ",".join(plugins), ) else: message += plugins[0] raise exceptions.SupportingPluginAvailableButNotInstalled(message) else: raise exceptions.NoSupportingPluginFound( "No suitable library found for %s" % file_type ) def get_all_formats(self): """return all supported formats""" all_formats = set( list(self.registry.keys()) + list(self.known_plugins.keys()) ) all_formats = all_formats.difference( set([constants.DB_SQL, constants.DB_DJANGO]) ) return all_formats class NewIOManager(IOManager): def load_me_later(self, plugin_info): PluginManager.load_me_later(self, plugin_info) _do_additional_registration_for_new_plugins(plugin_info) def register_a_plugin(self, cls, plugin_info): """for dynamically loaded plugin""" PluginManager.register_a_plugin(self, cls, plugin_info) _do_additional_registration_for_new_plugins(plugin_info) def get_a_plugin( self, file_type=None, location=None, library=None, **keywords ): __file_type = file_type.lower() plugin = self.load_me_now(f"{location}-{__file_type}", library=library) return plugin def raise_exception(self, file_type): file_type = file_type.split("-")[1] plugins = self.known_plugins.get(file_type, None) if plugins: message = "Please install " if len(plugins) > 1: message += ERROR_MESSAGE_FORMATTER % ( self.action, file_type, ",".join(plugins), ) else: message += plugins[0] raise exceptions.SupportingPluginAvailableButNotInstalled(message) else: raise exceptions.NoSupportingPluginFound( "No suitable library found for %s" % file_type ) def get_all_formats(self): """return all supported formats""" all_formats = set( [x.split("-")[1] for x in self.registry.keys()] + list(self.known_plugins.keys()) ) return all_formats def _do_additional_registration(plugin_info): for file_type in plugin_info.tags(): manager.register_stream_type(file_type, plugin_info.stream_type) manager.register_a_file_type(file_type, plugin_info.stream_type, None) def _do_additional_registration_for_new_plugins(plugin_info): for file_type in plugin_info.tags(): manager.register_stream_type( file_type.split("-")[1], plugin_info.stream_type ) manager.register_a_file_type( file_type.split("-")[1], plugin_info.stream_type, None ) class AllReaders: def get_all_formats(self): return OLD_READERS.get_all_formats().union( NEW_READERS.get_all_formats() ) - set([constants.DB_SQL, constants.DB_DJANGO]) class AllWriters: def get_all_formats(self): return OLD_WRITERS.get_all_formats().union( NEW_WRITERS.get_all_formats() ) - set([constants.DB_SQL, constants.DB_DJANGO]) OLD_READERS = IOManager(READER_PLUGIN, ioutils.AVAILABLE_READERS) OLD_WRITERS = IOManager(WRITER_PLUGIN, ioutils.AVAILABLE_WRITERS) NEW_WRITERS = NewIOManager(WRITER_PLUGIN_V2, ioutils.AVAILABLE_WRITERS) NEW_READERS = NewIOManager(READER_PLUGIN_V2, ioutils.AVAILABLE_READERS) READERS = AllReaders() WRITERS = AllWriters() def load_plugins(plugin_name_patterns, path, black_list, white_list): """Try to discover all pyexcel-io plugins""" scan_plugins_regex( plugin_name_patterns=plugin_name_patterns, pyinstaller_path=path, black_list=black_list, white_list=white_list, )