diff --git a/dotbot/cli.py b/dotbot/cli.py index 28485d1..b230b6d 100644 --- a/dotbot/cli.py +++ b/dotbot/cli.py @@ -1,4 +1,4 @@ -import os, glob +import glob import sys from argparse import ArgumentParser, RawTextHelpFormatter @@ -6,6 +6,7 @@ from .config import ConfigReader, ReadingError from .dispatcher import Dispatcher, DispatchError from .messenger import Messenger from .messenger import Level +from .plugins import Clean, Create, Link, Shell from .util import module import dotbot @@ -118,9 +119,10 @@ def main(): else: log.use_color(sys.stdout.isatty()) + plugins = [] plugin_directories = list(options.plugin_dirs) if not options.disable_built_in_plugins: - from .plugins import Clean, Create, Link, Shell + plugins.extend([Clean, Create, Link, Shell]) plugin_paths = [] for directory in plugin_directories: for plugin_path in glob.glob(os.path.join(directory, "*.py")): @@ -129,7 +131,7 @@ def main(): plugin_paths.append(plugin_path) for plugin_path in plugin_paths: abspath = os.path.abspath(plugin_path) - module.load(abspath) + plugins.extend(module.load(abspath)) if not options.config_file: log.error("No configuration file specified") exit(1) @@ -151,6 +153,7 @@ def main(): skip=options.skip, exit_on_failure=options.exit_on_failure, options=options, + plugins=plugins, ) success = dispatcher.dispatch(tasks) if success: diff --git a/dotbot/dispatcher.py b/dotbot/dispatcher.py index 18f0b0a..630c895 100644 --- a/dotbot/dispatcher.py +++ b/dotbot/dispatcher.py @@ -7,11 +7,12 @@ from .context import Context class Dispatcher(object): def __init__( - self, base_directory, only=None, skip=None, exit_on_failure=False, options=Namespace() + self, base_directory, only=None, skip=None, exit_on_failure=False, options=Namespace(), plugins=None, ): self._log = Messenger() self._setup_context(base_directory, options) - self._load_plugins() + plugins = plugins or [] + self._plugins = [plugin(self._context) for plugin in plugins] self._only = only self._skip = skip self._exit = exit_on_failure @@ -65,9 +66,6 @@ class Dispatcher(object): return False return success - def _load_plugins(self): - self._plugins = [plugin(self._context) for plugin in Plugin.__subclasses__()] - class DispatchError(Exception): pass diff --git a/dotbot/plugins/clean.py b/dotbot/plugins/clean.py index e2671ad..1d17b72 100644 --- a/dotbot/plugins/clean.py +++ b/dotbot/plugins/clean.py @@ -1,10 +1,10 @@ import os import sys -import dotbot +from ..plugin import Plugin -class Clean(dotbot.Plugin): +class Clean(Plugin): """ Cleans broken symbolic links. """ diff --git a/dotbot/plugins/create.py b/dotbot/plugins/create.py index 85557a6..c593d52 100644 --- a/dotbot/plugins/create.py +++ b/dotbot/plugins/create.py @@ -1,8 +1,9 @@ import os -import dotbot + +from ..plugin import Plugin -class Create(dotbot.Plugin): +class Create(Plugin): """ Create empty paths. """ diff --git a/dotbot/plugins/link.py b/dotbot/plugins/link.py index c938080..3e8c91e 100644 --- a/dotbot/plugins/link.py +++ b/dotbot/plugins/link.py @@ -2,11 +2,12 @@ import os import sys import glob import shutil -import dotbot -import dotbot.util + +from ..plugin import Plugin +from ..util import shell_command -class Link(dotbot.Plugin): +class Link(Plugin): """ Symbolically links dotfiles. """ @@ -139,7 +140,7 @@ class Link(dotbot.Plugin): return success def _test_success(self, command): - ret = dotbot.util.shell_command(command, cwd=self._context.base_directory()) + ret = shell_command(command, cwd=self._context.base_directory()) if ret != 0: self._log.debug("Test '%s' returned false" % command) return ret == 0 diff --git a/dotbot/plugins/shell.py b/dotbot/plugins/shell.py index bbdcb6d..2ee0d68 100644 --- a/dotbot/plugins/shell.py +++ b/dotbot/plugins/shell.py @@ -1,10 +1,8 @@ -import os -import subprocess -import dotbot -import dotbot.util +from ..plugin import Plugin +from ..util import shell_command -class Shell(dotbot.Plugin): +class Shell(Plugin): """ Run arbitrary shell commands. """ @@ -50,7 +48,7 @@ class Shell(dotbot.Plugin): self._log.lowinfo("%s [%s]" % (msg, cmd)) stdout = options.get("stdout", stdout) stderr = options.get("stderr", stderr) - ret = dotbot.util.shell_command( + ret = shell_command( cmd, cwd=self._context.base_directory(), enable_stdin=stdin, diff --git a/dotbot/util/module.py b/dotbot/util/module.py index ded485a..183cac2 100644 --- a/dotbot/util/module.py +++ b/dotbot/util/module.py @@ -1,4 +1,7 @@ -import sys, os.path +import os +import sys + +from dotbot.plugin import Plugin # We keep references to loaded modules so they don't get garbage collected. loaded_modules = [] @@ -7,8 +10,17 @@ 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) + loaded_module = load_module(module_name, path) + plugins = [] + for name in dir(loaded_module): + possible_plugin = getattr(loaded_module, name) + try: + if issubclass(possible_plugin, Plugin) and possible_plugin is not Plugin: + plugins.append(possible_plugin) + except TypeError: + pass + loaded_modules.append(loaded_module) + return plugins if sys.version_info >= (3, 5):