ansible-core 2.19.0b4__py3-none-any.whl → 2.19.0b6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. ansible/_internal/__init__.py +1 -1
  2. ansible/_internal/_ansiballz/__init__.py +0 -0
  3. ansible/_internal/_ansiballz/_builder.py +101 -0
  4. ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
  5. ansible/_internal/_collection_proxy.py +1 -1
  6. ansible/_internal/_errors/_alarm_timeout.py +66 -0
  7. ansible/_internal/_errors/_captured.py +25 -30
  8. ansible/_internal/_errors/_error_factory.py +89 -0
  9. ansible/_internal/_errors/_error_utils.py +240 -0
  10. ansible/_internal/_errors/_task_timeout.py +28 -0
  11. ansible/_internal/_event_formatting.py +127 -0
  12. ansible/_internal/_json/__init__.py +5 -5
  13. ansible/_internal/_json/_profiles/_cache_persistence.py +2 -0
  14. ansible/_internal/_json/_profiles/_inventory_legacy.py +1 -1
  15. ansible/_internal/_json/_profiles/_legacy.py +3 -11
  16. ansible/_internal/_ssh/__init__.py +0 -0
  17. ansible/_internal/_ssh/_agent_launch.py +91 -0
  18. ansible/{utils → _internal/_ssh}/_ssh_agent.py +55 -93
  19. ansible/_internal/_templating/__init__.py +5 -3
  20. ansible/_internal/_templating/_datatag.py +2 -1
  21. ansible/_internal/_templating/_engine.py +3 -4
  22. ansible/_internal/_templating/_jinja_bits.py +28 -20
  23. ansible/_internal/_templating/_jinja_common.py +18 -27
  24. ansible/_internal/_templating/_jinja_plugins.py +36 -5
  25. ansible/_internal/_templating/_lazy_containers.py +5 -5
  26. ansible/_internal/_templating/_template_vars.py +72 -0
  27. ansible/_internal/_templating/_transform.py +26 -19
  28. ansible/_internal/_templating/_utils.py +1 -1
  29. ansible/_internal/_yaml/_constructor.py +4 -4
  30. ansible/_internal/_yaml/_dumper.py +26 -18
  31. ansible/_internal/_yaml/_errors.py +7 -7
  32. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +1 -1
  33. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +1 -1
  34. ansible/cli/__init__.py +11 -93
  35. ansible/cli/arguments/option_helpers.py +3 -4
  36. ansible/cli/console.py +1 -1
  37. ansible/cli/doc.py +86 -30
  38. ansible/cli/inventory.py +5 -7
  39. ansible/compat/importlib_resources.py +9 -12
  40. ansible/config/base.yml +46 -0
  41. ansible/errors/__init__.py +98 -50
  42. ansible/executor/module_common.py +75 -49
  43. ansible/executor/powershell/async_watchdog.ps1 +2 -2
  44. ansible/executor/powershell/async_wrapper.ps1 +3 -3
  45. ansible/executor/powershell/become_wrapper.ps1 +20 -2
  46. ansible/executor/powershell/bootstrap_wrapper.ps1 +28 -6
  47. ansible/executor/powershell/coverage_wrapper.ps1 +15 -6
  48. ansible/executor/powershell/exec_wrapper.ps1 +219 -6
  49. ansible/executor/powershell/module_manifest.py +52 -0
  50. ansible/executor/powershell/module_wrapper.ps1 +47 -21
  51. ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
  52. ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
  53. ansible/executor/process/worker.py +40 -115
  54. ansible/executor/task_executor.py +26 -61
  55. ansible/executor/task_result.py +2 -4
  56. ansible/galaxy/api.py +1 -4
  57. ansible/galaxy/collection/__init__.py +2 -10
  58. ansible/galaxy/collection/concrete_artifact_manager.py +2 -8
  59. ansible/galaxy/role.py +2 -2
  60. ansible/inventory/manager.py +1 -1
  61. ansible/module_utils/_internal/__init__.py +7 -7
  62. ansible/module_utils/_internal/_ambient_context.py +3 -3
  63. ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
  64. ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
  65. ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
  66. ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
  67. ansible/module_utils/_internal/{_ansiballz.py → _ansiballz/_loader.py} +13 -39
  68. ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
  69. ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
  70. ansible/module_utils/_internal/_datatag/__init__.py +43 -15
  71. ansible/module_utils/_internal/_datatag/_tags.py +2 -2
  72. ansible/module_utils/_internal/_deprecator.py +67 -55
  73. ansible/module_utils/_internal/_errors.py +88 -17
  74. ansible/module_utils/_internal/_event_utils.py +61 -0
  75. ansible/module_utils/_internal/_json/_profiles/__init__.py +22 -4
  76. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +2 -0
  77. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +2 -0
  78. ansible/module_utils/_internal/_json/_profiles/_tagless.py +3 -1
  79. ansible/module_utils/{common/messages.py → _internal/_messages.py} +54 -49
  80. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
  81. ansible/module_utils/_internal/_plugin_info.py +15 -2
  82. ansible/module_utils/_internal/_stack.py +22 -0
  83. ansible/module_utils/_internal/_text_utils.py +6 -0
  84. ansible/module_utils/_internal/_traceback.py +11 -8
  85. ansible/module_utils/ansible_release.py +1 -1
  86. ansible/module_utils/basic.py +95 -71
  87. ansible/module_utils/common/arg_spec.py +2 -2
  88. ansible/module_utils/common/collections.py +6 -0
  89. ansible/module_utils/common/json.py +2 -2
  90. ansible/module_utils/common/respawn.py +4 -41
  91. ansible/module_utils/common/text/converters.py +3 -3
  92. ansible/module_utils/common/validation.py +1 -1
  93. ansible/module_utils/common/warnings.py +80 -23
  94. ansible/module_utils/common/yaml.py +1 -1
  95. ansible/module_utils/connection.py +8 -11
  96. ansible/module_utils/datatag.py +5 -2
  97. ansible/module_utils/facts/hardware/linux.py +1 -1
  98. ansible/module_utils/facts/sysctl.py +4 -6
  99. ansible/module_utils/facts/system/caps.py +2 -2
  100. ansible/module_utils/facts/system/distribution.py +16 -3
  101. ansible/module_utils/facts/system/local.py +1 -1
  102. ansible/module_utils/facts/virtual/linux.py +2 -2
  103. ansible/module_utils/service.py +3 -10
  104. ansible/module_utils/urls.py +4 -4
  105. ansible/modules/apt_repository.py +17 -39
  106. ansible/modules/assemble.py +2 -2
  107. ansible/modules/async_status.py +13 -11
  108. ansible/modules/async_wrapper.py +12 -22
  109. ansible/modules/command.py +3 -3
  110. ansible/modules/copy.py +4 -4
  111. ansible/modules/cron.py +1 -1
  112. ansible/modules/dnf5.py +14 -22
  113. ansible/modules/file.py +16 -17
  114. ansible/modules/find.py +3 -3
  115. ansible/modules/get_url.py +17 -0
  116. ansible/modules/git.py +9 -7
  117. ansible/modules/hostname.py +0 -1
  118. ansible/modules/known_hosts.py +12 -14
  119. ansible/modules/package.py +6 -0
  120. ansible/modules/replace.py +2 -2
  121. ansible/modules/service.py +3 -9
  122. ansible/modules/slurp.py +10 -13
  123. ansible/modules/stat.py +5 -7
  124. ansible/modules/unarchive.py +6 -6
  125. ansible/modules/user.py +1 -1
  126. ansible/modules/wait_for.py +28 -30
  127. ansible/modules/yum_repository.py +4 -3
  128. ansible/parsing/ajson.py +3 -5
  129. ansible/parsing/dataloader.py +6 -6
  130. ansible/parsing/mod_args.py +1 -1
  131. ansible/parsing/plugin_docs.py +2 -2
  132. ansible/parsing/utils/yaml.py +3 -3
  133. ansible/parsing/vault/__init__.py +10 -14
  134. ansible/playbook/base.py +7 -2
  135. ansible/playbook/included_file.py +3 -1
  136. ansible/playbook/play_context.py +2 -0
  137. ansible/playbook/playbook_include.py +1 -1
  138. ansible/playbook/taggable.py +19 -8
  139. ansible/playbook/task.py +2 -0
  140. ansible/plugins/__init__.py +0 -25
  141. ansible/plugins/action/__init__.py +8 -31
  142. ansible/plugins/action/add_host.py +1 -1
  143. ansible/plugins/action/assemble.py +8 -16
  144. ansible/plugins/action/async_status.py +7 -2
  145. ansible/plugins/action/copy.py +8 -7
  146. ansible/plugins/action/fetch.py +3 -3
  147. ansible/plugins/action/gather_facts.py +8 -8
  148. ansible/plugins/action/package.py +5 -8
  149. ansible/plugins/action/script.py +8 -15
  150. ansible/plugins/action/service.py +3 -7
  151. ansible/plugins/action/template.py +11 -10
  152. ansible/plugins/action/unarchive.py +5 -15
  153. ansible/plugins/action/uri.py +9 -20
  154. ansible/plugins/cache/__init__.py +17 -19
  155. ansible/plugins/callback/__init__.py +4 -6
  156. ansible/plugins/callback/junit.py +4 -2
  157. ansible/plugins/callback/tree.py +5 -5
  158. ansible/plugins/connection/local.py +6 -6
  159. ansible/plugins/connection/paramiko_ssh.py +5 -5
  160. ansible/plugins/connection/ssh.py +25 -15
  161. ansible/plugins/connection/winrm.py +6 -3
  162. ansible/plugins/doc_fragments/constructed.py +2 -2
  163. ansible/plugins/filter/core.py +32 -27
  164. ansible/plugins/filter/encryption.py +14 -6
  165. ansible/plugins/inventory/__init__.py +11 -10
  166. ansible/plugins/inventory/script.py +1 -1
  167. ansible/plugins/list.py +73 -19
  168. ansible/plugins/loader.py +7 -7
  169. ansible/plugins/lookup/csvfile.py +16 -71
  170. ansible/plugins/lookup/first_found.py +2 -1
  171. ansible/plugins/lookup/template.py +9 -4
  172. ansible/plugins/shell/__init__.py +56 -2
  173. ansible/plugins/shell/powershell.py +67 -9
  174. ansible/plugins/shell/sh.py +10 -5
  175. ansible/plugins/strategy/__init__.py +3 -3
  176. ansible/plugins/test/core.py +22 -16
  177. ansible/plugins/test/finished.yml +1 -1
  178. ansible/plugins/test/uri.py +2 -5
  179. ansible/release.py +1 -1
  180. ansible/template/__init__.py +38 -54
  181. ansible/utils/collection_loader/_collection_finder.py +3 -3
  182. ansible/utils/display.py +124 -138
  183. ansible/utils/galaxy.py +2 -2
  184. ansible/utils/hashing.py +6 -8
  185. ansible/utils/listify.py +6 -4
  186. ansible/utils/path.py +5 -7
  187. ansible/utils/py3compat.py +2 -1
  188. ansible/utils/ssh_functions.py +3 -2
  189. ansible/utils/unsafe_proxy.py +1 -1
  190. ansible/vars/hostvars.py +1 -1
  191. ansible/vars/plugins.py +3 -3
  192. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/METADATA +1 -1
  193. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/RECORD +224 -204
  194. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/WHEEL +1 -1
  195. ansible_test/_data/completion/docker.txt +3 -3
  196. ansible_test/_data/completion/remote.txt +1 -0
  197. ansible_test/_data/requirements/sanity.ansible-doc.txt +1 -1
  198. ansible_test/_data/requirements/sanity.changelog.txt +2 -2
  199. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  200. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  201. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  202. ansible_test/_internal/commands/integration/coverage.py +7 -2
  203. ansible_test/_internal/host_profiles.py +62 -10
  204. ansible_test/_internal/provisioning.py +10 -4
  205. ansible_test/_internal/ssh.py +1 -5
  206. ansible_test/_internal/thread.py +2 -1
  207. ansible_test/_internal/timeout.py +1 -1
  208. ansible_test/_internal/util.py +40 -12
  209. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +1 -0
  210. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
  211. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +1 -0
  212. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
  213. ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
  214. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +61 -7
  215. ansible_test/_util/target/setup/bootstrap.sh +31 -0
  216. ansible_test/_util/target/setup/requirements.py +3 -9
  217. ansible/_internal/_errors/_utils.py +0 -310
  218. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/entry_points.txt +0 -0
  219. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/COPYING +0 -0
  220. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  221. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  222. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  223. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  224. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  225. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/top_level.txt +0 -0
@@ -16,10 +16,9 @@ import typing as t
16
16
 
17
17
  import yaml
18
18
 
19
- from jinja2 import __version__ as j2_version
20
-
21
19
  import ansible
22
20
  from ansible import constants as C
21
+ from ansible._internal import _templating
23
22
  from ansible.module_utils.common.text.converters import to_native
24
23
  from ansible.module_utils.common.yaml import HAS_LIBYAML, yaml_load
25
24
  from ansible.release import __version__
@@ -244,7 +243,7 @@ def _git_repo_info(repo_path):
244
243
  repo_path = gitdir
245
244
  else:
246
245
  repo_path = os.path.join(repo_path[:-4], gitdir)
247
- except (IOError, AttributeError):
246
+ except (OSError, AttributeError):
248
247
  return ''
249
248
  with open(os.path.join(repo_path, "HEAD")) as f:
250
249
  line = f.readline().rstrip("\n")
@@ -313,7 +312,7 @@ def version(prog=None):
313
312
  result.append(" ansible collection location = %s" % ':'.join(C.COLLECTIONS_PATHS))
314
313
  result.append(" executable location = %s" % sys.argv[0])
315
314
  result.append(" python version = %s (%s)" % (''.join(sys.version.splitlines()), to_native(sys.executable)))
316
- result.append(" jinja version = %s" % j2_version)
315
+ result.append(f" jinja version = {_templating.jinja2_version}")
317
316
  result.append(f" pyyaml version = {yaml.__version__} ({libyaml_fragment})")
318
317
 
319
318
  return "\n".join(result)
ansible/cli/console.py CHANGED
@@ -573,7 +573,7 @@ class ConsoleCLI(CLI, cmd.Cmd):
573
573
  histfile = os.path.join(os.path.expanduser("~"), ".ansible-console_history")
574
574
  try:
575
575
  readline.read_history_file(histfile)
576
- except IOError:
576
+ except OSError:
577
577
  pass
578
578
 
579
579
  atexit.register(readline.write_history_file, histfile)
ansible/cli/doc.py CHANGED
@@ -9,12 +9,14 @@ from __future__ import annotations
9
9
  # ansible.cli needs to be imported first, to ensure the source bin/* scripts run that code first
10
10
  from ansible.cli import CLI
11
11
 
12
+ import collections.abc
12
13
  import importlib
13
14
  import pkgutil
14
15
  import os
15
16
  import os.path
16
17
  import re
17
18
  import textwrap
19
+ import typing as t
18
20
 
19
21
  import yaml
20
22
 
@@ -35,7 +37,7 @@ from ansible.parsing.plugin_docs import read_docstub
35
37
  from ansible.parsing.yaml.dumper import AnsibleDumper
36
38
  from ansible.parsing.yaml.loader import AnsibleLoader
37
39
  from ansible._internal._yaml._loader import AnsibleInstrumentedLoader
38
- from ansible.plugins.list import list_plugins
40
+ from ansible.plugins.list import _list_plugins_with_info, _PluginDocMetadata
39
41
  from ansible.plugins.loader import action_loader, fragment_loader
40
42
  from ansible.utils.collection_loader import AnsibleCollectionConfig, AnsibleCollectionRef
41
43
  from ansible.utils.collection_loader._collection_finder import _get_collection_name_from_path
@@ -44,6 +46,7 @@ from ansible.utils.display import Display
44
46
  from ansible.utils.plugin_docs import get_plugin_docs, get_docstring, get_versioned_doclink
45
47
  from ansible.template import trust_as_template
46
48
  from ansible._internal import _json
49
+ from ansible._internal._templating import _jinja_plugins
47
50
 
48
51
  display = Display()
49
52
 
@@ -134,8 +137,8 @@ class RoleMixin(object):
134
137
  data = yaml.load(trust_as_template(f), Loader=AnsibleLoader)
135
138
  if data is None:
136
139
  data = {}
137
- except (IOError, OSError) as ex:
138
- raise AnsibleParserError(f"Could not read the role {role_name!r} (at {path}).") from ex
140
+ except OSError as ex:
141
+ raise AnsibleParserError(f"Could not read the role {role_name!r} at {path!r}.") from ex
139
142
 
140
143
  return data
141
144
 
@@ -788,35 +791,47 @@ class DocCLI(CLI, RoleMixin):
788
791
  return coll_filter
789
792
 
790
793
  def _list_plugins(self, plugin_type, content):
791
-
792
- results = {}
793
- self.plugins = {}
794
- loader = DocCLI._prep_loader(plugin_type)
794
+ DocCLI._prep_loader(plugin_type)
795
795
 
796
796
  coll_filter = self._get_collection_filter()
797
- self.plugins.update(list_plugins(plugin_type, coll_filter))
797
+ plugins = _list_plugins_with_info(plugin_type, coll_filter)
798
+
799
+ # Remove the internal ansible._protomatter plugins if getting all plugins
800
+ if not coll_filter:
801
+ plugins = {k: v for k, v in plugins.items() if not k.startswith('ansible._protomatter.')}
798
802
 
799
803
  # get appropriate content depending on option
800
804
  if content == 'dir':
801
- results = self._get_plugin_list_descriptions(loader)
805
+ results = self._get_plugin_list_descriptions(plugins)
802
806
  elif content == 'files':
803
- results = {k: self.plugins[k][0] for k in self.plugins.keys()}
807
+ results = {k: v.path for k, v in plugins.items()}
804
808
  else:
805
- results = {k: {} for k in self.plugins.keys()}
809
+ results = {k: {} for k in plugins.keys()}
806
810
  self.plugin_list = set() # reset for next iteration
807
811
 
808
812
  return results
809
813
 
810
- def _get_plugins_docs(self, plugin_type, names, fail_ok=False, fail_on_errors=True):
811
-
814
+ def _get_plugins_docs(self, plugin_type: str, names: collections.abc.Iterable[str], fail_ok: bool = False, fail_on_errors: bool = True) -> dict[str, dict]:
812
815
  loader = DocCLI._prep_loader(plugin_type)
813
816
 
817
+ if plugin_type in ('filter', 'test'):
818
+ jinja2_builtins = _jinja_plugins.get_jinja_builtin_plugin_descriptions(plugin_type)
819
+ jinja2_builtins.update({name.split('.')[-1]: value for name, value in jinja2_builtins.items()}) # add short-named versions for lookup
820
+ else:
821
+ jinja2_builtins = {}
822
+
814
823
  # get the docs for plugins in the command line list
815
824
  plugin_docs = {}
816
825
  for plugin in names:
817
- doc = {}
826
+ doc: dict[str, t.Any] = {}
818
827
  try:
819
- doc, plainexamples, returndocs, metadata = get_plugin_docs(plugin, plugin_type, loader, fragment_loader, (context.CLIARGS['verbosity'] > 0))
828
+ doc, plainexamples, returndocs, metadata = self._get_plugin_docs_with_jinja2_builtins(
829
+ plugin,
830
+ plugin_type,
831
+ loader,
832
+ fragment_loader,
833
+ jinja2_builtins,
834
+ )
820
835
  except AnsiblePluginNotFound as e:
821
836
  display.warning(to_native(e))
822
837
  continue
@@ -853,6 +868,39 @@ class DocCLI(CLI, RoleMixin):
853
868
 
854
869
  return plugin_docs
855
870
 
871
+ def _get_plugin_docs_with_jinja2_builtins(
872
+ self,
873
+ plugin_name: str,
874
+ plugin_type: str,
875
+ loader: t.Any,
876
+ fragment_loader: t.Any,
877
+ jinja_builtins: dict[str, str],
878
+ ) -> tuple[dict, str | None, dict | None, dict | None]:
879
+ try:
880
+ return get_plugin_docs(plugin_name, plugin_type, loader, fragment_loader, (context.CLIARGS['verbosity'] > 0))
881
+ except Exception:
882
+ if (desc := jinja_builtins.get(plugin_name, ...)) is not ...:
883
+ short_name = plugin_name.split('.')[-1]
884
+ long_name = f'ansible.builtin.{short_name}'
885
+ # Dynamically build a doc stub for any Jinja2 builtin plugin we haven't
886
+ # explicitly documented.
887
+ doc = dict(
888
+ collection='ansible.builtin',
889
+ plugin_name=long_name,
890
+ filename='',
891
+ short_description=desc,
892
+ description=[
893
+ desc,
894
+ '',
895
+ f"This is the Jinja builtin {plugin_type} plugin {short_name!r}.",
896
+ f"See: U(https://jinja.palletsprojects.com/en/stable/templates/#jinja-{plugin_type}s.{short_name})",
897
+ ],
898
+ )
899
+
900
+ return doc, None, None, None
901
+
902
+ raise
903
+
856
904
  def _get_roles_path(self):
857
905
  """
858
906
  Add any 'roles' subdir in playbook dir to the roles search path.
@@ -1001,10 +1049,10 @@ class DocCLI(CLI, RoleMixin):
1001
1049
  def get_all_plugins_of_type(plugin_type):
1002
1050
  loader = getattr(plugin_loader, '%s_loader' % plugin_type)
1003
1051
  paths = loader._get_paths_with_context()
1004
- plugins = {}
1052
+ plugins = []
1005
1053
  for path_context in paths:
1006
- plugins.update(list_plugins(plugin_type))
1007
- return sorted(plugins.keys())
1054
+ plugins += _list_plugins_with_info(plugin_type).keys()
1055
+ return sorted(plugins)
1008
1056
 
1009
1057
  @staticmethod
1010
1058
  def get_plugin_metadata(plugin_type, plugin_name):
@@ -1101,18 +1149,20 @@ class DocCLI(CLI, RoleMixin):
1101
1149
 
1102
1150
  return text
1103
1151
 
1104
- def _get_plugin_list_descriptions(self, loader):
1152
+ def _get_plugin_list_descriptions(self, plugins: dict[str, _PluginDocMetadata]) -> dict[str, str]:
1105
1153
 
1106
1154
  descs = {}
1107
- for plugin in self.plugins.keys():
1155
+ for plugin, plugin_info in plugins.items():
1108
1156
  # TODO: move to plugin itself i.e: plugin.get_desc()
1109
1157
  doc = None
1110
- filename = Path(to_native(self.plugins[plugin][0]))
1158
+
1111
1159
  docerror = None
1112
- try:
1113
- doc = read_docstub(filename)
1114
- except Exception as e:
1115
- docerror = e
1160
+ if plugin_info.path:
1161
+ filename = Path(to_native(plugin_info.path))
1162
+ try:
1163
+ doc = read_docstub(filename)
1164
+ except Exception as e:
1165
+ docerror = e
1116
1166
 
1117
1167
  # plugin file was empty or had error, lets try other options
1118
1168
  if doc is None:
@@ -1127,9 +1177,15 @@ class DocCLI(CLI, RoleMixin):
1127
1177
  except Exception as e:
1128
1178
  docerror = e
1129
1179
 
1130
- if docerror:
1131
- display.warning("%s has a documentation formatting error: %s" % (plugin, docerror))
1132
- continue
1180
+ # Do a final fallback to see if the plugin is a shadowed Jinja2 plugin
1181
+ # without any explicit documentation.
1182
+ if doc is None and plugin_info.jinja_builtin_short_description:
1183
+ descs[plugin] = plugin_info.jinja_builtin_short_description
1184
+ continue
1185
+
1186
+ if docerror:
1187
+ display.error_as_warning(f"{plugin} has a documentation formatting error.", exception=docerror)
1188
+ continue
1133
1189
 
1134
1190
  if not doc or not isinstance(doc, dict):
1135
1191
  desc = 'UNDOCUMENTED'
@@ -1368,7 +1424,7 @@ class DocCLI(CLI, RoleMixin):
1368
1424
  try:
1369
1425
  text.append(yaml_dump(doc.pop('examples'), indent=2, default_flow_style=False))
1370
1426
  except Exception as e:
1371
- raise AnsibleParserError("Unable to parse examples section", orig_exc=e)
1427
+ raise AnsibleParserError("Unable to parse examples section.") from e
1372
1428
 
1373
1429
  return text
1374
1430
 
@@ -1406,7 +1462,7 @@ class DocCLI(CLI, RoleMixin):
1406
1462
  try:
1407
1463
  text.append('\t' + C.config.get_deprecated_msg_from_config(doc['deprecated'], True, collection_name=collection_name))
1408
1464
  except KeyError as e:
1409
- raise AnsibleError("Invalid deprecation documentation structure", orig_exc=e)
1465
+ raise AnsibleError("Invalid deprecation documentation structure.") from e
1410
1466
  else:
1411
1467
  text.append("%s" % doc['deprecated'])
1412
1468
  del doc['deprecated']
ansible/cli/inventory.py CHANGED
@@ -14,13 +14,12 @@ import sys
14
14
  import typing as t
15
15
 
16
16
  import argparse
17
- import functools
18
17
 
19
18
  from ansible import constants as C
20
19
  from ansible import context
21
20
  from ansible.cli.arguments import option_helpers as opt_help
22
21
  from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleRuntimeError
23
- from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
22
+ from ansible.module_utils.common.text.converters import to_bytes, to_text
24
23
  from ansible._internal._json._profiles import _inventory_legacy
25
24
  from ansible.utils.vars import combine_vars
26
25
  from ansible.utils.display import Display
@@ -152,8 +151,8 @@ class InventoryCLI(CLI):
152
151
  try:
153
152
  with open(to_bytes(outfile), 'wb') as f:
154
153
  f.write(to_bytes(results))
155
- except (OSError, IOError) as e:
156
- raise AnsibleError('Unable to write to destination file (%s): %s' % (to_native(outfile), to_native(e)))
154
+ except OSError as ex:
155
+ raise AnsibleError(f'Unable to write to destination file {outfile!r}.') from ex
157
156
  sys.exit(0)
158
157
 
159
158
  sys.exit(1)
@@ -162,11 +161,10 @@ class InventoryCLI(CLI):
162
161
  def dump(stuff):
163
162
  if context.CLIARGS['yaml']:
164
163
  import yaml
164
+
165
165
  from ansible.parsing.yaml.dumper import AnsibleDumper
166
166
 
167
- # DTFIX-RELEASE: need shared infra to smuggle custom kwargs to dumpers, since yaml.dump cannot (as of PyYAML 6.0.1)
168
- dumper = functools.partial(AnsibleDumper, dump_vault_tags=True)
169
- results = to_text(yaml.dump(stuff, Dumper=dumper, default_flow_style=False, allow_unicode=True))
167
+ results = to_text(yaml.dump(stuff, Dumper=AnsibleDumper, default_flow_style=False, allow_unicode=True))
170
168
  elif context.CLIARGS['toml']:
171
169
  results = toml_dumps(stuff)
172
170
  else:
@@ -3,17 +3,14 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
- import sys
6
+ from ansible.utils.display import Display as _Display
7
7
 
8
- HAS_IMPORTLIB_RESOURCES = False
8
+ from importlib.resources import files # pylint: disable=unused-import
9
9
 
10
- if sys.version_info < (3, 10):
11
- try:
12
- from importlib_resources import files # type: ignore[import] # pylint: disable=unused-import
13
- except ImportError:
14
- files = None # type: ignore[assignment]
15
- else:
16
- HAS_IMPORTLIB_RESOURCES = True
17
- else:
18
- from importlib.resources import files
19
- HAS_IMPORTLIB_RESOURCES = True
10
+ HAS_IMPORTLIB_RESOURCES = True
11
+
12
+ _Display().deprecated(
13
+ msg="The `ansible.compat.importlib_resources` module is deprecated.",
14
+ help_text="Use `importlib.resources` from the Python standard library instead.",
15
+ version="2.23",
16
+ )
ansible/config/base.yml CHANGED
@@ -1,6 +1,26 @@
1
1
  # Copyright (c) 2017 Ansible Project
2
2
  # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
3
3
  ---
4
+ _ANSIBALLZ_COVERAGE_CONFIG:
5
+ name: Configure the AnsiballZ code coverage extension
6
+ description:
7
+ - Enables and configures the AnsiballZ code coverage extension.
8
+ - This is for internal use only.
9
+ env:
10
+ - {name: _ANSIBLE_ANSIBALLZ_COVERAGE_CONFIG}
11
+ vars:
12
+ - {name: _ansible_ansiballz_coverage_config}
13
+ version_added: '2.19'
14
+ _ANSIBALLZ_DEBUGGER_CONFIG:
15
+ name: Configure the AnsiballZ remote debugging extension
16
+ description:
17
+ - Enables and configures the AnsiballZ remote debugging extension.
18
+ - This is for internal use only.
19
+ env:
20
+ - {name: _ANSIBLE_ANSIBALLZ_DEBUGGER_CONFIG}
21
+ vars:
22
+ - {name: _ansible_ansiballz_debugger_config}
23
+ version_added: '2.19'
4
24
  _ANSIBLE_CONNECTION_PATH:
5
25
  env:
6
26
  - name: _ANSIBLE_CONNECTION_PATH
@@ -906,6 +926,10 @@ DEFAULT_MANAGED_STR:
906
926
  ini:
907
927
  - {key: ansible_managed, section: defaults}
908
928
  yaml: {key: defaults.ansible_managed}
929
+ deprecated:
930
+ why: The `ansible_managed` variable can be set just like any other variable, or a different variable can be used.
931
+ version: "2.23"
932
+ alternatives: Set the `ansible_managed` variable, or use any custom variable in templates.
909
933
  DEFAULT_MODULE_ARGS:
910
934
  name: Adhoc default arguments
911
935
  default: ~
@@ -1335,6 +1359,7 @@ DISPLAY_TRACEBACK:
1335
1359
  - error
1336
1360
  - warning
1337
1361
  - deprecated
1362
+ - deprecated_value
1338
1363
  - always
1339
1364
  - never
1340
1365
  version_added: "2.19"
@@ -1961,6 +1986,14 @@ SSH_AGENT:
1961
1986
  env: [{name: ANSIBLE_SSH_AGENT}]
1962
1987
  ini: [{key: ssh_agent, section: connection}]
1963
1988
  version_added: '2.19'
1989
+ SSH_AGENT_EXECUTABLE:
1990
+ name: Executable to start for the ansible-managed SSH agent
1991
+ description: When ``SSH_AGENT`` is ``auto``, the path or name of the ssh agent executable to start.
1992
+ default: ssh-agent
1993
+ type: str
1994
+ env: [ { name: ANSIBLE_SSH_AGENT_EXECUTABLE } ]
1995
+ ini: [ { key: ssh_agent_executable, section: connection } ]
1996
+ version_added: '2.19'
1964
1997
  SSH_AGENT_KEY_LIFETIME:
1965
1998
  name: Set a maximum lifetime when adding identities to an agent
1966
1999
  description: For keys inserted into an agent defined by ``SSH_AGENT``, define a lifetime, in seconds, that the key may remain
@@ -2035,6 +2068,19 @@ TASK_TIMEOUT:
2035
2068
  - {key: task_timeout, section: defaults}
2036
2069
  type: integer
2037
2070
  version_added: '2.10'
2071
+ _TEMPLAR_SANDBOX_MODE:
2072
+ name: Control Jinja template sandbox behavior
2073
+ default: default
2074
+ description:
2075
+ - The default Jinja sandbox behavior blocks template access to all `_` prefixed object attributes and known collection mutation methods (e.g., `dict.clear()`, `list.append()`).
2076
+ type: choices
2077
+ choices:
2078
+ - default
2079
+ - allow_unsafe_attributes
2080
+ env: [{name: _ANSIBLE_TEMPLAR_SANDBOX_MODE}]
2081
+ deprecated:
2082
+ why: controlling sandbox behavior is a temporary workaround
2083
+ version: '2.23'
2038
2084
  _TEMPLAR_UNKNOWN_TYPE_CONVERSION:
2039
2085
  name: Templar unknown type conversion behavior
2040
2086
  default: warning
@@ -3,20 +3,18 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ import collections.abc as _c
6
7
  import enum
7
- import traceback
8
- import sys
9
8
  import types
10
9
  import typing as t
11
10
 
12
- from collections.abc import Sequence
13
-
14
11
  from json import JSONDecodeError
15
12
 
16
13
  from ansible.module_utils.common.text.converters import to_text
17
14
  from ..module_utils.datatag import native_type_name
18
15
  from ansible._internal._datatag import _tags
19
- from .._internal._errors import _utils
16
+ from .._internal._errors import _error_utils
17
+ from ansible.module_utils._internal import _text_utils
20
18
 
21
19
  if t.TYPE_CHECKING:
22
20
  from ansible.plugins import loader as _t_loader
@@ -73,7 +71,7 @@ class AnsibleError(Exception):
73
71
  message = str(message)
74
72
 
75
73
  if self._default_message and message:
76
- message = _utils.concat_message(self._default_message, message)
74
+ message = _text_utils.concat_message(self._default_message, message)
77
75
  elif self._default_message:
78
76
  message = self._default_message
79
77
  elif not message:
@@ -97,8 +95,9 @@ class AnsibleError(Exception):
97
95
  self._show_content = False
98
96
 
99
97
  Display().deprecated(
100
- msg=f"The `suppress_extended_error` argument to `{type(self).__name__}` is deprecated. Use `show_content=False` instead.",
98
+ msg=f"The `suppress_extended_error` argument to `{type(self).__name__}` is deprecated.",
101
99
  version="2.23",
100
+ help_text="Use `show_content=False` instead.",
102
101
  )
103
102
 
104
103
  @property
@@ -108,12 +107,10 @@ class AnsibleError(Exception):
108
107
  @property
109
108
  def message(self) -> str:
110
109
  """
111
- If `include_cause_message` is False, return the original message.
112
- Otherwise, return the original message with cause message(s) appended, stopping on (and including) the first non-AnsibleError.
113
- The recursion is due to `AnsibleError.__str__` calling this method, which uses `str` on child exceptions to create the cause message.
114
- Recursion stops on the first non-AnsibleError since those exceptions do not implement the custom `__str__` behavior.
110
+ Return the original message with cause message(s) appended.
111
+ The cause will not be followed on any `AnsibleError` with `_include_cause_message=False`.
115
112
  """
116
- return _utils.get_chained_message(self)
113
+ return _error_utils.format_exception_message(self)
117
114
 
118
115
  @message.setter
119
116
  def message(self, val) -> None:
@@ -121,8 +118,8 @@ class AnsibleError(Exception):
121
118
 
122
119
  @property
123
120
  def _formatted_source_context(self) -> str | None:
124
- with _utils.RedactAnnotatedSourceContext.when(not self._show_content):
125
- if source_context := _utils.SourceContext.from_value(self.obj):
121
+ with _error_utils.RedactAnnotatedSourceContext.when(not self._show_content):
122
+ if source_context := _error_utils.SourceContext.from_value(self.obj):
126
123
  return str(source_context)
127
124
 
128
125
  return None
@@ -238,8 +235,20 @@ class AnsibleModuleError(AnsibleRuntimeError):
238
235
  """A module failed somehow."""
239
236
 
240
237
 
241
- class AnsibleConnectionFailure(AnsibleRuntimeError):
242
- """The transport / connection_plugin had a fatal error."""
238
+ class AnsibleConnectionFailure(AnsibleRuntimeError, _error_utils.ContributesToTaskResult):
239
+ """
240
+ The transport / connection_plugin had a fatal error.
241
+
242
+ This exception provides a result dictionary via the ContributesToTaskResult mixin.
243
+ """
244
+
245
+ @property
246
+ def result_contribution(self) -> t.Mapping[str, object]:
247
+ return dict(unreachable=True)
248
+
249
+ @property
250
+ def omit_failed_key(self) -> bool:
251
+ return True
243
252
 
244
253
 
245
254
  class AnsibleAuthenticationFailure(AnsibleConnectionFailure):
@@ -319,7 +328,7 @@ class AnsibleFileNotFound(AnsibleRuntimeError):
319
328
  else:
320
329
  message += "Could not find file"
321
330
 
322
- if self.paths and isinstance(self.paths, Sequence):
331
+ if self.paths and isinstance(self.paths, _c.Sequence):
323
332
  searched = to_text('\n\t'.join(self.paths))
324
333
  if message:
325
334
  message += "\n"
@@ -331,47 +340,76 @@ class AnsibleFileNotFound(AnsibleRuntimeError):
331
340
  suppress_extended_error=suppress_extended_error, orig_exc=orig_exc)
332
341
 
333
342
 
334
- # These Exceptions are temporary, using them as flow control until we can get a better solution.
335
- # DO NOT USE as they will probably be removed soon.
336
- # We will port the action modules in our tree to use a context manager instead.
337
- class AnsibleAction(AnsibleRuntimeError):
343
+ class AnsibleAction(AnsibleRuntimeError, _error_utils.ContributesToTaskResult):
338
344
  """Base Exception for Action plugin flow control."""
339
345
 
340
346
  def __init__(self, message="", obj=None, show_content=True, suppress_extended_error=..., orig_exc=None, result=None):
341
- super(AnsibleAction, self).__init__(message=message, obj=obj, show_content=show_content,
342
- suppress_extended_error=suppress_extended_error, orig_exc=orig_exc)
343
- if result is None:
344
- self.result = {}
345
- else:
346
- self.result = result
347
+ super().__init__(message=message, obj=obj, show_content=show_content, suppress_extended_error=suppress_extended_error, orig_exc=orig_exc)
348
+
349
+ self._result = result or {}
350
+
351
+ @property
352
+ def result_contribution(self) -> _c.Mapping[str, object]:
353
+ return self._result
354
+
355
+ @property
356
+ def result(self) -> dict[str, object]:
357
+ """Backward compatibility property returning a mutable dictionary."""
358
+ return dict(self.result_contribution)
347
359
 
348
360
 
349
361
  class AnsibleActionSkip(AnsibleAction):
350
- """An action runtime skip."""
362
+ """
363
+ An action runtime skip.
351
364
 
352
- def __init__(self, message="", obj=None, show_content=True, suppress_extended_error=..., orig_exc=None, result=None):
353
- super(AnsibleActionSkip, self).__init__(message=message, obj=obj, show_content=show_content,
354
- suppress_extended_error=suppress_extended_error, orig_exc=orig_exc, result=result)
355
- self.result.update({'skipped': True, 'msg': message})
365
+ This exception provides a result dictionary via the ContributesToTaskResult mixin.
366
+ """
367
+
368
+ @property
369
+ def result_contribution(self) -> _c.Mapping[str, object]:
370
+ return self._result | dict(
371
+ skipped=True,
372
+ msg=self.message,
373
+ )
374
+
375
+ @property
376
+ def omit_failed_key(self) -> bool:
377
+ return True
378
+
379
+ @property
380
+ def omit_exception_key(self) -> bool:
381
+ return True
356
382
 
357
383
 
358
384
  class AnsibleActionFail(AnsibleAction):
359
- """An action runtime failure."""
385
+ """
386
+ An action runtime failure.
360
387
 
361
- def __init__(self, message="", obj=None, show_content=True, suppress_extended_error=..., orig_exc=None, result=None):
362
- super(AnsibleActionFail, self).__init__(message=message, obj=obj, show_content=show_content,
363
- suppress_extended_error=suppress_extended_error, orig_exc=orig_exc, result=result)
388
+ This exception provides a result dictionary via the ContributesToTaskResult mixin.
389
+ """
390
+
391
+ @property
392
+ def result_contribution(self) -> _c.Mapping[str, object]:
393
+ return self._result | dict(
394
+ failed=True,
395
+ msg=self.message,
396
+ )
364
397
 
365
- result_overrides = {'failed': True, 'msg': message}
366
- # deprecated: description='use sys.exception()' python_version='3.11'
367
- if sys.exc_info()[1]: # DTFIX-RELEASE: remove this hack once TaskExecutor is no longer shucking AnsibleActionFail and returning its result
368
- result_overrides['exception'] = traceback.format_exc()
369
398
 
370
- self.result.update(result_overrides)
399
+ class _ActionDone(AnsibleAction):
400
+ """
401
+ Imports as `_AnsibleActionDone` are deprecated. An action runtime early exit.
402
+
403
+ This exception provides a result dictionary via the ContributesToTaskResult mixin.
404
+ """
371
405
 
406
+ @property
407
+ def omit_failed_key(self) -> bool:
408
+ return not self._result.get('failed')
372
409
 
373
- class _AnsibleActionDone(AnsibleAction):
374
- """An action runtime early exit."""
410
+ @property
411
+ def omit_exception_key(self) -> bool:
412
+ return not self._result.get('failed')
375
413
 
376
414
 
377
415
  class AnsiblePluginError(AnsibleError):
@@ -422,13 +460,23 @@ def __getattr__(name: str) -> t.Any:
422
460
  """Inject import-time deprecation warnings."""
423
461
  from ..utils.display import Display
424
462
 
425
- if name == 'AnsibleFilterTypeError':
426
- Display().deprecated(
427
- msg="Importing 'AnsibleFilterTypeError' is deprecated.",
428
- help_text=f"Import {AnsibleTypeError.__name__!r} instead.",
429
- version="2.23",
430
- )
463
+ match name:
464
+ case 'AnsibleFilterTypeError':
465
+ Display().deprecated(
466
+ msg=f"Importing {name!r} is deprecated.",
467
+ help_text=f"Import {AnsibleTypeError.__name__!r} instead.",
468
+ version="2.23",
469
+ )
470
+
471
+ return AnsibleTypeError
472
+
473
+ case '_AnsibleActionDone':
474
+ Display().deprecated(
475
+ msg=f"Importing {name!r} is deprecated.",
476
+ help_text="Return directly from action plugins instead.",
477
+ version="2.23",
478
+ )
431
479
 
432
- return AnsibleTypeError
480
+ return _ActionDone
433
481
 
434
482
  raise AttributeError(f'module {__name__!r} has no attribute {name!r}')