forked from ansible-collections/community.general
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add filter keep_keys. Feature ansible-collections#8438.
- Loading branch information
Showing
6 changed files
with
247 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
major_changes: | ||
- Filter keep_keys. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
# -*- coding: utf-8 -*- | ||
# Copyright (c) 2024 Vladimir Botka <vbotka@gmail.com> | ||
# 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 | ||
|
||
from __future__ import (absolute_import, division, print_function) | ||
__metaclass__ = type | ||
|
||
DOCUMENTATION = ''' | ||
name: keep_keys | ||
short_description: Keep specific keys from dictionaries in a list. | ||
version_added: "2.17" | ||
author: Vladimir Botka (@vbotka) | ||
description: This filter keeps only specified keys from a provided list of dictionaries. | ||
options: | ||
_input: | ||
description: A list of dictionaries. | ||
type: list | ||
elements: dictionary | ||
required: true | ||
target: | ||
description: | ||
- A list of keys or keys patterns to keep. | ||
- The interpretation of the list items depends on the option C(matching_parameter) | ||
- For matching_parameter C(regex) only the first item is taken. | ||
type: list | ||
elements: str | ||
required: true | ||
matching_parameter: | ||
description: Specify the matching option of target keys. | ||
type: str | ||
default: equal | ||
choices: | ||
- equal | ||
- starts_with | ||
- ends_with | ||
- regex | ||
''' | ||
|
||
EXAMPLES = ''' | ||
l: | ||
- {k0_x0: A0, k1_x1: B0, k2_x2: [C0], k3_x3: foo} | ||
- {k0_x0: A1, k1_x1: B1, k2_x2: [C1], k3_x3: bar} | ||
# By default match equal keys. | ||
t: [k0_x0, k1_x1] | ||
r: "{{ l | keep_keys(target=t) }}" | ||
# Match keys that starts with any of the items in the target. | ||
t: [k0, k1] | ||
r: "{{ l | keep_keys(target=t, matching_parameter='starts_with') }}" | ||
# Match keys that ends with any of the items in target. | ||
t: [x0, x1] | ||
r: "{{ l | keep_keys(target=t, matching_parameter='ends_with') }}" | ||
# Match keys by the regex. | ||
t: ['^.*[01]_x.*$'] | ||
r: "{{ l | keep_keys(target=t, matching_parameter='regex') }}" | ||
# Match keys by the regex. The regex does not have to be in list. | ||
t: '^.*[01]_x.*$' | ||
r: "{{ l | keep_keys(target=t, matching_parameter='regex') }}" | ||
# The results of all examples are all the same. | ||
r: | ||
- {k0_x0: A0, k1_x1: B0} | ||
- {k0_x0: A1, k1_x1: B1} | ||
''' | ||
|
||
RETURN = ''' | ||
_value: | ||
description: The list of dictionaries with selected keys. | ||
type: list | ||
elements: dictionary | ||
''' | ||
|
||
from ansible.errors import AnsibleFilterError | ||
from ansible.module_utils.six import string_types | ||
from ansible.module_utils.common._collections_compat import Mapping, Sequence | ||
|
||
import re | ||
|
||
|
||
def keep_keys(data, target=[], matching_parameter='equal'): | ||
"""keep specific keys from dictionaries in a list""" | ||
|
||
ld = data | ||
tt = target | ||
mp = matching_parameter | ||
ml = ['equal', 'starts_with', 'ends_with', 'regex'] | ||
|
||
if not isinstance(ld, Sequence): | ||
msg = "First argument for keep_keys must be a list. %s is %s" | ||
raise AnsibleFilterError(msg % (ld, type(ld))) | ||
|
||
for elem in ld: | ||
if not isinstance(elem, Mapping): | ||
msg = "Elements of the data list for keep_keys must be dictionaries. %s is %s" | ||
raise AnsibleFilterError(msg % (elem, type(elem))) | ||
|
||
if mp not in ml: | ||
msg = ("The matching_parameter for keep_keys must be one of %s. matching_parameter is %s") | ||
raise AnsibleFilterError(msg % (ml, mp)) | ||
|
||
if mp == 'regex': | ||
if isinstance(target, string_types): | ||
tt = target | ||
elif isinstance(target, Sequence): | ||
tt = target[0] | ||
else: | ||
msg = ("The target for keep_keys must be a string or a list if matching_parameter is regex." | ||
"target is %s.") | ||
raise AnsibleFilterError(msg % target) | ||
try: | ||
re.compile(tt) | ||
is_valid = True | ||
except re.error: | ||
is_valid = False | ||
if not is_valid: | ||
msg = ("The target for keep_keys must be a valid regex if matching_parameter is regex." | ||
"target is %s") | ||
raise AnsibleFilterError(msg % tt) | ||
else: | ||
if not isinstance(tt, Sequence): | ||
msg = ("The target for keep_keys must be a list if matching_parameter is %s. %s is %s") | ||
raise AnsibleFilterError(msg % (mp, tt, type(tt))) | ||
for elem in tt: | ||
if not isinstance(elem, string_types): | ||
msg = "Elements of the targets for keep_keys must be strings. %s is %s" | ||
raise AnsibleFilterError(msg % (elem, type(elem))) | ||
|
||
if mp == 'equal': | ||
my_keys = [[k for k in i.keys() if k in tt] for i in ld] | ||
elif mp == 'starts_with': | ||
my_keys = [[k for k in i.keys() if k.startswith(tuple(tt))] for i in ld] | ||
elif mp == 'ends_with': | ||
my_keys = [[k for k in i.keys() if k.endswith(tuple(tt))] for i in ld] | ||
elif mp == 'regex': | ||
if isinstance(target, string_types): | ||
tt = target | ||
else: | ||
tt = target[0] | ||
my_keys = [[k for k in i.keys() if re.match(tt, k)] for i in ld] | ||
|
||
return [dict([(k, ld[i][k]) for k in j]) for i, j in enumerate(my_keys)] | ||
|
||
|
||
class FilterModule(object): | ||
|
||
def filters(self): | ||
return { | ||
'keep_keys': keep_keys, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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/2 |
61 changes: 61 additions & 0 deletions
61
tests/integration/targets/filter_keep_keys/tasks/keep_keys.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
--- | ||
# 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 | ||
|
||
- name: Debug ansible_version | ||
ansible.builtin.debug: | ||
var: ansible_version | ||
when: not quite_test | d(true) | bool | ||
tags: ansible_version | ||
|
||
- name: Test keep keys equal (default) | ||
ansible.builtin.assert: | ||
that: | ||
- (rr | difference(result1) | length) == 0 | ||
success_msg: | | ||
[OK] result: | ||
{{ rr | to_yaml }} | ||
fail_msg: | | ||
[ERR] result: | ||
{{ rr | to_yaml }} | ||
quiet: "{{ quite_test | d(true) | bool }}" | ||
vars: | ||
rr: "{{ list1 | community.general.keep_keys(target=tt) }}" | ||
tt: [k0_x0, k1_x1] | ||
tags: equal_default | ||
|
||
- name: Test keep keys regex string | ||
ansible.builtin.assert: | ||
that: | ||
- (rr | difference(result1) | length) == 0 | ||
success_msg: | | ||
[OK] result: | ||
{{ rr | to_yaml }} | ||
fail_msg: | | ||
[ERR] result: | ||
{{ rr | to_yaml }} | ||
quiet: "{{ quite_test | d(true) | bool }}" | ||
vars: | ||
rr: "{{ list1 | community.general.keep_keys(target=tt, matching_parameter=mp) }}" | ||
mp: regex | ||
tt: '^.*[01]_x.*$' | ||
tags: regex_string | ||
|
||
- name: Test keep keys matching_parameter list | ||
ansible.builtin.assert: | ||
that: | ||
- (rr | difference(result1) | length) == 0 | ||
success_msg: | | ||
[OK] result: | ||
{{ rr | to_yaml }} | ||
fail_msg: | | ||
[ERR] result: | ||
{{ rr | to_yaml }} | ||
quiet: "{{ quite_test | d(true) | bool }}" | ||
loop: "{{ targets }}" | ||
loop_control: | ||
label: "{{ item.mp }}: {{ item.tt }}" | ||
vars: | ||
rr: "{{ list1 | community.general.keep_keys(target=item.tt, matching_parameter=item.mp) }}" | ||
tags: matching_parameter lists |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
# 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 | ||
|
||
- name: Test keep_keys | ||
import_tasks: keep_keys.yml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
--- | ||
# 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 | ||
|
||
targets: | ||
- {mp: equal, tt: [k0_x0, k1_x1]} | ||
- {mp: starts_with, tt: [k0, k1]} | ||
- {mp: ends_with, tt: [x0, x1]} | ||
- {mp: regex, tt: ['^.*[01]_x.*$']} | ||
|
||
list1: | ||
- {k0_x0: A0, k1_x1: B0, k2_x2: [C0], k3_x3: foo} | ||
- {k0_x0: A1, k1_x1: B1, k2_x2: [C1], k3_x3: bar} | ||
|
||
result1: | ||
- {k0_x0: A0, k1_x1: B0} | ||
- {k0_x0: A1, k1_x1: B1} |