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
@@ -22,7 +22,7 @@ DOCUMENTATION = """
22
22
  default: true
23
23
  deprecated:
24
24
  why: This option is no longer used in the Ansible Core code base.
25
- version: "2.21"
25
+ version: "2.23"
26
26
  alternatives: Jinja2 native mode is now the default and only option, which is mutually exclusive with this option.
27
27
  variable_start_string:
28
28
  description: The string marking the beginning of a print statement.
@@ -45,7 +45,7 @@ DOCUMENTATION = """
45
45
  type: bool
46
46
  deprecated:
47
47
  why: This option is no longer used in the Ansible Core code base.
48
- version: "2.21"
48
+ version: "2.23"
49
49
  alternatives: Jinja2 native mode is now the default and only option.
50
50
  template_vars:
51
51
  description: A dictionary, the keys become additional variables available for templating.
@@ -105,7 +105,8 @@ import os
105
105
 
106
106
  from ansible.errors import AnsibleError
107
107
  from ansible.plugins.lookup import LookupBase
108
- from ansible.template import generate_ansible_template_vars, trust_as_template
108
+ from ansible.template import trust_as_template
109
+ from ansible._internal._templating import _template_vars
109
110
  from ansible.utils.display import Display
110
111
 
111
112
 
@@ -157,7 +158,11 @@ class LookupModule(LookupBase):
157
158
  # argument.
158
159
  # FIXME: why isn't this a chainmap with a sacrificial bottom layer?
159
160
  vars = deepcopy(variables)
160
- vars.update(generate_ansible_template_vars(term, fullpath=lookupfile))
161
+ vars.update(_template_vars.generate_ansible_template_vars(
162
+ path=term,
163
+ fullpath=lookupfile,
164
+ include_ansible_managed='ansible_managed' not in vars, # do not clobber ansible_managed when set by the user
165
+ ))
161
166
  vars.update(lookup_template_vars)
162
167
 
163
168
  overrides = dict(
@@ -16,6 +16,7 @@
16
16
  # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
17
17
  from __future__ import annotations
18
18
 
19
+ import dataclasses
19
20
  import os
20
21
  import os.path
21
22
  import re
@@ -33,6 +34,13 @@ from ansible.plugins import AnsiblePlugin
33
34
  _USER_HOME_PATH_RE = re.compile(r'^~[_.A-Za-z0-9][-_.A-Za-z0-9]*$')
34
35
 
35
36
 
37
+ @dataclasses.dataclass(frozen=True, kw_only=True, slots=True)
38
+ class _ShellCommand:
39
+ """Internal type returned by shell subsystems that may require both an execution payload and a command (eg powershell)."""
40
+ command: str
41
+ input_data: bytes | None = None
42
+
43
+
36
44
  class ShellBase(AnsiblePlugin):
37
45
  def __init__(self):
38
46
 
@@ -121,7 +129,13 @@ class ShellBase(AnsiblePlugin):
121
129
  cmd = ['test', '-e', self.quote(path)]
122
130
  return ' '.join(cmd)
123
131
 
124
- def mkdtemp(self, basefile=None, system=False, mode=0o700, tmpdir=None):
132
+ def mkdtemp(
133
+ self,
134
+ basefile: str | None = None,
135
+ system: bool = False,
136
+ mode: int = 0o700,
137
+ tmpdir: str | None = None,
138
+ ) -> str:
125
139
  if not basefile:
126
140
  basefile = self.__class__._generate_temp_dir_name()
127
141
 
@@ -163,7 +177,31 @@ class ShellBase(AnsiblePlugin):
163
177
 
164
178
  return cmd
165
179
 
166
- def expand_user(self, user_home_path, username=''):
180
+ def _mkdtemp2(
181
+ self,
182
+ basefile: str | None = None,
183
+ system: bool = False,
184
+ mode: int = 0o700,
185
+ tmpdir: str | None = None,
186
+ ) -> _ShellCommand:
187
+ """Gets command info to create a temporary directory.
188
+
189
+ This is an internal API that should not be used publicly.
190
+
191
+ :args basefile: The base name of the temporary directory.
192
+ :args system: If True, create the directory in a system-wide location.
193
+ :args mode: The permissions mode for the directory.
194
+ :args tmpdir: The directory in which to create the temporary directory.
195
+ :returns: The shell command to run to create the temp directory.
196
+ """
197
+ cmd = self.mkdtemp(basefile=basefile, system=system, mode=mode, tmpdir=tmpdir)
198
+ return _ShellCommand(command=cmd, input_data=None)
199
+
200
+ def expand_user(
201
+ self,
202
+ user_home_path: str,
203
+ username: str = '',
204
+ ) -> str:
167
205
  """ Return a command to expand tildes in a path
168
206
 
169
207
  It can be either "~" or "~username". We just ignore $HOME
@@ -184,6 +222,22 @@ class ShellBase(AnsiblePlugin):
184
222
 
185
223
  return 'echo %s' % user_home_path
186
224
 
225
+ def _expand_user2(
226
+ self,
227
+ user_home_path: str,
228
+ username: str = '',
229
+ ) -> _ShellCommand:
230
+ """Gets command to expand user path.
231
+
232
+ This is an internal API that should not be used publicly.
233
+
234
+ :args user_home_path: The path to expand.
235
+ :args username: The username to use for expansion.
236
+ :returns: The shell command to run to get the expanded user path.
237
+ """
238
+ cmd = self.expand_user(user_home_path, username=username)
239
+ return _ShellCommand(command=cmd, input_data=None)
240
+
187
241
  def pwd(self):
188
242
  """Return the working directory after connecting"""
189
243
  return 'echo %spwd%s' % (self._SHELL_SUB_LEFT, self._SHELL_SUB_RIGHT)
@@ -21,9 +21,13 @@ import shlex
21
21
  import xml.etree.ElementTree as ET
22
22
  import ntpath
23
23
 
24
- from ansible.executor.powershell.module_manifest import _get_powershell_script
24
+ from ansible.executor.powershell.module_manifest import _bootstrap_powershell_script, _get_powershell_script
25
25
  from ansible.module_utils.common.text.converters import to_bytes, to_text
26
- from ansible.plugins.shell import ShellBase
26
+ from ansible.plugins.shell import ShellBase, _ShellCommand
27
+ from ansible.utils.display import Display
28
+
29
+
30
+ display = Display()
27
31
 
28
32
  # This is weird, we are matching on byte sequences that match the utf-16-be
29
33
  # matches for '_x(a-fA-F0-9){4}_'. The \x00 and {4} will match the hex sequence
@@ -225,9 +229,15 @@ class ShellModule(ShellBase):
225
229
  else:
226
230
  return self._encode_script("""Remove-Item '%s' -Force;""" % path)
227
231
 
228
- def mkdtemp(self, basefile=None, system=False, mode=None, tmpdir=None):
229
- # Windows does not have an equivalent for the system temp files, so
230
- # the param is ignored
232
+ def mkdtemp(
233
+ self,
234
+ basefile: str | None = None,
235
+ system: bool = False,
236
+ mode: int = 0o700,
237
+ tmpdir: str | None = None,
238
+ ) -> str:
239
+ # This is not called in Ansible anymore but it is kept for backwards
240
+ # compatibility in case other action plugins outside Ansible calls this.
231
241
  if not basefile:
232
242
  basefile = self.__class__._generate_temp_dir_name()
233
243
  basefile = self._escape(self._unquote(basefile))
@@ -241,10 +251,38 @@ class ShellModule(ShellBase):
241
251
  """
242
252
  return self._encode_script(script.strip())
243
253
 
244
- def expand_user(self, user_home_path, username=''):
245
- # PowerShell only supports "~" (not "~username"). Resolve-Path ~ does
246
- # not seem to work remotely, though by default we are always starting
247
- # in the user's home directory.
254
+ def _mkdtemp2(
255
+ self,
256
+ basefile: str | None = None,
257
+ system: bool = False,
258
+ mode: int = 0o700,
259
+ tmpdir: str | None = None,
260
+ ) -> _ShellCommand:
261
+ # Windows does not have an equivalent for the system temp files, so
262
+ # the param is ignored
263
+ if not basefile:
264
+ basefile = self.__class__._generate_temp_dir_name()
265
+
266
+ basefile = self._unquote(basefile)
267
+ basetmpdir = tmpdir if tmpdir else self.get_option('remote_tmp')
268
+
269
+ script, stdin = _bootstrap_powershell_script("powershell_mkdtemp.ps1", {
270
+ 'Directory': basetmpdir,
271
+ 'Name': basefile,
272
+ })
273
+
274
+ return _ShellCommand(
275
+ command=self._encode_script(script),
276
+ input_data=stdin,
277
+ )
278
+
279
+ def expand_user(
280
+ self,
281
+ user_home_path: str,
282
+ username: str = '',
283
+ ) -> str:
284
+ # This is not called in Ansible anymore but it is kept for backwards
285
+ # compatibility in case other actions plugins outside Ansible called this.
248
286
  user_home_path = self._unquote(user_home_path)
249
287
  if user_home_path == '~':
250
288
  script = 'Write-Output (Get-Location).Path'
@@ -254,6 +292,21 @@ class ShellModule(ShellBase):
254
292
  script = "Write-Output '%s'" % self._escape(user_home_path)
255
293
  return self._encode_script(f"{self._CONSOLE_ENCODING}; {script}")
256
294
 
295
+ def _expand_user2(
296
+ self,
297
+ user_home_path: str,
298
+ username: str = '',
299
+ ) -> _ShellCommand:
300
+ user_home_path = self._unquote(user_home_path)
301
+ script, stdin = _bootstrap_powershell_script("powershell_expand_user.ps1", {
302
+ 'Path': user_home_path,
303
+ })
304
+
305
+ return _ShellCommand(
306
+ command=self._encode_script(script),
307
+ input_data=stdin,
308
+ )
309
+
257
310
  def exists(self, path):
258
311
  path = self._escape(self._unquote(path))
259
312
  script = """
@@ -271,6 +324,11 @@ class ShellModule(ShellBase):
271
324
  return self._encode_script(script)
272
325
 
273
326
  def checksum(self, path, *args, **kwargs):
327
+ display.deprecated(
328
+ msg="The `ShellModule.checksum` method is deprecated.",
329
+ version="2.23",
330
+ help_text="Use `ActionBase._execute_remote_stat()` instead.",
331
+ )
274
332
  path = self._escape(self._unquote(path))
275
333
  script = """
276
334
  If (Test-Path -PathType Leaf '%(path)s')
@@ -14,6 +14,10 @@ extends_documentation_fragment:
14
14
  """
15
15
 
16
16
  from ansible.plugins.shell import ShellBase
17
+ from ansible.utils.display import Display
18
+
19
+
20
+ display = Display()
17
21
 
18
22
 
19
23
  class ShellModule(ShellBase):
@@ -43,6 +47,11 @@ class ShellModule(ShellBase):
43
47
  _SHELL_GROUP_RIGHT = ')'
44
48
 
45
49
  def checksum(self, path, python_interp):
50
+ display.deprecated(
51
+ msg="The `ShellModule.checksum` method is deprecated.",
52
+ version="2.23",
53
+ help_text="Use `ActionBase._execute_remote_stat()` instead.",
54
+ )
46
55
  # In the following test, each condition is a check and logical
47
56
  # comparison (|| or &&) that sets the rc value. Every check is run so
48
57
  # the last check in the series to fail will be the rc that is returned.
@@ -67,11 +76,7 @@ class ShellModule(ShellBase):
67
76
  # "one-liner".
68
77
  shell_escaped_path = self.quote(path)
69
78
  test = "rc=flag; [ -r %(p)s ] %(shell_or)s rc=2; [ -f %(p)s ] %(shell_or)s rc=1; [ -d %(p)s ] %(shell_and)s rc=3; %(i)s -V 2>/dev/null %(shell_or)s rc=4; [ x\"$rc\" != \"xflag\" ] %(shell_and)s echo \"${rc} \"%(p)s %(shell_and)s exit 0" % dict(p=shell_escaped_path, i=python_interp, shell_and=self._SHELL_AND, shell_or=self._SHELL_OR) # NOQA
70
- csums = [
71
- u"({0} -c 'import hashlib; BLOCKSIZE = 65536; hasher = hashlib.sha1();{2}afile = open(\"'{1}'\", \"rb\"){2}buf = afile.read(BLOCKSIZE){2}while len(buf) > 0:{2}\thasher.update(buf){2}\tbuf = afile.read(BLOCKSIZE){2}afile.close(){2}print(hasher.hexdigest())' 2>/dev/null)".format(python_interp, shell_escaped_path, self._SHELL_EMBEDDED_PY_EOL), # NOQA Python > 2.4 (including python3)
72
- u"({0} -c 'import sha; BLOCKSIZE = 65536; hasher = sha.sha();{2}afile = open(\"'{1}'\", \"rb\"){2}buf = afile.read(BLOCKSIZE){2}while len(buf) > 0:{2}\thasher.update(buf){2}\tbuf = afile.read(BLOCKSIZE){2}afile.close(){2}print(hasher.hexdigest())' 2>/dev/null)".format(python_interp, shell_escaped_path, self._SHELL_EMBEDDED_PY_EOL), # NOQA Python == 2.4
73
- ]
79
+ cmd = "({0} -c 'import hashlib; BLOCKSIZE = 65536; hasher = hashlib.sha1();{2}afile = open(\"'{1}'\", \"rb\"){2}buf = afile.read(BLOCKSIZE){2}while len(buf) > 0:{2}\thasher.update(buf){2}\tbuf = afile.read(BLOCKSIZE){2}afile.close(){2}print(hasher.hexdigest())' 2>/dev/null)".format(python_interp, shell_escaped_path, self._SHELL_EMBEDDED_PY_EOL) # NOQA
74
80
 
75
- cmd = (" %s " % self._SHELL_OR).join(csums)
76
81
  cmd = "%s; %s %s (echo \'0 \'%s)" % (test, cmd, self._SHELL_OR, shell_escaped_path)
77
82
  return cmd
@@ -128,7 +128,7 @@ def results_thread_main(strategy: StrategyBase) -> None:
128
128
  strategy._workers[result.worker_id].worker_queue.put(value)
129
129
  else:
130
130
  display.warning('Received an invalid object (%s) in the result queue: %r' % (type(result), result))
131
- except (IOError, EOFError):
131
+ except (OSError, EOFError):
132
132
  break
133
133
  except queue.Empty:
134
134
  pass
@@ -402,9 +402,9 @@ class StrategyBase:
402
402
  time.sleep(0.0001)
403
403
 
404
404
  self._pending_results += 1
405
- except (EOFError, IOError, AssertionError) as e:
405
+ except (EOFError, OSError, AssertionError) as ex:
406
406
  # most likely an abort
407
- display.debug("got an error while queuing: %s" % e)
407
+ display.debug(f"got an error while queuing: {ex}")
408
408
  return
409
409
  display.debug("exiting _queue_task() for %s/%s" % (host.name, task.action))
410
410
 
@@ -31,7 +31,7 @@ from ansible import errors
31
31
  from ansible.module_utils.common.text.converters import to_native, to_text, to_bytes
32
32
  from ansible._internal._templating._jinja_common import Marker, UndefinedMarker
33
33
  from ansible.module_utils.parsing.convert_bool import boolean
34
- from ansible.plugins import accept_args_markers
34
+ from ansible.template import accept_args_markers
35
35
  from ansible.parsing.vault import is_encrypted_file, VaultHelper, VaultLib
36
36
  from ansible.utils.display import Display
37
37
  from ansible.utils.version import SemanticVersion
@@ -49,37 +49,41 @@ def timedout(result):
49
49
  """ Test if task result yields a time out"""
50
50
  if not isinstance(result, MutableMapping):
51
51
  raise errors.AnsibleFilterError("The 'timedout' test expects a dictionary")
52
- return result.get('timedout', False) and bool(result['timedout'].get('period', False))
52
+
53
+ return bool(result.get('timedout') and bool(result['timedout'].get('period')))
53
54
 
54
55
 
55
56
  def failed(result):
56
57
  """ Test if task result yields failed """
57
58
  if not isinstance(result, MutableMapping):
58
59
  raise errors.AnsibleFilterError("The 'failed' test expects a dictionary")
59
- return result.get('failed', False)
60
+
61
+ return bool(result.get('failed'))
60
62
 
61
63
 
62
64
  def success(result):
63
65
  """ Test if task result yields success """
64
- return not failed(result)
66
+ return not bool(failed(result))
65
67
 
66
68
 
67
69
  def unreachable(result):
68
70
  """ Test if task result yields unreachable """
69
71
  if not isinstance(result, MutableMapping):
70
72
  raise errors.AnsibleFilterError("The 'unreachable' test expects a dictionary")
71
- return result.get('unreachable', False)
73
+
74
+ return bool(result.get('unreachable'))
72
75
 
73
76
 
74
77
  def reachable(result):
75
78
  """ Test if task result yields reachable """
76
- return not unreachable(result)
79
+ return bool(not unreachable(result))
77
80
 
78
81
 
79
82
  def changed(result):
80
83
  """ Test if task result yields changed """
81
84
  if not isinstance(result, MutableMapping):
82
85
  raise errors.AnsibleFilterError("The 'changed' test expects a dictionary")
86
+
83
87
  if 'changed' not in result:
84
88
  changed = False
85
89
  if (
@@ -88,29 +92,31 @@ def changed(result):
88
92
  isinstance(result['results'][0], MutableMapping)
89
93
  ):
90
94
  for res in result['results']:
91
- if res.get('changed', False):
95
+ if res.get('changed'):
92
96
  changed = True
93
97
  break
94
98
  else:
95
- changed = result.get('changed', False)
96
- return changed
99
+ changed = result.get('changed')
100
+
101
+ return bool(changed)
97
102
 
98
103
 
99
104
  def skipped(result):
100
105
  """ Test if task result yields skipped """
101
106
  if not isinstance(result, MutableMapping):
102
107
  raise errors.AnsibleFilterError("The 'skipped' test expects a dictionary")
103
- return result.get('skipped', False)
108
+
109
+ return bool(result.get('skipped'))
104
110
 
105
111
 
106
112
  def started(result):
107
113
  """ Test if async task has started """
108
114
  if not isinstance(result, MutableMapping):
109
115
  raise errors.AnsibleFilterError("The 'started' test expects a dictionary")
116
+
110
117
  if 'started' in result:
111
118
  # For async tasks, return status
112
- # NOTE: The value of started is 0 or 1, not False or True :-/
113
- return result.get('started', 0) == 1
119
+ return bool(result.get('started'))
114
120
  else:
115
121
  # For non-async tasks, warn user, but return as if started
116
122
  display.warning("The 'started' test expects an async task, but a non-async task was tested")
@@ -121,10 +127,10 @@ def finished(result):
121
127
  """ Test if async task has finished """
122
128
  if not isinstance(result, MutableMapping):
123
129
  raise errors.AnsibleFilterError("The 'finished' test expects a dictionary")
130
+
124
131
  if 'finished' in result:
125
132
  # For async tasks, return status
126
- # NOTE: The value of finished is 0 or 1, not False or True :-/
127
- return result.get('finished', 0) == 1
133
+ return bool(result.get('finished'))
128
134
  else:
129
135
  # For non-async tasks, warn user, but return as if finished
130
136
  display.warning("The 'finished' test expects an async task, but a non-async task was tested")
@@ -169,8 +175,8 @@ def vaulted_file(value):
169
175
  try:
170
176
  with open(to_bytes(value), 'rb') as f:
171
177
  return is_encrypted_file(f)
172
- except (OSError, IOError) as e:
173
- raise errors.AnsibleFilterError(f"Cannot test if the file {value} is a vault", orig_exc=e)
178
+ except OSError as ex:
179
+ raise errors.AnsibleFilterError(f"Cannot test if the file {value!r} is a vault.") from ex
174
180
 
175
181
 
176
182
  def match(value, pattern='', ignorecase=False, multiline=False):
@@ -5,7 +5,7 @@ DOCUMENTATION:
5
5
  short_description: Did async task finish
6
6
  description:
7
7
  - Used to test if an async task has finished, it will also work with normal tasks but will issue a warning.
8
- - This test checks for the existence of a C(finished) key in the input dictionary and that it is V(1) if present
8
+ - This test checks for the existence of a C(finished) key in the input dictionary and that it is V(True) if present
9
9
  options:
10
10
  _input:
11
11
  description: registered result from an Ansible task
@@ -20,11 +20,8 @@ def is_url(value, schemes=None):
20
20
 
21
21
  isit = is_uri(value, schemes)
22
22
  if isit:
23
- try:
24
- x = urlparse(value)
25
- isit = bool(x.netloc or x.scheme == 'file')
26
- except Exception as e:
27
- isit = False
23
+ x = urlparse(value)
24
+ isit = bool(x.netloc or x.scheme == 'file')
28
25
  return isit
29
26
 
30
27
 
ansible/release.py CHANGED
@@ -17,6 +17,6 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- __version__ = '2.19.0b4'
20
+ __version__ = '2.19.0b6'
21
21
  __author__ = 'Ansible, Inc.'
22
22
  __codename__ = "What Is and What Should Never Be"
@@ -1,22 +1,18 @@
1
1
  from __future__ import annotations as _annotations
2
2
 
3
3
  import contextlib as _contextlib
4
- import datetime as _datetime
5
4
  import io as _io
6
5
  import os as _os
7
- import pwd as _pwd
8
- import time as _time
9
6
  import typing as _t
10
7
 
11
8
  from jinja2 import environment as _environment
12
9
 
13
10
  from ansible import _internal
14
- from ansible import constants as _constants
15
11
  from ansible import errors as _errors
16
12
  from ansible._internal._datatag import _tags, _wrappers
17
- from ansible._internal._templating import _jinja_bits, _engine, _jinja_common
13
+ from ansible._internal._templating import _jinja_bits, _engine, _jinja_common, _template_vars
14
+
18
15
  from ansible.module_utils import datatag as _module_utils_datatag
19
- from ansible.module_utils._internal import _datatag
20
16
  from ansible.utils.display import Display as _Display
21
17
 
22
18
  if _t.TYPE_CHECKING: # pragma: nocover
@@ -98,7 +94,7 @@ class Templar:
98
94
  @property
99
95
  def basedir(self) -> str:
100
96
  """The basedir from DataLoader."""
101
- # DTFIX-RELEASE: come up with a better way to handle this so it can be deprecated
97
+ # DTFIX-FUTURE: come up with a better way to handle this so it can be deprecated
102
98
  return self._engine.basedir
103
99
 
104
100
  @property
@@ -352,57 +348,17 @@ class Templar:
352
348
  )
353
349
 
354
350
 
355
- def generate_ansible_template_vars(path: str, fullpath: str | None = None, dest_path: str | None = None) -> dict[str, object]:
351
+ def generate_ansible_template_vars(
352
+ path: str,
353
+ fullpath: str | None = None,
354
+ dest_path: str | None = None,
355
+ ) -> dict[str, object]:
356
356
  """
357
357
  Generate and return a dictionary with variable metadata about the template specified by `fullpath`.
358
358
  If `fullpath` is `None`, `path` will be used instead.
359
359
  """
360
- if fullpath is None:
361
- fullpath = _os.path.abspath(path)
362
-
363
- template_path = fullpath
364
- template_stat = _os.stat(template_path)
365
-
366
- template_uid: int | str
367
-
368
- try:
369
- template_uid = _pwd.getpwuid(template_stat.st_uid).pw_name
370
- except KeyError:
371
- template_uid = template_stat.st_uid
372
-
373
- managed_default = _constants.config.get_config_value('DEFAULT_MANAGED_STR')
374
-
375
- managed_str = managed_default.format(
376
- # IMPORTANT: These values must be constant strings to avoid template injection.
377
- # Use Jinja template expressions where variables are needed.
378
- host="{{ template_host }}",
379
- uid="{{ template_uid }}",
380
- file="{{ template_path }}",
381
- )
382
-
383
- ansible_managed = _time.strftime(managed_str, _time.localtime(template_stat.st_mtime))
384
- # DTFIX-RELEASE: this should not be tag_copy, it should either be an origin copy or some kind of derived origin
385
- ansible_managed = _datatag.AnsibleTagHelper.tag_copy(managed_default, ansible_managed)
386
- ansible_managed = trust_as_template(ansible_managed)
387
- ansible_managed = _module_utils_datatag.deprecate_value(
388
- value=ansible_managed,
389
- msg="The `ansible_managed` variable is deprecated.",
390
- help_text="Define and use a custom variable instead.",
391
- version='2.23',
392
- )
393
-
394
- temp_vars = dict(
395
- template_host=_os.uname()[1],
396
- template_path=path,
397
- template_mtime=_datetime.datetime.fromtimestamp(template_stat.st_mtime),
398
- template_uid=template_uid,
399
- template_run_date=_datetime.datetime.now(),
400
- template_destpath=dest_path,
401
- template_fullpath=fullpath,
402
- ansible_managed=ansible_managed,
403
- )
404
-
405
- return temp_vars
360
+ # deprecated description="deprecate `generate_ansible_template_vars`, collections should inline the necessary variables" core_version="2.23"
361
+ return _template_vars.generate_ansible_template_vars(path=path, fullpath=fullpath, dest_path=dest_path, include_ansible_managed=True)
406
362
 
407
363
 
408
364
  def trust_as_template(value: _TTrustable) -> _TTrustable:
@@ -427,3 +383,31 @@ def is_trusted_as_template(value: object) -> bool:
427
383
  This function should not be needed for production code, but may be useful in unit tests.
428
384
  """
429
385
  return isinstance(value, _TRUSTABLE_TYPES) and _tags.TrustedAsTemplate.is_tagged_on(value)
386
+
387
+
388
+ _TCallable = _t.TypeVar('_TCallable', bound=_t.Callable)
389
+
390
+
391
+ def accept_args_markers(plugin: _TCallable) -> _TCallable:
392
+ """
393
+ A decorator to mark a Jinja plugin as capable of handling `Marker` values for its top-level arguments.
394
+ Non-decorated plugin invocation is skipped when a top-level argument is a `Marker`, with the first such value substituted as the plugin result.
395
+ This ensures that only plugins which understand `Marker` instances for top-level arguments will encounter them.
396
+ """
397
+ plugin.accept_args_markers = True
398
+
399
+ return plugin
400
+
401
+
402
+ def accept_lazy_markers(plugin: _TCallable) -> _TCallable:
403
+ """
404
+ A decorator to mark a Jinja plugin as capable of handling `Marker` values retrieved from lazy containers.
405
+ Non-decorated plugins will trigger a `MarkerError` exception when attempting to retrieve a `Marker` from a lazy container.
406
+ This ensures that only plugins which understand lazy retrieval of `Marker` instances will encounter them.
407
+ """
408
+ plugin.accept_lazy_markers = True
409
+
410
+ return plugin
411
+
412
+
413
+ get_first_marker_arg = _jinja_common.get_first_marker_arg
@@ -1095,8 +1095,8 @@ def _get_collection_playbook_path(playbook):
1095
1095
  try:
1096
1096
  # get_collection_path
1097
1097
  pkg = import_module(acr.n_python_collection_package_name)
1098
- except (IOError, ModuleNotFoundError) as e:
1099
- # leaving e as debug target, even though not used in normal code
1098
+ except (OSError, ModuleNotFoundError) as ex:
1099
+ # leaving ex as debug target, even though not used in normal code
1100
1100
  pkg = None
1101
1101
 
1102
1102
  if pkg:
@@ -1151,7 +1151,7 @@ def _get_collection_resource_path(name, ref_type, collection_list=None):
1151
1151
  path = os.path.dirname(_to_bytes(sys.modules[acr.n_python_package_name].__file__))
1152
1152
  return resource, _to_text(path), collection_name
1153
1153
 
1154
- except (IOError, ModuleNotFoundError) as e:
1154
+ except (OSError, ModuleNotFoundError) as ex:
1155
1155
  continue
1156
1156
  except Exception as ex:
1157
1157
  # FIXME: pick out typical import errors first, then error logging