From 1afc8bfbbffa5c7ea410625582c7bc4668551ea7 Mon Sep 17 00:00:00 2001 From: Anish Athalye Date: Sun, 31 Jan 2016 08:03:06 -0500 Subject: [PATCH 1/2] Update dates --- LICENSE.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 3699607..f21257e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,7 +1,7 @@ The MIT License (MIT) ===================== -**Copyright (c) 2014-2015 Anish Athalye (me@anishathalye.com)** +**Copyright (c) 2014-2016 Anish Athalye (me@anishathalye.com)** Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index e2dfeb1..83b16c5 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,7 @@ Do you have a feature request, bug report, or patch? Great! See License ------- -Copyright (c) 2014-2015 Anish Athalye. Released under the MIT License. See +Copyright (c) 2014-2016 Anish Athalye. Released under the MIT License. See [LICENSE.md][license] for details. [init-dotfiles]: https://github.com/Aviator45003/init-dotfiles From e8014b531f494bb4faafd52b62bbaab295445cbd Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sat, 16 Jan 2016 19:00:15 -0800 Subject: [PATCH 2/2] Added a plugin loader --- dotbot/__init__.py | 1 + dotbot/cli.py | 27 +++++++++++++++-- dotbot/dispatcher.py | 4 +-- dotbot/executor/__init__.py | 4 --- dotbot/{executor/executor.py => plugin.py} | 8 ++--- dotbot/util/module.py | 29 +++++++++++++++++++ .../executor/cleaner.py => plugins/clean.py | 7 ++--- dotbot/executor/linker.py => plugins/link.py | 7 ++--- .../commandrunner.py => plugins/shell.py | 7 ++--- 9 files changed, 69 insertions(+), 25 deletions(-) delete mode 100644 dotbot/executor/__init__.py rename dotbot/{executor/executor.py => plugin.py} (68%) create mode 100644 dotbot/util/module.py rename dotbot/executor/cleaner.py => plugins/clean.py (92%) rename dotbot/executor/linker.py => plugins/link.py (97%) rename dotbot/executor/commandrunner.py => plugins/shell.py (92%) diff --git a/dotbot/__init__.py b/dotbot/__init__.py index 401da57..1d03464 100644 --- a/dotbot/__init__.py +++ b/dotbot/__init__.py @@ -1 +1,2 @@ from .cli import main +from .plugin import Plugin diff --git a/dotbot/cli.py b/dotbot/cli.py index dc90909..ab40eb0 100644 --- a/dotbot/cli.py +++ b/dotbot/cli.py @@ -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(plugin_path) tasks = read_config(options.config_file[0]) if not isinstance(tasks, list): raise ReadingError('Configuration file must be a list of tasks') diff --git a/dotbot/dispatcher.py b/dotbot/dispatcher.py index c3c7fe8..79231a0 100644 --- a/dotbot/dispatcher.py +++ b/dotbot/dispatcher.py @@ -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 diff --git a/dotbot/executor/__init__.py b/dotbot/executor/__init__.py deleted file mode 100644 index d87ca4b..0000000 --- a/dotbot/executor/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .executor import Executor -from .linker import Linker -from .cleaner import Cleaner -from .commandrunner import CommandRunner diff --git a/dotbot/executor/executor.py b/dotbot/plugin.py similarity index 68% rename from dotbot/executor/executor.py rename to dotbot/plugin.py index e6862c0..a79639e 100644 --- a/dotbot/executor/executor.py +++ b/dotbot/plugin.py @@ -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 diff --git a/dotbot/util/module.py b/dotbot/util/module.py new file mode 100644 index 0000000..0a88686 --- /dev/null +++ b/dotbot/util/module.py @@ -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) diff --git a/dotbot/executor/cleaner.py b/plugins/clean.py similarity index 92% rename from dotbot/executor/cleaner.py rename to plugins/clean.py index 504c0de..22ec450 100644 --- a/dotbot/executor/cleaner.py +++ b/plugins/clean.py @@ -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): diff --git a/dotbot/executor/linker.py b/plugins/link.py similarity index 97% rename from dotbot/executor/linker.py rename to plugins/link.py index 5c12ea0..429158d 100644 --- a/dotbot/executor/linker.py +++ b/plugins/link.py @@ -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): diff --git a/dotbot/executor/commandrunner.py b/plugins/shell.py similarity index 92% rename from dotbot/executor/commandrunner.py rename to plugins/shell.py index 45d40f5..a2d9c1a 100644 --- a/dotbot/executor/commandrunner.py +++ b/plugins/shell.py @@ -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)