# -*- coding: utf-8 -*- # Copyright (c) 2013, Serge van Ginderachter # Copyright (c) 2017 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 from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = ''' name: flattened author: Serge van Ginderachter (!UNKNOWN) short_description: return single list completely flattened description: - Given one or more lists, this lookup will flatten any list elements found recursively until only 1 list is left. options: _terms: description: lists to flatten type: list elements: raw required: true notes: - Unlike the R(items lookup,ansible_collections.ansible.builtin.items_lookup) which only flattens 1 level, this plugin will continue to flatten until it cannot find lists anymore. - Aka highlander plugin, there can only be one (list). ''' EXAMPLES = """ - name: "'unnest' all elements into single list" ansible.builtin.debug: msg: "all in one list {{lookup('community.general.flattened', [1,2,3,[5,6]], ['a','b','c'], [[5,6,1,3], [34,'a','b','c']])}}" """ RETURN = """ _raw: description: - flattened list type: list """ from ansible.errors import AnsibleError from ansible.module_utils.six import string_types from ansible.plugins.lookup import LookupBase from ansible.utils.listify import listify_lookup_plugin_terms class LookupModule(LookupBase): def _check_list_of_one_list(self, term): # make sure term is not a list of one (list of one..) item # return the final non list item if so if isinstance(term, list) and len(term) == 1: term = term[0] if isinstance(term, list): term = self._check_list_of_one_list(term) return term def _do_flatten(self, terms, variables): ret = [] for term in terms: term = self._check_list_of_one_list(term) if term == 'None' or term == 'null': # ignore undefined items break if isinstance(term, string_types): # convert a variable to a list try: term2 = listify_lookup_plugin_terms(term, templar=self._templar) except TypeError: # The loader argument is deprecated in ansible-core 2.14+. Fall back to # pre-2.14 behavior for older ansible-core versions. term2 = listify_lookup_plugin_terms(term, templar=self._templar, loader=self._loader) # but avoid converting a plain string to a list of one string if term2 != [term]: term = term2 if isinstance(term, list): # if it's a list, check recursively for items that are a list term = self._do_flatten(term, variables) ret.extend(term) else: ret.append(term) return ret def run(self, terms, variables=None, **kwargs): if not isinstance(terms, list): raise AnsibleError("with_flattened expects a list") self.set_options(var_options=variables, direct=kwargs) return self._do_flatten(terms, variables)