Merge varFind and varLookup

This commit is contained in:
Daniel Hokka Zakrisson 2012-10-20 22:34:31 +02:00
parent 572c66b7f6
commit c9c5fc1456
2 changed files with 47 additions and 37 deletions

View file

@ -220,36 +220,39 @@ def parse_json(raw_data):
_LISTRE = re.compile(r"(\w+)\[(\d+)\]") _LISTRE = re.compile(r"(\w+)\[(\d+)\]")
class VarNotFoundException(Exception): def _varFindLimitSpace(space, part, depth):
pass if space is None:
return space
def _varLookup(path, vars, depth=0): if part[0] == '{' and part[-1] == '}':
''' find the contents of a possibly complex variable in vars. ''' part = part[1:-1]
part = varReplace(part, vars, depth=depth + 1)
space = vars if part in space:
for part in path: space = space[part]
part = varReplace(part, vars, depth=depth + 1) elif "[" in part:
if part in space: m = _LISTRE.search(part)
space = space[part] if not m:
elif "[" in part: return None
m = _LISTRE.search(part) else:
if not m:
raise VarNotFoundException()
try: try:
space = space[m.group(1)][int(m.group(2))] space = space[m.group(1)][int(m.group(2))]
except (KeyError, IndexError): except (KeyError, IndexError):
raise VarNotFoundException() return None
else: else:
raise VarNotFoundException() return None
return space return space
def _varFind(text): def _varFind(text, vars, depth=0):
start = text.find("$") start = text.find("$")
if start == -1: if start == -1:
return None return None
var_start = start + 1 # $ as last character
if var_start >= len(text): if start + 1 == len(text):
return None return None
# Escaped var
if start > 0 and text[start - 1] == '\\':
return {'replacement': '$', 'start': start - 1, 'end': start + 1}
var_start = start + 1
if text[var_start] == '{': if text[var_start] == '{':
is_complex = True is_complex = True
brace_level = 1 brace_level = 1
@ -260,6 +263,7 @@ def _varFind(text):
end = var_start end = var_start
path = [] path = []
part_start = (var_start, brace_level) part_start = (var_start, brace_level)
space = vars
while end < len(text) and ((is_complex and brace_level > 0) or not is_complex): while end < len(text) and ((is_complex and brace_level > 0) or not is_complex):
if text[end].isalnum() or text[end] == '_': if text[end].isalnum() or text[end] == '_':
pass pass
@ -271,10 +275,7 @@ def _varFind(text):
pass pass
elif is_complex and text[end] == '.': elif is_complex and text[end] == '.':
if brace_level == part_start[1]: if brace_level == part_start[1]:
if text[part_start[0]] == '{': space = _varFindLimitSpace(space, text[part_start[0]:end], depth)
path.append(text[part_start[0] + 1:end - 1])
else:
path.append(text[part_start[0]:end])
part_start = (end + 1, brace_level) part_start = (end + 1, brace_level)
else: else:
break break
@ -284,8 +285,10 @@ def _varFind(text):
var_end -= 1 var_end -= 1
if text[var_end] != '}' or brace_level != 0: if text[var_end] != '}' or brace_level != 0:
return None return None
path.append(text[part_start[0]:var_end]) if var_end == part_start[0]:
return {'path': path, 'start': start, 'end': end} return None
space = _varFindLimitSpace(space, text[part_start[0]:var_end], depth)
return {'replacement': space, 'start': start, 'end': end}
def varReplace(raw, vars, depth=0, expand_lists=False): def varReplace(raw, vars, depth=0, expand_lists=False):
''' Perform variable replacement of $variables in string raw using vars dictionary ''' ''' Perform variable replacement of $variables in string raw using vars dictionary '''
@ -297,7 +300,7 @@ def varReplace(raw, vars, depth=0, expand_lists=False):
done = [] # Completed chunks to return done = [] # Completed chunks to return
while raw: while raw:
m = _varFind(raw) m = _varFind(raw, vars, depth)
if not m: if not m:
done.append(raw) done.append(raw)
break break
@ -305,13 +308,12 @@ def varReplace(raw, vars, depth=0, expand_lists=False):
# Determine replacement value (if unknown variable then preserve # Determine replacement value (if unknown variable then preserve
# original) # original)
try: replacement = m['replacement']
replacement = _varLookup(m['path'], vars, depth) if expand_lists and isinstance(replacement, (list, tuple)):
if expand_lists and isinstance(replacement, (list, tuple)): replacement = ",".join(replacement)
replacement = ",".join(replacement) if isinstance(replacement, (str, unicode)):
if isinstance(replacement, (str, unicode)): replacement = varReplace(replacement, vars, depth=depth+1, expand_lists=expand_lists)
replacement = varReplace(replacement, vars, depth=depth+1, expand_lists=expand_lists) if replacement is None:
except VarNotFoundException:
replacement = raw[m['start']:m['end']] replacement = raw[m['start']:m['end']]
start, end = m['start'], m['end'] start, end = m['start'], m['end']
@ -362,12 +364,12 @@ def varReplaceWithItems(basedir, varname, vars):
''' helper function used by with_items ''' ''' helper function used by with_items '''
if isinstance(varname, basestring): if isinstance(varname, basestring):
m = _varFind(varname) m = _varFind(varname, vars)
if not m: if not m:
return varname return varname
if m['start'] == 0 and m['end'] == len(varname): if m['start'] == 0 and m['end'] == len(varname):
try: try:
return varReplaceWithItems(basedir, _varLookup(m['path'], vars), vars) return varReplaceWithItems(basedir, m['replacement'], vars)
except VarNotFoundException: except VarNotFoundException:
return varname return varname
else: else:

View file

@ -230,6 +230,14 @@ class TestUtils(unittest.TestCase):
res = ansible.utils.varReplace(template, vars, expand_lists=True) res = ansible.utils.varReplace(template, vars, expand_lists=True)
assert res == 'yum pkg=foo,bar,baz state=installed' assert res == 'yum pkg=foo,bar,baz state=installed'
def test_varReplace_escaped_var(self):
vars = {
'foo': 'bar',
}
template = 'action \$foo'
res = ansible.utils.varReplace(template, vars)
assert res == 'action $foo'
def test_template_varReplace_iterated(self): def test_template_varReplace_iterated(self):
template = 'hello $who' template = 'hello $who'
vars = { vars = {