We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Invalid yml causes include_vars to hang or freeze without output
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and devel branch are affected too.
Always add information AFTER of these html comments.
-->

I made some attempt to sift through the 3,511 issues. Apology for adding yet another to this hopelessly large list. I did *not* set-up for development and try to reproduce.

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report
 
##### COMPONENT NAME
<!---
Insert below this comment the name of the module, plugin, task or feature.
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path
-->
include_vars

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.4.3.0
  config file = /Users/dclo/Documents/ansible/ansible.cfg
  configured module search path = ['/Users/dclo/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.6/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.6.4 (default, Mar 22 2018, 13:54:22) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
```
DEFAULT_HOST_LIST(/Users/dclo/Documents/ansible/ansible.cfg) = ['/Users/dclo/Documents/ansible/staging']
DEFAULT_VAULT_PASSWORD_FILE(/Users/dclo/Documents/ansible/ansible.cfg) = /Users/dclo/Documents/ansible/.vault_pass
```

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
macOS High Sierra 10.13.3

##### SUMMARY
<!--- Explain the problem briefly -->
`ansible-playbook test.yml -vvv` outputs the beginning of the `include_vars` task and then hangs.

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->
- Create a vars file with ansible-vault (create/edit) that has malformed yaml.
- Include it in the playbook.

<!--- Paste example playbooks or commands between quotes below -->
```yaml
---
- hosts: all
   gather_facts: no
   tasks:
   - include_vars: "vars/vault.yml"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Ansible outputs syntax error on the yaml file.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
Ansible hangs

<!--- Paste verbatim command output between quotes below -->
```
TASK [include_vars] ********************************************************************************
task path: /Users/dclo/Documents/ansible/test.yml:5
^CProcess WorkerProcess-2:
 [ERROR]: User interrupted execution
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/ansible/plugins/action/include_vars.py`
Content:
```
1 # (c) 2016, Allen Sanabria <asanabria@linuxdynasty.org>
2 #
3 # This file is part of Ansible
4 #
5 # Ansible is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Ansible is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17 
18 from __future__ import (absolute_import, division, print_function)
19 __metaclass__ = type
20 
21 from os import path, walk
22 import re
23 
24 from ansible.errors import AnsibleError
25 from ansible.module_utils.six import string_types
26 from ansible.module_utils._text import to_native, to_text
27 from ansible.plugins.action import ActionBase
28 
29 
30 class ActionModule(ActionBase):
31 
32     TRANSFERS_FILES = False
33 
34     VALID_FILE_EXTENSIONS = ['yaml', 'yml', 'json']
35     VALID_DIR_ARGUMENTS = ['dir', 'depth', 'files_matching', 'ignore_files', 'extensions']
36     VALID_FILE_ARGUMENTS = ['file', '_raw_params']
37     VALID_ALL = ['name']
38 
39     def _set_dir_defaults(self):
40         if not self.depth:
41             self.depth = 0
42 
43         if self.files_matching:
44             self.matcher = re.compile(r'{0}'.format(self.files_matching))
45         else:
46             self.matcher = None
47 
48         if not self.ignore_files:
49             self.ignore_files = list()
50 
51         if isinstance(self.ignore_files, str):
52             self.ignore_files = self.ignore_files.split()
53 
54         elif isinstance(self.ignore_files, dict):
55             return {
56                 'failed': True,
57                 'message': '{0} must be a list'.format(self.ignore_files)
58             }
59 
60     def _set_args(self):
61         """ Set instance variables based on the arguments that were passed """
62 
63         self.return_results_as_name = self._task.args.get('name', None)
64         self.source_dir = self._task.args.get('dir', None)
65         self.source_file = self._task.args.get('file', None)
66         if not self.source_dir and not self.source_file:
67             self.source_file = self._task.args.get('_raw_params')
68 
69         self.depth = self._task.args.get('depth', None)
70         self.files_matching = self._task.args.get('files_matching', None)
71         self.ignore_files = self._task.args.get('ignore_files', None)
72         self.valid_extensions = self._task.args.get('extensions', self.VALID_FILE_EXTENSIONS)
73 
74         # convert/validate extensions list
75         if isinstance(self.valid_extensions, string_types):
76             self.valid_extensions = list(self.valid_extensions)
77         if not isinstance(self.valid_extensions, list):
78             raise AnsibleError('Invalid type for "extensions" option, it must be a list')
79 
80     def run(self, tmp=None, task_vars=None):
81         """ Load yml files recursively from a directory.
82         """
83         del tmp  # tmp no longer has any effect
84 
85         if task_vars is None:
86             task_vars = dict()
87 
88         self.show_content = True
89         self.included_files = []
90 
91         # Validate arguments
92         dirs = 0
93         files = 0
94         for arg in self._task.args:
95             if arg in self.VALID_DIR_ARGUMENTS:
96                 dirs += 1
97             elif arg in self.VALID_FILE_ARGUMENTS:
98                 files += 1
99             elif arg in self.VALID_ALL:
100                 pass
101             else:
102                 raise AnsibleError('{0} is not a valid option in debug'.format(arg))
103 
104         if dirs and files:
105             raise AnsibleError("Your are mixing file only and dir only arguments, these are incompatible")
106 
107         # set internal vars from args
108         self._set_args()
109 
110         results = dict()
111         if self.source_dir:
112             self._set_dir_defaults()
113             self._set_root_dir()
114             if not path.exists(self.source_dir):
115                 failed = True
116                 err_msg = ('{0} directory does not exist'.format(self.source_dir))
117             elif not path.isdir(self.source_dir):
118                 failed = True
119                 err_msg = ('{0} is not a directory'.format(self.source_dir))
120             else:
121                 for root_dir, filenames in self._traverse_dir_depth():
122                     failed, err_msg, updated_results = (self._load_files_in_dir(root_dir, filenames))
123                     if failed:
124                         break
125                     results.update(updated_results)
126         else:
127             try:
128                 self.source_file = self._find_needle('vars', self.source_file)
129                 failed, err_msg, updated_results = (
130                     self._load_files(self.source_file)
131                 )
132                 if not failed:
133                     results.update(updated_results)
134 
135             except AnsibleError as e:
136                 failed = True
137                 err_msg = to_native(e)
138 
139         if self.return_results_as_name:
140             scope = dict()
141             scope[self.return_results_as_name] = results
142             results = scope
143 
144         result = super(ActionModule, self).run(task_vars=task_vars)
145 
146         if failed:
147             result['failed'] = failed
148             result['message'] = err_msg
149 
150         result['ansible_included_var_files'] = self.included_files
151         result['ansible_facts'] = results
152         result['_ansible_no_log'] = not self.show_content
153 
154         return result
155 
156     def _set_root_dir(self):
157         if self._task._role:
158             if self.source_dir.split('/')[0] == 'vars':
159                 path_to_use = (
160                     path.join(self._task._role._role_path, self.source_dir)
161                 )
162                 if path.exists(path_to_use):
163                     self.source_dir = path_to_use
164             else:
165                 path_to_use = (
166                     path.join(
167                         self._task._role._role_path, 'vars', self.source_dir
168                     )
169                 )
170                 self.source_dir = path_to_use
171         else:
172             current_dir = (
173                 "/".join(self._task._ds._data_source.split('/')[:-1])
174             )
175             self.source_dir = path.join(current_dir, self.source_dir)
176 
177     def _traverse_dir_depth(self):
178         """ Recursively iterate over a directory and sort the files in
179             alphabetical order. Do not iterate pass the set depth.
180             The default depth is unlimited.
181         """
182         current_depth = 0
183         sorted_walk = list(walk(self.source_dir))
184         sorted_walk.sort(key=lambda x: x[0])
185         for current_root, current_dir, current_files in sorted_walk:
186             current_depth += 1
187             if current_depth <= self.depth or self.depth == 0:
188                 current_files.sort()
189                 yield (current_root, current_files)
190             else:
191                 break
192 
193     def _ignore_file(self, filename):
194         """ Return True if a file matches the list of ignore_files.
195         Args:
196             filename (str): The filename that is being matched against.
197 
198         Returns:
199             Boolean
200         """
201         for file_type in self.ignore_files:
202             try:
203                 if re.search(r'{0}$'.format(file_type), filename):
204                     return True
205             except Exception:
206                 err_msg = 'Invalid regular expression: {0}'.format(file_type)
207                 raise AnsibleError(err_msg)
208         return False
209 
210     def _is_valid_file_ext(self, source_file):
211         """ Verify if source file has a valid extension
212         Args:
213             source_file (str): The full path of source file or source file.
214         Returns:
215             Bool
216         """
217         file_ext = path.splitext(source_file)
218         return bool(len(file_ext) > 1 and file_ext[-1][1:] in self.valid_extensions)
219 
220     def _load_files(self, filename, validate_extensions=False):
221         """ Loads a file and converts the output into a valid Python dict.
222         Args:
223             filename (str): The source file.
224 
225         Returns:
226             Tuple (bool, str, dict)
227         """
228         results = dict()
229         failed = False
230         err_msg = ''
231         if validate_extensions and not self._is_valid_file_ext(filename):
232             failed = True
233             err_msg = ('{0} does not have a valid extension: {1}' .format(filename, ', '.join(self.valid_extensions)))
234         else:
235             b_data, show_content = self._loader._get_file_contents(filename)
236             data = to_text(b_data, errors='surrogate_or_strict')
237 
238             self.show_content = show_content
239             data = self._loader.load(data, show_content)
240             if not data:
241                 data = dict()
242             if not isinstance(data, dict):
243                 failed = True
244                 err_msg = ('{0} must be stored as a dictionary/hash' .format(filename))
245             else:
246                 self.included_files.append(filename)
247                 results.update(data)
248 
249         return failed, err_msg, results
250 
251     def _load_files_in_dir(self, root_dir, var_files):
252         """ Load the found yml files and update/overwrite the dictionary.
253         Args:
254             root_dir (str): The base directory of the list of files that is being passed.
255             var_files: (list): List of files to iterate over and load into a dictionary.
256 
257         Returns:
258             Tuple (bool, str, dict)
259         """
260         results = dict()
261         failed = False
262         err_msg = ''
263         for filename in var_files:
264             stop_iter = False
265             # Never include main.yml from a role, as that is the default included by the role
266             if self._task._role:
267                 if filename == 'main.yml':
268                     stop_iter = True
269                     continue
270 
271             filepath = path.join(root_dir, filename)
272             if self.files_matching:
273                 if not self.matcher.search(filename):
274                     stop_iter = True
275 
276             if not stop_iter and not failed:
277                 if path.exists(filepath) and not self._ignore_file(filename):
278                     failed, err_msg, loaded_data = self._load_files(filepath, validate_extensions=True)
279                     if not failed:
280                         results.update(loaded_data)
281 
282         return failed, err_msg, results
283
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/ansible/plugins/action/include_vars.py b/lib/ansible/plugins/action/include_vars.py
--- a/lib/ansible/plugins/action/include_vars.py
+++ b/lib/ansible/plugins/action/include_vars.py
@@ -236,7 +236,7 @@
             data = to_text(b_data, errors='surrogate_or_strict')
 
             self.show_content = show_content
-            data = self._loader.load(data, show_content)
+            data = self._loader.load(data, file_name=filename, show_content=show_content)
             if not data:
                 data = dict()
             if not isinstance(data, dict):

{"golden_diff": "diff --git a/lib/ansible/plugins/action/include_vars.py b/lib/ansible/plugins/action/include_vars.py\n--- a/lib/ansible/plugins/action/include_vars.py\n+++ b/lib/ansible/plugins/action/include_vars.py\n@@ -236,7 +236,7 @@\n             data = to_text(b_data, errors='surrogate_or_strict')\n \n             self.show_content = show_content\n-            data = self._loader.load(data, show_content)\n+            data = self._loader.load(data, file_name=filename, show_content=show_content)\n             if not data:\n                 data = dict()\n             if not isinstance(data, dict):\n", "issue": "Invalid yml causes include_vars to hang or freeze without output\n<!---\r\nVerify first that your issue/request is not already reported on GitHub.\r\nAlso test if the latest release, and devel branch are affected too.\r\nAlways add information AFTER of these html comments.\r\n-->\r\n\r\nI made some attempt to sift through the 3,511 issues. Apology for adding yet another to this hopelessly large list. I did *not* set-up for development and try to reproduce.\r\n\r\n##### ISSUE TYPE\r\n<!--- Pick one below and delete the rest -->\r\n - Bug Report\r\n \r\n##### COMPONENT NAME\r\n<!---\r\nInsert below this comment the name of the module, plugin, task or feature.\r\nDo not include extra details here, e.g. \"vyos_command\" not \"the network module vyos_command\" or the full path\r\n-->\r\ninclude_vars\r\n\r\n##### ANSIBLE VERSION\r\n<!--- Paste verbatim output from \"ansible --version\" between quotes below -->\r\n```\r\nansible 2.4.3.0\r\n  config file = /Users/dclo/Documents/ansible/ansible.cfg\r\n  configured module search path = ['/Users/dclo/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']\r\n  ansible python module location = /usr/local/lib/python3.6/site-packages/ansible\r\n  executable location = /usr/local/bin/ansible\r\n  python version = 3.6.4 (default, Mar 22 2018, 13:54:22) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]\r\n```\r\n\r\n##### CONFIGURATION\r\n<!---\r\nIf using Ansible 2.4 or above, paste the results of \"ansible-config dump --only-changed\"\r\nOtherwise, mention any settings you have changed/added/removed in ansible.cfg\r\n(or using the ANSIBLE_* environment variables).\r\n-->\r\n```\r\nDEFAULT_HOST_LIST(/Users/dclo/Documents/ansible/ansible.cfg) = ['/Users/dclo/Documents/ansible/staging']\r\nDEFAULT_VAULT_PASSWORD_FILE(/Users/dclo/Documents/ansible/ansible.cfg) = /Users/dclo/Documents/ansible/.vault_pass\r\n```\r\n\r\n##### OS / ENVIRONMENT\r\n<!---\r\nMention the OS you are running Ansible from, and the OS you are\r\nmanaging, or say \"N/A\" for anything that is not platform-specific.\r\nAlso mention the specific version of what you are trying to control,\r\ne.g. if this is a network bug the version of firmware on the network device.\r\n-->\r\nmacOS High Sierra 10.13.3\r\n\r\n##### SUMMARY\r\n<!--- Explain the problem briefly -->\r\n`ansible-playbook test.yml -vvv` outputs the beginning of the `include_vars` task and then hangs.\r\n\r\n##### STEPS TO REPRODUCE\r\n<!---\r\nFor bugs, show exactly how to reproduce the problem, using a minimal test-case.\r\nFor new features, show how the feature would be used.\r\n-->\r\n- Create a vars file with ansible-vault (create/edit) that has malformed yaml.\r\n- Include it in the playbook.\r\n\r\n<!--- Paste example playbooks or commands between quotes below -->\r\n```yaml\r\n---\r\n- hosts: all\r\n   gather_facts: no\r\n   tasks:\r\n   - include_vars: \"vars/vault.yml\"\r\n```\r\n\r\n<!--- You can also paste gist.github.com links for larger files -->\r\n\r\n##### EXPECTED RESULTS\r\n<!--- What did you expect to happen when running the steps above? -->\r\nAnsible outputs syntax error on the yaml file.\r\n\r\n##### ACTUAL RESULTS\r\n<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->\r\nAnsible hangs\r\n\r\n<!--- Paste verbatim command output between quotes below -->\r\n```\r\nTASK [include_vars] ********************************************************************************\r\ntask path: /Users/dclo/Documents/ansible/test.yml:5\r\n^CProcess WorkerProcess-2:\r\n [ERROR]: User interrupted execution\r\n```\r\n\n", "before_files": [{"content": "# (c) 2016, Allen Sanabria <asanabria@linuxdynasty.org>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nfrom os import path, walk\nimport re\n\nfrom ansible.errors import AnsibleError\nfrom ansible.module_utils.six import string_types\nfrom ansible.module_utils._text import to_native, to_text\nfrom ansible.plugins.action import ActionBase\n\n\nclass ActionModule(ActionBase):\n\n    TRANSFERS_FILES = False\n\n    VALID_FILE_EXTENSIONS = ['yaml', 'yml', 'json']\n    VALID_DIR_ARGUMENTS = ['dir', 'depth', 'files_matching', 'ignore_files', 'extensions']\n    VALID_FILE_ARGUMENTS = ['file', '_raw_params']\n    VALID_ALL = ['name']\n\n    def _set_dir_defaults(self):\n        if not self.depth:\n            self.depth = 0\n\n        if self.files_matching:\n            self.matcher = re.compile(r'{0}'.format(self.files_matching))\n        else:\n            self.matcher = None\n\n        if not self.ignore_files:\n            self.ignore_files = list()\n\n        if isinstance(self.ignore_files, str):\n            self.ignore_files = self.ignore_files.split()\n\n        elif isinstance(self.ignore_files, dict):\n            return {\n                'failed': True,\n                'message': '{0} must be a list'.format(self.ignore_files)\n            }\n\n    def _set_args(self):\n        \"\"\" Set instance variables based on the arguments that were passed \"\"\"\n\n        self.return_results_as_name = self._task.args.get('name', None)\n        self.source_dir = self._task.args.get('dir', None)\n        self.source_file = self._task.args.get('file', None)\n        if not self.source_dir and not self.source_file:\n            self.source_file = self._task.args.get('_raw_params')\n\n        self.depth = self._task.args.get('depth', None)\n        self.files_matching = self._task.args.get('files_matching', None)\n        self.ignore_files = self._task.args.get('ignore_files', None)\n        self.valid_extensions = self._task.args.get('extensions', self.VALID_FILE_EXTENSIONS)\n\n        # convert/validate extensions list\n        if isinstance(self.valid_extensions, string_types):\n            self.valid_extensions = list(self.valid_extensions)\n        if not isinstance(self.valid_extensions, list):\n            raise AnsibleError('Invalid type for \"extensions\" option, it must be a list')\n\n    def run(self, tmp=None, task_vars=None):\n        \"\"\" Load yml files recursively from a directory.\n        \"\"\"\n        del tmp  # tmp no longer has any effect\n\n        if task_vars is None:\n            task_vars = dict()\n\n        self.show_content = True\n        self.included_files = []\n\n        # Validate arguments\n        dirs = 0\n        files = 0\n        for arg in self._task.args:\n            if arg in self.VALID_DIR_ARGUMENTS:\n                dirs += 1\n            elif arg in self.VALID_FILE_ARGUMENTS:\n                files += 1\n            elif arg in self.VALID_ALL:\n                pass\n            else:\n                raise AnsibleError('{0} is not a valid option in debug'.format(arg))\n\n        if dirs and files:\n            raise AnsibleError(\"Your are mixing file only and dir only arguments, these are incompatible\")\n\n        # set internal vars from args\n        self._set_args()\n\n        results = dict()\n        if self.source_dir:\n            self._set_dir_defaults()\n            self._set_root_dir()\n            if not path.exists(self.source_dir):\n                failed = True\n                err_msg = ('{0} directory does not exist'.format(self.source_dir))\n            elif not path.isdir(self.source_dir):\n                failed = True\n                err_msg = ('{0} is not a directory'.format(self.source_dir))\n            else:\n                for root_dir, filenames in self._traverse_dir_depth():\n                    failed, err_msg, updated_results = (self._load_files_in_dir(root_dir, filenames))\n                    if failed:\n                        break\n                    results.update(updated_results)\n        else:\n            try:\n                self.source_file = self._find_needle('vars', self.source_file)\n                failed, err_msg, updated_results = (\n                    self._load_files(self.source_file)\n                )\n                if not failed:\n                    results.update(updated_results)\n\n            except AnsibleError as e:\n                failed = True\n                err_msg = to_native(e)\n\n        if self.return_results_as_name:\n            scope = dict()\n            scope[self.return_results_as_name] = results\n            results = scope\n\n        result = super(ActionModule, self).run(task_vars=task_vars)\n\n        if failed:\n            result['failed'] = failed\n            result['message'] = err_msg\n\n        result['ansible_included_var_files'] = self.included_files\n        result['ansible_facts'] = results\n        result['_ansible_no_log'] = not self.show_content\n\n        return result\n\n    def _set_root_dir(self):\n        if self._task._role:\n            if self.source_dir.split('/')[0] == 'vars':\n                path_to_use = (\n                    path.join(self._task._role._role_path, self.source_dir)\n                )\n                if path.exists(path_to_use):\n                    self.source_dir = path_to_use\n            else:\n                path_to_use = (\n                    path.join(\n                        self._task._role._role_path, 'vars', self.source_dir\n                    )\n                )\n                self.source_dir = path_to_use\n        else:\n            current_dir = (\n                \"/\".join(self._task._ds._data_source.split('/')[:-1])\n            )\n            self.source_dir = path.join(current_dir, self.source_dir)\n\n    def _traverse_dir_depth(self):\n        \"\"\" Recursively iterate over a directory and sort the files in\n            alphabetical order. Do not iterate pass the set depth.\n            The default depth is unlimited.\n        \"\"\"\n        current_depth = 0\n        sorted_walk = list(walk(self.source_dir))\n        sorted_walk.sort(key=lambda x: x[0])\n        for current_root, current_dir, current_files in sorted_walk:\n            current_depth += 1\n            if current_depth <= self.depth or self.depth == 0:\n                current_files.sort()\n                yield (current_root, current_files)\n            else:\n                break\n\n    def _ignore_file(self, filename):\n        \"\"\" Return True if a file matches the list of ignore_files.\n        Args:\n            filename (str): The filename that is being matched against.\n\n        Returns:\n            Boolean\n        \"\"\"\n        for file_type in self.ignore_files:\n            try:\n                if re.search(r'{0}$'.format(file_type), filename):\n                    return True\n            except Exception:\n                err_msg = 'Invalid regular expression: {0}'.format(file_type)\n                raise AnsibleError(err_msg)\n        return False\n\n    def _is_valid_file_ext(self, source_file):\n        \"\"\" Verify if source file has a valid extension\n        Args:\n            source_file (str): The full path of source file or source file.\n        Returns:\n            Bool\n        \"\"\"\n        file_ext = path.splitext(source_file)\n        return bool(len(file_ext) > 1 and file_ext[-1][1:] in self.valid_extensions)\n\n    def _load_files(self, filename, validate_extensions=False):\n        \"\"\" Loads a file and converts the output into a valid Python dict.\n        Args:\n            filename (str): The source file.\n\n        Returns:\n            Tuple (bool, str, dict)\n        \"\"\"\n        results = dict()\n        failed = False\n        err_msg = ''\n        if validate_extensions and not self._is_valid_file_ext(filename):\n            failed = True\n            err_msg = ('{0} does not have a valid extension: {1}' .format(filename, ', '.join(self.valid_extensions)))\n        else:\n            b_data, show_content = self._loader._get_file_contents(filename)\n            data = to_text(b_data, errors='surrogate_or_strict')\n\n            self.show_content = show_content\n            data = self._loader.load(data, show_content)\n            if not data:\n                data = dict()\n            if not isinstance(data, dict):\n                failed = True\n                err_msg = ('{0} must be stored as a dictionary/hash' .format(filename))\n            else:\n                self.included_files.append(filename)\n                results.update(data)\n\n        return failed, err_msg, results\n\n    def _load_files_in_dir(self, root_dir, var_files):\n        \"\"\" Load the found yml files and update/overwrite the dictionary.\n        Args:\n            root_dir (str): The base directory of the list of files that is being passed.\n            var_files: (list): List of files to iterate over and load into a dictionary.\n\n        Returns:\n            Tuple (bool, str, dict)\n        \"\"\"\n        results = dict()\n        failed = False\n        err_msg = ''\n        for filename in var_files:\n            stop_iter = False\n            # Never include main.yml from a role, as that is the default included by the role\n            if self._task._role:\n                if filename == 'main.yml':\n                    stop_iter = True\n                    continue\n\n            filepath = path.join(root_dir, filename)\n            if self.files_matching:\n                if not self.matcher.search(filename):\n                    stop_iter = True\n\n            if not stop_iter and not failed:\n                if path.exists(filepath) and not self._ignore_file(filename):\n                    failed, err_msg, loaded_data = self._load_files(filepath, validate_extensions=True)\n                    if not failed:\n                        results.update(loaded_data)\n\n        return failed, err_msg, results\n", "path": "lib/ansible/plugins/action/include_vars.py"}], "after_files": [{"content": "# (c) 2016, Allen Sanabria <asanabria@linuxdynasty.org>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nfrom os import path, walk\nimport re\n\nfrom ansible.errors import AnsibleError\nfrom ansible.module_utils.six import string_types\nfrom ansible.module_utils._text import to_native, to_text\nfrom ansible.plugins.action import ActionBase\n\n\nclass ActionModule(ActionBase):\n\n    TRANSFERS_FILES = False\n\n    VALID_FILE_EXTENSIONS = ['yaml', 'yml', 'json']\n    VALID_DIR_ARGUMENTS = ['dir', 'depth', 'files_matching', 'ignore_files', 'extensions']\n    VALID_FILE_ARGUMENTS = ['file', '_raw_params']\n    VALID_ALL = ['name']\n\n    def _set_dir_defaults(self):\n        if not self.depth:\n            self.depth = 0\n\n        if self.files_matching:\n            self.matcher = re.compile(r'{0}'.format(self.files_matching))\n        else:\n            self.matcher = None\n\n        if not self.ignore_files:\n            self.ignore_files = list()\n\n        if isinstance(self.ignore_files, str):\n            self.ignore_files = self.ignore_files.split()\n\n        elif isinstance(self.ignore_files, dict):\n            return {\n                'failed': True,\n                'message': '{0} must be a list'.format(self.ignore_files)\n            }\n\n    def _set_args(self):\n        \"\"\" Set instance variables based on the arguments that were passed \"\"\"\n\n        self.return_results_as_name = self._task.args.get('name', None)\n        self.source_dir = self._task.args.get('dir', None)\n        self.source_file = self._task.args.get('file', None)\n        if not self.source_dir and not self.source_file:\n            self.source_file = self._task.args.get('_raw_params')\n\n        self.depth = self._task.args.get('depth', None)\n        self.files_matching = self._task.args.get('files_matching', None)\n        self.ignore_files = self._task.args.get('ignore_files', None)\n        self.valid_extensions = self._task.args.get('extensions', self.VALID_FILE_EXTENSIONS)\n\n        # convert/validate extensions list\n        if isinstance(self.valid_extensions, string_types):\n            self.valid_extensions = list(self.valid_extensions)\n        if not isinstance(self.valid_extensions, list):\n            raise AnsibleError('Invalid type for \"extensions\" option, it must be a list')\n\n    def run(self, tmp=None, task_vars=None):\n        \"\"\" Load yml files recursively from a directory.\n        \"\"\"\n        del tmp  # tmp no longer has any effect\n\n        if task_vars is None:\n            task_vars = dict()\n\n        self.show_content = True\n        self.included_files = []\n\n        # Validate arguments\n        dirs = 0\n        files = 0\n        for arg in self._task.args:\n            if arg in self.VALID_DIR_ARGUMENTS:\n                dirs += 1\n            elif arg in self.VALID_FILE_ARGUMENTS:\n                files += 1\n            elif arg in self.VALID_ALL:\n                pass\n            else:\n                raise AnsibleError('{0} is not a valid option in debug'.format(arg))\n\n        if dirs and files:\n            raise AnsibleError(\"Your are mixing file only and dir only arguments, these are incompatible\")\n\n        # set internal vars from args\n        self._set_args()\n\n        results = dict()\n        if self.source_dir:\n            self._set_dir_defaults()\n            self._set_root_dir()\n            if not path.exists(self.source_dir):\n                failed = True\n                err_msg = ('{0} directory does not exist'.format(self.source_dir))\n            elif not path.isdir(self.source_dir):\n                failed = True\n                err_msg = ('{0} is not a directory'.format(self.source_dir))\n            else:\n                for root_dir, filenames in self._traverse_dir_depth():\n                    failed, err_msg, updated_results = (self._load_files_in_dir(root_dir, filenames))\n                    if failed:\n                        break\n                    results.update(updated_results)\n        else:\n            try:\n                self.source_file = self._find_needle('vars', self.source_file)\n                failed, err_msg, updated_results = (\n                    self._load_files(self.source_file)\n                )\n                if not failed:\n                    results.update(updated_results)\n\n            except AnsibleError as e:\n                failed = True\n                err_msg = to_native(e)\n\n        if self.return_results_as_name:\n            scope = dict()\n            scope[self.return_results_as_name] = results\n            results = scope\n\n        result = super(ActionModule, self).run(task_vars=task_vars)\n\n        if failed:\n            result['failed'] = failed\n            result['message'] = err_msg\n\n        result['ansible_included_var_files'] = self.included_files\n        result['ansible_facts'] = results\n        result['_ansible_no_log'] = not self.show_content\n\n        return result\n\n    def _set_root_dir(self):\n        if self._task._role:\n            if self.source_dir.split('/')[0] == 'vars':\n                path_to_use = (\n                    path.join(self._task._role._role_path, self.source_dir)\n                )\n                if path.exists(path_to_use):\n                    self.source_dir = path_to_use\n            else:\n                path_to_use = (\n                    path.join(\n                        self._task._role._role_path, 'vars', self.source_dir\n                    )\n                )\n                self.source_dir = path_to_use\n        else:\n            current_dir = (\n                \"/\".join(self._task._ds._data_source.split('/')[:-1])\n            )\n            self.source_dir = path.join(current_dir, self.source_dir)\n\n    def _traverse_dir_depth(self):\n        \"\"\" Recursively iterate over a directory and sort the files in\n            alphabetical order. Do not iterate pass the set depth.\n            The default depth is unlimited.\n        \"\"\"\n        current_depth = 0\n        sorted_walk = list(walk(self.source_dir))\n        sorted_walk.sort(key=lambda x: x[0])\n        for current_root, current_dir, current_files in sorted_walk:\n            current_depth += 1\n            if current_depth <= self.depth or self.depth == 0:\n                current_files.sort()\n                yield (current_root, current_files)\n            else:\n                break\n\n    def _ignore_file(self, filename):\n        \"\"\" Return True if a file matches the list of ignore_files.\n        Args:\n            filename (str): The filename that is being matched against.\n\n        Returns:\n            Boolean\n        \"\"\"\n        for file_type in self.ignore_files:\n            try:\n                if re.search(r'{0}$'.format(file_type), filename):\n                    return True\n            except Exception:\n                err_msg = 'Invalid regular expression: {0}'.format(file_type)\n                raise AnsibleError(err_msg)\n        return False\n\n    def _is_valid_file_ext(self, source_file):\n        \"\"\" Verify if source file has a valid extension\n        Args:\n            source_file (str): The full path of source file or source file.\n        Returns:\n            Bool\n        \"\"\"\n        file_ext = path.splitext(source_file)\n        return bool(len(file_ext) > 1 and file_ext[-1][1:] in self.valid_extensions)\n\n    def _load_files(self, filename, validate_extensions=False):\n        \"\"\" Loads a file and converts the output into a valid Python dict.\n        Args:\n            filename (str): The source file.\n\n        Returns:\n            Tuple (bool, str, dict)\n        \"\"\"\n        results = dict()\n        failed = False\n        err_msg = ''\n        if validate_extensions and not self._is_valid_file_ext(filename):\n            failed = True\n            err_msg = ('{0} does not have a valid extension: {1}' .format(filename, ', '.join(self.valid_extensions)))\n        else:\n            b_data, show_content = self._loader._get_file_contents(filename)\n            data = to_text(b_data, errors='surrogate_or_strict')\n\n            self.show_content = show_content\n            data = self._loader.load(data, file_name=filename, show_content=show_content)\n            if not data:\n                data = dict()\n            if not isinstance(data, dict):\n                failed = True\n                err_msg = ('{0} must be stored as a dictionary/hash' .format(filename))\n            else:\n                self.included_files.append(filename)\n                results.update(data)\n\n        return failed, err_msg, results\n\n    def _load_files_in_dir(self, root_dir, var_files):\n        \"\"\" Load the found yml files and update/overwrite the dictionary.\n        Args:\n            root_dir (str): The base directory of the list of files that is being passed.\n            var_files: (list): List of files to iterate over and load into a dictionary.\n\n        Returns:\n            Tuple (bool, str, dict)\n        \"\"\"\n        results = dict()\n        failed = False\n        err_msg = ''\n        for filename in var_files:\n            stop_iter = False\n            # Never include main.yml from a role, as that is the default included by the role\n            if self._task._role:\n                if filename == 'main.yml':\n                    stop_iter = True\n                    continue\n\n            filepath = path.join(root_dir, filename)\n            if self.files_matching:\n                if not self.matcher.search(filename):\n                    stop_iter = True\n\n            if not stop_iter and not failed:\n                if path.exists(filepath) and not self._ignore_file(filename):\n                    failed, err_msg, loaded_data = self._load_files(filepath, validate_extensions=True)\n                    if not failed:\n                        results.update(loaded_data)\n\n        return failed, err_msg, results\n", "path": "lib/ansible/plugins/action/include_vars.py"}]}