Add the accumulate filter (#9133)

* Add the accumulate filter

- Add myself as a maintainer for it.
- Some integration tests.

* accumulate: fix documentation and add test aliases

The aliases file was copied over from
tests/integrations/targets/filter_dict/aliases as the documentation[1]
suggests to use the same group as existing similar tests.

[1]: https://docs.ansible.com/ansible/latest/dev_guide/testing/sanity/integration-aliases.html

Suggested-by: Felix Fontein <felix@fontein.de>

* accumulate: documentation: markup consistency with other plugins

Suggested-by: Felix Fontein <felix@fontein.de>
Suggested-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* filter/accumulate: Validate input is a Sequence

Accepting arbitrary iterables might lead to surprising behavior so we
are stricter on what we accept in the filter.
Relaxing those requirements is easier than retrofitting them, in terms
of backwards compatibility.

Suggested-by: Felix Fontein <felix@fontein.de>
Signed-off-by: Max Gautier <mg@max.gautier.name>

* filter/accumulate: Document the behavior with a string

Signed-off-by: Max Gautier <mg@max.gautier.name>

---------

Signed-off-by: Max Gautier <mg@max.gautier.name>
pull/9223/head
Max Gautier 2024-12-02 20:20:13 +01:00 committed by GitHub
parent d826dd1c88
commit a789bd128f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 104 additions and 0 deletions

2
.github/BOTMETA.yml vendored
View File

@ -135,6 +135,8 @@ files:
$doc_fragments/xenserver.py: $doc_fragments/xenserver.py:
labels: xenserver labels: xenserver
maintainers: bvitnik maintainers: bvitnik
$filters/accumulate.py:
maintainers: VannTen
$filters/counter.py: $filters/counter.py:
maintainers: keilr maintainers: keilr
$filters/crc32.py: $filters/crc32.py:

View File

@ -0,0 +1,62 @@
# Copyright (c) Max Gautier <mg@max.gautier.name>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
DOCUMENTATION = '''
name: accumulate
short_description: Produce a list of accumulated sums of the input list contents
version_added: 10.1.0
author: Max Gautier (@VannTen)
description:
- Passthrough to the L(Python itertools.accumulate function,https://docs.python.org/3/library/itertools.html#itertools.accumulate).
- Transforms an input list into the cumulative list of results from applying addition to the elements of the input list.
- Addition means the default Python implementation of C(+) for input list elements type.
options:
_input:
description: A list.
type: list
elements: any
required: true
'''
RETURN = '''
_value:
description: A list of cumulated sums of the elements of the input list.
type: list
elements: any
'''
EXAMPLES = '''
- name: Enumerate parent directories of some path
ansible.builtin.debug:
var: >
"/some/path/to/my/file"
| split('/') | map('split', '/')
| community.general.accumulate | map('join', '/')
# Produces: ['', '/some', '/some/path', '/some/path/to', '/some/path/to/my', '/some/path/to/my/file']
- name: Growing string
ansible.builtin.debug:
var: "'abc' | community.general.accumulate"
# Produces ['a', 'ab', 'abc']
'''
from itertools import accumulate
from collections.abc import Sequence
from ansible.errors import AnsibleFilterError
def list_accumulate(sequence):
if not isinstance(sequence, Sequence):
raise AnsibleFilterError('Invalid value type (%s) for accumulate (%r)' %
(type(sequence), sequence))
return accumulate(sequence)
class FilterModule(object):
def filters(self):
return {
'accumulate': list_accumulate,
}

View File

@ -0,0 +1,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/3

View File

@ -0,0 +1,35 @@
---
# Copyright (c), Max Gautier <mg@max.gautier.name>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Filter | Accumulate | Test valid values
assert:
that:
- "'abc' | community.general.accumulate == ['a', 'ab', 'abc']"
- "['a', 'b'] | community.general.accumulate == ['a', 'ab']"
- "[1, 2, 3] | community.general.accumulate == [1, 3, 6]"
- "[['foo'],['bar'],['foobar']] | community.general.accumulate == [['foo'], ['foo', 'bar'], ['foo', 'bar', 'foobar']]"
- "'path/to/file' | split('/') | map('split', '/') | community.general.accumulate | map('join', '/') == ['path', 'path/to', 'path/to/file']"
- "[{'foo':1}, {'bar':2}] | map('dict2items') | community.general.accumulate | map('items2dict') == [{'foo':1}, {'foo':1, 'bar':2}]"
- name: Filter | Accumulate | Test invalid values | Integer
debug:
var: "1 | community.general.accumulate"
register: integer_result
ignore_errors: true
- name: Filter | Accumulate | Test invalid values | Non uniform list
debug:
var: "['aa', 1] | community.general.accumulate"
register: non_uniform_list_result
ignore_errors: true
- name: Filter | Accumulate | Test invalid values | Check errors
assert:
that:
- integer_result is failed
- integer_result.msg is match('Invalid value type.*')
- non_uniform_list_result is failed
- non_uniform_list_result.msg is match('Unexpected templating type error.*can only concatenate str.*')