Source code for tuiview.pluginmanager


"""
Contains the PluginManager class.
"""
# This file is part of 'TuiView' - a simple Raster viewer
# Copyright (C) 2012  Sam Gillingham
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import os
import sys
import importlib.util
import importlib.machinery

PLUGINS_ENV = 'TUIVIEW_PLUGINS_PATH'
PLUGINS_SUBDIR = 'plugins'

PLUGIN_NAME_FN = 'name'
PLUGIN_ACTION_FN = 'action'
PLUGIN_AUTHOR_FN = 'author'
PLUGIN_DESC_FN = 'description'

PLUGIN_REQUIRED_FNS = (PLUGIN_NAME_FN, PLUGIN_ACTION_FN, PLUGIN_AUTHOR_FN, 
    PLUGIN_DESC_FN)

PLUGIN_ACTION_INIT = 0
PLUGIN_ACTION_NEWVIEWER = 1
PLUGIN_ACTION_NEWQUERY = 2


[docs]class PluginManager(object): def __init__(self): self.plugins = {} self.pluginNameIndex = 1
[docs] def callAction(self, actioncode, param): """ Calls PLUGIN_ACTION_FN on each of the loaded plugins with the supplit PLUGIN_ACTION_* constant and a parameter """ for name in self.plugins.keys(): mod = self.plugins[name] try: action = getattr(mod, PLUGIN_ACTION_FN) action(actioncode, param) except Exception: self.printTraceback(mod)
[docs] @staticmethod def printTraceback(name): import traceback print('Exception raised when calling plugin %s:' % name) (ttype, value, tb) = sys.exc_info() stack = traceback.extract_tb(tb) trace = '\n'.join(traceback.format_list(stack)) print(trace, ttype.__name__, ':', value)
[docs] def loadPlugins(self): """ Order is: 1. If there is a plugins directory under where we are running 2. Any directories listed in os.getenv(PLUGINS_ENV) 3. Under ~/.tuiview/plugins Reads any Python file that has the required entries """ # check where we are running appDir = os.path.dirname(os.path.abspath(sys.argv[0])) subdir = os.path.join(appDir, PLUGINS_SUBDIR) if os.path.isdir(subdir): self.loadPluginsFromDir(subdir) # now do the environment variable pluginPath = os.getenv(PLUGINS_ENV) if pluginPath is not None: for subdir in pluginPath.split(os.pathsep): if os.path.isdir(subdir): self.loadPluginsFromDir(subdir) # now home directory homeDir = os.path.expanduser('~') subdir = os.path.join(homeDir, '.tuiview', PLUGINS_SUBDIR) if os.path.isdir(subdir): self.loadPluginsFromDir(subdir)
[docs] def loadPluginsFromDir(self, directory): """ Attempt to load all the files in the given directory that match the Python suffix """ for fname in os.listdir(directory): for suffix in importlib.machinery.SOURCE_SUFFIXES: if fname.endswith(suffix) and not fname.startswith('__'): path = os.path.join(directory, fname) self.loadPluginFromPath(path)
[docs] def loadPluginFromPath(self, path): """ Try loading a plugin given a path to it. Module put into self.plugins with the key being the name the module describes itself as """ # make up a name to keep Python happy name = 'tuiview.plugin%d' % self.pluginNameIndex self.pluginNameIndex += 1 try: spec = importlib.util.spec_from_file_location(name, path) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) # all required fns? for fn in PLUGIN_REQUIRED_FNS: if not hasattr(mod, fn): msg = 'Plugin %s does not have required functions' print(msg % path) return # must be ok. Get name and save it try: name = getattr(mod, PLUGIN_NAME_FN) modname = name() except Exception: self.printTraceback(modname) return self.plugins[modname] = mod print('loaded plugin %s' % modname) except ImportError as e: print('Unable to import %s' % path) print(str(e))