Add filter function
Add a new HOT intrinsic function to filter out values from lists. Closes-Bug: #1633512 Change-Id: Ie90001e6436b5ed5b29db7d3c166aba42299d3df
This commit is contained in:
parent
c404896a7f
commit
df674a715b
@ -260,10 +260,12 @@ for the ``heat_template_version`` key:
|
||||
The key with value ``2017-02-24`` or ``ocata`` indicates that the YAML
|
||||
document is a HOT template and it may contain features added and/or removed
|
||||
up until the Ocata release. This version adds the ``str_replace_strict``
|
||||
function which raises errors for missing params. The complete list of
|
||||
supported functions is::
|
||||
function which raises errors for missing params and the ``filter`` function
|
||||
which filters out values from lists. The complete list of supported
|
||||
functions is::
|
||||
|
||||
digest
|
||||
filter
|
||||
get_attr
|
||||
get_file
|
||||
get_param
|
||||
@ -1782,3 +1784,33 @@ Another example reference other conditions
|
||||
|
||||
This function returns true if any one of other_condition_1 or
|
||||
other_condition_2 evaluate to true, otherwise returns false.
|
||||
|
||||
filter
|
||||
------
|
||||
The ``filter`` function removes values from lists.
|
||||
|
||||
The syntax of the ``filter`` function is
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
filter:
|
||||
- <values>
|
||||
- <list>
|
||||
|
||||
For example
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
parameters:
|
||||
list_param:
|
||||
type: comma_delimited_list
|
||||
default: [1, 2, 3]
|
||||
|
||||
outputs:
|
||||
output_list:
|
||||
value:
|
||||
filter:
|
||||
- [3]
|
||||
- {get_param: list_param}
|
||||
|
||||
output_list will be evaluated to [1, 2].
|
||||
|
@ -1271,3 +1271,49 @@ class Or(ConditionBoolean):
|
||||
def result(self):
|
||||
return any(self._get_condition(cd)
|
||||
for cd in function.resolve(self.args))
|
||||
|
||||
|
||||
class Filter(function.Function):
|
||||
"""A function for filtering out values from lists.
|
||||
|
||||
Takes the form::
|
||||
|
||||
filter:
|
||||
- <values>
|
||||
- <list>
|
||||
|
||||
Returns a new list without the values.
|
||||
"""
|
||||
def __init__(self, stack, fn_name, args):
|
||||
super(Filter, self).__init__(stack, fn_name, args)
|
||||
|
||||
self._values, self._sequence = self._parse_args()
|
||||
|
||||
def _parse_args(self):
|
||||
if (not isinstance(self.args, collections.Sequence) or
|
||||
isinstance(self.args, six.string_types)):
|
||||
raise TypeError(_('Argument to "%s" must be a list') %
|
||||
self.fn_name)
|
||||
|
||||
if len(self.args) != 2:
|
||||
raise ValueError(_('"%(fn)s" expected 2 arguments of the form '
|
||||
'[values, sequence] but got %(len)d arguments '
|
||||
'instead') %
|
||||
{'fn': self.fn_name, 'len': len(self.args)})
|
||||
|
||||
return self.args[0], self.args[1]
|
||||
|
||||
def result(self):
|
||||
sequence = function.resolve(self._sequence)
|
||||
if not sequence:
|
||||
return sequence
|
||||
if not isinstance(sequence, list):
|
||||
raise TypeError(_('"%s" only works with lists') % self.fn_name)
|
||||
|
||||
values = function.resolve(self._values)
|
||||
if not values:
|
||||
return sequence
|
||||
if not isinstance(values, list):
|
||||
raise TypeError(
|
||||
_('"%(fn)s" filters a list of values') % self.fn_name)
|
||||
return [i for i in sequence if i not in values]
|
||||
|
@ -539,6 +539,7 @@ class HOTemplate20170224(HOTemplate20161014):
|
||||
'if': hot_funcs.If,
|
||||
|
||||
# functions added in 2017-02-24
|
||||
'filter': hot_funcs.Filter,
|
||||
'str_replace_strict': hot_funcs.ReplaceJsonStrict,
|
||||
|
||||
# functions removed from 2015-10-15
|
||||
|
@ -1757,6 +1757,46 @@ conditions:
|
||||
|
||||
self.assertEqual(hot_tpl['resources'], empty.t['resources'])
|
||||
|
||||
def test_filter(self):
|
||||
snippet = {'filter': [[None], [1, None, 4, 2, None]]}
|
||||
tmpl = template.Template(hot_ocata_tpl_empty)
|
||||
stack = parser.Stack(utils.dummy_context(), 'test_stack', tmpl)
|
||||
resolved = self.resolve(snippet, tmpl, stack=stack)
|
||||
|
||||
self.assertEqual([1, 4, 2], resolved)
|
||||
|
||||
def test_filter_wrong_args_type(self):
|
||||
snippet = {'filter': 'foo'}
|
||||
tmpl = template.Template(hot_ocata_tpl_empty)
|
||||
stack = parser.Stack(utils.dummy_context(), 'test_stack', tmpl)
|
||||
self.assertRaises(exception.StackValidationFailed, self.resolve,
|
||||
snippet, tmpl, stack=stack)
|
||||
|
||||
def test_filter_wrong_args_number(self):
|
||||
snippet = {'filter': [[None], [1, 2], 'foo']}
|
||||
tmpl = template.Template(hot_ocata_tpl_empty)
|
||||
stack = parser.Stack(utils.dummy_context(), 'test_stack', tmpl)
|
||||
self.assertRaises(exception.StackValidationFailed, self.resolve,
|
||||
snippet, tmpl, stack=stack)
|
||||
|
||||
def test_filter_dict(self):
|
||||
snippet = {'filter': [[None], {'a': 1}]}
|
||||
tmpl = template.Template(hot_ocata_tpl_empty)
|
||||
stack = parser.Stack(utils.dummy_context(), 'test_stack', tmpl)
|
||||
self.assertRaises(TypeError, self.resolve, snippet, tmpl, stack=stack)
|
||||
|
||||
def test_filter_str(self):
|
||||
snippet = {'filter': [['a'], 'abcd']}
|
||||
tmpl = template.Template(hot_ocata_tpl_empty)
|
||||
stack = parser.Stack(utils.dummy_context(), 'test_stack', tmpl)
|
||||
self.assertRaises(TypeError, self.resolve, snippet, tmpl, stack=stack)
|
||||
|
||||
def test_filter_str_values(self):
|
||||
snippet = {'filter': ['abcd', ['a', 'b', 'c', 'd']]}
|
||||
tmpl = template.Template(hot_ocata_tpl_empty)
|
||||
stack = parser.Stack(utils.dummy_context(), 'test_stack', tmpl)
|
||||
self.assertRaises(TypeError, self.resolve, snippet, tmpl, stack=stack)
|
||||
|
||||
|
||||
class HotStackTest(common.HeatTestCase):
|
||||
"""Test stack function when stack was created from HOT template."""
|
||||
|
Loading…
Reference in New Issue
Block a user