diff options
Diffstat (limited to 'lib/ansible/module_utils/common/collections.py')
-rw-r--r-- | lib/ansible/module_utils/common/collections.py | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/lib/ansible/module_utils/common/collections.py b/lib/ansible/module_utils/common/collections.py new file mode 100644 index 0000000..fdb9108 --- /dev/null +++ b/lib/ansible/module_utils/common/collections.py @@ -0,0 +1,112 @@ +# Copyright: (c) 2018, Sviatoslav Sydorenko <ssydoren@redhat.com> +# Copyright: (c) 2018, Ansible Project +# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) +"""Collection of low-level utility functions.""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +from ansible.module_utils.six import binary_type, text_type +from ansible.module_utils.common._collections_compat import Hashable, Mapping, MutableMapping, Sequence + + +class ImmutableDict(Hashable, Mapping): + """Dictionary that cannot be updated""" + def __init__(self, *args, **kwargs): + self._store = dict(*args, **kwargs) + + def __getitem__(self, key): + return self._store[key] + + def __iter__(self): + return self._store.__iter__() + + def __len__(self): + return self._store.__len__() + + def __hash__(self): + return hash(frozenset(self.items())) + + def __eq__(self, other): + try: + if self.__hash__() == hash(other): + return True + except TypeError: + pass + + return False + + def __repr__(self): + return 'ImmutableDict({0})'.format(repr(self._store)) + + def union(self, overriding_mapping): + """ + Create an ImmutableDict as a combination of the original and overriding_mapping + + :arg overriding_mapping: A Mapping of replacement and additional items + :return: A copy of the ImmutableDict with key-value pairs from the overriding_mapping added + + If any of the keys in overriding_mapping are already present in the original ImmutableDict, + the overriding_mapping item replaces the one in the original ImmutableDict. + """ + return ImmutableDict(self._store, **overriding_mapping) + + def difference(self, subtractive_iterable): + """ + Create an ImmutableDict as a combination of the original minus keys in subtractive_iterable + + :arg subtractive_iterable: Any iterable containing keys that should not be present in the + new ImmutableDict + :return: A copy of the ImmutableDict with keys from the subtractive_iterable removed + """ + remove_keys = frozenset(subtractive_iterable) + keys = (k for k in self._store.keys() if k not in remove_keys) + return ImmutableDict((k, self._store[k]) for k in keys) + + +def is_string(seq): + """Identify whether the input has a string-like type (inclding bytes).""" + # AnsibleVaultEncryptedUnicode inherits from Sequence, but is expected to be a string like object + return isinstance(seq, (text_type, binary_type)) or getattr(seq, '__ENCRYPTED__', False) + + +def is_iterable(seq, include_strings=False): + """Identify whether the input is an iterable.""" + if not include_strings and is_string(seq): + return False + + try: + iter(seq) + return True + except TypeError: + return False + + +def is_sequence(seq, include_strings=False): + """Identify whether the input is a sequence. + + Strings and bytes are not sequences here, + unless ``include_string`` is ``True``. + + Non-indexable things are never of a sequence type. + """ + if not include_strings and is_string(seq): + return False + + return isinstance(seq, Sequence) + + +def count(seq): + """Returns a dictionary with the number of appearances of each element of the iterable. + + Resembles the collections.Counter class functionality. It is meant to be used when the + code is run on Python 2.6.* where collections.Counter is not available. It should be + deprecated and replaced when support for Python < 2.7 is dropped. + """ + if not is_iterable(seq): + raise Exception('Argument provided is not an iterable') + counters = dict() + for elem in seq: + counters[elem] = counters.get(elem, 0) + 1 + return counters |