-
-
Save kamilion/2d842a4cbf87d04d4fc5aaf7a4a0143e to your computer and use it in GitHub Desktop.
Pluggable command line tool
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
import os | |
# Automatically set the __all__ variable with all | |
# the available plugins. | |
plugin_dir = "plugins" | |
__all__ = [] | |
for filename in os.listdir(plugin_dir): | |
filename = plugin_dir + "/" + filename | |
if os.path.isfile(filename): | |
basename = os.path.basename(filename) | |
base, extension = os.path.splitext(basename) | |
if extension == ".py" and not basename.startswith("_"): | |
__all__.append(base) |
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
#!/usr/bin/env python | |
class AbsPlugin: | |
""" Abstract plugin """ | |
def _commands(self): | |
""" Get the list of commands for the current plugin. | |
By default all public methods in the plugin implementation | |
will be used as plugin commands. This method can be overriden | |
in subclasses to customize the available command list """ | |
attrs = filter(lambda attr: not attr.startswith('_'), dir(self)) | |
commands = {} | |
for attr in attrs: | |
method = getattr(self, attr) | |
commands[attr] = method | |
return commands |
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
#!/usr/bin/env python | |
from pluginmanager import PluginManager | |
import sys | |
class CLI: | |
""" Main command line interface """ | |
def __init__(self): | |
""" Initialize the plugin manager """ | |
self.__pluginmanager = PluginManager() | |
def parse_input(self): | |
""" Validates user input and delegates to the plugin manager """ | |
if len(sys.argv) < 2: | |
print "Usage: cli <plugin> <command> [<options>]" | |
print "The following plugins are available:\n" | |
self.__pluginmanager.help_all() | |
elif len(sys.argv) == 2: | |
print "Usage: cli <plugin> <command> [<options>]" | |
# Call the given plugin without command to print | |
# the help of the plugin | |
return self.__pluginmanager.call(sys.argv[1], None, None) | |
else: | |
# Call the command in the given plugin with the | |
# remaining arguments | |
return self.__pluginmanager.call(sys.argv[1], | |
sys.argv[2], sys.argv[3:]) | |
if __name__ == "__main__": | |
cli = CLI() | |
ret = cli.parse_input() | |
exit(ret) if ret else exit() |
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
#!/usr/bin/env python | |
from plugins import __all__ | |
class PluginManager: | |
""" Manages available plugins """ | |
def __init__(self): | |
""" Initialize the plugin list """ | |
self.__plugins = {} | |
def load_plugin(self, plugin_name): | |
""" Loads a single plugin given its name """ | |
if not plugin_name in __all__: | |
raise KeyError("Plugin " + plugin_name + " not found") | |
try: | |
plugin = self.__plugins[plugin_name] | |
except KeyError: | |
# Load the plugin only if not loaded yet | |
module = __import__("plugins." + plugin_name, fromlist=["plugins"]) | |
plugin = module.load() | |
self.__plugins[plugin_name] = plugin | |
return plugin | |
def call(self, plugin_name, command_name, args): | |
""" Calls the given command on the given plugin """ | |
try: | |
plugin = self.load_plugin(plugin_name) | |
if not command_name: | |
self.help(plugin) | |
else: | |
try: | |
command = plugin._commands()[command_name] | |
return command(args) | |
except KeyError: | |
# Command not found in plugin. Print only plugin help | |
self.help(plugin) | |
except KeyError: | |
# Plugin not found, pring generic help | |
self.help_all() | |
def help(self, plugin): | |
""" Prints the help for the given plugin """ | |
commands = plugin._commands() | |
plugin_name = plugin.__module__.split('.')[-1] | |
print "%s" % plugin.__doc__ | |
for command in sorted(commands.iterkeys()): | |
print " %s %s\t%s" % (plugin_name, command, | |
commands[command].__doc__) | |
def help_all(self): | |
""" Prints the help for all registered plugins """ | |
for name in sorted(__all__): | |
plugin = self.load_plugin(name) | |
self.help(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
#!/usr/bin/env python | |
# This is an example plugin that can be used as a | |
# skeleton for new plugins. | |
# The documentation string in the plugin class will be used to | |
# print the help of the plugin. | |
from abstract import AbsPlugin | |
class SkeletonPlugin(AbsPlugin): | |
""" An example plugin that prints dummy messages """ | |
def __init__(self): | |
pass | |
# Public methods will be considered plugin commands. | |
# The name of the command will be the method name. | |
# The documentation string in command methods will be used to | |
# print the help of the command. | |
# The arguments are the options given to the command itself | |
def dummy(self, args): | |
""" Prints a dummy message """ | |
print "This is the print_handler in the example plugin" | |
# Each plugin must provide a load method at module level that will be | |
# used to instantiate the plugin | |
def load(): | |
""" Loads the current plugin """ | |
return SkeletonPlugin() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment