1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-04-14 16:05:04 +00:00

Reformat everything.

This commit is contained in:
Felix Fontein 2025-11-01 12:08:41 +01:00
parent 3f2213791a
commit 340ff8586d
1008 changed files with 61301 additions and 58309 deletions

View file

@ -49,14 +49,13 @@ from ansible.errors import AnsibleFilterError
def list_accumulate(sequence):
if not isinstance(sequence, Sequence):
raise AnsibleFilterError(f'Invalid value type ({type(sequence)}) for accumulate ({sequence!r})')
raise AnsibleFilterError(f"Invalid value type ({type(sequence)}) for accumulate ({sequence!r})")
return accumulate(sequence)
class FilterModule:
def filters(self):
return {
'accumulate': list_accumulate,
"accumulate": list_accumulate,
}

View file

@ -40,9 +40,11 @@ from collections import Counter
def counter(sequence):
''' Count elements in a sequence. Returns dict with count result. '''
"""Count elements in a sequence. Returns dict with count result."""
if not isinstance(sequence, Sequence):
raise AnsibleFilterError(f'Argument for community.general.counter must be a sequence (string or list). {sequence} is {type(sequence)}')
raise AnsibleFilterError(
f"Argument for community.general.counter must be a sequence (string or list). {sequence} is {type(sequence)}"
)
try:
result = dict(Counter(sequence))
@ -54,11 +56,11 @@ def counter(sequence):
class FilterModule:
''' Ansible counter jinja2 filters '''
"""Ansible counter jinja2 filters"""
def filters(self):
filters = {
'counter': counter,
"counter": counter,
}
return filters

View file

@ -9,6 +9,7 @@ from ansible.module_utils.common.collections import is_string
try:
from zlib import crc32
HAS_ZLIB = True
except ImportError:
HAS_ZLIB = False
@ -45,17 +46,17 @@ _value:
def crc32s(value):
if not is_string(value):
raise AnsibleFilterError(f'Invalid value type ({type(value)}) for crc32 ({value!r})')
raise AnsibleFilterError(f"Invalid value type ({type(value)}) for crc32 ({value!r})")
if not HAS_ZLIB:
raise AnsibleFilterError('Failed to import zlib module')
raise AnsibleFilterError("Failed to import zlib module")
data = to_bytes(value, errors='surrogate_or_strict')
return f"{crc32(data) & 0xffffffff:x}"
data = to_bytes(value, errors="surrogate_or_strict")
return f"{crc32(data) & 0xFFFFFFFF:x}"
class FilterModule:
def filters(self):
return {
'crc32': crc32s,
"crc32": crc32s,
}

View file

@ -1,4 +1,3 @@
# Copyright (c) 2021, Felix Fontein <felix@fontein.de>
# 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
@ -61,17 +60,17 @@ _value:
def dict_filter(sequence):
'''Convert a list of tuples to a dictionary.
"""Convert a list of tuples to a dictionary.
Example: ``[[1, 2], ['a', 'b']] | community.general.dict`` results in ``{1: 2, 'a': 'b'}``
'''
"""
return dict(sequence)
class FilterModule:
'''Ansible jinja2 filters'''
"""Ansible jinja2 filters"""
def filters(self):
return {
'dict': dict_filter,
"dict": dict_filter,
}

View file

@ -38,7 +38,7 @@ _value:
def dict_kv(value, key):
'''Return a dictionary with a single key-value pair
"""Return a dictionary with a single key-value pair
Example:
@ -89,14 +89,12 @@ def dict_kv(value, key):
}
]
}
'''
"""
return {key: value}
class FilterModule:
''' Query filter '''
"""Query filter"""
def filters(self):
return {
'dict_kv': dict_kv
}
return {"dict_kv": dict_kv}

View file

@ -1,4 +1,3 @@
# Copyright (c) 2021, Andrew Pantuso (@ajpantuso) <ajpantuso@gmail.com>
# Copyright (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.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)
@ -80,13 +79,16 @@ _value:
from ansible.errors import AnsibleFilterError
from ansible_collections.community.general.plugins.module_utils.csv import (initialize_dialect, read_csv, CSVError,
DialectNotAvailableError,
CustomDialectFailureError)
from ansible_collections.community.general.plugins.module_utils.csv import (
initialize_dialect,
read_csv,
CSVError,
DialectNotAvailableError,
CustomDialectFailureError,
)
def from_csv(data, dialect='excel', fieldnames=None, delimiter=None, skipinitialspace=None, strict=None):
def from_csv(data, dialect="excel", fieldnames=None, delimiter=None, skipinitialspace=None, strict=None):
dialect_params = {
"delimiter": delimiter,
"skipinitialspace": skipinitialspace,
@ -112,8 +114,5 @@ def from_csv(data, dialect='excel', fieldnames=None, delimiter=None, skipinitial
class FilterModule:
def filters(self):
return {
'from_csv': from_csv
}
return {"from_csv": from_csv}

View file

@ -1,4 +1,3 @@
# Copyright (c) 2023, Steffen Scheib <steffen@scheib.me>
# 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
@ -51,7 +50,7 @@ from ansible.errors import AnsibleFilterError
class IniParser(ConfigParser):
''' Implements a configparser which is able to return a dict '''
"""Implements a configparser which is able to return a dict"""
def __init__(self):
super().__init__(interpolation=None)
@ -61,35 +60,32 @@ class IniParser(ConfigParser):
d = dict(self._sections)
for k in d:
d[k] = dict(self._defaults, **d[k])
d[k].pop('__name__', None)
d[k].pop("__name__", None)
if self._defaults:
d['DEFAULT'] = dict(self._defaults)
d["DEFAULT"] = dict(self._defaults)
return d
def from_ini(obj):
''' Read the given string as INI file and return a dict '''
"""Read the given string as INI file and return a dict"""
if not isinstance(obj, str):
raise AnsibleFilterError(f'from_ini requires a str, got {type(obj)}')
raise AnsibleFilterError(f"from_ini requires a str, got {type(obj)}")
parser = IniParser()
try:
parser.read_file(StringIO(obj))
except Exception as ex:
raise AnsibleFilterError(f'from_ini failed to parse given string: {ex}', orig_exc=ex)
raise AnsibleFilterError(f"from_ini failed to parse given string: {ex}", orig_exc=ex)
return parser.as_dict()
class FilterModule:
''' Query filter '''
"""Query filter"""
def filters(self):
return {
'from_ini': from_ini
}
return {"from_ini": from_ini}

View file

@ -57,33 +57,33 @@ from collections.abc import Mapping, Sequence
def groupby_as_dict(sequence, attribute):
'''
"""
Given a sequence of dictionaries and an attribute name, returns a dictionary mapping
the value of this attribute to the dictionary.
If multiple dictionaries in the sequence have the same value for this attribute,
the filter will fail.
'''
"""
if not isinstance(sequence, Sequence):
raise AnsibleFilterError('Input is not a sequence')
raise AnsibleFilterError("Input is not a sequence")
result = dict()
for list_index, element in enumerate(sequence):
if not isinstance(element, Mapping):
raise AnsibleFilterError(f'Sequence element #{list_index} is not a mapping')
raise AnsibleFilterError(f"Sequence element #{list_index} is not a mapping")
if attribute not in element:
raise AnsibleFilterError(f'Attribute not contained in element #{list_index} of sequence')
raise AnsibleFilterError(f"Attribute not contained in element #{list_index} of sequence")
result_index = element[attribute]
if result_index in result:
raise AnsibleFilterError(f'Multiple sequence entries have attribute value {result_index!r}')
raise AnsibleFilterError(f"Multiple sequence entries have attribute value {result_index!r}")
result[result_index] = element
return result
class FilterModule:
''' Ansible list filters '''
"""Ansible list filters"""
def filters(self):
return {
'groupby_as_dict': groupby_as_dict,
"groupby_as_dict": groupby_as_dict,
}

View file

@ -1,4 +1,3 @@
# Copyright (c) 2021, Andrew Pantuso (@ajpantuso) <ajpantuso@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
@ -20,6 +19,7 @@ except ImportError:
try:
from hashids import Hashids
HAS_HASHIDS = True
except ImportError:
HAS_HASHIDS = False
@ -35,27 +35,21 @@ def initialize_hashids(**kwargs):
return Hashids(**params)
except TypeError as e:
raise AnsibleFilterError(
"The provided parameters %s are invalid: %s" % (
', '.join(["%s=%s" % (k, v) for k, v in params.items()]),
to_native(e)
)
"The provided parameters %s are invalid: %s"
% (", ".join(["%s=%s" % (k, v) for k, v in params.items()]), to_native(e))
)
def hashids_encode(nums, salt=None, alphabet=None, min_length=None):
"""Generates a YouTube-like hash from a sequence of ints
:nums: Sequence of one or more ints to hash
:salt: String to use as salt when hashing
:alphabet: String of 16 or more unique characters to produce a hash
:min_length: Minimum length of hash produced
:nums: Sequence of one or more ints to hash
:salt: String to use as salt when hashing
:alphabet: String of 16 or more unique characters to produce a hash
:min_length: Minimum length of hash produced
"""
hashids = initialize_hashids(
salt=salt,
alphabet=alphabet,
min_length=min_length
)
hashids = initialize_hashids(salt=salt, alphabet=alphabet, min_length=min_length)
# Handles the case where a single int is not encapsulated in a list or tuple.
# User convenience seems preferable to strict typing in this case
@ -74,25 +68,20 @@ def hashids_encode(nums, salt=None, alphabet=None, min_length=None):
def hashids_decode(hashid, salt=None, alphabet=None, min_length=None):
"""Decodes a YouTube-like hash to a sequence of ints
:hashid: Hash string to decode
:salt: String to use as salt when hashing
:alphabet: String of 16 or more unique characters to produce a hash
:min_length: Minimum length of hash produced
:hashid: Hash string to decode
:salt: String to use as salt when hashing
:alphabet: String of 16 or more unique characters to produce a hash
:min_length: Minimum length of hash produced
"""
hashids = initialize_hashids(
salt=salt,
alphabet=alphabet,
min_length=min_length
)
hashids = initialize_hashids(salt=salt, alphabet=alphabet, min_length=min_length)
nums = hashids.decode(hashid)
return list(nums)
class FilterModule:
def filters(self):
return {
'hashids_encode': hashids_encode,
'hashids_decode': hashids_decode,
"hashids_encode": hashids_encode,
"hashids_decode": hashids_decode,
}

View file

@ -79,6 +79,7 @@ import importlib
try:
import jc
HAS_LIB = True
except ImportError:
HAS_LIB = False
@ -133,26 +134,28 @@ def jc_filter(data, parser, quiet=True, raw=False):
"""
if not HAS_LIB:
raise AnsibleError('You need to install "jc" as a Python library on the Ansible controller prior to running jc filter')
raise AnsibleError(
'You need to install "jc" as a Python library on the Ansible controller prior to running jc filter'
)
try:
# new API (jc v1.18.0 and higher) allows use of plugin parsers
if hasattr(jc, 'parse'):
if hasattr(jc, "parse"):
return jc.parse(parser, data, quiet=quiet, raw=raw)
# old API (jc v1.17.7 and lower)
else:
jc_parser = importlib.import_module(f'jc.parsers.{parser}')
jc_parser = importlib.import_module(f"jc.parsers.{parser}")
return jc_parser.parse(data, quiet=quiet, raw=raw)
except Exception as e:
raise AnsibleFilterError(f'Error in jc filter plugin: {e}')
raise AnsibleFilterError(f"Error in jc filter plugin: {e}")
class FilterModule:
''' Query filter '''
"""Query filter"""
def filters(self):
return {
'jc': jc_filter,
"jc": jc_filter,
}

View file

@ -35,39 +35,28 @@ class FilterModule:
try:
return loads(inp)
except Exception as e:
raise AnsibleFilterError(
f"{filter_name}: could not decode JSON from {object_name}: {e}"
) from e
raise AnsibleFilterError(f"{filter_name}: could not decode JSON from {object_name}: {e}") from e
if not isinstance(inp, (list, dict)):
raise AnsibleFilterError(
f"{filter_name}: {object_name} is not dictionary, list or string"
)
raise AnsibleFilterError(f"{filter_name}: {object_name} is not dictionary, list or string")
return inp
def check_patch_arguments(self, filter_name: str, args: dict):
if "op" not in args or not isinstance(args["op"], str):
raise AnsibleFilterError(f"{filter_name}: 'op' argument is not a string")
if args["op"] not in OPERATIONS_AVAILABLE:
raise AnsibleFilterError(
f"{filter_name}: unsupported 'op' argument: {args['op']}"
)
raise AnsibleFilterError(f"{filter_name}: unsupported 'op' argument: {args['op']}")
if "path" not in args or not isinstance(args["path"], str):
raise AnsibleFilterError(f"{filter_name}: 'path' argument is not a string")
if args["op"] in OPERATIONS_NEEDING_FROM:
if "from" not in args:
raise AnsibleFilterError(
f"{filter_name}: 'from' argument missing for '{args['op']}' operation"
)
raise AnsibleFilterError(f"{filter_name}: 'from' argument missing for '{args['op']}' operation")
if not isinstance(args["from"], str):
raise AnsibleFilterError(
f"{filter_name}: 'from' argument is not a string"
)
raise AnsibleFilterError(f"{filter_name}: 'from' argument is not a string")
def json_patch(
self,
@ -77,7 +66,6 @@ class FilterModule:
value: Any = None,
**kwargs: dict,
) -> Any:
if not HAS_LIB:
raise AnsibleFilterError(
"You need to install 'jsonpatch' package prior to running 'json_patch' filter"
@ -88,9 +76,7 @@ class FilterModule:
fail_test = kwargs.pop("fail_test", False)
if kwargs:
raise AnsibleFilterError(
f"json_patch: unexpected keywords arguments: {', '.join(sorted(kwargs))}"
)
raise AnsibleFilterError(f"json_patch: unexpected keywords arguments: {', '.join(sorted(kwargs))}")
if not isinstance(fail_test, bool):
raise AnsibleFilterError("json_patch: 'fail_test' argument is not a bool")
@ -109,9 +95,7 @@ class FilterModule:
result = jsonpatch.apply_patch(inp, [args])
except jsonpatch.JsonPatchTestFailed as e:
if fail_test:
raise AnsibleFilterError(
f"json_patch: test operation failed: {e}"
) from e
raise AnsibleFilterError(f"json_patch: test operation failed: {e}") from e
else:
pass
except Exception as e:
@ -126,16 +110,13 @@ class FilterModule:
/,
fail_test: bool = False,
) -> Any:
if not HAS_LIB:
raise AnsibleFilterError(
"You need to install 'jsonpatch' package prior to running 'json_patch_recipe' filter"
) from JSONPATCH_IMPORT_ERROR
if not isinstance(operations, list):
raise AnsibleFilterError(
"json_patch_recipe: 'operations' needs to be a list"
)
raise AnsibleFilterError("json_patch_recipe: 'operations' needs to be a list")
if not isinstance(fail_test, bool):
raise AnsibleFilterError("json_patch: 'fail_test' argument is not a bool")
@ -150,9 +131,7 @@ class FilterModule:
result = jsonpatch.apply_patch(inp, operations)
except jsonpatch.JsonPatchTestFailed as e:
if fail_test:
raise AnsibleFilterError(
f"json_patch_recipe: test operation failed: {e}"
) from e
raise AnsibleFilterError(f"json_patch_recipe: test operation failed: {e}") from e
else:
pass
except Exception as e:
@ -165,7 +144,6 @@ class FilterModule:
inp: Union[str, list, dict, bytes, bytearray],
target: Union[str, list, dict, bytes, bytearray],
) -> list:
if not HAS_LIB:
raise AnsibleFilterError(
"You need to install 'jsonpatch' package prior to running 'json_diff' filter"

View file

@ -109,44 +109,46 @@ from ansible.errors import AnsibleError, AnsibleFilterError
try:
import jmespath
HAS_LIB = True
except ImportError:
HAS_LIB = False
def json_query(data, expr):
'''Query data using jmespath query language ( http://jmespath.org ). Example:
"""Query data using jmespath query language ( http://jmespath.org ). Example:
- ansible.builtin.debug: msg="{{ instance | json_query(tagged_instances[*].block_device_mapping.*.volume_id') }}"
'''
"""
if not HAS_LIB:
raise AnsibleError('You need to install "jmespath" prior to running '
'json_query filter')
raise AnsibleError('You need to install "jmespath" prior to running json_query filter')
# Hack to handle Ansible Unsafe text, AnsibleMapping and AnsibleSequence
# See issues https://github.com/ansible-collections/community.general/issues/320
# and https://github.com/ansible/ansible/issues/85600.
jmespath.functions.REVERSE_TYPES_MAP['string'] = jmespath.functions.REVERSE_TYPES_MAP['string'] + (
'AnsibleUnicode', 'AnsibleUnsafeText', '_AnsibleTaggedStr',
jmespath.functions.REVERSE_TYPES_MAP["string"] = jmespath.functions.REVERSE_TYPES_MAP["string"] + (
"AnsibleUnicode",
"AnsibleUnsafeText",
"_AnsibleTaggedStr",
)
jmespath.functions.REVERSE_TYPES_MAP['array'] = jmespath.functions.REVERSE_TYPES_MAP['array'] + (
'AnsibleSequence', '_AnsibleLazyTemplateList',
jmespath.functions.REVERSE_TYPES_MAP["array"] = jmespath.functions.REVERSE_TYPES_MAP["array"] + (
"AnsibleSequence",
"_AnsibleLazyTemplateList",
)
jmespath.functions.REVERSE_TYPES_MAP['object'] = jmespath.functions.REVERSE_TYPES_MAP['object'] + (
'AnsibleMapping', '_AnsibleLazyTemplateDict',
jmespath.functions.REVERSE_TYPES_MAP["object"] = jmespath.functions.REVERSE_TYPES_MAP["object"] + (
"AnsibleMapping",
"_AnsibleLazyTemplateDict",
)
try:
return jmespath.search(expr, data)
except jmespath.exceptions.JMESPathError as e:
raise AnsibleFilterError(f'JMESPathError in json_query filter plugin:\n{e}')
raise AnsibleFilterError(f"JMESPathError in json_query filter plugin:\n{e}")
except Exception as e:
# For older jmespath, we can get ValueError and TypeError without much info.
raise AnsibleFilterError(f'Error in jmespath.search in json_query filter plugin:\n{e}')
raise AnsibleFilterError(f"Error in jmespath.search in json_query filter plugin:\n{e}")
class FilterModule:
''' Query filter '''
"""Query filter"""
def filters(self):
return {
'json_query': json_query
}
return {"json_query": json_query}

View file

@ -101,10 +101,11 @@ _value:
from ansible_collections.community.general.plugins.plugin_utils.keys_filter import (
_keys_filter_params,
_keys_filter_target_str)
_keys_filter_target_str,
)
def keep_keys(data, target=None, matching_parameter='equal'):
def keep_keys(data, target=None, matching_parameter="equal"):
"""keep specific keys from dictionaries in a list"""
# test parameters
@ -112,16 +113,20 @@ def keep_keys(data, target=None, matching_parameter='equal'):
# test and transform target
tt = _keys_filter_target_str(target, matching_parameter)
if matching_parameter == 'equal':
if matching_parameter == "equal":
def keep_key(key):
return key in tt
elif matching_parameter == 'starts_with':
elif matching_parameter == "starts_with":
def keep_key(key):
return key.startswith(tt)
elif matching_parameter == 'ends_with':
elif matching_parameter == "ends_with":
def keep_key(key):
return key.endswith(tt)
elif matching_parameter == 'regex':
elif matching_parameter == "regex":
def keep_key(key):
return tt.match(key) is not None
@ -129,8 +134,7 @@ def keep_keys(data, target=None, matching_parameter='equal'):
class FilterModule:
def filters(self):
return {
'keep_keys': keep_keys,
"keep_keys": keep_keys,
}

View file

@ -32,7 +32,7 @@ def flatten_list(lst):
result = []
for sublist in lst:
if not is_sequence(sublist):
msg = ("All arguments must be lists. %s is %s")
msg = "All arguments must be lists. %s is %s"
raise AnsibleFilterError(msg % (sublist, type(sublist)))
if len(sublist) > 0:
if all(is_sequence(sub) for sub in sublist):
@ -45,13 +45,11 @@ def flatten_list(lst):
def lists_union(*args, **kwargs):
lists = args
flatten = kwargs.pop('flatten', False)
flatten = kwargs.pop("flatten", False)
if kwargs:
# Some unused kwargs remain
raise AnsibleFilterError(
f"lists_union() got unexpected keywords arguments: {', '.join(kwargs.keys())}"
)
raise AnsibleFilterError(f"lists_union() got unexpected keywords arguments: {', '.join(kwargs.keys())}")
if flatten:
lists = flatten_list(args)
@ -74,13 +72,11 @@ def do_union(a, b):
def lists_intersect(*args, **kwargs):
lists = args
flatten = kwargs.pop('flatten', False)
flatten = kwargs.pop("flatten", False)
if kwargs:
# Some unused kwargs remain
raise AnsibleFilterError(
f"lists_intersect() got unexpected keywords arguments: {', '.join(kwargs.keys())}"
)
raise AnsibleFilterError(f"lists_intersect() got unexpected keywords arguments: {', '.join(kwargs.keys())}")
if flatten:
lists = flatten_list(args)
@ -112,13 +108,11 @@ def do_intersect(a, b):
def lists_difference(*args, **kwargs):
lists = args
flatten = kwargs.pop('flatten', False)
flatten = kwargs.pop("flatten", False)
if kwargs:
# Some unused kwargs remain
raise AnsibleFilterError(
f"lists_difference() got unexpected keywords arguments: {', '.join(kwargs.keys())}"
)
raise AnsibleFilterError(f"lists_difference() got unexpected keywords arguments: {', '.join(kwargs.keys())}")
if flatten:
lists = flatten_list(args)
@ -150,13 +144,11 @@ def do_difference(a, b):
def lists_symmetric_difference(*args, **kwargs):
lists = args
flatten = kwargs.pop('flatten', False)
flatten = kwargs.pop("flatten", False)
if kwargs:
# Some unused kwargs remain
raise AnsibleFilterError(
f"lists_difference() got unexpected keywords arguments: {', '.join(kwargs.keys())}"
)
raise AnsibleFilterError(f"lists_difference() got unexpected keywords arguments: {', '.join(kwargs.keys())}")
if flatten:
lists = flatten_list(args)
@ -189,12 +181,12 @@ def do_symmetric_difference(a, b):
class FilterModule:
''' Ansible lists jinja2 filters '''
"""Ansible lists jinja2 filters"""
def filters(self):
return {
'lists_union': lists_union,
'lists_intersect': lists_intersect,
'lists_difference': lists_difference,
'lists_symmetric_difference': lists_symmetric_difference,
"lists_union": lists_union,
"lists_intersect": lists_intersect,
"lists_difference": lists_difference,
"lists_symmetric_difference": lists_symmetric_difference,
}

View file

@ -202,11 +202,11 @@ from collections import defaultdict
from operator import itemgetter
def list_mergeby(x, y, index, recursive=False, list_merge='replace'):
'''Merge 2 lists by attribute 'index'. The function 'merge_hash'
from ansible.utils.vars is used. This function is used by the
function lists_mergeby.
'''
def list_mergeby(x, y, index, recursive=False, list_merge="replace"):
"""Merge 2 lists by attribute 'index'. The function 'merge_hash'
from ansible.utils.vars is used. This function is used by the
function lists_mergeby.
"""
d = defaultdict(dict)
for lst in (x, y):
@ -220,13 +220,13 @@ def list_mergeby(x, y, index, recursive=False, list_merge='replace'):
def lists_mergeby(*terms, **kwargs):
'''Merge 2 or more lists by attribute 'index'. To learn details
on how to use the parameters 'recursive' and 'list_merge' see
the filter ansible.builtin.combine.
'''
"""Merge 2 or more lists by attribute 'index'. To learn details
on how to use the parameters 'recursive' and 'list_merge' see
the filter ansible.builtin.combine.
"""
recursive = kwargs.pop('recursive', False)
list_merge = kwargs.pop('list_merge', 'replace')
recursive = kwargs.pop("recursive", False)
list_merge = kwargs.pop("list_merge", "replace")
if kwargs:
raise AnsibleFilterError("'recursive' and 'list_merge' are the only valid keyword arguments.")
if len(terms) < 2:
@ -236,8 +236,7 @@ def lists_mergeby(*terms, **kwargs):
flat_list = []
for sublist in terms[:-1]:
if not isinstance(sublist, Sequence):
msg = ("All arguments before the argument index for community.general.lists_mergeby "
"must be lists. %s is %s")
msg = "All arguments before the argument index for community.general.lists_mergeby must be lists. %s is %s"
raise AnsibleFilterError(msg % (sublist, type(sublist)))
if len(sublist) > 0:
if all(isinstance(lst, Sequence) for lst in sublist):
@ -256,8 +255,7 @@ def lists_mergeby(*terms, **kwargs):
index = terms[-1]
if not isinstance(index, str):
msg = ("First argument after the lists for community.general.lists_mergeby must be string. "
"%s is %s")
msg = "First argument after the lists for community.general.lists_mergeby must be string. %s is %s"
raise AnsibleFilterError(msg % (index, type(index)))
high_to_low_prio_list_iterator = reversed(lists)
@ -269,9 +267,9 @@ def lists_mergeby(*terms, **kwargs):
class FilterModule:
''' Ansible list filters '''
"""Ansible list filters"""
def filters(self):
return {
'lists_mergeby': lists_mergeby,
"lists_mergeby": lists_mergeby,
}

View file

@ -47,29 +47,29 @@ from ansible.errors import AnsibleFilterError
def random_mac(value, seed=None):
''' takes string prefix, and return it completed with random bytes
to get a complete 6 bytes MAC address '''
"""takes string prefix, and return it completed with random bytes
to get a complete 6 bytes MAC address"""
if not isinstance(value, str):
raise AnsibleFilterError(f'Invalid value type ({type(value)}) for random_mac ({value})')
raise AnsibleFilterError(f"Invalid value type ({type(value)}) for random_mac ({value})")
value = value.lower()
mac_items = value.split(':')
mac_items = value.split(":")
if len(mac_items) > 5:
raise AnsibleFilterError(f'Invalid value ({value}) for random_mac: 5 colon(:) separated items max')
raise AnsibleFilterError(f"Invalid value ({value}) for random_mac: 5 colon(:) separated items max")
err = ""
for mac in mac_items:
if not mac:
err += ",empty item"
continue
if not re.match('[a-f0-9]{2}', mac):
if not re.match("[a-f0-9]{2}", mac):
err += f",{mac} not hexa byte"
err = err.strip(',')
err = err.strip(",")
if err:
raise AnsibleFilterError(f'Invalid value ({value}) for random_mac: {err}')
raise AnsibleFilterError(f"Invalid value ({value}) for random_mac: {err}")
if seed is None:
r = SystemRandom()
@ -79,13 +79,14 @@ def random_mac(value, seed=None):
v = r.randint(68719476736, 1099511627775)
# Select first n chars to complement input prefix
remain = 2 * (6 - len(mac_items))
rnd = f'{v:x}'[:remain]
return value + re.sub(r'(..)', r':\1', rnd)
rnd = f"{v:x}"[:remain]
return value + re.sub(r"(..)", r":\1", rnd)
class FilterModule:
''' Ansible jinja2 filters '''
"""Ansible jinja2 filters"""
def filters(self):
return {
'random_mac': random_mac,
"random_mac": random_mac,
}

View file

@ -101,10 +101,11 @@ _value:
from ansible_collections.community.general.plugins.plugin_utils.keys_filter import (
_keys_filter_params,
_keys_filter_target_str)
_keys_filter_target_str,
)
def remove_keys(data, target=None, matching_parameter='equal'):
def remove_keys(data, target=None, matching_parameter="equal"):
"""remove specific keys from dictionaries in a list"""
# test parameters
@ -112,16 +113,20 @@ def remove_keys(data, target=None, matching_parameter='equal'):
# test and transform target
tt = _keys_filter_target_str(target, matching_parameter)
if matching_parameter == 'equal':
if matching_parameter == "equal":
def keep_key(key):
return key not in tt
elif matching_parameter == 'starts_with':
elif matching_parameter == "starts_with":
def keep_key(key):
return not key.startswith(tt)
elif matching_parameter == 'ends_with':
elif matching_parameter == "ends_with":
def keep_key(key):
return not key.endswith(tt)
elif matching_parameter == 'regex':
elif matching_parameter == "regex":
def keep_key(key):
return tt.match(key) is None
@ -129,8 +134,7 @@ def remove_keys(data, target=None, matching_parameter='equal'):
class FilterModule:
def filters(self):
return {
'remove_keys': remove_keys,
"remove_keys": remove_keys,
}

View file

@ -131,10 +131,11 @@ _value:
from ansible_collections.community.general.plugins.plugin_utils.keys_filter import (
_keys_filter_params,
_keys_filter_target_dict)
_keys_filter_target_dict,
)
def replace_keys(data, target=None, matching_parameter='equal'):
def replace_keys(data, target=None, matching_parameter="equal"):
"""replace specific keys in a list of dictionaries"""
# test parameters
@ -142,25 +143,29 @@ def replace_keys(data, target=None, matching_parameter='equal'):
# test and transform target
tz = _keys_filter_target_dict(target, matching_parameter)
if matching_parameter == 'equal':
if matching_parameter == "equal":
def replace_key(key):
for b, a in tz:
if key == b:
return a
return key
elif matching_parameter == 'starts_with':
elif matching_parameter == "starts_with":
def replace_key(key):
for b, a in tz:
if key.startswith(b):
return a
return key
elif matching_parameter == 'ends_with':
elif matching_parameter == "ends_with":
def replace_key(key):
for b, a in tz:
if key.endswith(b):
return a
return key
elif matching_parameter == 'regex':
elif matching_parameter == "regex":
def replace_key(key):
for b, a in tz:
if b.match(key):
@ -171,8 +176,7 @@ def replace_keys(data, target=None, matching_parameter='equal'):
class FilterModule:
def filters(self):
return {
'replace_keys': replace_keys,
"replace_keys": replace_keys,
}

View file

@ -140,8 +140,5 @@ def reveal_ansible_type(data, alias=None):
class FilterModule:
def filters(self):
return {
'reveal_ansible_type': reveal_ansible_type
}
return {"reveal_ansible_type": reveal_ansible_type}

View file

@ -9,30 +9,30 @@ from ansible.errors import AnsibleFilterError
UNIT_FACTORS = {
'ms': [],
's': [1000],
'm': [1000, 60],
'h': [1000, 60, 60],
'd': [1000, 60, 60, 24],
'w': [1000, 60, 60, 24, 7],
'mo': [1000, 60, 60, 24, 30],
'y': [1000, 60, 60, 24, 365],
"ms": [],
"s": [1000],
"m": [1000, 60],
"h": [1000, 60, 60],
"d": [1000, 60, 60, 24],
"w": [1000, 60, 60, 24, 7],
"mo": [1000, 60, 60, 24, 30],
"y": [1000, 60, 60, 24, 365],
}
UNIT_TO_SHORT_FORM = {
'millisecond': 'ms',
'msec': 'ms',
'msecond': 'ms',
'sec': 's',
'second': 's',
'hour': 'h',
'min': 'm',
'minute': 'm',
'day': 'd',
'week': 'w',
'month': 'mo',
'year': 'y',
"millisecond": "ms",
"msec": "ms",
"msecond": "ms",
"sec": "s",
"second": "s",
"hour": "h",
"min": "m",
"minute": "m",
"day": "d",
"week": "w",
"month": "mo",
"year": "y",
}
@ -43,8 +43,8 @@ def multiply(factors):
return result
def to_time_unit(human_time, unit='ms', **kwargs):
''' Return a time unit from a human readable string '''
def to_time_unit(human_time, unit="ms", **kwargs):
"""Return a time unit from a human readable string"""
# No need to handle 0
if human_time == "0":
@ -53,35 +53,35 @@ def to_time_unit(human_time, unit='ms', **kwargs):
unit_to_short_form = UNIT_TO_SHORT_FORM
unit_factors = UNIT_FACTORS
unit = unit_to_short_form.get(unit.rstrip('s'), unit)
unit = unit_to_short_form.get(unit.rstrip("s"), unit)
if unit not in unit_factors:
raise AnsibleFilterError((
f"to_time_unit() can not convert to the following unit: {unit}. Available units (singular or plural):"
f"{', '.join(unit_to_short_form.keys())}. Available short units: {', '.join(unit_factors.keys())}"
))
raise AnsibleFilterError(
(
f"to_time_unit() can not convert to the following unit: {unit}. Available units (singular or plural):"
f"{', '.join(unit_to_short_form.keys())}. Available short units: {', '.join(unit_factors.keys())}"
)
)
if 'year' in kwargs:
unit_factors['y'] = unit_factors['y'][:-1] + [kwargs.pop('year')]
if 'month' in kwargs:
unit_factors['mo'] = unit_factors['mo'][:-1] + [kwargs.pop('month')]
if "year" in kwargs:
unit_factors["y"] = unit_factors["y"][:-1] + [kwargs.pop("year")]
if "month" in kwargs:
unit_factors["mo"] = unit_factors["mo"][:-1] + [kwargs.pop("month")]
if kwargs:
raise AnsibleFilterError(f"to_time_unit() got unknown keyword arguments: {', '.join(kwargs.keys())}")
result = 0
for h_time_string in human_time.split():
res = re.match(r'(-?\d+)(\w+)', h_time_string)
res = re.match(r"(-?\d+)(\w+)", h_time_string)
if not res:
raise AnsibleFilterError(
f"to_time_unit() can not interpret following string: {human_time}")
raise AnsibleFilterError(f"to_time_unit() can not interpret following string: {human_time}")
h_time_int = int(res.group(1))
h_time_unit = res.group(2)
h_time_unit = unit_to_short_form.get(h_time_unit.rstrip('s'), h_time_unit)
h_time_unit = unit_to_short_form.get(h_time_unit.rstrip("s"), h_time_unit)
if h_time_unit not in unit_factors:
raise AnsibleFilterError(
f"to_time_unit() can not interpret following string: {human_time}")
raise AnsibleFilterError(f"to_time_unit() can not interpret following string: {human_time}")
time_in_milliseconds = h_time_int * multiply(unit_factors[h_time_unit])
result += time_in_milliseconds
@ -89,59 +89,59 @@ def to_time_unit(human_time, unit='ms', **kwargs):
def to_milliseconds(human_time, **kwargs):
''' Return milli seconds from a human readable string '''
return to_time_unit(human_time, 'ms', **kwargs)
"""Return milli seconds from a human readable string"""
return to_time_unit(human_time, "ms", **kwargs)
def to_seconds(human_time, **kwargs):
''' Return seconds from a human readable string '''
return to_time_unit(human_time, 's', **kwargs)
"""Return seconds from a human readable string"""
return to_time_unit(human_time, "s", **kwargs)
def to_minutes(human_time, **kwargs):
''' Return minutes from a human readable string '''
return to_time_unit(human_time, 'm', **kwargs)
"""Return minutes from a human readable string"""
return to_time_unit(human_time, "m", **kwargs)
def to_hours(human_time, **kwargs):
''' Return hours from a human readable string '''
return to_time_unit(human_time, 'h', **kwargs)
"""Return hours from a human readable string"""
return to_time_unit(human_time, "h", **kwargs)
def to_days(human_time, **kwargs):
''' Return days from a human readable string '''
return to_time_unit(human_time, 'd', **kwargs)
"""Return days from a human readable string"""
return to_time_unit(human_time, "d", **kwargs)
def to_weeks(human_time, **kwargs):
''' Return weeks from a human readable string '''
return to_time_unit(human_time, 'w', **kwargs)
"""Return weeks from a human readable string"""
return to_time_unit(human_time, "w", **kwargs)
def to_months(human_time, **kwargs):
''' Return months from a human readable string '''
return to_time_unit(human_time, 'mo', **kwargs)
"""Return months from a human readable string"""
return to_time_unit(human_time, "mo", **kwargs)
def to_years(human_time, **kwargs):
''' Return years from a human readable string '''
return to_time_unit(human_time, 'y', **kwargs)
"""Return years from a human readable string"""
return to_time_unit(human_time, "y", **kwargs)
class FilterModule:
''' Ansible time jinja2 filters '''
"""Ansible time jinja2 filters"""
def filters(self):
filters = {
'to_time_unit': to_time_unit,
'to_milliseconds': to_milliseconds,
'to_seconds': to_seconds,
'to_minutes': to_minutes,
'to_hours': to_hours,
'to_days': to_days,
'to_weeks': to_weeks,
'to_months': to_months,
'to_years': to_years,
"to_time_unit": to_time_unit,
"to_milliseconds": to_milliseconds,
"to_seconds": to_seconds,
"to_minutes": to_minutes,
"to_hours": to_hours,
"to_days": to_days,
"to_weeks": to_weeks,
"to_months": to_months,
"to_years": to_years,
}
return filters

View file

@ -1,4 +1,3 @@
# Copyright (c) 2023, Steffen Scheib <steffen@scheib.me>
# 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
@ -55,7 +54,7 @@ from ansible.errors import AnsibleFilterError
class IniParser(ConfigParser):
''' Implements a configparser which sets the correct optionxform '''
"""Implements a configparser which sets the correct optionxform"""
def __init__(self):
super().__init__(interpolation=None)
@ -63,23 +62,21 @@ class IniParser(ConfigParser):
def to_ini(obj):
''' Read the given dict and return an INI formatted string '''
"""Read the given dict and return an INI formatted string"""
if not isinstance(obj, Mapping):
raise AnsibleFilterError(f'to_ini requires a dict, got {type(obj)}')
raise AnsibleFilterError(f"to_ini requires a dict, got {type(obj)}")
ini_parser = IniParser()
try:
ini_parser.read_dict(obj)
except Exception as ex:
raise AnsibleFilterError('to_ini failed to parse given dict:'
f'{ex}', orig_exc=ex)
raise AnsibleFilterError(f"to_ini failed to parse given dict:{ex}", orig_exc=ex)
# catching empty dicts
if obj == dict():
raise AnsibleFilterError('to_ini received an empty dict. '
'An empty dict cannot be converted.')
raise AnsibleFilterError("to_ini received an empty dict. An empty dict cannot be converted.")
config = StringIO()
ini_parser.write(config)
@ -87,14 +84,11 @@ def to_ini(obj):
# config.getvalue() returns two \n at the end
# with the below insanity, we remove the very last character of
# the resulting string
return ''.join(config.getvalue().rsplit(config.getvalue()[-1], 1))
return "".join(config.getvalue().rsplit(config.getvalue()[-1], 1))
class FilterModule:
''' Query filter '''
"""Query filter"""
def filters(self):
return {
'to_ini': to_ini
}
return {"to_ini": to_ini}

View file

@ -110,6 +110,7 @@ _value:
try:
import prettytable
HAS_PRETTYTABLE = True
except ImportError:
HAS_PRETTYTABLE = False
@ -125,6 +126,7 @@ class TypeValidationError(AnsibleFilterError):
obj: The object with incorrect type
expected: Description of expected type
"""
def __init__(self, obj, expected):
type_name = "string" if isinstance(obj, str) else type(obj).__name__
super().__init__(f"Expected {expected}, got a {type_name}")
@ -142,10 +144,7 @@ def _validate_list_param(param, param_name, ensure_strings=True):
AnsibleFilterError: If validation fails
"""
# Map parameter names to their original error message format
error_messages = {
"column_order": "a list of column names",
"header_names": "a list of header names"
}
error_messages = {"column_order": "a list of column names", "header_names": "a list of header names"}
# Use the specific error message if available, otherwise use a generic one
error_msg = error_messages.get(param_name, f"a list for {param_name}")
@ -182,9 +181,9 @@ def _match_key(item_dict, lookup_key):
# Try boolean conversion for 'true'/'false' strings
if isinstance(lookup_key, str):
if lookup_key.lower() == 'true' and True in item_dict:
if lookup_key.lower() == "true" and True in item_dict:
return True
if lookup_key.lower() == 'false' and False in item_dict:
if lookup_key.lower() == "false" and False in item_dict:
return False
# Try numeric conversion for string numbers
@ -258,9 +257,7 @@ def to_prettytable(data, *args, **kwargs):
String containing the ASCII table
"""
if not HAS_PRETTYTABLE:
raise AnsibleFilterError(
'You need to install "prettytable" Python module to use this filter'
)
raise AnsibleFilterError('You need to install "prettytable" Python module to use this filter')
# === Input validation ===
# Validate list type
@ -278,7 +275,7 @@ def to_prettytable(data, *args, **kwargs):
# === Process column order ===
# Handle both positional and keyword column_order
column_order = kwargs.pop('column_order', None)
column_order = kwargs.pop("column_order", None)
# Check for conflict between args and column_order
if args and column_order is not None:
@ -295,7 +292,8 @@ def to_prettytable(data, *args, **kwargs):
# Validate column_order doesn't exceed the number of fields (skip if data is empty)
if data and len(column_order) > max_fields:
raise AnsibleFilterError(
f"'column_order' has more elements ({len(column_order)}) than available fields in data ({max_fields})")
f"'column_order' has more elements ({len(column_order)}) than available fields in data ({max_fields})"
)
# === Process headers ===
# Determine field names and ensure they are strings
@ -306,24 +304,26 @@ def to_prettytable(data, *args, **kwargs):
field_names = [to_text(k) for k in sample_dict]
# Process custom headers
header_names = kwargs.pop('header_names', None)
header_names = kwargs.pop("header_names", None)
if header_names is not None:
_validate_list_param(header_names, "header_names")
# Validate header_names doesn't exceed the number of fields (skip if data is empty)
if data and len(header_names) > max_fields:
raise AnsibleFilterError(
f"'header_names' has more elements ({len(header_names)}) than available fields in data ({max_fields})")
f"'header_names' has more elements ({len(header_names)}) than available fields in data ({max_fields})"
)
# Validate that column_order and header_names have the same size if both provided
if column_order is not None and len(column_order) != len(header_names):
raise AnsibleFilterError(
f"'column_order' and 'header_names' must have the same number of elements. "
f"Got {len(column_order)} columns and {len(header_names)} headers.")
f"Got {len(column_order)} columns and {len(header_names)} headers."
)
# === Process alignments ===
# Get column alignments and validate
column_alignments = kwargs.pop('column_alignments', {})
column_alignments = kwargs.pop("column_alignments", {})
valid_alignments = {"left", "center", "right", "l", "c", "r"}
# Validate column_alignments is a dictionary
@ -344,12 +344,14 @@ def to_prettytable(data, *args, **kwargs):
if value.lower() not in valid_alignments:
raise AnsibleFilterError(
f"Invalid alignment '{value}' in 'column_alignments'. "
f"Valid alignments are: {', '.join(sorted(valid_alignments))}")
f"Valid alignments are: {', '.join(sorted(valid_alignments))}"
)
# Validate column_alignments doesn't have more keys than fields (skip if data is empty)
if data and len(column_alignments) > max_fields:
raise AnsibleFilterError(
f"'column_alignments' has more elements ({len(column_alignments)}) than available fields in data ({max_fields})")
f"'column_alignments' has more elements ({len(column_alignments)}) than available fields in data ({max_fields})"
)
# Check for unknown parameters
if kwargs:
@ -404,6 +406,4 @@ class FilterModule:
"""Ansible core jinja2 filters."""
def filters(self):
return {
'to_prettytable': to_prettytable
}
return {"to_prettytable": to_prettytable}

View file

@ -8,16 +8,19 @@ import typing as t
from collections.abc import Mapping, Set
from yaml import dump
try:
from yaml.cyaml import CSafeDumper as SafeDumper
except ImportError:
from yaml import SafeDumper # type: ignore
from ansible.module_utils.common.collections import is_sequence
try:
# This is ansible-core 2.19+
from ansible.utils.vars import transform_to_native_types
from ansible.parsing.vault import VaultHelper, VaultLib
HAS_TRANSFORM_TO_NATIVE_TYPES = True
except ImportError:
HAS_TRANSFORM_TO_NATIVE_TYPES = False
@ -36,7 +39,9 @@ def _to_native_types_compat(value: t.Any, *, redact_value: str | None) -> t.Any:
# But that's fine, since this code path isn't taken on ansible-core 2.19+ anyway.
if isinstance(value, Mapping):
return {
_to_native_types_compat(key, redact_value=redact_value): _to_native_types_compat(val, redact_value=redact_value)
_to_native_types_compat(key, redact_value=redact_value): _to_native_types_compat(
val, redact_value=redact_value
)
for key, val in value.items()
}
if isinstance(value, Set):
@ -80,11 +85,15 @@ def remove_all_tags(value: t.Any, *, redact_sensitive_values: bool = False) -> t
return _to_native_types_compat( # type: ignore[unreachable]
value,
redact_value="<redacted>" if redact_sensitive_values else None, # same string as in ansible-core 2.19 by transform_to_native_types()
redact_value="<redacted>"
if redact_sensitive_values
else None, # same string as in ansible-core 2.19 by transform_to_native_types()
)
def to_yaml(value: t.Any, *, redact_sensitive_values: bool = False, default_flow_style: bool | None = None, **kwargs) -> str:
def to_yaml(
value: t.Any, *, redact_sensitive_values: bool = False, default_flow_style: bool | None = None, **kwargs
) -> str:
"""Serialize input as terse flow-style YAML."""
return dump(
remove_all_tags(value, redact_sensitive_values=redact_sensitive_values),
@ -95,7 +104,9 @@ def to_yaml(value: t.Any, *, redact_sensitive_values: bool = False, default_flow
)
def to_nice_yaml(value: t.Any, *, redact_sensitive_values: bool = False, indent: int = 2, default_flow_style: bool = False, **kwargs) -> str:
def to_nice_yaml(
value: t.Any, *, redact_sensitive_values: bool = False, indent: int = 2, default_flow_style: bool = False, **kwargs
) -> str:
"""Serialize input as verbose multi-line YAML."""
return to_yaml(
value,
@ -109,6 +120,6 @@ def to_nice_yaml(value: t.Any, *, redact_sensitive_values: bool = False, indent:
class FilterModule:
def filters(self):
return {
'to_yaml': to_yaml,
'to_nice_yaml': to_nice_yaml,
"to_yaml": to_yaml,
"to_nice_yaml": to_nice_yaml,
}

View file

@ -1,4 +1,3 @@
# Copyright (c) 2021, Andrew Pantuso (@ajpantuso) <ajpantuso@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
@ -55,7 +54,7 @@ except ImportError:
from ansible.errors import AnsibleFilterTypeError as AnsibleTypeError # type: ignore
def unicode_normalize(data, form='NFC'):
def unicode_normalize(data, form="NFC"):
"""Applies normalization to 'unicode' strings.
Args:
@ -70,7 +69,7 @@ def unicode_normalize(data, form='NFC'):
if not isinstance(data, str):
raise AnsibleTypeError(f"{type(data)} is not a valid input type")
if form not in ('NFC', 'NFD', 'NFKC', 'NFKD'):
if form not in ("NFC", "NFD", "NFKC", "NFKD"):
raise AnsibleFilterError(f"{form!r} is not a valid form")
return normalize(form, data)
@ -79,5 +78,5 @@ def unicode_normalize(data, form='NFC'):
class FilterModule:
def filters(self):
return {
'unicode_normalize': unicode_normalize,
"unicode_normalize": unicode_normalize,
}

View file

@ -37,14 +37,12 @@ from ansible_collections.community.general.plugins.module_utils.version import L
def version_sort(value, reverse=False):
'''Sort a list according to loose versions so that e.g. 2.9 is smaller than 2.10'''
"""Sort a list according to loose versions so that e.g. 2.9 is smaller than 2.10"""
return sorted(value, key=LooseVersion, reverse=reverse)
class FilterModule:
''' Version sort filter '''
"""Version sort filter"""
def filters(self):
return {
'version_sort': version_sort
}
return {"version_sort": version_sort}