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
@@ -8,6 +8,7 @@ A future release will remove the provisional status.
8
8
  from __future__ import annotations as _annotations
9
9
 
10
10
  import dataclasses as _dataclasses
11
+ import enum as _enum
11
12
  import sys as _sys
12
13
  import typing as _t
13
14
 
@@ -21,14 +22,37 @@ else:
21
22
  _dataclass_kwargs = dict(frozen=True)
22
23
 
23
24
 
25
+ class PluginType(_datatag.AnsibleSerializableEnum):
26
+ """Enum of Ansible plugin types."""
27
+
28
+ ACTION = _enum.auto()
29
+ BECOME = _enum.auto()
30
+ CACHE = _enum.auto()
31
+ CALLBACK = _enum.auto()
32
+ CLICONF = _enum.auto()
33
+ CONNECTION = _enum.auto()
34
+ DOC_FRAGMENTS = _enum.auto()
35
+ FILTER = _enum.auto()
36
+ HTTPAPI = _enum.auto()
37
+ INVENTORY = _enum.auto()
38
+ LOOKUP = _enum.auto()
39
+ MODULE = _enum.auto()
40
+ NETCONF = _enum.auto()
41
+ SHELL = _enum.auto()
42
+ STRATEGY = _enum.auto()
43
+ TERMINAL = _enum.auto()
44
+ TEST = _enum.auto()
45
+ VARS = _enum.auto()
46
+
47
+
24
48
  @_dataclasses.dataclass(**_dataclass_kwargs)
25
49
  class PluginInfo(_datatag.AnsibleSerializableDataclass):
26
50
  """Information about a loaded plugin."""
27
51
 
28
- resolved_name: str
52
+ resolved_name: _t.Optional[str]
29
53
  """The resolved canonical plugin name; always fully-qualified for collection plugins."""
30
54
 
31
- type: str
55
+ type: _t.Optional[PluginType]
32
56
  """The plugin type."""
33
57
 
34
58
 
@@ -21,5 +21,18 @@ def get_plugin_info(value: HasPluginInfo) -> _messages.PluginInfo:
21
21
  """Utility method that returns a `PluginInfo` from an object implementing the `HasPluginInfo` protocol."""
22
22
  return _messages.PluginInfo(
23
23
  resolved_name=value.ansible_name,
24
- type=value.plugin_type,
24
+ type=normalize_plugin_type(value.plugin_type),
25
25
  )
26
+
27
+
28
+ def normalize_plugin_type(value: str) -> _messages.PluginType | None:
29
+ """Normalize value and return it as a PluginType, or None if the value does match any known plugin type."""
30
+ value = value.lower()
31
+
32
+ if value == 'modules':
33
+ value = 'module'
34
+
35
+ try:
36
+ return _messages.PluginType(value)
37
+ except ValueError:
38
+ return None
@@ -17,6 +17,6 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- __version__ = '2.19.0b5'
20
+ __version__ = '2.19.0b7'
21
21
  __author__ = 'Ansible, Inc.'
22
22
  __codename__ = "What Is and What Should Never Be"
@@ -480,9 +480,11 @@ class AnsibleModule(object):
480
480
  if basedir is not None and not os.path.exists(basedir):
481
481
  try:
482
482
  os.makedirs(basedir, mode=0o700)
483
- except (OSError, IOError) as e:
484
- self.warn("Unable to use %s as temporary directory, "
485
- "failing back to system: %s" % (basedir, to_native(e)))
483
+ except OSError as ex:
484
+ self.error_as_warning(
485
+ msg=f"Unable to use {basedir!r} as temporary directory, falling back to system default.",
486
+ exception=ex,
487
+ )
486
488
  basedir = None
487
489
  else:
488
490
  self.warn("Module remote_tmp %s did not exist and was "
@@ -494,11 +496,11 @@ class AnsibleModule(object):
494
496
  basefile = "ansible-moduletmp-%s-" % time.time()
495
497
  try:
496
498
  tmpdir = tempfile.mkdtemp(prefix=basefile, dir=basedir)
497
- except (OSError, IOError) as e:
498
- self.fail_json(
499
- msg="Failed to create remote module tmp path at dir %s "
500
- "with prefix %s: %s" % (basedir, basefile, to_native(e))
501
- )
499
+ except OSError as ex:
500
+ raise Exception(
501
+ f"Failed to create remote module tmp path at dir {basedir!r} "
502
+ f"with prefix {basefile!r}.",
503
+ ) from ex
502
504
  if not self._keep_remote_files:
503
505
  atexit.register(shutil.rmtree, tmpdir)
504
506
  self._tmpdir = tmpdir
@@ -658,11 +660,8 @@ class AnsibleModule(object):
658
660
  return context
659
661
  try:
660
662
  ret = selinux.lgetfilecon_raw(to_native(path, errors='surrogate_or_strict'))
661
- except OSError as e:
662
- if e.errno == errno.ENOENT:
663
- self.fail_json(path=path, msg='path %s does not exist' % path)
664
- else:
665
- self.fail_json(path=path, msg='failed to retrieve selinux context')
663
+ except OSError as ex:
664
+ self.fail_json(path=path, msg='Failed to retrieve selinux context.', exception=ex)
666
665
  if ret[0] == -1:
667
666
  return context
668
667
  # Limit split to 4 because the selevel, the last in the list,
@@ -802,9 +801,9 @@ class AnsibleModule(object):
802
801
  return True
803
802
  try:
804
803
  os.lchown(b_path, uid, -1)
805
- except (IOError, OSError) as e:
804
+ except OSError as ex:
806
805
  path = to_text(b_path)
807
- self.fail_json(path=path, msg='chown failed: %s' % (to_text(e)))
806
+ self.fail_json(path=path, msg='chown failed', exception=ex)
808
807
  changed = True
809
808
  return changed
810
809
 
@@ -1330,7 +1329,7 @@ class AnsibleModule(object):
1330
1329
  else:
1331
1330
  journal.send(MESSAGE=u"%s %s" % (module, journal_msg),
1332
1331
  **dict(journal_args))
1333
- except IOError:
1332
+ except OSError:
1334
1333
  # fall back to syslog since logging to journal failed
1335
1334
  self._log_to_syslog(journal_msg)
1336
1335
  else:
@@ -1658,10 +1657,11 @@ class AnsibleModule(object):
1658
1657
  ext = time.strftime("%Y-%m-%d@%H:%M:%S~", time.localtime(time.time()))
1659
1658
  backupdest = '%s.%s.%s' % (fn, os.getpid(), ext)
1660
1659
 
1661
- try:
1662
- self.preserved_copy(fn, backupdest)
1663
- except (shutil.Error, IOError) as e:
1664
- self.fail_json(msg='Could not make backup of %s to %s: %s' % (fn, backupdest, to_native(e)))
1660
+ if not self.check_mode:
1661
+ try:
1662
+ self.preserved_copy(fn, backupdest)
1663
+ except (shutil.Error, IOError) as ex:
1664
+ raise Exception(f'Could not make backup of {fn!r} to {backupdest!r}.') from ex
1665
1665
 
1666
1666
  return backupdest
1667
1667
 
@@ -1735,28 +1735,25 @@ class AnsibleModule(object):
1735
1735
  try:
1736
1736
  # Optimistically try a rename, solves some corner cases and can avoid useless work, throws exception if not atomic.
1737
1737
  os.rename(b_src, b_dest)
1738
- except (IOError, OSError) as e:
1739
- if e.errno not in [errno.EPERM, errno.EXDEV, errno.EACCES, errno.ETXTBSY, errno.EBUSY]:
1738
+ except OSError as ex:
1739
+ if ex.errno in (errno.EPERM, errno.EXDEV, errno.EACCES, errno.ETXTBSY, errno.EBUSY):
1740
1740
  # only try workarounds for errno 18 (cross device), 1 (not permitted), 13 (permission denied)
1741
1741
  # and 26 (text file busy) which happens on vagrant synced folders and other 'exotic' non posix file systems
1742
- self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, to_native(e)))
1743
- else:
1744
1742
  # Use bytes here. In the shippable CI, this fails with
1745
1743
  # a UnicodeError with surrogateescape'd strings for an unknown
1746
1744
  # reason (doesn't happen in a local Ubuntu16.04 VM)
1747
1745
  b_dest_dir = os.path.dirname(b_dest)
1748
1746
  b_suffix = os.path.basename(b_dest)
1749
- error_msg = None
1750
1747
  tmp_dest_name = None
1751
1748
  try:
1752
1749
  tmp_dest_fd, tmp_dest_name = tempfile.mkstemp(prefix=b'.ansible_tmp', dir=b_dest_dir, suffix=b_suffix)
1753
- except (OSError, IOError) as e:
1754
- error_msg = 'The destination directory (%s) is not writable by the current user. Error was: %s' % (os.path.dirname(dest), to_native(e))
1755
-
1750
+ except OSError as ex:
1756
1751
  if unsafe_writes:
1757
1752
  self._unsafe_writes(b_src, b_dest)
1758
1753
  else:
1759
- self.fail_json(msg=error_msg)
1754
+ raise Exception(
1755
+ f'The destination directory {os.path.dirname(dest)!r} is not writable by the current user.'
1756
+ ) from ex
1760
1757
 
1761
1758
  if tmp_dest_name:
1762
1759
  b_tmp_dest_name = to_bytes(tmp_dest_name, errors='surrogate_or_strict')
@@ -1785,24 +1782,27 @@ class AnsibleModule(object):
1785
1782
  if dest_stat and (tmp_stat.st_uid != dest_stat.st_uid or tmp_stat.st_gid != dest_stat.st_gid):
1786
1783
  os.chown(b_tmp_dest_name, dest_stat.st_uid, dest_stat.st_gid)
1787
1784
  os.utime(b_tmp_dest_name, times=(time.time(), time.time()))
1788
- except OSError as e:
1789
- if e.errno != errno.EPERM:
1785
+ except OSError as ex:
1786
+ if ex.errno != errno.EPERM:
1790
1787
  raise
1791
1788
  try:
1792
1789
  os.rename(b_tmp_dest_name, b_dest)
1793
- except (shutil.Error, OSError, IOError) as e:
1794
- if unsafe_writes and e.errno == errno.EBUSY:
1790
+ except (shutil.Error, OSError) as ex:
1791
+ if unsafe_writes and ex.errno == errno.EBUSY:
1795
1792
  self._unsafe_writes(b_tmp_dest_name, b_dest)
1796
1793
  else:
1797
- self.fail_json(msg='Unable to make %s into to %s, failed final rename from %s: %s' %
1798
- (src, dest, b_tmp_dest_name, to_native(e)))
1799
- except (shutil.Error, OSError, IOError) as e:
1794
+ raise Exception(
1795
+ f'Unable to make {src!r} into to {dest!r}, failed final rename from {to_text(b_tmp_dest_name)!r}.'
1796
+ ) from ex
1797
+ except (shutil.Error, OSError) as ex:
1800
1798
  if unsafe_writes:
1801
1799
  self._unsafe_writes(b_src, b_dest)
1802
1800
  else:
1803
- self.fail_json(msg='Failed to replace file: %s to %s: %s' % (src, dest, to_native(e)))
1801
+ raise Exception(f'Failed to replace {dest!r} with {src!r}.') from ex
1804
1802
  finally:
1805
1803
  self.cleanup(b_tmp_dest_name)
1804
+ else:
1805
+ raise Exception(f'Could not replace {dest!r} with {src!r}.') from ex
1806
1806
 
1807
1807
  if creating:
1808
1808
  # make sure the file has the correct permissions
@@ -1829,18 +1829,11 @@ class AnsibleModule(object):
1829
1829
  # sadly there are some situations where we cannot ensure atomicity, but only if
1830
1830
  # the user insists and we get the appropriate error we update the file unsafely
1831
1831
  try:
1832
- out_dest = in_src = None
1833
- try:
1834
- out_dest = open(dest, 'wb')
1835
- in_src = open(src, 'rb')
1836
- shutil.copyfileobj(in_src, out_dest)
1837
- finally: # assuring closed files in 2.4 compatible way
1838
- if out_dest:
1839
- out_dest.close()
1840
- if in_src:
1841
- in_src.close()
1842
- except (shutil.Error, OSError, IOError) as e:
1843
- self.fail_json(msg='Could not write data to file (%s) from (%s): %s' % (dest, src, to_native(e)))
1832
+ with open(dest, 'wb') as out_dest:
1833
+ with open(src, 'rb') as in_src:
1834
+ shutil.copyfileobj(in_src, out_dest)
1835
+ except (shutil.Error, OSError) as ex:
1836
+ raise Exception(f'Could not write data to file {dest!r} from {src!r}.') from ex
1844
1837
 
1845
1838
  def _clean_args(self, args):
1846
1839
 
@@ -1907,18 +1900,18 @@ class AnsibleModule(object):
1907
1900
  the execution to hang (especially if no input data is specified)
1908
1901
  :kw environ_update: dictionary to *update* environ variables with
1909
1902
  :kw umask: Umask to be used when running the command. Default None
1910
- :kw encoding: Since we return native strings, on python3 we need to
1903
+ :kw encoding: Since we return strings, we need to
1911
1904
  know the encoding to use to transform from bytes to text. If you
1912
1905
  want to always get bytes back, use encoding=None. The default is
1913
1906
  "utf-8". This does not affect transformation of strings given as
1914
1907
  args.
1915
- :kw errors: Since we return native strings, on python3 we need to
1908
+ :kw errors: Since we return strings, we need to
1916
1909
  transform stdout and stderr from bytes to text. If the bytes are
1917
1910
  undecodable in the ``encoding`` specified, then use this error
1918
1911
  handler to deal with them. The default is ``surrogate_or_strict``
1919
1912
  which means that the bytes will be decoded using the
1920
1913
  surrogateescape error handler if available (available on all
1921
- python3 versions we support) otherwise a UnicodeError traceback
1914
+ Python versions we support) otherwise a UnicodeError traceback
1922
1915
  will be raised. This does not affect transformations of strings
1923
1916
  given as args.
1924
1917
  :kw expand_user_and_vars: When ``use_unsafe_shell=False`` this argument
@@ -1926,10 +1919,8 @@ class AnsibleModule(object):
1926
1919
  are expanded before running the command. When ``True`` a string such as
1927
1920
  ``$SHELL`` will be expanded regardless of escaping. When ``False`` and
1928
1921
  ``use_unsafe_shell=False`` no path or variable expansion will be done.
1929
- :kw pass_fds: When running on Python 3 this argument
1930
- dictates which file descriptors should be passed
1931
- to an underlying ``Popen`` constructor. On Python 2, this will
1932
- set ``close_fds`` to False.
1922
+ :kw pass_fds: This argument dictates which file descriptors should be passed
1923
+ to an underlying ``Popen`` constructor.
1933
1924
  :kw before_communicate_callback: This function will be called
1934
1925
  after ``Popen`` object will be created
1935
1926
  but before communicating to the process.
@@ -1940,11 +1931,10 @@ class AnsibleModule(object):
1940
1931
  :kw handle_exceptions: This flag indicates whether an exception will
1941
1932
  be handled inline and issue a failed_json or if the caller should
1942
1933
  handle it.
1943
- :returns: A 3-tuple of return code (integer), stdout (native string),
1944
- and stderr (native string). On python2, stdout and stderr are both
1945
- byte strings. On python3, stdout and stderr are text strings converted
1946
- according to the encoding and errors parameters. If you want byte
1947
- strings on python3, use encoding=None to turn decoding to text off.
1934
+ :returns: A 3-tuple of return code (int), stdout (str), and stderr (str).
1935
+ stdout and stderr are text strings converted according to the encoding
1936
+ and errors parameters. If you want byte strings, use encoding=None
1937
+ to turn decoding to text off.
1948
1938
  """
1949
1939
  # used by clean args later on
1950
1940
  self._clean = None
@@ -2126,18 +2116,16 @@ class AnsibleModule(object):
2126
2116
  selector.close()
2127
2117
 
2128
2118
  rc = cmd.returncode
2129
- except (OSError, IOError) as e:
2130
- self.log("Error Executing CMD:%s Exception:%s" % (self._clean_args(args), to_native(e)))
2119
+ except OSError as ex:
2131
2120
  if handle_exceptions:
2132
- self.fail_json(rc=e.errno, stdout=b'', stderr=b'', msg=to_native(e), cmd=self._clean_args(args))
2121
+ self.fail_json(rc=ex.errno, stdout='', stderr='', msg="Error executing command.", cmd=self._clean_args(args), exception=ex)
2133
2122
  else:
2134
- raise e
2135
- except Exception as e:
2136
- self.log("Error Executing CMD:%s Exception:%s" % (self._clean_args(args), to_native(traceback.format_exc())))
2123
+ raise
2124
+ except Exception as ex:
2137
2125
  if handle_exceptions:
2138
- self.fail_json(rc=257, stdout=b'', stderr=b'', msg=to_native(e), cmd=self._clean_args(args))
2126
+ self.fail_json(rc=257, stdout='', stderr='', msg="Error executing command.", cmd=self._clean_args(args), exception=ex)
2139
2127
  else:
2140
- raise e
2128
+ raise
2141
2129
 
2142
2130
  if rc != 0 and check_rc:
2143
2131
  msg = heuristic_log_sanitize(stderr.rstrip(), self.no_log_values)
@@ -2208,7 +2196,7 @@ def __getattr__(importable_name):
2208
2196
  importable = repeat
2209
2197
  elif importable_name in {
2210
2198
  'PY2', 'PY3', 'b', 'binary_type', 'integer_types',
2211
- 'iteritems', 'string_types', 'test_type'
2199
+ 'iteritems', 'string_types', 'text_type',
2212
2200
  }:
2213
2201
  import importlib
2214
2202
  importable = getattr(
@@ -10,6 +10,7 @@ import sys
10
10
  import typing as t
11
11
 
12
12
  from ansible.module_utils.common.text.converters import to_bytes
13
+ from ansible.module_utils._internal._ansiballz import _respawn
13
14
 
14
15
  _ANSIBLE_PARENT_PATH = pathlib.Path(__file__).parents[3]
15
16
 
@@ -39,7 +40,7 @@ def respawn_module(interpreter_path) -> t.NoReturn:
39
40
  raise Exception('module has already been respawned')
40
41
 
41
42
  # FUTURE: we need a safe way to log that a respawn has occurred for forensic/debug purposes
42
- payload = _create_payload()
43
+ payload = _respawn.create_payload()
43
44
  stdin_read, stdin_write = os.pipe()
44
45
  os.write(stdin_write, to_bytes(payload))
45
46
  os.close(stdin_write)
@@ -59,10 +60,12 @@ def probe_interpreters_for_module(interpreter_paths, module_name):
59
60
  :arg module_name: fully-qualified Python module name to probe for (for example, ``selinux``)
60
61
  """
61
62
  PYTHONPATH = os.getenv('PYTHONPATH', '')
63
+
62
64
  env = os.environ.copy()
63
65
  env.update({
64
66
  'PYTHONPATH': f'{_ANSIBLE_PARENT_PATH}:{PYTHONPATH}'.rstrip(': ')
65
67
  })
68
+
66
69
  for interpreter_path in interpreter_paths:
67
70
  if not os.path.exists(interpreter_path):
68
71
  continue
@@ -81,43 +84,3 @@ def probe_interpreters_for_module(interpreter_paths, module_name):
81
84
  continue
82
85
 
83
86
  return None
84
-
85
-
86
- def _create_payload():
87
- # FIXME: move this into _ansiballz and skip the template
88
- from ansible.module_utils import basic
89
-
90
- module_fqn = sys.modules['__main__']._module_fqn
91
- modlib_path = sys.modules['__main__']._modlib_path
92
-
93
- respawn_code_template = """
94
- if __name__ == '__main__':
95
- import runpy
96
- import sys
97
-
98
- json_params = {json_params!r}
99
- profile = {profile!r}
100
- module_fqn = {module_fqn!r}
101
- modlib_path = {modlib_path!r}
102
-
103
- sys.path.insert(0, modlib_path)
104
-
105
- from ansible.module_utils._internal import _ansiballz
106
-
107
- _ansiballz.run_module(
108
- json_params=json_params,
109
- profile=profile,
110
- module_fqn=module_fqn,
111
- modlib_path=modlib_path,
112
- init_globals=dict(_respawned=True),
113
- )
114
- """
115
-
116
- respawn_code = respawn_code_template.format(
117
- json_params=basic._ANSIBLE_ARGS,
118
- profile=basic._ANSIBLE_PROFILE,
119
- module_fqn=module_fqn,
120
- modlib_path=modlib_path,
121
- )
122
-
123
- return respawn_code
@@ -26,7 +26,7 @@ else:
26
26
 
27
27
  # DTFIX-FUTURE: refactor this to share the implementation with the controller version
28
28
  # use an abstract base class, with __init_subclass__ for representer registration, and instance methods for overridable representers
29
- # then tests can be consolidated intead of having two nearly identical copies
29
+ # then tests can be consolidated instead of having two nearly identical copies
30
30
 
31
31
  if HAS_YAML:
32
32
  try:
@@ -33,7 +33,6 @@ import json
33
33
  import pickle
34
34
  import socket
35
35
  import struct
36
- import traceback
37
36
  import uuid
38
37
 
39
38
  from functools import partial
@@ -136,12 +135,11 @@ class Connection(object):
136
135
 
137
136
  try:
138
137
  out = self.send(data)
139
- except socket.error as e:
138
+ except OSError as ex:
140
139
  raise ConnectionError(
141
- 'unable to connect to socket %s. See Troubleshooting socket path issues '
142
- 'in the Network Debug and Troubleshooting Guide' % self.socket_path,
143
- err=to_text(e, errors='surrogate_then_replace'), exception=traceback.format_exc()
144
- )
140
+ f'Unable to connect to socket {self.socket_path!r}. See Troubleshooting socket path issues '
141
+ 'in the Network Debug and Troubleshooting Guide.'
142
+ ) from ex
145
143
 
146
144
  try:
147
145
  response = json.loads(out)
@@ -192,13 +190,12 @@ class Connection(object):
192
190
  send_data(sf, to_bytes(data))
193
191
  response = recv_data(sf)
194
192
 
195
- except socket.error as e:
193
+ except OSError as ex:
196
194
  sf.close()
197
195
  raise ConnectionError(
198
- 'unable to connect to socket %s. See the socket path issue category in '
199
- 'Network Debug and Troubleshooting Guide' % self.socket_path,
200
- err=to_text(e, errors='surrogate_then_replace'), exception=traceback.format_exc()
201
- )
196
+ f'Unable to connect to socket {self.socket_path!r}. See the socket path issue category in '
197
+ 'Network Debug and Troubleshooting Guide.',
198
+ ) from ex
202
199
 
203
200
  sf.close()
204
201
 
@@ -1696,7 +1696,7 @@ namespace Ansible.Basic
1696
1696
  if ((attr & FileAttributes.ReadOnly) != 0)
1697
1697
  {
1698
1698
  // Windows does not allow files set with ReadOnly to be
1699
- // deleted. Pre-emptively unset the attribute.
1699
+ // deleted. Preemptively unset the attribute.
1700
1700
  // FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE is quite new,
1701
1701
  // look at using that flag with POSIX delete once Server 2019
1702
1702
  // is the baseline.
@@ -228,7 +228,7 @@ namespace Ansible.Privilege
228
228
  }
229
229
 
230
230
  /// <summary>
231
- /// Get's the status of all the privileges on the token specified
231
+ /// Gets the status of all the privileges on the token specified
232
232
  /// </summary>
233
233
  /// <param name="token">The process token to get the privilege status on</param>
234
234
  /// <returns>Dictionary where the key is the privilege constant and the value is the PrivilegeAttributes flags</returns>
@@ -342,7 +342,7 @@ namespace Ansible.Privilege
342
342
  // Need to manually marshal the bytes requires for newState as the constant size
343
343
  // of LUID_AND_ATTRIBUTES is set to 1 and can't be overridden at runtime, TOKEN_PRIVILEGES
344
344
  // always contains at least 1 entry so we need to calculate the extra size if there are
345
- // nore than 1 LUID_AND_ATTRIBUTES entry
345
+ // more than 1 LUID_AND_ATTRIBUTES entry
346
346
  int tokenPrivilegesSize = Marshal.SizeOf(typeof(NativeHelpers.TOKEN_PRIVILEGES));
347
347
  int luidAttrSize = 0;
348
348
  if (newState.Length > 1)
@@ -49,7 +49,7 @@ class HardwareCollector(BaseFactCollector):
49
49
  _fact_ids = set(['processor',
50
50
  'processor_cores',
51
51
  'processor_count',
52
- # TODO: mounts isnt exactly hardware
52
+ # TODO: mounts isn't exactly hardware
53
53
  'mounts',
54
54
  'devices']) # type: t.Set[str]
55
55
  _fact_class = Hardware
@@ -182,7 +182,7 @@ class LinuxHardware(Hardware):
182
182
  xen = True
183
183
  # Only interested in the first line
184
184
  break
185
- except IOError:
185
+ except OSError:
186
186
  pass
187
187
 
188
188
  if not os.access("/proc/cpuinfo", os.R_OK):
@@ -61,7 +61,7 @@ class FacterFactCollector(BaseFactCollector):
61
61
  return out
62
62
 
63
63
  def collect(self, module=None, collected_facts=None):
64
- # Note that this mirrors previous facter behavior, where there isnt
64
+ # Note that this mirrors previous facter behavior, where there isn't
65
65
  # a 'ansible_facter' key in the main fact dict, but instead, 'facter_whatever'
66
66
  # items are added to the main dict.
67
67
  facter_dict = {}
@@ -17,8 +17,6 @@ from __future__ import annotations
17
17
 
18
18
  import re
19
19
 
20
- from ansible.module_utils.common.text.converters import to_text
21
-
22
20
 
23
21
  def get_sysctl(module, prefixes):
24
22
 
@@ -31,8 +29,8 @@ def get_sysctl(module, prefixes):
31
29
 
32
30
  try:
33
31
  rc, out, err = module.run_command(cmd)
34
- except (IOError, OSError) as e:
35
- module.warn('Unable to read sysctl: %s' % to_text(e))
32
+ except OSError as ex:
33
+ module.error_as_warning('Unable to read sysctl.', exception=ex)
36
34
  rc = 1
37
35
 
38
36
  if rc == 0:
@@ -54,8 +52,8 @@ def get_sysctl(module, prefixes):
54
52
 
55
53
  try:
56
54
  (key, value) = re.split(r'\s?=\s?|: ', line, maxsplit=1)
57
- except Exception as e:
58
- module.warn('Unable to split sysctl line (%s): %s' % (to_text(line), to_text(e)))
55
+ except Exception as ex:
56
+ module.error_as_warning(f'Unable to split sysctl line {line!r}.', exception=ex)
59
57
 
60
58
  if key:
61
59
  sysctl[key] = value.strip()
@@ -38,8 +38,8 @@ class SystemCapabilitiesFactCollector(BaseFactCollector):
38
38
  # NOTE: -> get_caps_data()/parse_caps_data() for easier mocking -akl
39
39
  try:
40
40
  rc, out, err = module.run_command([capsh_path, "--print"], errors='surrogate_then_replace', handle_exceptions=False)
41
- except (IOError, OSError) as e:
42
- module.warn('Could not query system capabilities: %s' % str(e))
41
+ except OSError as ex:
42
+ module.error_as_warning('Could not query system capabilities.', exception=ex)
43
43
 
44
44
  if rc == 0:
45
45
  enforced_caps = []
@@ -100,7 +100,7 @@ class DistributionFiles:
100
100
  return get_file_content(path)
101
101
 
102
102
  def _get_dist_file_content(self, path, allow_empty=False):
103
- # cant find that dist file or it is incorrectly empty
103
+ # can't find that dist file, or it is incorrectly empty
104
104
  if not _file_exists(path, allow_empty=allow_empty):
105
105
  return False, None
106
106
 
@@ -585,7 +585,7 @@ class Distribution(object):
585
585
  distribution_facts.update(dist_file_facts)
586
586
 
587
587
  distro = distribution_facts['distribution']
588
- # look for a os family alias for the 'distribution', if there isnt one, use 'distribution'
588
+ # look for an os family alias for the 'distribution', if there isn't one, use 'distribution'
589
589
  distribution_facts['os_family'] = self.OS_FAMILY.get(distro, None) or distro
590
590
 
591
591
  return distribution_facts
@@ -50,7 +50,7 @@ class LocalFactCollector(BaseFactCollector):
50
50
  rc, out, err = module.run_command(fn)
51
51
  if rc != 0:
52
52
  failed = 'Failure executing fact script (%s), rc: %s, err: %s' % (fn, rc, err)
53
- except (IOError, OSError) as e:
53
+ except OSError as e:
54
54
  failed = 'Could not execute fact script (%s): %s' % (fn, to_text(e))
55
55
 
56
56
  if failed is not None:
@@ -129,7 +129,7 @@ class LinuxVirtual(Virtual):
129
129
  for line in get_file_lines('/proc/xen/capabilities'):
130
130
  if "control_d" in line:
131
131
  is_xen_host = True
132
- except IOError:
132
+ except OSError:
133
133
  pass
134
134
 
135
135
  if is_xen_host:
@@ -5,7 +5,7 @@ Function Add-CSharpType {
5
5
  <#
6
6
  .SYNOPSIS
7
7
  Compiles one or more C# scripts similar to Add-Type. This exposes
8
- more configuration options that are useable within Ansible and it
8
+ more configuration options that are usable within Ansible and it
9
9
  also allows multiple C# sources to be compiled together.
10
10
 
11
11
  .PARAMETER References
@@ -21,7 +21,7 @@ Function Convert-StringToSnakeCase($string) {
21
21
  return $string
22
22
  }
23
23
 
24
- # used by Convert-DictToSnakeCase to covert list entries from camelCase
24
+ # used by Convert-DictToSnakeCase to convert list entries from camelCase
25
25
  # to snake_case
26
26
  Function Convert-ListToSnakeCase($list) {
27
27
  $snake_list = [System.Collections.ArrayList]@()
@@ -6,7 +6,7 @@
6
6
  Function Get-ExecutablePath {
7
7
  <#
8
8
  .SYNOPSIS
9
- Get's the full path to an executable, will search the directory specified or ones in the PATH env var.
9
+ Gets the full path to an executable, will search the directory specified or ones in the PATH env var.
10
10
 
11
11
  .PARAMETER executable
12
12
  [String]The executable to search for.
@@ -17,7 +17,7 @@ Function Get-AnsibleWebRequest {
17
17
  The protocol method to use, if omitted, will use the default value for the URI protocol specified.
18
18
 
19
19
  .PARAMETER FollowRedirects
20
- Whether to follow redirect reponses. This is only valid when using a HTTP URI.
20
+ Whether to follow redirect responses. This is only valid when using a HTTP URI.
21
21
  all - Will follow all redirects
22
22
  none - Will follow no redirects
23
23
  safe - Will only follow redirects when GET or HEAD is used as the Method