Return to Snippet

Revision: 5446
at March 9, 2008 14:42 by rob_cowie


Initial Code
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')

Initial URL


Initial Description


Initial Title
Fabric Plugin Diff

Initial Tags
textmate, plugin, python

Initial Language
Other