ansible-core 2.19.0b5__py3-none-any.whl → 2.19.0b7__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 (184) hide show
  1. ansible/_internal/_ansiballz/__init__.py +0 -0
  2. ansible/_internal/_ansiballz/_builder.py +101 -0
  3. ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
  4. ansible/_internal/_templating/_jinja_bits.py +22 -4
  5. ansible/_internal/_templating/_jinja_common.py +1 -1
  6. ansible/_internal/_templating/_jinja_plugins.py +5 -2
  7. ansible/_internal/_templating/_template_vars.py +72 -0
  8. ansible/_internal/_templating/_transform.py +6 -0
  9. ansible/_internal/_yaml/_constructor.py +4 -4
  10. ansible/_internal/_yaml/_dumper.py +26 -18
  11. ansible/cli/__init__.py +9 -14
  12. ansible/cli/adhoc.py +6 -3
  13. ansible/cli/arguments/option_helpers.py +1 -1
  14. ansible/cli/console.py +2 -2
  15. ansible/cli/doc.py +4 -4
  16. ansible/cli/inventory.py +5 -7
  17. ansible/config/base.yml +33 -6
  18. ansible/errors/__init__.py +2 -1
  19. ansible/executor/module_common.py +75 -44
  20. ansible/executor/powershell/psrp_put_file.ps1 +1 -1
  21. ansible/executor/process/worker.py +2 -2
  22. ansible/executor/task_executor.py +2 -2
  23. ansible/executor/task_queue_manager.py +34 -70
  24. ansible/executor/task_result.py +1 -1
  25. ansible/galaxy/api.py +3 -6
  26. ansible/galaxy/collection/__init__.py +1 -6
  27. ansible/galaxy/collection/concrete_artifact_manager.py +4 -10
  28. ansible/galaxy/dependency_resolution/providers.py +3 -3
  29. ansible/galaxy/role.py +2 -2
  30. ansible/inventory/group.py +6 -1
  31. ansible/inventory/host.py +6 -1
  32. ansible/module_utils/_internal/__init__.py +7 -4
  33. ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
  34. ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
  35. ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
  36. ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
  37. ansible/module_utils/_internal/{_ansiballz.py → _ansiballz/_loader.py} +10 -38
  38. ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
  39. ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
  40. ansible/module_utils/_internal/_datatag/__init__.py +23 -1
  41. ansible/module_utils/_internal/_deprecator.py +39 -34
  42. ansible/module_utils/_internal/_json/_profiles/__init__.py +1 -0
  43. ansible/module_utils/_internal/_messages.py +26 -2
  44. ansible/module_utils/_internal/_plugin_info.py +14 -1
  45. ansible/module_utils/ansible_release.py +1 -1
  46. ansible/module_utils/basic.py +58 -70
  47. ansible/module_utils/common/respawn.py +4 -41
  48. ansible/module_utils/common/yaml.py +1 -1
  49. ansible/module_utils/connection.py +8 -11
  50. ansible/module_utils/csharp/Ansible.Basic.cs +1 -1
  51. ansible/module_utils/csharp/Ansible.Privilege.cs +2 -2
  52. ansible/module_utils/facts/hardware/base.py +1 -1
  53. ansible/module_utils/facts/hardware/linux.py +1 -1
  54. ansible/module_utils/facts/other/facter.py +1 -1
  55. ansible/module_utils/facts/sysctl.py +4 -6
  56. ansible/module_utils/facts/system/caps.py +2 -2
  57. ansible/module_utils/facts/system/distribution.py +2 -2
  58. ansible/module_utils/facts/system/local.py +1 -1
  59. ansible/module_utils/facts/virtual/linux.py +1 -1
  60. ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
  61. ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
  62. ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 +1 -1
  63. ansible/module_utils/powershell/Ansible.ModuleUtils.WebRequest.psm1 +1 -1
  64. ansible/module_utils/service.py +1 -1
  65. ansible/module_utils/urls.py +5 -5
  66. ansible/modules/apt.py +9 -3
  67. ansible/modules/apt_repository.py +10 -10
  68. ansible/modules/assemble.py +7 -5
  69. ansible/modules/async_wrapper.py +7 -17
  70. ansible/modules/command.py +3 -3
  71. ansible/modules/copy.py +4 -4
  72. ansible/modules/cron.py +1 -1
  73. ansible/modules/expect.py +5 -5
  74. ansible/modules/file.py +16 -17
  75. ansible/modules/find.py +3 -3
  76. ansible/modules/get_url.py +17 -0
  77. ansible/modules/git.py +9 -7
  78. ansible/modules/hostname.py +2 -2
  79. ansible/modules/known_hosts.py +12 -14
  80. ansible/modules/package.py +6 -0
  81. ansible/modules/pip.py +9 -11
  82. ansible/modules/raw.py +2 -2
  83. ansible/modules/replace.py +2 -2
  84. ansible/modules/slurp.py +10 -13
  85. ansible/modules/stat.py +6 -8
  86. ansible/modules/unarchive.py +6 -6
  87. ansible/modules/user.py +1 -1
  88. ansible/modules/wait_for.py +38 -33
  89. ansible/modules/yum_repository.py +4 -3
  90. ansible/parsing/dataloader.py +2 -2
  91. ansible/parsing/mod_args.py +38 -20
  92. ansible/parsing/vault/__init__.py +9 -13
  93. ansible/playbook/base.py +7 -4
  94. ansible/playbook/helpers.py +1 -1
  95. ansible/playbook/included_file.py +3 -1
  96. ansible/playbook/play_context.py +2 -0
  97. ansible/playbook/playbook_include.py +23 -56
  98. ansible/playbook/role/__init__.py +38 -21
  99. ansible/playbook/taggable.py +19 -5
  100. ansible/playbook/task.py +2 -0
  101. ansible/plugins/action/__init__.py +2 -2
  102. ansible/plugins/action/assemble.py +2 -1
  103. ansible/plugins/action/assert.py +2 -2
  104. ansible/plugins/action/fetch.py +3 -3
  105. ansible/plugins/action/script.py +5 -4
  106. ansible/plugins/action/template.py +9 -3
  107. ansible/plugins/cache/__init__.py +17 -19
  108. ansible/plugins/callback/__init__.py +77 -87
  109. ansible/plugins/callback/default.py +0 -3
  110. ansible/plugins/callback/junit.py +0 -6
  111. ansible/plugins/callback/tree.py +5 -5
  112. ansible/plugins/connection/local.py +4 -4
  113. ansible/plugins/connection/paramiko_ssh.py +5 -5
  114. ansible/plugins/connection/ssh.py +9 -7
  115. ansible/plugins/connection/winrm.py +1 -1
  116. ansible/plugins/filter/core.py +19 -21
  117. ansible/plugins/filter/encryption.py +10 -2
  118. ansible/plugins/filter/pow.yml +1 -1
  119. ansible/plugins/filter/root.yml +1 -1
  120. ansible/plugins/filter/strftime.yml +3 -3
  121. ansible/plugins/filter/to_uuid.yml +1 -1
  122. ansible/plugins/inventory/script.py +1 -1
  123. ansible/plugins/list.py +5 -4
  124. ansible/plugins/loader.py +5 -0
  125. ansible/plugins/lookup/password.py +4 -6
  126. ansible/plugins/lookup/template.py +9 -4
  127. ansible/plugins/shell/powershell.py +3 -2
  128. ansible/plugins/shell/sh.py +3 -2
  129. ansible/plugins/strategy/__init__.py +3 -3
  130. ansible/plugins/test/core.py +2 -2
  131. ansible/release.py +1 -1
  132. ansible/template/__init__.py +9 -53
  133. ansible/utils/collection_loader/_collection_finder.py +3 -3
  134. ansible/utils/display.py +38 -37
  135. ansible/utils/galaxy.py +2 -2
  136. ansible/utils/hashing.py +6 -7
  137. ansible/utils/path.py +6 -8
  138. ansible/utils/py3compat.py +2 -1
  139. ansible/utils/ssh_functions.py +3 -2
  140. ansible/utils/vars.py +4 -1
  141. ansible/vars/manager.py +6 -3
  142. ansible/vars/plugins.py +3 -3
  143. ansible/vars/reserved.py +6 -4
  144. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/METADATA +1 -1
  145. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/RECORD +184 -173
  146. ansible_test/_internal/__init__.py +5 -0
  147. ansible_test/_internal/ansible_util.py +1 -1
  148. ansible_test/_internal/classification/python.py +6 -0
  149. ansible_test/_internal/cli/commands/__init__.py +0 -5
  150. ansible_test/_internal/cli/environments.py +51 -5
  151. ansible_test/_internal/commands/coverage/__init__.py +1 -1
  152. ansible_test/_internal/commands/integration/__init__.py +18 -5
  153. ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
  154. ansible_test/_internal/commands/integration/coverage.py +7 -2
  155. ansible_test/_internal/commands/sanity/__init__.py +3 -1
  156. ansible_test/_internal/commands/sanity/integration_aliases.py +11 -0
  157. ansible_test/_internal/commands/shell/__init__.py +43 -4
  158. ansible_test/_internal/commands/units/__init__.py +4 -1
  159. ansible_test/_internal/config.py +21 -13
  160. ansible_test/_internal/debugging.py +166 -0
  161. ansible_test/_internal/delegation.py +21 -13
  162. ansible_test/_internal/host_profiles.py +259 -16
  163. ansible_test/_internal/inventory.py +4 -0
  164. ansible_test/_internal/metadata.py +94 -4
  165. ansible_test/_internal/processes.py +80 -0
  166. ansible_test/_internal/provisioning.py +10 -4
  167. ansible_test/_internal/python_requirements.py +27 -0
  168. ansible_test/_internal/ssh.py +1 -5
  169. ansible_test/_internal/target.py +8 -0
  170. ansible_test/_internal/thread.py +2 -1
  171. ansible_test/_internal/timeout.py +1 -1
  172. ansible_test/_internal/util.py +20 -12
  173. ansible_test/_internal/util_common.py +13 -3
  174. ansible_test/_util/target/injector/python.py +8 -0
  175. ansible_test/_util/target/setup/requirements.py +3 -9
  176. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/WHEEL +0 -0
  177. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/entry_points.txt +0 -0
  178. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/COPYING +0 -0
  179. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  180. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  181. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  182. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  183. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  184. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/top_level.txt +0 -0
@@ -286,7 +286,7 @@ def is_systemd_managed(module):
286
286
  with open('/proc/1/comm', 'r') as init_proc:
287
287
  init = init_proc.readline().strip()
288
288
  return init == 'systemd'
289
- except IOError:
289
+ except OSError:
290
290
  # If comm doesn't exist, old kernel, no systemd
291
291
  return False
292
292
 
@@ -580,7 +580,7 @@ def get_ca_certs(cafile=None, capath=None):
580
580
  cadata[b_der] = None
581
581
  except Exception:
582
582
  continue
583
- except (OSError, IOError):
583
+ except OSError:
584
584
  pass
585
585
 
586
586
  # paths_checked isn't used any more, but is kept just for ease of debugging
@@ -694,7 +694,7 @@ def _configure_auth(url, url_username, url_password, use_gssapi, force_basic_aut
694
694
  try:
695
695
  rc = netrc.netrc(os.environ.get('NETRC'))
696
696
  login = rc.authenticators(parsed.hostname)
697
- except IOError:
697
+ except OSError:
698
698
  login = None
699
699
 
700
700
  if login:
@@ -1155,7 +1155,7 @@ def url_argument_spec():
1155
1155
 
1156
1156
  def url_redirect_argument_spec():
1157
1157
  """
1158
- Creates an addition arugment spec to `url_argument_spec`
1158
+ Creates an addition argument spec to `url_argument_spec`
1159
1159
  for `follow_redirects` argument
1160
1160
  """
1161
1161
  return dict(
@@ -1303,8 +1303,8 @@ def fetch_url(module, url, data=None, headers=None, method=None,
1303
1303
  except urllib.error.URLError as e:
1304
1304
  code = int(getattr(e, 'code', -1))
1305
1305
  info.update(dict(msg="Request failed: %s" % to_native(e), status=code))
1306
- except socket.error as e:
1307
- info.update(dict(msg="Connection failure: %s" % to_native(e), status=-1))
1306
+ except OSError as ex:
1307
+ info.update(dict(msg=f"Connection failure: {ex}", status=-1))
1308
1308
  except http.client.BadStatusLine as e:
1309
1309
  info.update(dict(msg="Connection failure: connection was closed before a valid response was received: %s" % to_native(e.line), status=-1))
1310
1310
  except Exception as ex:
ansible/modules/apt.py CHANGED
@@ -855,6 +855,7 @@ def install_deb(
855
855
  allow_downgrade,
856
856
  allow_change_held_packages,
857
857
  dpkg_options,
858
+ lock_timeout,
858
859
  ):
859
860
  changed = False
860
861
  deps_to_install = []
@@ -903,13 +904,14 @@ def install_deb(
903
904
  # install the deps through apt
904
905
  retvals = {}
905
906
  if deps_to_install:
907
+ install_dpkg_options = f"{expand_dpkg_options(dpkg_options)} -o DPkg::Lock::Timeout={lock_timeout}"
906
908
  (success, retvals) = install(m=m, pkgspec=deps_to_install, cache=cache,
907
909
  install_recommends=install_recommends,
908
910
  fail_on_autoremove=fail_on_autoremove,
909
911
  allow_unauthenticated=allow_unauthenticated,
910
912
  allow_downgrade=allow_downgrade,
911
913
  allow_change_held_packages=allow_change_held_packages,
912
- dpkg_options=expand_dpkg_options(dpkg_options))
914
+ dpkg_options=install_dpkg_options)
913
915
  if not success:
914
916
  m.fail_json(**retvals)
915
917
  changed = retvals.get('changed', False)
@@ -1269,7 +1271,7 @@ def main():
1269
1271
 
1270
1272
  p = module.params
1271
1273
  install_recommends = p['install_recommends']
1272
- dpkg_options = expand_dpkg_options(p['dpkg_options'])
1274
+ dpkg_options = f"{expand_dpkg_options(p['dpkg_options'])} -o DPkg::Lock::Timeout={p['lock_timeout']}"
1273
1275
 
1274
1276
  if not HAS_PYTHON_APT:
1275
1277
  # This interpreter can't see the apt Python library- we'll do the following to try and fix that:
@@ -1470,7 +1472,11 @@ def main():
1470
1472
  allow_unauthenticated=allow_unauthenticated,
1471
1473
  allow_change_held_packages=allow_change_held_packages,
1472
1474
  allow_downgrade=allow_downgrade,
1473
- force=force_yes, fail_on_autoremove=fail_on_autoremove, dpkg_options=p['dpkg_options'])
1475
+ force=force_yes,
1476
+ fail_on_autoremove=fail_on_autoremove,
1477
+ dpkg_options=p['dpkg_options'],
1478
+ lock_timeout=p['lock_timeout']
1479
+ )
1474
1480
 
1475
1481
  unfiltered_packages = p['package'] or ()
1476
1482
  packages = [package.strip() for package in unfiltered_packages if package != '*']
@@ -333,8 +333,8 @@ class SourcesList(object):
333
333
 
334
334
  try:
335
335
  fd, tmp_path = tempfile.mkstemp(prefix=".%s-" % fn, dir=d)
336
- except (OSError, IOError) as e:
337
- self.module.fail_json(msg='Unable to create temp file at "%s" for apt source: %s' % (d, to_native(e)))
336
+ except OSError as ex:
337
+ raise Exception(f'Unable to create temp file at {d!r} for apt source.') from ex
338
338
 
339
339
  f = os.fdopen(fd, 'w')
340
340
  for n, valid, enabled, source, comment in sources:
@@ -350,8 +350,8 @@ class SourcesList(object):
350
350
 
351
351
  try:
352
352
  f.write(line)
353
- except IOError as ex:
354
- self.module.fail_json(msg="Failed to write to file %s: %s" % (tmp_path, to_native(ex)))
353
+ except OSError as ex:
354
+ raise Exception(f"Failed to write to file {tmp_path!r}.") from ex
355
355
  if filename in self.files_mapping:
356
356
  # Write to symlink target instead of replacing symlink as a normal file
357
357
  self.module.atomic_move(tmp_path, self.files_mapping[filename])
@@ -507,8 +507,8 @@ class UbuntuSourcesList(SourcesList):
507
507
  if os.path.exists(key_file):
508
508
  try:
509
509
  rc, out, err = self.module.run_command([self.gpg_bin, '--list-packets', key_file])
510
- except (IOError, OSError) as e:
511
- self.debug("Could check key against file %s: %s" % (key_file, to_native(e)))
510
+ except OSError as ex:
511
+ self.debug(f"Could check key against file {key_file!r}: {ex}")
512
512
  continue
513
513
 
514
514
  if key_fingerprint in out:
@@ -557,8 +557,8 @@ class UbuntuSourcesList(SourcesList):
557
557
  with open(keyfile, 'wb') as f:
558
558
  f.write(stdout)
559
559
  self.module.log('Added repo key "%s" for apt to file "%s"' % (info['signing_key_fingerprint'], keyfile))
560
- except (OSError, IOError) as e:
561
- self.module.fail_json(msg='Unable to add required signing key for%s ', rc=rc, stderr=stderr, error=to_native(e))
560
+ except OSError as ex:
561
+ self.module.fail_json(msg='Unable to add required signing key.', rc=rc, stderr=stderr, error=str(ex), exception=ex)
562
562
 
563
563
  # apt source file
564
564
  file = file or self._suggest_filename('%s_%s' % (line, self.codename))
@@ -752,9 +752,9 @@ def main():
752
752
  )
753
753
  module.fail_json(msg=msg)
754
754
 
755
- except (OSError, IOError) as ex:
755
+ except OSError as ex:
756
756
  revert_sources_list(sources_before, sources_after, sourceslist_before)
757
- module.fail_json(msg=to_native(ex))
757
+ raise
758
758
 
759
759
  module.exit_json(changed=changed, repo=repo, sources_added=sources_added, sources_removed=sources_removed, state=state, diff=diff)
760
760
 
@@ -80,7 +80,7 @@ attributes:
80
80
  bypass_host_loop:
81
81
  support: none
82
82
  check_mode:
83
- support: none
83
+ support: full
84
84
  diff_mode:
85
85
  support: full
86
86
  platform:
@@ -186,10 +186,10 @@ def cleanup(module, path, result=None):
186
186
  if os.path.exists(path):
187
187
  try:
188
188
  os.remove(path)
189
- except (IOError, OSError) as e:
189
+ except OSError as ex:
190
190
  # don't error on possible race conditions, but keep warning
191
191
  if result is not None:
192
- module.warn('Unable to remove temp file (%s): %s' % (path, to_native(e)))
192
+ module.error_as_warning(f'Unable to remove temp file {path!r}.', exception=ex)
193
193
 
194
194
 
195
195
  def main():
@@ -212,6 +212,7 @@ def main():
212
212
  decrypt=dict(type='bool', default=True),
213
213
  ),
214
214
  add_file_common_args=True,
215
+ supports_check_mode=True,
215
216
  )
216
217
 
217
218
  changed = False
@@ -266,12 +267,13 @@ def main():
266
267
  if backup and dest_hash is not None:
267
268
  result['backup_file'] = module.backup_local(dest)
268
269
 
269
- module.atomic_move(path, dest, unsafe_writes=module.params['unsafe_writes'])
270
+ if not module.check_mode:
271
+ module.atomic_move(path, dest, unsafe_writes=module.params['unsafe_writes'])
270
272
  changed = True
271
273
 
272
274
  cleanup(module, path, result)
273
275
 
274
- # handle file permissions
276
+ # handle file permissions (check mode aware)
275
277
  file_args = module.load_file_common_arguments(module.params)
276
278
  result['changed'] = module.set_fs_attributes_if_different(file_args, changed)
277
279
 
@@ -6,7 +6,6 @@
6
6
  from __future__ import annotations
7
7
 
8
8
 
9
- import errno
10
9
  import json
11
10
  import shlex
12
11
  import shutil
@@ -122,24 +121,14 @@ def _get_interpreter(module_path):
122
121
  return head[2:head.index(b'\n')].strip().split(b' ')
123
122
 
124
123
 
125
- def _make_temp_dir(path):
126
- # TODO: Add checks for permissions on path.
127
- try:
128
- os.makedirs(path)
129
- except OSError as e:
130
- if e.errno != errno.EEXIST:
131
- raise
132
-
133
-
134
124
  def jwrite(info):
135
-
136
125
  jobfile = job_path + ".tmp"
137
126
  tjob = open(jobfile, "w")
138
127
  try:
139
128
  tjob.write(json.dumps(info))
140
- except (IOError, OSError) as e:
141
- notice('failed to write to %s: %s' % (jobfile, str(e)))
142
- raise e
129
+ except OSError as ex:
130
+ notice(f'failed to write to {jobfile!r}: {ex}')
131
+ raise
143
132
  finally:
144
133
  tjob.close()
145
134
  os.rename(jobfile, job_path)
@@ -200,7 +189,7 @@ def _run_module(wrapped_cmd, jid):
200
189
  result['stderr'] = stderr
201
190
  jwrite(result)
202
191
 
203
- except (OSError, IOError):
192
+ except OSError:
204
193
  e = sys.exc_info()[1]
205
194
  result = {
206
195
  "failed": True,
@@ -212,7 +201,7 @@ def _run_module(wrapped_cmd, jid):
212
201
  result['ansible_job_id'] = jid
213
202
  jwrite(result)
214
203
 
215
- except (ValueError, Exception):
204
+ except Exception:
216
205
  result = {
217
206
  "failed": True,
218
207
  "cmd": wrapped_cmd,
@@ -257,7 +246,8 @@ def main():
257
246
  job_path = os.path.join(jobdir, jid)
258
247
 
259
248
  try:
260
- _make_temp_dir(jobdir)
249
+ # TODO: Add checks for permissions on path.
250
+ os.makedirs(jobdir, exist_ok=True)
261
251
  except Exception as e:
262
252
  end({
263
253
  "failed": True,
@@ -299,9 +299,9 @@ def main():
299
299
 
300
300
  try:
301
301
  os.chdir(chdir)
302
- except (IOError, OSError) as e:
303
- r['msg'] = 'Unable to change directory before execution: %s' % to_text(e)
304
- module.fail_json(**r)
302
+ except OSError as ex:
303
+ r['msg'] = 'Unable to change directory before execution.'
304
+ module.fail_json(**r, exception=ex)
305
305
 
306
306
  # check_mode partial support, since it only really works in checking creates/removes
307
307
  if module.check_mode:
ansible/modules/copy.py CHANGED
@@ -522,8 +522,8 @@ def main():
522
522
  if os.path.isfile(src):
523
523
  try:
524
524
  checksum_src = module.sha1(src)
525
- except (OSError, IOError) as e:
526
- module.warn("Unable to calculate src checksum, assuming change: %s" % to_native(e))
525
+ except OSError as ex:
526
+ module.error_as_warning("Unable to calculate src checksum, assuming change.", exception=ex)
527
527
  try:
528
528
  # Backwards compat only. This will be None in FIPS mode
529
529
  md5sum_src = module.md5(src)
@@ -636,8 +636,8 @@ def main():
636
636
  # at this point we should always have tmp file
637
637
  module.atomic_move(b_mysrc, dest, unsafe_writes=module.params['unsafe_writes'], keep_dest_attrs=not remote_src)
638
638
 
639
- except (IOError, OSError):
640
- module.fail_json(msg=f"Failed to copy {src!r} to {dest!r}.")
639
+ except OSError as ex:
640
+ raise Exception(f"Failed to copy {src!r} to {dest!r}.") from ex
641
641
  changed = True
642
642
 
643
643
  # If neither have checksums, both src and dest are directories.
ansible/modules/cron.py CHANGED
@@ -271,7 +271,7 @@ class CronTab(object):
271
271
  with open(self.b_cron_file, 'rb') as f:
272
272
  self.n_existing = to_native(f.read(), errors='surrogate_or_strict')
273
273
  self.lines = self.n_existing.splitlines()
274
- except IOError:
274
+ except OSError:
275
275
  # cron file does not exist
276
276
  return
277
277
  except Exception:
ansible/modules/expect.py CHANGED
@@ -218,7 +218,7 @@ def main():
218
218
  rc=0
219
219
  )
220
220
 
221
- startd = datetime.datetime.now()
221
+ start_date = datetime.datetime.now()
222
222
 
223
223
  try:
224
224
  try:
@@ -246,8 +246,8 @@ def main():
246
246
  except pexpect.ExceptionPexpect as e:
247
247
  module.fail_json(msg='%s' % to_native(e))
248
248
 
249
- endd = datetime.datetime.now()
250
- delta = endd - startd
249
+ end_date = datetime.datetime.now()
250
+ delta = end_date - start_date
251
251
 
252
252
  if b_out is None:
253
253
  b_out = b''
@@ -256,8 +256,8 @@ def main():
256
256
  cmd=args,
257
257
  stdout=to_native(b_out).rstrip('\r\n'),
258
258
  rc=rc,
259
- start=str(startd),
260
- end=str(endd),
259
+ start=str(start_date),
260
+ end=str(end_date),
261
261
  delta=str(delta),
262
262
  changed=True,
263
263
  )
ansible/modules/file.py CHANGED
@@ -323,11 +323,8 @@ def get_state(path):
323
323
  return 'file'
324
324
 
325
325
  return 'absent'
326
- except OSError as e:
327
- if e.errno == errno.ENOENT: # It may already have been removed
328
- return 'absent'
329
- else:
330
- raise
326
+ except FileNotFoundError:
327
+ return 'absent'
331
328
 
332
329
 
333
330
  # This should be moved into the common file utilities
@@ -527,12 +524,14 @@ def ensure_absent(path):
527
524
  else:
528
525
  try:
529
526
  os.unlink(b_path)
530
- except OSError as e:
531
- if e.errno != errno.ENOENT: # It may already have been removed
532
- module.fail_json(
533
- msg=f"unlinking failed: {to_native(e)}",
534
- path=path
535
- )
527
+ except FileNotFoundError:
528
+ pass
529
+ except OSError as ex:
530
+ module.fail_json(
531
+ msg="Unlinking failed.",
532
+ path=path,
533
+ exception=ex,
534
+ )
536
535
 
537
536
  result.update({'path': path, 'changed': True, 'diff': diff, 'state': 'absent'})
538
537
  else:
@@ -560,10 +559,11 @@ def execute_touch(path, follow, timestamps):
560
559
  try:
561
560
  open(b_path, 'wb').close()
562
561
  changed = True
563
- except (OSError, IOError) as e:
562
+ except OSError as ex:
564
563
  module.fail_json(
565
- msg=f"Error, could not touch target: {to_native(e, nonstring='simplerepr')}",
566
- path=path
564
+ msg="Error, could not touch target.",
565
+ path=path,
566
+ exception=ex,
567
567
  )
568
568
  # Update the attributes on the file
569
569
  diff = initial_diff(path, 'touch', prev_state)
@@ -894,9 +894,8 @@ def ensure_hardlink(path, src, follow, force, timestamps):
894
894
  if os.path.exists(b_path):
895
895
  try:
896
896
  os.unlink(b_path)
897
- except OSError as e:
898
- if e.errno != errno.ENOENT: # It may already have been removed
899
- raise
897
+ except FileNotFoundError:
898
+ pass
900
899
  os.link(b_src, b_tmppath)
901
900
  os.rename(b_tmppath, b_path)
902
901
  except OSError as e:
ansible/modules/find.py CHANGED
@@ -571,9 +571,9 @@ def main():
571
571
 
572
572
  try:
573
573
  st = os.lstat(fsname)
574
- except (IOError, OSError) as e:
575
- module.warn("Skipped entry '%s' due to this access issue: %s\n" % (fsname, to_text(e)))
576
- skipped[fsname] = to_text(e)
574
+ except OSError as ex:
575
+ module.error_as_warning(f"Skipped entry {fsname!r} due to access issue.", exception=ex)
576
+ skipped[fsname] = str(ex)
577
577
  has_warnings = True
578
578
  continue
579
579
 
@@ -436,6 +436,23 @@ def url_get(module, url, dest, use_proxy, last_mod_time, force, timeout=10, head
436
436
  module.fail_json(msg="failed to create temporary content file: %s" % to_native(e), elapsed=elapsed)
437
437
  f.close()
438
438
  rsp.close()
439
+
440
+ # Since shutil.copyfileobj() will read from HTTPResponse in chunks, HTTPResponse.read() will not recognize
441
+ # if the entire content-length of data was not read. We need to do that validation here, unless a 'chunked'
442
+ # transfer-encoding was used, in which case we will not know content-length because it will not be returned.
443
+ # But in that case, HTTPResponse will behave correctly and recognize an IncompleteRead.
444
+
445
+ is_gzip = info.get('content-encoding') == 'gzip'
446
+
447
+ if not module.check_mode and 'content-length' in info:
448
+ # If data is decompressed, then content-length won't match the amount of data we've read, so skip.
449
+ if not is_gzip or (is_gzip and not decompress):
450
+ st = os.stat(tempname)
451
+ cl = int(info['content-length'])
452
+ if st.st_size != cl:
453
+ diff = cl - st.st_size
454
+ module.fail_json(msg=f'Incomplete read, ({rsp.length=}, {cl=}, {st.st_size=}) failed to read remaining {diff} bytes')
455
+
439
456
  return tempname, info
440
457
 
441
458
 
ansible/modules/git.py CHANGED
@@ -357,11 +357,11 @@ def relocate_repo(module, result, repo_dir, old_repo_dir, worktree_dir):
357
357
  dot_git_file.write('gitdir: %s' % repo_dir)
358
358
  result['git_dir_before'] = old_repo_dir
359
359
  result['git_dir_now'] = repo_dir
360
- except (IOError, OSError) as err:
360
+ except OSError as ex:
361
361
  # if we already moved the .git dir, roll it back
362
362
  if os.path.exists(repo_dir):
363
363
  shutil.move(repo_dir, old_repo_dir)
364
- module.fail_json(msg=u'Unable to move git dir. %s' % to_text(err))
364
+ raise Exception('Unable to move git dir.') from ex
365
365
 
366
366
 
367
367
  def head_splitter(headfile, remote, module=None, fail_on_error=False):
@@ -439,7 +439,7 @@ def write_ssh_wrapper(module):
439
439
  fd, wrapper_path = tempfile.mkstemp(prefix=module.tmpdir + '/')
440
440
  else:
441
441
  raise OSError
442
- except (IOError, OSError):
442
+ except OSError:
443
443
  fd, wrapper_path = tempfile.mkstemp()
444
444
 
445
445
  # use existing git_ssh/ssh_command, fallback to 'ssh'
@@ -824,13 +824,14 @@ def get_head_branch(git_path, module, dest, remote, bare=False):
824
824
  """
825
825
  try:
826
826
  repo_path = get_repo_path(dest, bare)
827
- except (IOError, ValueError) as err:
827
+ except (OSError, ValueError) as ex:
828
828
  # No repo path found
829
829
  # ``.git`` file does not have a valid format for detached Git dir.
830
830
  module.fail_json(
831
831
  msg='Current repo does not have a valid reference to a '
832
832
  'separate Git dir or it refers to the invalid path',
833
- details=to_text(err),
833
+ details=str(ex),
834
+ exception=ex,
834
835
  )
835
836
  # Read .git/HEAD for the name of the branch.
836
837
  # If we're in a detached HEAD state, look up the branch associated with
@@ -1290,13 +1291,14 @@ def main():
1290
1291
  if not module.check_mode:
1291
1292
  relocate_repo(module, result, separate_git_dir, repo_path, dest)
1292
1293
  repo_path = separate_git_dir
1293
- except (IOError, ValueError) as err:
1294
+ except (OSError, ValueError) as ex:
1294
1295
  # No repo path found
1295
1296
  # ``.git`` file does not have a valid format for detached Git dir.
1296
1297
  module.fail_json(
1297
1298
  msg='Current repo does not have a valid reference to a '
1298
1299
  'separate Git dir or it refers to the invalid path',
1299
- details=to_text(err),
1300
+ details=str(ex),
1301
+ exception=ex,
1300
1302
  )
1301
1303
  gitconfig = os.path.join(repo_path, 'config')
1302
1304
 
@@ -608,8 +608,8 @@ class Hostname(object):
608
608
  self.use = module.params['use']
609
609
 
610
610
  if self.use is not None:
611
- strat = globals()['%sStrategy' % STRATS[self.use]]
612
- self.strategy = strat(module)
611
+ strategy = globals()['%sStrategy' % STRATS[self.use]]
612
+ self.strategy = strategy(module)
613
613
  elif platform.system() == 'Linux' and ServiceMgrFactCollector.is_systemd_managed(module):
614
614
  # This is Linux and systemd is active
615
615
  self.strategy = SystemdStrategy(module)
@@ -102,7 +102,6 @@ EXAMPLES = r"""
102
102
 
103
103
  import base64
104
104
  import copy
105
- import errno
106
105
  import hashlib
107
106
  import hmac
108
107
  import os
@@ -169,11 +168,10 @@ def enforce_state(module, params):
169
168
  if replace_or_add or found != (state == "present"):
170
169
  try:
171
170
  inf = open(path, "r")
172
- except IOError as e:
173
- if e.errno == errno.ENOENT:
174
- inf = None
175
- else:
176
- module.fail_json(msg="Failed to read %s: %s" % (path, str(e)))
171
+ except FileNotFoundError:
172
+ inf = None
173
+ except OSError as ex:
174
+ raise Exception(f"Failed to read {path!r}.") from ex
177
175
  try:
178
176
  with tempfile.NamedTemporaryFile(mode='w+', dir=os.path.dirname(path), delete=False) as outf:
179
177
  if inf is not None:
@@ -184,8 +182,8 @@ def enforce_state(module, params):
184
182
  inf.close()
185
183
  if state == 'present':
186
184
  outf.write(key)
187
- except (IOError, OSError) as e:
188
- module.fail_json(msg="Failed to write to file %s: %s" % (path, to_native(e)))
185
+ except OSError as ex:
186
+ raise Exception(f"Failed to write to file {path!r}.") from ex
189
187
  else:
190
188
  module.atomic_move(outf.name, path)
191
189
 
@@ -220,9 +218,8 @@ def sanity_check(module, host, key, sshkeygen):
220
218
  try:
221
219
  outf.write(key)
222
220
  outf.flush()
223
- except IOError as e:
224
- module.fail_json(msg="Failed to write to temporary file %s: %s" %
225
- (outf.name, to_native(e)))
221
+ except OSError as ex:
222
+ raise Exception(f"Failed to write to temporary file {outf.name!r}.") from ex
226
223
 
227
224
  sshkeygen_command = [sshkeygen, '-F', host, '-f', outf.name]
228
225
  rc, stdout, stderr = module.run_command(sshkeygen_command)
@@ -337,9 +334,10 @@ def compute_diff(path, found_line, replace_or_add, state, key):
337
334
  }
338
335
  try:
339
336
  inf = open(path, "r")
340
- except IOError as e:
341
- if e.errno == errno.ENOENT:
342
- diff['before_header'] = '/dev/null'
337
+ except FileNotFoundError:
338
+ diff['before_header'] = '/dev/null'
339
+ except OSError:
340
+ pass
343
341
  else:
344
342
  diff['before'] = inf.read()
345
343
  inf.close()
@@ -85,4 +85,10 @@ EXAMPLES = """
85
85
  - httpd
86
86
  - mariadb-server
87
87
  state: latest
88
+
89
+ - name: Use the dnf package manager to install httpd
90
+ ansible.builtin.package:
91
+ name: httpd
92
+ state: present
93
+ use: dnf
88
94
  """
ansible/modules/pip.py CHANGED
@@ -60,7 +60,7 @@ options:
60
60
  virtualenv_python:
61
61
  description:
62
62
  - The Python executable used for creating the virtual environment.
63
- For example V(python3.12), V(python2.7). When not specified, the
63
+ For example V(python3.13). When not specified, the
64
64
  Python version used to run the ansible module is used. This parameter
65
65
  should not be used when O(virtualenv_command) is using V(pyvenv) or
66
66
  the C(-m venv) module.
@@ -93,8 +93,8 @@ options:
93
93
  description:
94
94
  - The explicit executable or pathname for the C(pip) executable,
95
95
  if different from the Ansible Python interpreter. For
96
- example V(pip3.3), if there are both Python 2.7 and 3.3 installations
97
- in the system and you want to run pip for the Python 3.3 installation.
96
+ example V(pip3.13), if there are multiple Python installations
97
+ in the system and you want to run pip for the Python 3.13 installation.
98
98
  - Mutually exclusive with O(virtualenv) (added in 2.1).
99
99
  - Does not affect the Ansible Python interpreter.
100
100
  - The C(setuptools) package must be installed for both the Ansible Python interpreter
@@ -134,7 +134,7 @@ notes:
134
134
  the virtualenv needs to be created.
135
135
  - Although it executes using the Ansible Python interpreter, the pip module shells out to
136
136
  run the actual pip command, so it can use any pip version you specify with O(executable).
137
- By default, it uses the pip version for the Ansible Python interpreter. For example, pip3 on python 3, and pip2 or pip on python 2.
137
+ By default, it uses the pip version for the Ansible Python interpreter.
138
138
  - The interpreter used by Ansible
139
139
  (see R(ansible_python_interpreter, ansible_python_interpreter))
140
140
  requires the setuptools package, regardless of the version of pip set with
@@ -197,11 +197,11 @@ EXAMPLES = """
197
197
  virtualenv: /my_app/venv
198
198
  virtualenv_site_packages: yes
199
199
 
200
- - name: Install bottle into the specified (virtualenv), using Python 2.7
200
+ - name: Install bottle into the specified (virtualenv), using Python 3.13
201
201
  ansible.builtin.pip:
202
202
  name: bottle
203
203
  virtualenv: /my_app/venv
204
- virtualenv_command: virtualenv-2.7
204
+ virtualenv_command: virtualenv-3.13
205
205
 
206
206
  - name: Install bottle within a user home directory
207
207
  ansible.builtin.pip:
@@ -227,10 +227,10 @@ EXAMPLES = """
227
227
  requirements: /my_app/requirements.txt
228
228
  extra_args: "--no-index --find-links=file:///my_downloaded_packages_dir"
229
229
 
230
- - name: Install bottle for Python 3.3 specifically, using the 'pip3.3' executable
230
+ - name: Install bottle for Python 3.13 specifically, using the 'pip3.13' executable
231
231
  ansible.builtin.pip:
232
232
  name: bottle
233
- executable: pip3.3
233
+ executable: pip3.13
234
234
 
235
235
  - name: Install bottle, forcing reinstallation if it's already installed
236
236
  ansible.builtin.pip:
@@ -460,9 +460,7 @@ def _get_pip(module, env=None, executable=None):
460
460
  candidate_pip_basenames = (executable,)
461
461
  elif executable is None and env is None and _have_pip_module():
462
462
  # If no executable or virtualenv were specified, use the pip module for the current Python interpreter if available.
463
- # Use of `__main__` is required to support Python 2.6 since support for executing packages with `runpy` was added in Python 2.7.
464
- # Without it Python 2.6 gives the following error: pip is a package and cannot be directly executed
465
- pip = [sys.executable, '-m', 'pip.__main__']
463
+ pip = [sys.executable, '-m', 'pip']
466
464
 
467
465
  if pip is None:
468
466
  if env is None:
ansible/modules/raw.py CHANGED
@@ -73,8 +73,8 @@ author:
73
73
  """
74
74
 
75
75
  EXAMPLES = r"""
76
- - name: Bootstrap a host without python2 installed
77
- ansible.builtin.raw: dnf install -y python2 python2-dnf libselinux-python
76
+ - name: Bootstrap a host without Python installed
77
+ ansible.builtin.raw: dnf install -y python3 python3-libdnf
78
78
 
79
79
  - name: Run a command that uses non-posix shell-isms (in this example /bin/sh doesn't handle redirection and wildcards together but bash does)
80
80
  ansible.builtin.raw: cat < /tmp/*txt