Permalink
Browse files

Allow escaped commas in per-task arguments.

Implements #137; major thanks to Erich Heine.
  • Loading branch information...
1 parent 7d95fdc commit 2c8aa1e0995289d925e06e23aa0390180eedbb2c @bitprophet bitprophet committed Sep 3, 2010
Showing with 59 additions and 8 deletions.
  1. +1 −0 AUTHORS
  2. +17 −5 docs/usage/fab.rst
  3. +29 −2 fabric/main.py
  4. +12 −1 tests/test_main.py
View
@@ -28,3 +28,4 @@ Alex Koshelev
Mich Matuson
Morgan Goose
Carl Meyer
+Erich Heine
View
@@ -216,18 +216,20 @@ Answering both these needs is the concept of "per-task arguments", which is a
special syntax you can tack onto the end of any task name:
* Use a colon (``:``) to separate the task name from its arguments;
-* Use commas (``,``) to separate arguments from one another;
+* Use commas (``,``) to separate arguments from one another (may be escaped
+ by using a backslash, i.e. ``\,``);
* Use equals signs (``=``) for keyword arguments, or omit them for positional
arguments;
Additionally, since this process involves string parsing, all values will end
up as Python strings, so plan accordingly. (We hope to improve upon this in
future versions of Fabric, provided an intuitive syntax can be found.)
-For example, a "create a new user" task might be defined like so (omitting the
-actual logic for brevity)::
+For example, a "create a new user" task might be defined like so (omitting most
+of the actual logic for brevity)::
- def new_user(username, admin='no'):
+ def new_user(username, admin='no', comment="No comment provided"):
+ log_action("New User (%s): %s" % (username, comment))
pass
You can specify just the username::
@@ -246,10 +248,20 @@ Or mix and match, just like in Python::
$ fab new_user:myusername,admin=yes
+The ``log_action`` call above is useful for illustrating escaped commas, like
+so::
+
+ $ fab new_user:myusername,admin=no,comment='Gary\, new developer (starts Monday)'
+
+.. note::
+ Quoting the backslash-escaped comma is required, as not doing so will cause
+ shell syntax errors. Quotes are also needed whenever an argument involves
+ other shell-related characters such as spaces.
+
All of the above are translated into the expected Python function calls. For
example, the last call above would become::
- >>> new_user('myusername', admin='yes')
+ >>> new_user('myusername', admin='yes', comment='Gary, new developer (starts Monday)')
Roles and hosts
---------------
View
@@ -14,7 +14,7 @@
import os
import sys
-from fabric import api # For checking callables against the API
+from fabric import api # For checking callables against the API
from fabric.contrib import console, files, project # Ditto
from fabric.network import denormalize, interpret_host_string, disconnect_all
from fabric import state # For easily-mockable access to roles, env and etc
@@ -252,6 +252,33 @@ def display_command(command):
sys.exit(0)
+def _escape_split(sep, argstr):
+ """
+ Allows for escaping of the separator: e.g. task:arg='foo\, bar'
+
+ It should be noted that the way bash et. al. do command line parsing, those
+ single quotes are required.
+ """
+ escaped_sep = r'\%s' % sep
+
+ if escaped_sep not in argstr:
+ return argstr.split(sep)
+
+ before, _, after = argstr.partition(escaped_sep)
+ startlist = before.split(sep) # a regular split is fine here
+ unfinished = startlist[-1]
+ startlist = startlist[:-1]
+
+ # recurse because there may be more escaped separators
+ endlist = _escape_split(sep, after)
+
+ # finish building the escaped value. we use endlist[0] becaue the first
+ # part of the string sent in recursion is the rest of the escaped value.
+ unfinished += sep + endlist[0]
+
+ return startlist + [unfinished] + endlist[1:] # put together all the parts
+
+
def parse_arguments(arguments):
"""
Parse string list into list of tuples: command, args, kwargs, hosts, roles.
@@ -266,7 +293,7 @@ def parse_arguments(arguments):
roles = []
if ':' in cmd:
cmd, argstr = cmd.split(':', 1)
- for pair in argstr.split(','):
+ for pair in _escape_split(',', argstr):
k, _, v = pair.partition('=')
if v:
# Catch, interpret host/hosts/role/roles kwargs
View
@@ -2,7 +2,7 @@
from nose.tools import eq_, raises
from fabric.decorators import hosts, roles
-from fabric.main import get_hosts, parse_arguments, _merge
+from fabric.main import get_hosts, parse_arguments, _merge, _escape_split
import fabric.state
from fabric.state import _AttributeDict
@@ -117,3 +117,14 @@ def test_lazy_roles():
def command():
pass
eq_hosts(command, ['a', 'b'])
+
+
+def test_escaped_task_arg_split():
+ """
+ Allow backslashes to escape the task argument separator character
+ """
+ argstr = r"foo,bar\,biz\,baz,what comes after baz?"
+ eq_(
+ _escape_split(',', argstr),
+ ['foo', 'bar,biz,baz', 'what comes after baz?']
+ )

0 comments on commit 2c8aa1e

Please sign in to comment.