-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
858 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,43 @@ | ||
<p align="center"><img src="https://cdn.rawgit.com/arcticicestudio/snowsaw/develop/assets/snowsaw-banner.svg"/></p> | ||
|
||
<p align="center"><img src="https://assets-cdn.github.com/favicon.ico" width=24 height=24/> <a href="https://github.com/arcticicestudio/snowsaw/releases/latest"><img src="https://img.shields.io/github/release/arcticicestudio/snowsaw.svg"/></a> <a href="https://github.com/arcticicestudio/snowsaw/releases/latest"><img src="https://img.shields.io/badge/pre--release---_-blue.svg"/></a></p> | ||
<p align="center"><img src="https://assets-cdn.github.com/favicon.ico" width=24 height=24/> <a href="https://github.com/arcticicestudio/snowsaw/releases/latest"><img src="https://img.shields.io/github/release/arcticicestudio/snowsaw.svg"/></a> <a href="https://github.com/arcticicestudio/snowsaw/releases/latest"><img src="https://img.shields.io/badge/pre--release---_-blue.svg"/></a> <img src="https://www.python.org/static/favicon.ico" width=24 height=24/> <img src="https://img.shields.io/badge/Python-3.5+-blue.svg"/></p> | ||
|
||
--- | ||
|
||
# 0.1.0 | ||
*2017-01-07* | ||
## Features | ||
❯ Implemented the [CLI][readme-cli] (@arcticicestudio, #7, 35584e0e) and public [Plugin API][readme-plugin-api] (@arcticicestudio, #6, 7bee974a). | ||
|
||
❯ Implemented the snowsaw core logic classes | ||
- [`snowsaw.ConfigReader`](https://github.com/arcticicestudio/snowsaw/blob/develop/snowsaw/config.py) (@arcticicestudio, #1, bc9468df) | ||
- [`snowsaw.Context`](https://github.com/arcticicestudio/snowsaw/blob/develop/snowsaw/context.py) (@arcticicestudio, #2, 528d1710) | ||
- [`snowsaw.Dispatcher`](https://github.com/arcticicestudio/snowsaw/blob/develop/snowsaw/dispatcher.py) (@arcticicestudio, #5, 5bb0873a) | ||
|
||
This includes the custom logger class [`snowsaw.logging.Logger`](https://github.com/arcticicestudio/snowsaw/blob/develop/snowsaw/logging/logger.py) (@arcticicestudio, #3, c56a7195) and the [`util`](https://github.com/arcticicestudio/snowsaw/tree/develop/snowsaw/util) (@arcticicestudio, #4, 695f1fd3) package which provides project util methods and classes. | ||
|
||
❯ Implemented the [`setup.py`](https://github.com/arcticicestudio/snowsaw/blob/develop/snowsaw/setup.py) file. (@arcticicestudio, #8, 4fad0759) | ||
|
||
❯ Implemented the core plugins | ||
- [Clean][readme-core-tasks-clean] (@arcticicestudio, #9, 7fa022fd) | ||
- [Link][readme-core-tasks-link] (@arcticicestudio, #10, 0cfd0b94) | ||
- [Shell][readme-core-tasks-shell] (@arcticicestudio, #11, a51b61ba) | ||
|
||
❯ Implemented the main snowsaw executeable binary [`bin/snowsaw`](https://github.com/arcticicestudio/snowsaw/blob/develop/bin/snowsaw). (@arcticicestudio, #12, 91b9febe) | ||
|
||
A detailed [integration guide][readme-integration-guide] and more information about the public [Plugin API][readme-plugin-api], the [design concept][readme-design-concept] and the [configuration documentation][readme-configuration-documentation] can be found in the [README][readme] and the [project wiki][wiki]. | ||
|
||
# 0.0.0 | ||
*2017-01-07* | ||
**Project Initialization** | ||
|
||
[readme]: https://github.com/arcticicestudio/snowsaw/blob/develop/README.md | ||
[readme-cli]: https://github.com/arcticicestudio/snowsaw#cli | ||
[readme-configuration-documentation]: https://github.com/arcticicestudio/snowsaw#configuration | ||
[readme-design-concept]: https://github.com/arcticicestudio/snowsaw#design-concept | ||
[readme-integration-guide]: https://github.com/arcticicestudio/snowsaw#integration | ||
[readme-plugin-api]: https://github.com/arcticicestudio/snowsaw#plugin-api | ||
[readme-core-tasks-link]: https://github.com/arcticicestudio/snowsaw#link | ||
[readme-core-tasks-clean]: https://github.com/arcticicestudio/snowsaw#clean | ||
[readme-core-tasks-shell]: https://github.com/arcticicestudio/snowsaw#shell | ||
[wiki]: https://github.com/arcticicestudio/snowsaw/wiki |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#!/usr/bin/env python | ||
|
||
import sys | ||
import os | ||
|
||
PROJECT_ROOT_DIRECTORY = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) | ||
|
||
if os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'snowsaw')): | ||
if PROJECT_ROOT_DIRECTORY not in sys.path: | ||
sys.path.insert(0, PROJECT_ROOT_DIRECTORY) | ||
os.putenv('PYTHONPATH', PROJECT_ROOT_DIRECTORY) | ||
|
||
import snowsaw | ||
|
||
|
||
def main(): | ||
snowsaw.cli.main() | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .cli import main | ||
from .plugin import Plugin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
""" | ||
The snowsaw CLI. | ||
This is the main entry point of the public API. | ||
""" | ||
from argparse import ArgumentParser | ||
import glob | ||
import os | ||
|
||
from .config import ConfigReader, ReadingError | ||
from .dispatcher import Dispatcher, DispatchError | ||
from .logging import Level | ||
from .logging import Logger | ||
from .util import module | ||
|
||
|
||
def add_options(parser): | ||
""" | ||
Adds all options to the specified parser. | ||
:param parser: The parser to add all options to | ||
:return: None | ||
""" | ||
parser.add_argument('-Q', '--super-quiet', dest='super_quiet', action='store_true', help='suppress almost all output') | ||
parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', help='suppress most output') | ||
parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='enable verbose output') | ||
parser.add_argument('-s', '--snowblocks-directory', nargs=1, dest='snowblocks_directory', | ||
help='base snowblock directory to run all tasks of', metavar='SNOWBLOCKSDIR', required=True) | ||
parser.add_argument('-c', '--config-file', nargs=1, dest='config_file', help='run tasks for the specified snowblock', metavar='CONFIGFILE') | ||
parser.add_argument('-p', '--plugin', action='append', dest='plugins', default=[], help='load PLUGIN as a plugin', metavar='PLUGIN') | ||
parser.add_argument('--disable-core-plugins', dest='disable_core_plugins', action='store_true', help='disable all core 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): | ||
""" | ||
Reads the specified configuration file. | ||
:param config_file: The configuration file to read | ||
:return: The read configuration data | ||
""" | ||
reader = ConfigReader(config_file) | ||
return reader.get_config() | ||
|
||
|
||
def main(): | ||
""" | ||
Processes all parsed options and hands it over to the dispatcher for each snowblock. | ||
:return: True if all tasks have been executed successfully, False otherwise | ||
""" | ||
log = Logger() | ||
try: | ||
parser = ArgumentParser() | ||
snowblock_config_filename = "snowblock.json" | ||
add_options(parser) | ||
options = parser.parse_args() | ||
|
||
if options.super_quiet: | ||
log.set_level(Level.WARNING) | ||
if options.quiet: | ||
log.set_level(Level.INFO) | ||
if options.verbose: | ||
log.set_level(Level.DEBUG) | ||
|
||
plugin_directories = list(options.plugin_dirs) | ||
if not options.disable_core_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) | ||
|
||
if options.config_file: | ||
snowblocks = [os.path.basename(os.path.dirname(options.config_file[0]))] | ||
else: | ||
snowblocks = [snowblock for snowblock in os.listdir(options.snowblocks_directory[0]) | ||
if os.path.isdir(os.path.join(options.snowblocks_directory[0], snowblock))] | ||
|
||
for snowblock in snowblocks: | ||
if os.path.isfile(os.path.join(snowblock, snowblock_config_filename)): | ||
log.info("❄ {}".format(snowblock)) | ||
tasks = read_config(os.path.join(snowblock, snowblock_config_filename)) | ||
|
||
if not isinstance(tasks, list): | ||
raise ReadingError("Configuration file must be a list of tasks") | ||
|
||
dispatcher = Dispatcher(snowblock) | ||
success = dispatcher.dispatch(tasks) | ||
if success: | ||
log.info("==> All tasks executed successfully\n") | ||
else: | ||
raise DispatchError("\n==> Some tasks were not executed successfully") | ||
else: | ||
log.lowinfo("Skipped snowblock \"{}\": No configuration file found".format(snowblock)) | ||
except (ReadingError, DispatchError) as e: | ||
log.error("{}".format(e)) | ||
exit(1) | ||
except KeyboardInterrupt: | ||
log.error("\n==> Operation aborted") | ||
exit(1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import json | ||
import os.path | ||
|
||
|
||
class ConfigReader(object): | ||
""" | ||
Snowblock configuration file reader. | ||
""" | ||
def __init__(self, config_file_path): | ||
self._config = self._read(config_file_path) | ||
|
||
def _read(self, config_file_path): | ||
""" | ||
Reads the specified configuration file. | ||
:param config_file_path: The path to the configuration file to read | ||
:return: The read configuration data | ||
""" | ||
try: | ||
_, ext = os.path.splitext(config_file_path) | ||
with open(config_file_path) as fin: | ||
data = json.load(fin) | ||
return data | ||
except Exception as e: | ||
raise ReadingError('Could not read config file:\n{}'.format(e)) | ||
|
||
def get_config(self): | ||
""" | ||
Gets the snowblock configuration data. | ||
:return: The read snowblock configuration data | ||
""" | ||
return self._config | ||
|
||
|
||
class ReadingError(Exception): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import copy | ||
|
||
|
||
class Context(object): | ||
""" | ||
Contextual data and information for plugins | ||
""" | ||
def __init__(self, snowblock_dir): | ||
self._snowblock_dir = snowblock_dir | ||
self._defaults = {} | ||
pass | ||
|
||
def set_snowblock_dir(self, snowblock_dir): | ||
self._snowblock_dir = snowblock_dir | ||
|
||
def snowblock_dir(self): | ||
return self._snowblock_dir | ||
|
||
def set_defaults(self, defaults): | ||
self._defaults = defaults | ||
|
||
def defaults(self): | ||
return copy.deepcopy(self._defaults) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import os | ||
from .plugin import Plugin | ||
from .logging import Logger | ||
from .context import Context | ||
|
||
|
||
class Dispatcher(object): | ||
""" | ||
Dispatches tasks to loaded plugins. | ||
""" | ||
def __init__(self, snowblock_dir): | ||
self._log = Logger() | ||
self._setup_context(snowblock_dir) | ||
self._load_plugins() | ||
|
||
def _setup_context(self, snowblock_dir): | ||
""" | ||
Sets up the plugin context for the specified snowblock. | ||
:param snowblock_dir: The directory of the snowblock | ||
:return: The plugin context | ||
""" | ||
path = os.path.abspath(os.path.realpath(os.path.expanduser(snowblock_dir))) | ||
if not os.path.exists(path): | ||
raise DispatchError("Nonexistent snowblock directory") | ||
self._context = Context(path) | ||
|
||
def dispatch(self, tasks): | ||
""" | ||
Dispatches the specified tasks to the loaded plugins. | ||
:param tasks: The tasks to dispatch | ||
:return: True if all tasks have been handled by the loaded plugins successfully, False otherwise | ||
""" | ||
success = True | ||
for task in tasks: | ||
for action in task: | ||
handled = False | ||
if action == "defaults": | ||
self._context.set_defaults(task[action]) | ||
handled = True | ||
for plugin in self._plugins: | ||
if plugin.can_handle(action): | ||
try: | ||
success &= plugin.handle(action, task[action]) | ||
handled = True | ||
except Exception: | ||
self._log.error("An error was encountered while executing action \"{}\"".format(action)) | ||
if not handled: | ||
success = False | ||
self._log.error("Action \"{}\" not handled".format(action)) | ||
return success | ||
|
||
def _load_plugins(self): | ||
""" | ||
Loads all found plugins. | ||
:return: None | ||
""" | ||
self._plugins = [plugin(self._context) for plugin in Plugin.__subclasses__()] | ||
|
||
|
||
class DispatchError(Exception): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
""" | ||
Provides a custom logging class with level color support. | ||
""" | ||
from .logger import Logger | ||
from .color import Color | ||
from .level import Level |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
class Color(object): | ||
""" | ||
Terminal escape sequences for colored logging. | ||
""" | ||
NONE = "" | ||
RESET = "\033[0m" | ||
RED = "\033[91m" | ||
GREEN = "\033[92m" | ||
YELLOW = "\033[93m" | ||
BLUE = "\033[94m" | ||
MAGENTA = "\033[95m" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
class Level(object): | ||
""" | ||
Logging level names as numeric values. | ||
""" | ||
NOTSET = 0 | ||
DEBUG = 10 | ||
LOWINFO = 15 | ||
INFO = 20 | ||
WARNING = 30 | ||
ERROR = 40 |
Oops, something went wrong.