Add plugin loader
This commit is contained in:
parent
ba9e9cbe70
commit
eeb4c284fb
9 changed files with 69 additions and 25 deletions
|
@ -1 +1,2 @@
|
|||
from .cli import main
|
||||
from .plugin import Plugin
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import os, glob
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from .config import ConfigReader, ReadingError
|
||||
from .dispatcher import Dispatcher, DispatchError
|
||||
from .messenger import Messenger
|
||||
from .messenger import Level
|
||||
from .util import module
|
||||
|
||||
def add_options(parser):
|
||||
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',
|
||||
help='run commands given in CONFIGFILE', metavar='CONFIGFILE',
|
||||
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):
|
||||
reader = ConfigReader(config_file)
|
||||
|
@ -28,12 +37,24 @@ def main():
|
|||
parser = ArgumentParser()
|
||||
add_options(parser)
|
||||
options = parser.parse_args()
|
||||
if (options.super_quiet):
|
||||
if options.super_quiet:
|
||||
log.set_level(Level.WARNING)
|
||||
if (options.quiet):
|
||||
if options.quiet:
|
||||
log.set_level(Level.INFO)
|
||||
if (options.verbose):
|
||||
if options.verbose:
|
||||
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(abspath)
|
||||
tasks = read_config(options.config_file[0])
|
||||
if not isinstance(tasks, list):
|
||||
raise ReadingError('Configuration file must be a list of tasks')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import os
|
||||
from .executor import Executor
|
||||
from .plugin import Plugin
|
||||
from .messenger import Messenger
|
||||
|
||||
class Dispatcher(object):
|
||||
|
@ -37,7 +37,7 @@ class Dispatcher(object):
|
|||
|
||||
def _load_plugins(self):
|
||||
self._plugins = [plugin(self._base_directory)
|
||||
for plugin in Executor.__subclasses__()]
|
||||
for plugin in Plugin.__subclasses__()]
|
||||
|
||||
class DispatchError(Exception):
|
||||
pass
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
from .executor import Executor
|
||||
from .linker import Linker
|
||||
from .cleaner import Cleaner
|
||||
from .commandrunner import CommandRunner
|
|
@ -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.
|
||||
'''
|
||||
|
@ -11,7 +11,7 @@ class Executor(object):
|
|||
|
||||
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
|
||||
|
||||
|
@ -19,6 +19,6 @@ class Executor(object):
|
|||
'''
|
||||
Executes the directive.
|
||||
|
||||
Returns true if the Executor successfully handled the directive.
|
||||
Returns true if the Plugin successfully handled the directive.
|
||||
'''
|
||||
raise NotImplementedError
|
29
dotbot/util/module.py
Normal file
29
dotbot/util/module.py
Normal 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
|
||||
|
||||
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)
|
|
@ -1,7 +1,6 @@
|
|||
import os
|
||||
from . import Executor
|
||||
import os, dotbot
|
||||
|
||||
class Cleaner(Executor):
|
||||
class Clean(dotbot.Plugin):
|
||||
'''
|
||||
Cleans broken symbolic links.
|
||||
'''
|
||||
|
@ -13,7 +12,7 @@ class Cleaner(Executor):
|
|||
|
||||
def handle(self, directive, data):
|
||||
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)
|
||||
|
||||
def _process_clean(self, targets):
|
|
@ -1,7 +1,6 @@
|
|||
import os, shutil
|
||||
from . import Executor
|
||||
import os, shutil, dotbot
|
||||
|
||||
class Linker(Executor):
|
||||
class Link(dotbot.Plugin):
|
||||
'''
|
||||
Symbolically links dotfiles.
|
||||
'''
|
||||
|
@ -13,7 +12,7 @@ class Linker(Executor):
|
|||
|
||||
def handle(self, directive, data):
|
||||
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)
|
||||
|
||||
def _process_links(self, links):
|
|
@ -1,7 +1,6 @@
|
|||
import os, subprocess
|
||||
from . import Executor
|
||||
import os, subprocess, dotbot
|
||||
|
||||
class CommandRunner(Executor):
|
||||
class Shell(dotbot.Plugin):
|
||||
'''
|
||||
Run arbitrary shell commands.
|
||||
'''
|
||||
|
@ -13,7 +12,7 @@ class CommandRunner(Executor):
|
|||
|
||||
def handle(self, directive, data):
|
||||
if directive != self._directive:
|
||||
raise ValueError('CommandRunner cannot handle directive %s' %
|
||||
raise ValueError('Shell cannot handle directive %s' %
|
||||
directive)
|
||||
return self._process_commands(data)
|
||||
|
Loading…
Reference in a new issue