ansible-core 2.19.4__py3-none-any.whl → 2.20.0__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.

Potentially problematic release.


This version of ansible-core might be problematic. Click here for more details.

Files changed (215) hide show
  1. ansible/_internal/__init__.py +1 -4
  2. ansible/_internal/_ansiballz/_builder.py +1 -3
  3. ansible/_internal/_collection_proxy.py +7 -9
  4. ansible/_internal/_json/__init__.py +3 -4
  5. ansible/_internal/_templating/_engine.py +1 -1
  6. ansible/_internal/_templating/_jinja_plugins.py +1 -2
  7. ansible/_internal/_wrapt.py +105 -301
  8. ansible/cli/__init__.py +11 -10
  9. ansible/cli/adhoc.py +1 -2
  10. ansible/cli/arguments/option_helpers.py +1 -1
  11. ansible/cli/config.py +5 -6
  12. ansible/cli/doc.py +67 -67
  13. ansible/cli/galaxy.py +15 -24
  14. ansible/cli/inventory.py +0 -1
  15. ansible/cli/playbook.py +0 -1
  16. ansible/cli/pull.py +0 -1
  17. ansible/cli/scripts/ansible_connection_cli_stub.py +1 -1
  18. ansible/config/base.yml +1 -25
  19. ansible/config/manager.py +0 -2
  20. ansible/executor/play_iterator.py +42 -20
  21. ansible/executor/playbook_executor.py +0 -9
  22. ansible/executor/task_executor.py +26 -18
  23. ansible/executor/task_queue_manager.py +1 -3
  24. ansible/galaxy/api.py +33 -80
  25. ansible/galaxy/collection/__init__.py +11 -21
  26. ansible/galaxy/dependency_resolution/__init__.py +10 -9
  27. ansible/galaxy/dependency_resolution/dataclasses.py +86 -70
  28. ansible/galaxy/dependency_resolution/providers.py +54 -134
  29. ansible/galaxy/dependency_resolution/versioning.py +2 -4
  30. ansible/galaxy/role.py +1 -33
  31. ansible/inventory/manager.py +2 -3
  32. ansible/keyword_desc.yml +0 -3
  33. ansible/module_utils/_internal/_datatag/__init__.py +2 -10
  34. ansible/module_utils/_internal/_no_six.py +86 -0
  35. ansible/module_utils/_text.py +28 -8
  36. ansible/module_utils/ansible_release.py +2 -2
  37. ansible/module_utils/basic.py +26 -23
  38. ansible/module_utils/common/_collections_compat.py +11 -2
  39. ansible/module_utils/common/collections.py +8 -3
  40. ansible/module_utils/common/dict_transformations.py +1 -2
  41. ansible/module_utils/common/network.py +4 -2
  42. ansible/module_utils/common/parameters.py +32 -41
  43. ansible/module_utils/common/text/converters.py +109 -23
  44. ansible/module_utils/common/text/formatters.py +6 -2
  45. ansible/module_utils/common/validation.py +11 -9
  46. ansible/module_utils/connection.py +8 -3
  47. ansible/module_utils/facts/hardware/linux.py +23 -7
  48. ansible/module_utils/facts/hardware/netbsd.py +1 -1
  49. ansible/module_utils/facts/hardware/sunos.py +2 -1
  50. ansible/module_utils/facts/packages.py +6 -2
  51. ansible/module_utils/facts/system/distribution.py +2 -1
  52. ansible/module_utils/facts/system/env.py +6 -3
  53. ansible/module_utils/facts/system/local.py +3 -1
  54. ansible/module_utils/parsing/convert_bool.py +6 -2
  55. ansible/module_utils/service.py +2 -3
  56. ansible/module_utils/six/__init__.py +11 -6
  57. ansible/module_utils/yumdnf.py +0 -5
  58. ansible/modules/apt.py +18 -13
  59. ansible/modules/apt_repository.py +1 -1
  60. ansible/modules/assemble.py +5 -9
  61. ansible/modules/blockinfile.py +39 -23
  62. ansible/modules/cron.py +26 -35
  63. ansible/modules/deb822_repository.py +83 -12
  64. ansible/modules/dnf.py +3 -7
  65. ansible/modules/dnf5.py +4 -6
  66. ansible/modules/expect.py +0 -3
  67. ansible/modules/find.py +1 -2
  68. ansible/modules/get_url.py +1 -1
  69. ansible/modules/git.py +4 -5
  70. ansible/modules/include_vars.py +1 -1
  71. ansible/modules/known_hosts.py +7 -1
  72. ansible/modules/lineinfile.py +71 -63
  73. ansible/modules/package_facts.py +1 -1
  74. ansible/modules/pip.py +8 -2
  75. ansible/modules/replace.py +6 -6
  76. ansible/modules/service.py +3 -4
  77. ansible/modules/stat.py +20 -0
  78. ansible/modules/uri.py +9 -10
  79. ansible/modules/user.py +1 -2
  80. ansible/modules/wait_for.py +2 -2
  81. ansible/modules/wait_for_connection.py +2 -1
  82. ansible/modules/yum_repository.py +1 -16
  83. ansible/parsing/dataloader.py +24 -31
  84. ansible/parsing/vault/__init__.py +1 -2
  85. ansible/playbook/base.py +8 -56
  86. ansible/playbook/block.py +0 -60
  87. ansible/playbook/collectionsearch.py +1 -2
  88. ansible/playbook/handler.py +1 -7
  89. ansible/playbook/helpers.py +0 -7
  90. ansible/playbook/included_file.py +1 -1
  91. ansible/playbook/play.py +102 -36
  92. ansible/playbook/play_context.py +4 -0
  93. ansible/playbook/role/__init__.py +10 -65
  94. ansible/playbook/role/definition.py +3 -4
  95. ansible/playbook/role/include.py +2 -3
  96. ansible/playbook/role/metadata.py +1 -12
  97. ansible/playbook/role/requirement.py +1 -2
  98. ansible/playbook/role_include.py +1 -2
  99. ansible/playbook/taggable.py +16 -5
  100. ansible/playbook/task.py +11 -50
  101. ansible/plugins/action/__init__.py +20 -19
  102. ansible/plugins/action/add_host.py +1 -2
  103. ansible/plugins/action/fetch.py +3 -5
  104. ansible/plugins/action/group_by.py +1 -2
  105. ansible/plugins/action/include_vars.py +20 -22
  106. ansible/plugins/action/script.py +1 -3
  107. ansible/plugins/action/template.py +1 -2
  108. ansible/plugins/action/uri.py +4 -2
  109. ansible/plugins/cache/__init__.py +1 -0
  110. ansible/plugins/callback/__init__.py +13 -6
  111. ansible/plugins/connection/__init__.py +3 -7
  112. ansible/plugins/connection/local.py +2 -3
  113. ansible/plugins/connection/psrp.py +0 -2
  114. ansible/plugins/connection/ssh.py +2 -7
  115. ansible/plugins/connection/winrm.py +0 -2
  116. ansible/plugins/doc_fragments/result_format_callback.py +15 -0
  117. ansible/plugins/filter/core.py +4 -5
  118. ansible/plugins/filter/encryption.py +3 -27
  119. ansible/plugins/filter/mathstuff.py +1 -2
  120. ansible/plugins/filter/to_nice_yaml.yml +31 -3
  121. ansible/plugins/filter/to_yaml.yml +29 -12
  122. ansible/plugins/inventory/__init__.py +1 -2
  123. ansible/plugins/inventory/toml.py +3 -6
  124. ansible/plugins/inventory/yaml.py +1 -2
  125. ansible/plugins/loader.py +3 -4
  126. ansible/plugins/lookup/password.py +1 -2
  127. ansible/plugins/lookup/subelements.py +2 -3
  128. ansible/plugins/lookup/url.py +1 -1
  129. ansible/plugins/lookup/varnames.py +1 -2
  130. ansible/plugins/shell/__init__.py +9 -4
  131. ansible/plugins/shell/powershell.py +8 -24
  132. ansible/plugins/strategy/__init__.py +5 -2
  133. ansible/plugins/test/core.py +4 -1
  134. ansible/plugins/test/falsy.yml +1 -1
  135. ansible/plugins/test/regex.yml +18 -6
  136. ansible/plugins/test/truthy.yml +1 -1
  137. ansible/release.py +2 -2
  138. ansible/template/__init__.py +3 -7
  139. ansible/utils/collection_loader/_collection_config.py +5 -0
  140. ansible/utils/collection_loader/_collection_finder.py +11 -14
  141. ansible/utils/context_objects.py +7 -4
  142. ansible/utils/display.py +7 -6
  143. ansible/utils/encrypt.py +0 -5
  144. ansible/utils/helpers.py +6 -2
  145. ansible/utils/jsonrpc.py +7 -3
  146. ansible/utils/plugin_docs.py +49 -38
  147. ansible/utils/ssh_functions.py +0 -19
  148. ansible/utils/unsafe_proxy.py +7 -7
  149. ansible/vars/clean.py +2 -3
  150. ansible/vars/manager.py +28 -22
  151. ansible/vars/plugins.py +1 -31
  152. {ansible_core-2.19.4.dist-info → ansible_core-2.20.0.dist-info}/METADATA +4 -4
  153. {ansible_core-2.19.4.dist-info → ansible_core-2.20.0.dist-info}/RECORD +213 -214
  154. ansible_test/_data/completion/docker.txt +7 -7
  155. ansible_test/_data/completion/network.txt +0 -1
  156. ansible_test/_data/completion/remote.txt +4 -4
  157. ansible_test/_data/requirements/ansible-test.txt +1 -1
  158. ansible_test/_data/requirements/ansible.txt +1 -1
  159. ansible_test/_data/requirements/sanity.ansible-doc.txt +2 -2
  160. ansible_test/_data/requirements/sanity.changelog.txt +2 -2
  161. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
  162. ansible_test/_data/requirements/sanity.import.txt +1 -1
  163. ansible_test/_data/requirements/sanity.integration-aliases.txt +1 -1
  164. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  165. ansible_test/_data/requirements/sanity.pylint.txt +6 -6
  166. ansible_test/_data/requirements/sanity.runtime-metadata.txt +1 -1
  167. ansible_test/_data/requirements/sanity.validate-modules.txt +2 -2
  168. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  169. ansible_test/_internal/cache.py +2 -5
  170. ansible_test/_internal/cli/compat.py +1 -1
  171. ansible_test/_internal/commands/coverage/combine.py +1 -3
  172. ansible_test/_internal/commands/integration/__init__.py +3 -7
  173. ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
  174. ansible_test/_internal/commands/integration/coverage.py +1 -3
  175. ansible_test/_internal/commands/integration/filters.py +5 -10
  176. ansible_test/_internal/commands/sanity/pylint.py +11 -0
  177. ansible_test/_internal/commands/sanity/validate_modules.py +1 -5
  178. ansible_test/_internal/commands/units/__init__.py +1 -13
  179. ansible_test/_internal/compat/packaging.py +2 -2
  180. ansible_test/_internal/compat/yaml.py +2 -2
  181. ansible_test/_internal/completion.py +2 -5
  182. ansible_test/_internal/config.py +2 -7
  183. ansible_test/_internal/coverage_util.py +1 -1
  184. ansible_test/_internal/delegation.py +2 -0
  185. ansible_test/_internal/docker_util.py +1 -1
  186. ansible_test/_internal/host_profiles.py +6 -11
  187. ansible_test/_internal/provider/__init__.py +2 -5
  188. ansible_test/_internal/provisioning.py +2 -5
  189. ansible_test/_internal/pypi_proxy.py +1 -1
  190. ansible_test/_internal/python_requirements.py +1 -1
  191. ansible_test/_internal/target.py +2 -6
  192. ansible_test/_internal/thread.py +1 -4
  193. ansible_test/_internal/util.py +9 -14
  194. ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +14 -19
  195. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +48 -45
  196. ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +9 -7
  197. ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +51 -37
  198. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +31 -18
  199. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +1 -2
  200. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +59 -71
  201. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -2
  202. ansible_test/_util/target/cli/ansible_test_cli_stub.py +4 -2
  203. ansible_test/_util/target/common/constants.py +2 -2
  204. ansible_test/_util/target/setup/bootstrap.sh +0 -6
  205. ansible/utils/py3compat.py +0 -27
  206. ansible_test/_data/pytest/config/legacy.ini +0 -4
  207. {ansible_core-2.19.4.dist-info → ansible_core-2.20.0.dist-info}/WHEEL +0 -0
  208. {ansible_core-2.19.4.dist-info → ansible_core-2.20.0.dist-info}/entry_points.txt +0 -0
  209. {ansible_core-2.19.4.dist-info → ansible_core-2.20.0.dist-info}/licenses/COPYING +0 -0
  210. {ansible_core-2.19.4.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  211. {ansible_core-2.19.4.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  212. {ansible_core-2.19.4.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  213. {ansible_core-2.19.4.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  214. {ansible_core-2.19.4.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  215. {ansible_core-2.19.4.dist-info → ansible_core-2.20.0.dist-info}/top_level.txt +0 -0
ansible/modules/cron.py CHANGED
@@ -219,20 +219,20 @@ import os
219
219
  import platform
220
220
  import pwd
221
221
  import re
222
+ import shlex
222
223
  import sys
223
224
  import tempfile
224
225
 
225
226
  from ansible.module_utils.basic import AnsibleModule
226
227
  from ansible.module_utils.common.file import S_IRWU_RWG_RWO
227
228
  from ansible.module_utils.common.text.converters import to_bytes, to_native
228
- from ansible.module_utils.six.moves import shlex_quote
229
229
 
230
230
 
231
231
  class CronTabError(Exception):
232
232
  pass
233
233
 
234
234
 
235
- class CronTab(object):
235
+ class CronTab:
236
236
  """
237
237
  CronTab object to write time based crontab file
238
238
 
@@ -243,8 +243,8 @@ class CronTab(object):
243
243
  def __init__(self, module, user=None, cron_file=None):
244
244
  self.module = module
245
245
  self.user = user
246
- self.root = (os.getuid() == 0)
247
- self.lines = None
246
+ self.root = os.getuid() == 0
247
+ self.lines = []
248
248
  self.ansible = "#Ansible: "
249
249
  self.n_existing = ''
250
250
  self.cron_cmd = self.module.get_bin_path('crontab', required=True)
@@ -264,7 +264,6 @@ class CronTab(object):
264
264
 
265
265
  def read(self):
266
266
  # Read in the crontab from the system
267
- self.lines = []
268
267
  if self.cron_file:
269
268
  # read the cronfile
270
269
  try:
@@ -280,7 +279,7 @@ class CronTab(object):
280
279
  # FIXME: using safely quoted shell for now, but this really should be two non-shell calls instead.
281
280
  (rc, out, err) = self.module.run_command(self._read_user_execute(), use_unsafe_shell=True)
282
281
 
283
- if rc != 0 and rc != 1: # 1 can mean that there are no jobs.
282
+ if rc not in (0, 1): # 1 can mean that there are no jobs.
284
283
  raise CronTabError("Unable to read crontab")
285
284
 
286
285
  self.n_existing = out
@@ -300,11 +299,10 @@ class CronTab(object):
300
299
  def is_empty(self):
301
300
  if len(self.lines) == 0:
302
301
  return True
303
- else:
304
- for line in self.lines:
305
- if line.strip():
306
- return False
307
- return True
302
+ for line in self.lines:
303
+ if line.strip():
304
+ return False
305
+ return True
308
306
 
309
307
  def write(self, backup_file=None):
310
308
  """
@@ -451,13 +449,10 @@ class CronTab(object):
451
449
  if special:
452
450
  if self.cron_file:
453
451
  return "%s@%s %s %s" % (disable_prefix, special, self.user, job)
454
- else:
455
- return "%s@%s %s" % (disable_prefix, special, job)
456
- else:
457
- if self.cron_file:
458
- return "%s%s %s %s %s %s %s %s" % (disable_prefix, minute, hour, day, month, weekday, self.user, job)
459
- else:
460
- return "%s%s %s %s %s %s %s" % (disable_prefix, minute, hour, day, month, weekday, job)
452
+ return "%s@%s %s" % (disable_prefix, special, job)
453
+ if self.cron_file:
454
+ return "%s%s %s %s %s %s %s %s" % (disable_prefix, minute, hour, day, month, weekday, self.user, job)
455
+ return "%s%s %s %s %s %s %s" % (disable_prefix, minute, hour, day, month, weekday, job)
461
456
 
462
457
  def get_jobnames(self):
463
458
  jobnames = []
@@ -495,8 +490,7 @@ class CronTab(object):
495
490
 
496
491
  if len(newlines) == 0:
497
492
  return True
498
- else:
499
- return False # TODO add some more error testing
493
+ return False # TODO add some more error testing
500
494
 
501
495
  def _update_env(self, name, decl, addenvfunction):
502
496
  newlines = []
@@ -529,13 +523,13 @@ class CronTab(object):
529
523
  user = ''
530
524
  if self.user:
531
525
  if platform.system() == 'SunOS':
532
- return "su %s -c '%s -l'" % (shlex_quote(self.user), shlex_quote(self.cron_cmd))
533
- elif platform.system() == 'AIX':
534
- return "%s -l %s" % (shlex_quote(self.cron_cmd), shlex_quote(self.user))
535
- elif platform.system() == 'HP-UX':
536
- return "%s %s %s" % (self.cron_cmd, '-l', shlex_quote(self.user))
537
- elif pwd.getpwuid(os.getuid())[0] != self.user:
538
- user = '-u %s' % shlex_quote(self.user)
526
+ return "su %s -c '%s -l'" % (shlex.quote(self.user), shlex.quote(self.cron_cmd))
527
+ if platform.system() == 'AIX':
528
+ return "%s -l %s" % (shlex.quote(self.cron_cmd), shlex.quote(self.user))
529
+ if platform.system() == 'HP-UX':
530
+ return "%s %s %s" % (self.cron_cmd, '-l', shlex.quote(self.user))
531
+ if pwd.getpwuid(os.getuid())[0] != self.user:
532
+ user = '-u %s' % shlex.quote(self.user)
539
533
  return "%s %s %s" % (self.cron_cmd, user, '-l')
540
534
 
541
535
  def _write_execute(self, path):
@@ -546,10 +540,10 @@ class CronTab(object):
546
540
  if self.user:
547
541
  if platform.system() in ['SunOS', 'HP-UX', 'AIX']:
548
542
  return "chown %s %s ; su '%s' -c '%s %s'" % (
549
- shlex_quote(self.user), shlex_quote(path), shlex_quote(self.user), self.cron_cmd, shlex_quote(path))
550
- elif pwd.getpwuid(os.getuid())[0] != self.user:
551
- user = '-u %s' % shlex_quote(self.user)
552
- return "%s %s %s" % (self.cron_cmd, user, shlex_quote(path))
543
+ shlex.quote(self.user), shlex.quote(path), shlex.quote(self.user), self.cron_cmd, shlex.quote(path))
544
+ if pwd.getpwuid(os.getuid())[0] != self.user:
545
+ user = '-u %s' % shlex.quote(self.user)
546
+ return "%s %s %s" % (self.cron_cmd, user, shlex.quote(path))
553
547
 
554
548
 
555
549
  def main():
@@ -668,7 +662,7 @@ def main():
668
662
 
669
663
  # if requested make a backup before making a change
670
664
  if backup and not module.check_mode:
671
- (backuph, backup_file) = tempfile.mkstemp(prefix='crontab')
665
+ (dummy, backup_file) = tempfile.mkstemp(prefix='crontab')
672
666
  crontab.write(backup_file)
673
667
 
674
668
  if env:
@@ -763,9 +757,6 @@ def main():
763
757
 
764
758
  module.exit_json(**res_args)
765
759
 
766
- # --- should never get here
767
- module.exit_json(msg="Unable to execute cron task.")
768
-
769
760
 
770
761
  if __name__ == '__main__':
771
762
  main()
@@ -67,6 +67,17 @@ options:
67
67
  - Determines the path to the C(InRelease) file, relative to the normal
68
68
  position of an C(InRelease) file.
69
69
  type: str
70
+ install_python_debian:
71
+ description:
72
+ - Whether to automatically try to install the Python C(debian) library or not, if it is not already installed.
73
+ Without this library, the module does not work.
74
+ - Runs C(apt install python3-debian).
75
+ - Only works with the system Python. If you are using a Python on the remote that is not
76
+ the system Python, set O(install_python_debian=false) and ensure that the Python C(debian) library
77
+ for your Python version is installed some other way.
78
+ type: bool
79
+ default: false
80
+ version_added: '2.20'
70
81
  languages:
71
82
  description:
72
83
  - Defines which languages information such as translated
@@ -228,6 +239,7 @@ key_filename:
228
239
 
229
240
  import os
230
241
  import re
242
+ import sys
231
243
  import tempfile
232
244
  import textwrap
233
245
 
@@ -235,9 +247,9 @@ from ansible.module_utils.basic import AnsibleModule
235
247
  from ansible.module_utils.basic import missing_required_lib
236
248
  from ansible.module_utils.common.collections import is_sequence
237
249
  from ansible.module_utils.common.file import S_IRWXU_RXG_RXO, S_IRWU_RG_RO
250
+ from ansible.module_utils.common.respawn import has_respawned, probe_interpreters_for_module, respawn_module
238
251
  from ansible.module_utils.common.text.converters import to_bytes
239
252
  from ansible.module_utils.common.text.converters import to_native
240
- from ansible.module_utils.six import raise_from # type: ignore[attr-defined]
241
253
  from ansible.module_utils.urls import generic_urlparse
242
254
  from ansible.module_utils.urls import open_url
243
255
  from ansible.module_utils.urls import get_user_agent
@@ -326,7 +338,7 @@ def write_signed_by_key(module, v, slug):
326
338
  try:
327
339
  r = open_url(v, http_agent=get_user_agent())
328
340
  except Exception as exc:
329
- raise_from(RuntimeError(to_native(exc)), exc)
341
+ raise RuntimeError('Could not fetch signed_by key.') from exc
330
342
  else:
331
343
  b_data = r.read()
332
344
  else:
@@ -357,6 +369,21 @@ def write_signed_by_key(module, v, slug):
357
369
  return changed, filename, None
358
370
 
359
371
 
372
+ def install_python_debian(module, deb_pkg_name):
373
+
374
+ if not module.check_mode:
375
+ apt_path = module.get_bin_path('apt', required=True)
376
+ if apt_path:
377
+ rc, so, se = module.run_command([apt_path, 'update'])
378
+ if rc != 0:
379
+ module.fail_json(msg=f"Failed update while auto installing {deb_pkg_name} due to '{se.strip()}'")
380
+ rc, so, se = module.run_command([apt_path, 'install', deb_pkg_name, '-y', '-q'])
381
+ if rc != 0:
382
+ module.fail_json(msg=f"Failed to auto-install {deb_pkg_name} due to : '{se.strip()}'")
383
+ else:
384
+ module.fail_json(msg=f"{deb_pkg_name} must be installed to use check mode")
385
+
386
+
360
387
  def main():
361
388
  module = AnsibleModule(
362
389
  argument_spec={
@@ -395,6 +422,10 @@ def main():
395
422
  'inrelease_path': {
396
423
  'type': 'str',
397
424
  },
425
+ 'install_python_debian': {
426
+ 'type': 'bool',
427
+ 'default': False,
428
+ },
398
429
  'languages': {
399
430
  'elements': 'str',
400
431
  'type': 'list',
@@ -453,8 +484,53 @@ def main():
453
484
  )
454
485
 
455
486
  if not HAS_DEBIAN:
456
- module.fail_json(msg=missing_required_lib("python3-debian"),
457
- exception=DEBIAN_IMP_ERR)
487
+ deb_pkg_name = 'python3-debian'
488
+ # This interpreter can't see the debian Python library- we'll do the following to try and fix that as per
489
+ # the apt_repository module:
490
+ # 1) look in common locations for system-owned interpreters that can see it; if we find one, respawn under it
491
+ # 2) finding none, try to install a matching python-debian package for the current interpreter version;
492
+ # we limit to the current interpreter version to try and avoid installing a whole other Python just
493
+ # for deb support
494
+ # 3) if we installed a support package, try to respawn under what we think is the right interpreter (could be
495
+ # the current interpreter again, but we'll let it respawn anyway for simplicity)
496
+ # 4) if still not working, return an error and give up (some corner cases not covered, but this shouldn't be
497
+ # made any more complex than it already is to try and cover more, eg, custom interpreters taking over
498
+ # system locations)
499
+
500
+ if has_respawned():
501
+ # this shouldn't be possible; short-circuit early if it happens...
502
+ module.fail_json(msg=f"{deb_pkg_name} must be installed and visible from {sys.executable}.")
503
+
504
+ interpreters = ['/usr/bin/python3', '/usr/bin/python']
505
+
506
+ interpreter = probe_interpreters_for_module(interpreters, 'debian')
507
+
508
+ if interpreter:
509
+ # found the Python bindings; respawn this module under the interpreter where we found them
510
+ respawn_module(interpreter)
511
+ # this is the end of the line for this process, it will exit here once the respawned module has completed
512
+
513
+ # don't make changes if we're in check_mode
514
+ if module.check_mode:
515
+ module.fail_json(msg=f"{deb_pkg_name} must be installed to use check mode. If run with install_python_debian, this module can auto-install it.")
516
+
517
+ if module.params['install_python_debian']:
518
+ install_python_debian(module, deb_pkg_name)
519
+ else:
520
+ module.fail_json(msg=f'{deb_pkg_name} is not installed, and install_python_debian is False')
521
+
522
+ # try again to find the bindings in common places
523
+ interpreter = probe_interpreters_for_module(interpreters, 'debian')
524
+
525
+ if interpreter:
526
+ # found the Python bindings; respawn this module under the interpreter where we found them
527
+ # NB: respawn is somewhat wasteful if it's this interpreter, but simplifies the code
528
+ respawn_module(interpreter)
529
+ # this is the end of the line for this process, it will exit here once the respawned module has completed
530
+ else:
531
+ # we've done all we can do; just tell the user it's busted and get out
532
+ module.fail_json(msg=missing_required_lib(deb_pkg_name),
533
+ exception=DEBIAN_IMP_ERR)
458
534
 
459
535
  check_mode = module.check_mode
460
536
 
@@ -510,14 +586,9 @@ def main():
510
586
  elif is_sequence(value):
511
587
  value = format_list(value)
512
588
  elif key == 'signed_by':
513
- try:
514
- key_changed, signed_by_filename, signed_by_data = write_signed_by_key(module, value, slug)
515
- value = signed_by_filename or signed_by_data
516
- changed |= key_changed
517
- except RuntimeError as exc:
518
- module.fail_json(
519
- msg='Could not fetch signed_by key: %s' % to_native(exc)
520
- )
589
+ key_changed, signed_by_filename, signed_by_data = write_signed_by_key(module, value, slug)
590
+ value = signed_by_filename or signed_by_data
591
+ changed |= key_changed
521
592
 
522
593
  if value.count('\n') > 0:
523
594
  value = format_multiline(value)
ansible/modules/dnf.py CHANGED
@@ -213,13 +213,6 @@ options:
213
213
  type: bool
214
214
  default: "no"
215
215
  version_added: "2.7"
216
- install_repoquery:
217
- description:
218
- - This is effectively a no-op in DNF as it is not needed with DNF.
219
- - This option is deprecated and will be removed in ansible-core 2.20.
220
- type: bool
221
- default: "yes"
222
- version_added: "2.7"
223
216
  download_only:
224
217
  description:
225
218
  - Only download the packages, do not install them.
@@ -544,6 +537,9 @@ class DnfModule(YumDnf):
544
537
  conf.sslverify = sslverify
545
538
 
546
539
  # Set installroot
540
+ if not os.path.isdir(installroot):
541
+ self.module.fail_json(msg=f"Installroot {installroot} must be a directory")
542
+
547
543
  conf.installroot = installroot
548
544
 
549
545
  # Load substitutions from the filesystem
ansible/modules/dnf5.py CHANGED
@@ -182,12 +182,6 @@ options:
182
182
  using this in combination with wildcard characters in O(name) may result in an unexpected results.
183
183
  type: bool
184
184
  default: "no"
185
- install_repoquery:
186
- description:
187
- - This is effectively a no-op in DNF as it is not needed with DNF.
188
- - This option is deprecated and will be removed in ansible-core 2.20.
189
- type: bool
190
- default: "yes"
191
185
  download_only:
192
186
  description:
193
187
  - Only download the packages, do not install them.
@@ -601,6 +595,10 @@ class Dnf5Module(YumDnf):
601
595
  conf.localpkg_gpgcheck = not self.disable_gpg_check
602
596
  conf.sslverify = self.sslverify
603
597
  conf.clean_requirements_on_remove = self.autoremove
598
+
599
+ if not os.path.isdir(self.installroot):
600
+ self.module.fail_json(msg=f"Installroot {self.installroot} must be a directory")
601
+
604
602
  conf.installroot = self.installroot
605
603
  conf.use_host_config = True # needed for installroot
606
604
  conf.cacheonly = "all" if self.cacheonly else "none"
ansible/modules/expect.py CHANGED
@@ -249,9 +249,6 @@ def main():
249
249
  end_date = datetime.datetime.now()
250
250
  delta = end_date - start_date
251
251
 
252
- if b_out is None:
253
- b_out = b''
254
-
255
252
  result = dict(
256
253
  cmd=args,
257
254
  stdout=to_native(b_out).rstrip('\r\n'),
ansible/modules/find.py CHANGED
@@ -291,7 +291,6 @@ import time
291
291
 
292
292
  from ansible.module_utils.common.text.converters import to_text, to_native
293
293
  from ansible.module_utils.basic import AnsibleModule
294
- from ansible.module_utils.six import string_types
295
294
 
296
295
 
297
296
  class _Object:
@@ -496,7 +495,7 @@ def main():
496
495
 
497
496
  params = module.params
498
497
 
499
- if params['mode'] and not isinstance(params['mode'], string_types):
498
+ if params['mode'] and not isinstance(params['mode'], str):
500
499
  module.fail_json(
501
500
  msg="argument 'mode' is not a string and conversion is not allowed, value is of type %s" % params['mode'].__class__.__name__
502
501
  )
@@ -374,9 +374,9 @@ import shutil
374
374
  import tempfile
375
375
 
376
376
  from datetime import datetime, timezone
377
+ from urllib.parse import urlsplit
377
378
 
378
379
  from ansible.module_utils.basic import AnsibleModule
379
- from ansible.module_utils.six.moves.urllib.parse import urlsplit
380
380
  from ansible.module_utils.common.text.converters import to_native
381
381
  from ansible.module_utils.urls import fetch_url, url_argument_spec
382
382
 
ansible/modules/git.py CHANGED
@@ -343,7 +343,6 @@ from ansible.module_utils.common.text.converters import to_native, to_text
343
343
  from ansible.module_utils.basic import AnsibleModule
344
344
  from ansible.module_utils.common.locale import get_best_parsable_locale
345
345
  from ansible.module_utils.common.process import get_bin_path
346
- from ansible.module_utils.six import b, string_types
347
346
 
348
347
 
349
348
  def relocate_repo(module, result, repo_dir, old_repo_dir, worktree_dir):
@@ -443,12 +442,12 @@ def write_ssh_wrapper(module):
443
442
  fd, wrapper_path = tempfile.mkstemp()
444
443
 
445
444
  # use existing git_ssh/ssh_command, fallback to 'ssh'
446
- template = b("""#!/bin/sh
445
+ template = """#!/bin/sh
447
446
  %s $GIT_SSH_OPTS "$@"
448
- """ % os.environ.get('GIT_SSH', os.environ.get('GIT_SSH_COMMAND', 'ssh')))
447
+ """ % os.environ.get('GIT_SSH', os.environ.get('GIT_SSH_COMMAND', 'ssh'))
449
448
 
450
449
  # write it
451
- with os.fdopen(fd, 'w+b') as fh:
450
+ with os.fdopen(fd, 'w') as fh:
452
451
  fh.write(template)
453
452
 
454
453
  # set execute
@@ -1257,7 +1256,7 @@ def main():
1257
1256
 
1258
1257
  # evaluate and set the umask before doing anything else
1259
1258
  if umask is not None:
1260
- if not isinstance(umask, string_types):
1259
+ if not isinstance(umask, str):
1261
1260
  module.fail_json(msg="umask must be defined as a quoted octal integer")
1262
1261
  try:
1263
1262
  umask = int(umask, 8)
@@ -66,7 +66,7 @@ options:
66
66
  description:
67
67
  - Ignore unknown file extensions within the directory.
68
68
  - This allows users to specify a directory containing vars files that are intermingled with non-vars files extension types
69
- (e.g. a directory with a README in it and vars files).
69
+ (for example, a directory with a README in it and vars files).
70
70
  type: bool
71
71
  default: no
72
72
  version_added: "2.7"
@@ -225,7 +225,13 @@ def sanity_check(module, host, key, sshkeygen):
225
225
  rc, stdout, stderr = module.run_command(sshkeygen_command)
226
226
 
227
227
  if stdout == '': # host not found
228
- module.fail_json(msg="Host parameter does not match hashed host field in supplied key")
228
+ results = {
229
+ "msg": "Host parameter does not match hashed host field in supplied key",
230
+ "rc": rc,
231
+ }
232
+ if stderr:
233
+ results["stderr"] = stderr
234
+ module.fail_json(**results)
229
235
 
230
236
 
231
237
  def search_for_host_key(module, host, key, path, sshkeygen):