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:
Thomas Herve 2016-10-15 20:57:51 +02:00
parent c404896a7f
commit df674a715b
4 changed files with 121 additions and 2 deletions

View File

@ -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].

View File

@ -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]

View File

@ -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

View File

@ -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."""