1
0
Fork 0
mirror of synced 2024-11-22 16:25:34 -05:00

Added a plugin loader

This commit is contained in:
Casey Rodarmor 2016-01-16 19:00:15 -08:00
parent 1afc8bfbbf
commit e8014b531f
9 changed files with 69 additions and 25 deletions

View file

@ -1 +1,2 @@
from .cli import main from .cli import main
from .plugin import Plugin

View file

@ -1,8 +1,11 @@
import os, glob
from argparse import ArgumentParser from argparse import ArgumentParser
from .config import ConfigReader, ReadingError from .config import ConfigReader, ReadingError
from .dispatcher import Dispatcher, DispatchError from .dispatcher import Dispatcher, DispatchError
from .messenger import Messenger from .messenger import Messenger
from .messenger import Level from .messenger import Level
from .util import module
def add_options(parser): def add_options(parser):
parser.add_argument('-Q', '--super-quiet', dest='super_quiet', action='store_true', parser.add_argument('-Q', '--super-quiet', dest='super_quiet', action='store_true',
@ -17,6 +20,12 @@ def add_options(parser):
parser.add_argument('-c', '--config-file', nargs=1, dest='config_file', parser.add_argument('-c', '--config-file', nargs=1, dest='config_file',
help='run commands given in CONFIGFILE', metavar='CONFIGFILE', help='run commands given in CONFIGFILE', metavar='CONFIGFILE',
required=True) required=True)
parser.add_argument('-p', '--plugin', action='append', dest='plugins', default=[],
help='load PLUGIN as a plugin', metavar='PLUGIN')
parser.add_argument('--disable-built-in-plugins', dest='disable_built_in_plugins',
action='store_true', help='disable built-in plugins')
parser.add_argument('--plugin-dir', action='append', dest='plugin_dirs', default=[],
metavar='PLUGIN_DIR', help='load all plugins in PLUGIN_DIR')
def read_config(config_file): def read_config(config_file):
reader = ConfigReader(config_file) reader = ConfigReader(config_file)
@ -28,12 +37,24 @@ def main():
parser = ArgumentParser() parser = ArgumentParser()
add_options(parser) add_options(parser)
options = parser.parse_args() options = parser.parse_args()
if (options.super_quiet): if options.super_quiet:
log.set_level(Level.WARNING) log.set_level(Level.WARNING)
if (options.quiet): if options.quiet:
log.set_level(Level.INFO) log.set_level(Level.INFO)
if (options.verbose): if options.verbose:
log.set_level(Level.DEBUG) log.set_level(Level.DEBUG)
plugin_directories = list(options.plugin_dirs)
if not options.disable_built_in_plugins:
plugin_directories.append(os.path.join(os.path.dirname(__file__), '..', 'plugins'))
plugin_paths = []
for directory in plugin_directories:
for plugin_path in glob.glob(os.path.join(directory, '*.py')):
plugin_paths.append(plugin_path)
for plugin_path in options.plugins:
plugin_paths.append(plugin_path)
for plugin_path in plugin_paths:
abspath = os.path.abspath(plugin_path)
module.load(plugin_path)
tasks = read_config(options.config_file[0]) tasks = read_config(options.config_file[0])
if not isinstance(tasks, list): if not isinstance(tasks, list):
raise ReadingError('Configuration file must be a list of tasks') raise ReadingError('Configuration file must be a list of tasks')

View file

@ -1,5 +1,5 @@
import os import os
from .executor import Executor from .plugin import Plugin
from .messenger import Messenger from .messenger import Messenger
class Dispatcher(object): class Dispatcher(object):
@ -37,7 +37,7 @@ class Dispatcher(object):
def _load_plugins(self): def _load_plugins(self):
self._plugins = [plugin(self._base_directory) self._plugins = [plugin(self._base_directory)
for plugin in Executor.__subclasses__()] for plugin in Plugin.__subclasses__()]
class DispatchError(Exception): class DispatchError(Exception):
pass pass

View file

@ -1,4 +0,0 @@
from .executor import Executor
from .linker import Linker
from .cleaner import Cleaner
from .commandrunner import CommandRunner

View file

@ -1,6 +1,6 @@
from ..messenger import Messenger from .messenger import Messenger
class Executor(object): class Plugin(object):
''' '''
Abstract base class for commands that process directives. Abstract base class for commands that process directives.
''' '''
@ -11,7 +11,7 @@ class Executor(object):
def can_handle(self, directive): def can_handle(self, directive):
''' '''
Returns true if the Executor can handle the directive. Returns true if the Plugin can handle the directive.
''' '''
raise NotImplementedError raise NotImplementedError
@ -19,6 +19,6 @@ class Executor(object):
''' '''
Executes the directive. Executes the directive.
Returns true if the Executor successfully handled the directive. Returns true if the Plugin successfully handled the directive.
''' '''
raise NotImplementedError raise NotImplementedError

29
dotbot/util/module.py Normal file
View file

@ -0,0 +1,29 @@
import sys, os.path
# We keep references to loaded modules so they don't get garbage collected.
loaded_modules = []
def load(path):
basename = os.path.basename(path)
module_name, extension = os.path.splitext(basename)
plugin = load_module(module_name, path)
loaded_modules.append(plugin)
if sys.version_info >= (3, 5):
import importlib.util, os.path
def load_module(module_name, path):
spec = importlib.util.spec_from_file_location(module_name, path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
elif sys.version_info >= (3, 3):
from importlib.machinery import SourceFileLoader
def load_module(module_name, path):
return SourceFileLoader(module_name, path).load_module()
else:
import imp
def load_module(module_name, path):
return imp.load_source(module_name, path)

View file

@ -1,7 +1,6 @@
import os import os, dotbot
from . import Executor
class Cleaner(Executor): class Clean(dotbot.Plugin):
''' '''
Cleans broken symbolic links. Cleans broken symbolic links.
''' '''
@ -13,7 +12,7 @@ class Cleaner(Executor):
def handle(self, directive, data): def handle(self, directive, data):
if directive != self._directive: if directive != self._directive:
raise ValueError('Cleaner cannot handle directive %s' % directive) raise ValueError('Clean cannot handle directive %s' % directive)
return self._process_clean(data) return self._process_clean(data)
def _process_clean(self, targets): def _process_clean(self, targets):

View file

@ -1,7 +1,6 @@
import os, shutil import os, shutil, dotbot
from . import Executor
class Linker(Executor): class Link(dotbot.Plugin):
''' '''
Symbolically links dotfiles. Symbolically links dotfiles.
''' '''
@ -13,7 +12,7 @@ class Linker(Executor):
def handle(self, directive, data): def handle(self, directive, data):
if directive != self._directive: if directive != self._directive:
raise ValueError('Linker cannot handle directive %s' % directive) raise ValueError('Link cannot handle directive %s' % directive)
return self._process_links(data) return self._process_links(data)
def _process_links(self, links): def _process_links(self, links):

View file

@ -1,7 +1,6 @@
import os, subprocess import os, subprocess, dotbot
from . import Executor
class CommandRunner(Executor): class Shell(dotbot.Plugin):
''' '''
Run arbitrary shell commands. Run arbitrary shell commands.
''' '''
@ -13,7 +12,7 @@ class CommandRunner(Executor):
def handle(self, directive, data): def handle(self, directive, data):
if directive != self._directive: if directive != self._directive:
raise ValueError('CommandRunner cannot handle directive %s' % raise ValueError('Shell cannot handle directive %s' %
directive) directive)
return self._process_commands(data) return self._process_commands(data)