Revision: 5446
Initial Code
Initial URL
Initial Description
Initial Title
Initial Tags
Initial Language
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