Perform str_replace trying to match longest string first

Purpose is to avoid str_replace from doing partial replace
when a shorter instance of a matching key is found.

Conflicts:
	heat/engine/hot/functions.py

Change-Id: I63c13f4eb3b1375cb2df1a34fd4f09561564f400
Co-Authored-By: Zane Bitter <zbitter@redhat.com>
Closes-Bug: 1600209
(cherry picked from commit a2f5b5cb9a)
This commit is contained in:
Giulio Fidente 2016-08-12 16:00:03 -04:00
parent 1ed38cdbce
commit f32dbe7838
3 changed files with 51 additions and 7 deletions

View File

@ -394,8 +394,9 @@ class Replace(function.Function):
"<value_1> <value_2>"
This is implemented using python str.replace on each key. The order in
which replacements are performed is undefined.
This is implemented using python str.replace on each key. Longer keys are
substituted before shorter ones, but the order in which replacements are
performed is otherwise undefined.
"""
def __init__(self, stack, fn_name, args):
@ -455,6 +456,9 @@ class Replace(function.Function):
return string.replace(placeholder, six.text_type(value))
mapping = collections.OrderedDict(sorted(mapping.items(),
key=lambda t: len(t[0]),
reverse=True))
return six.moves.reduce(replace, six.iteritems(mapping), template)

View File

@ -247,8 +247,9 @@ class Replace(cfn_funcs.Replace):
"<value_1> <value_2>"
This is implemented using Python's str.replace on each key. The order in
which replacements are performed is undefined.
This is implemented using python str.replace on each key. Longer keys are
substituted before shorter ones, but the order in which replacements are
performed is otherwise undefined.
"""
def _parse_args(self):
@ -275,9 +276,25 @@ class ReplaceJson(Replace):
'''
A function for performing string substitutions.
Behaves the same as Replace, but tolerates non-string parameter
values, e.g map/list - these are serialized as json before doing
the string substitution.
Takes the form::
str_replace:
template: <key_1> <key_2>
params:
<key_1>: <value_1>
<key_2>: <value_2>
...
And resolves to::
"<value_1> <value_2>"
This is implemented using python str.replace on each key. Longer keys are
substituted before shorter ones, but the order in which replacements are
performed is otherwise undefined.
Non-string param values (e.g maps or lists) are serialized as JSON before
being substituted in.
'''
def result(self):
@ -319,6 +336,9 @@ class ReplaceJson(Replace):
return string.replace(placeholder, six.text_type(value))
mapping = collections.OrderedDict(sorted(mapping.items(),
key=lambda t: len(t[0]),
reverse=True))
return six.moves.reduce(replace, six.iteritems(mapping), template)

View File

@ -558,6 +558,26 @@ class HOTemplateTest(common.HeatTestCase):
self.assertEqual(snippet_resolved, self.resolve(snippet, tmpl))
def test_str_replace_order(self, tpl=hot_tpl_empty):
"""Test str_replace function substitution order."""
snippet = {'str_replace': {'template': '1234567890',
'params': {'1': 'a',
'12': 'b',
'123': 'c',
'1234': 'd',
'12345': 'e',
'123456': 'f',
'1234567': 'g'}}}
tmpl = template.Template(tpl)
self.assertEqual('g890', self.resolve(snippet, tmpl))
def test_str_replace_liberty_order(self):
"""Test str_replace function substitution order."""
self.test_str_replace_order(hot_liberty_tpl_empty)
def test_str_replace_syntax(self):
"""
Test str_replace function syntax.