/ Published in: Other
Expand |
Embed | Plain Text
Copy this code and paste it in your HTML
diff --git a/fabric.py b/fabric.py index 5023d29..bd3cc1e 100644 --- a/fabric.py +++ b/fabric.py @@ -28,6 +28,7 @@ import threading import time import types import datetime +import pkg_resources try: import paramiko as ssh @@ -62,12 +63,46 @@ ENV = { 'fab_debug':False, } +#TODO: find out why local eggs are not being loaded +#TODO: If they are, find out how to correctly define entry points in a module/package +#TODO: Think about how to 'inject' the current fabric context into operations without the user defining it + CONNECTIONS = [] COMMANDS = {} OPERATIONS = {} STRATEGIES = {} _LAZY_FORMAT_SUBSTITUTER = re.compile(r'$((?P<var>w+?))') +ENTRYPOINTS = {'fabric.commands': COMMANDS, + 'fabric.strategies': STRATEGIES, + 'fabric.operations': OPERATIONS} + +## Plugin discovery +def load_plugins(additional_paths=[]): + """Discover plugins registered to fabric entry points""" + ## 1. Create a pkg_resources environment in which to search for loadable 'distributions' (modules) + ## We always search the current working directory and sys.path + sys.path.append(os.getcwd()) + cwd_environment = pkg_resources.Environment() + print os.getcwd() + cwd_environment.scan(os.getcwd()) + for path in additional_paths: + cwd_environment.scan(os.path.abspath(os.path.expandvars(os.path.normpath(path)))) + ## 2. Now search this environment for non-conflicting distributions and add to the working set (a global in pkg_resources module) + distributions, errors = pkg_resources.working_set.find_plugins(cwd_environment) + print len(distributions) + map(pkg_resources.working_set.add, distributions) + ## 3. Now iterate throught our entrypoints, looking for plugins registered in the working_set. + ## Load into our application registries if found + print list(pkg_resources.working_set.iter_entry_points(group='fabric.operations', name=None)) + for entrypoint, registry in ENTRYPOINTS.items(): + for obj in pkg_resources.working_set.iter_entry_points(group=entrypoint, name=None): + obj = obj.load() + print "===> ", obj.__name__ + ## TODO: Is this correct? Adding operations to both OPERATIONS and __builtins__? + registry[obj.__name__] = obj + __builtins__[obj.__name__] = obj + # # Helper decorators: # @@ -166,6 +201,43 @@ def require(var, **kvargs): exit(1) @operation +def prompt(varname, msg, validate=None, default=None): + """Display a prompt to the user. Input is set as an ENV variable. + If EOFError is raised, continue without setting the variable. + + validate is a callable that raises an exception on invalid inputs and returns + the input for storage in ENV. It may process the input. + i.e. lambda x: int(x) will ensure that the input is a valid integer and will cast it + + Example: + prompt('myvar', 'Please enter a value for myvar', default='SpamAndEggs') + """ + if varname in ENV: + return + + ## Set the default value if default is callable or is not None + if callable(default): + default = default() + elif default: + default = default + + try: + input = raw_input('default: %sn%s: ' % (default, msg.strip()) ) + if not input: + input = default + + ## Validate input + if callable(validate): + try: + input = validate(input) + except Exception: + raise + + set(**{varname: input}) + except EOFError: + return + +@operation @run_per_host def put(host, client, env, localpath, remotepath, **kvargs): """Upload a file to the current hosts. @@ -936,6 +1008,7 @@ def _execute_commands(cmds): def main(args): try: + load_plugins() print(__greeter__ % ENV) fabfile = _pick_fabfile() load(fabfile, fail='warn')