ansible-core 2.19.0b2__py3-none-any.whl → 2.19.0b4__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 (85) hide show
  1. ansible/_internal/__init__.py +1 -1
  2. ansible/_internal/_ansiballz.py +1 -4
  3. ansible/_internal/_json/__init__.py +1 -1
  4. ansible/_internal/_templating/_datatag.py +3 -4
  5. ansible/_internal/_templating/_engine.py +6 -1
  6. ansible/_internal/_templating/_jinja_plugins.py +2 -6
  7. ansible/_internal/_testing.py +26 -0
  8. ansible/cli/__init__.py +3 -2
  9. ansible/cli/arguments/option_helpers.py +10 -3
  10. ansible/cli/doc.py +0 -1
  11. ansible/config/base.yml +5 -23
  12. ansible/config/manager.py +144 -103
  13. ansible/constants.py +1 -63
  14. ansible/errors/__init__.py +6 -2
  15. ansible/executor/module_common.py +11 -7
  16. ansible/executor/task_executor.py +6 -8
  17. ansible/galaxy/api.py +1 -1
  18. ansible/galaxy/collection/__init__.py +3 -3
  19. ansible/inventory/manager.py +1 -0
  20. ansible/module_utils/_internal/_ansiballz.py +4 -30
  21. ansible/module_utils/_internal/_datatag/_tags.py +3 -25
  22. ansible/module_utils/_internal/_deprecator.py +134 -0
  23. ansible/module_utils/_internal/_plugin_info.py +25 -0
  24. ansible/module_utils/_internal/_validation.py +14 -0
  25. ansible/module_utils/ansible_release.py +1 -1
  26. ansible/module_utils/basic.py +64 -17
  27. ansible/module_utils/common/arg_spec.py +8 -3
  28. ansible/module_utils/common/messages.py +40 -23
  29. ansible/module_utils/common/process.py +0 -1
  30. ansible/module_utils/common/respawn.py +0 -7
  31. ansible/module_utils/common/warnings.py +13 -13
  32. ansible/module_utils/datatag.py +13 -13
  33. ansible/module_utils/facts/virtual/linux.py +1 -1
  34. ansible/module_utils/parsing/convert_bool.py +6 -0
  35. ansible/modules/assemble.py +4 -4
  36. ansible/modules/async_status.py +1 -1
  37. ansible/modules/cron.py +3 -5
  38. ansible/modules/dnf5.py +2 -1
  39. ansible/modules/get_url.py +1 -1
  40. ansible/modules/git.py +1 -6
  41. ansible/modules/pip.py +2 -4
  42. ansible/modules/sysvinit.py +3 -3
  43. ansible/playbook/task.py +0 -2
  44. ansible/plugins/__init__.py +18 -8
  45. ansible/plugins/action/__init__.py +7 -15
  46. ansible/plugins/action/gather_facts.py +2 -4
  47. ansible/plugins/action/template.py +3 -0
  48. ansible/plugins/callback/oneline.py +7 -1
  49. ansible/plugins/callback/tree.py +7 -1
  50. ansible/plugins/connection/local.py +1 -1
  51. ansible/plugins/connection/paramiko_ssh.py +9 -2
  52. ansible/plugins/doc_fragments/action_core.py +1 -1
  53. ansible/plugins/filter/core.py +4 -1
  54. ansible/plugins/inventory/__init__.py +2 -2
  55. ansible/plugins/loader.py +197 -132
  56. ansible/plugins/lookup/url.py +2 -2
  57. ansible/plugins/strategy/__init__.py +6 -6
  58. ansible/release.py +1 -1
  59. ansible/template/__init__.py +1 -1
  60. ansible/utils/collection_loader/__init__.py +2 -0
  61. ansible/utils/collection_loader/_collection_meta.py +5 -3
  62. ansible/utils/display.py +137 -71
  63. ansible/utils/plugin_docs.py +2 -1
  64. ansible/utils/py3compat.py +1 -7
  65. ansible/utils/ssh_functions.py +4 -1
  66. ansible/vars/manager.py +18 -10
  67. ansible/vars/plugins.py +4 -4
  68. {ansible_core-2.19.0b2.dist-info → ansible_core-2.19.0b4.dist-info}/METADATA +3 -2
  69. {ansible_core-2.19.0b2.dist-info → ansible_core-2.19.0b4.dist-info}/RECORD +82 -79
  70. {ansible_core-2.19.0b2.dist-info → ansible_core-2.19.0b4.dist-info}/WHEEL +1 -1
  71. ansible_test/_internal/commands/sanity/pylint.py +1 -0
  72. ansible_test/_internal/docker_util.py +4 -3
  73. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +486 -0
  74. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_comment.py +137 -0
  75. ansible/module_utils/_internal/_dataclass_annotation_patch.py +0 -64
  76. ansible/module_utils/_internal/_plugin_exec_context.py +0 -49
  77. ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +0 -399
  78. {ansible_core-2.19.0b2.dist-info → ansible_core-2.19.0b4.dist-info}/entry_points.txt +0 -0
  79. {ansible_core-2.19.0b2.dist-info → ansible_core-2.19.0b4.dist-info/licenses}/COPYING +0 -0
  80. {ansible_core-2.19.0b2.dist-info → ansible_core-2.19.0b4.dist-info/licenses/licenses}/Apache-License.txt +0 -0
  81. {ansible_core-2.19.0b2.dist-info → ansible_core-2.19.0b4.dist-info/licenses/licenses}/BSD-3-Clause.txt +0 -0
  82. {ansible_core-2.19.0b2.dist-info → ansible_core-2.19.0b4.dist-info/licenses/licenses}/MIT-license.txt +0 -0
  83. {ansible_core-2.19.0b2.dist-info → ansible_core-2.19.0b4.dist-info/licenses/licenses}/PSF-license.txt +0 -0
  84. {ansible_core-2.19.0b2.dist-info → ansible_core-2.19.0b4.dist-info/licenses/licenses}/simplified_bsd.txt +0 -0
  85. {ansible_core-2.19.0b2.dist-info → ansible_core-2.19.0b4.dist-info}/top_level.txt +0 -0
@@ -4,15 +4,12 @@
4
4
 
5
5
  from __future__ import annotations as _annotations
6
6
 
7
- import datetime as _datetime
8
7
  import typing as _t
9
8
 
10
- from ansible.module_utils._internal import _traceback, _plugin_exec_context
9
+ from ansible.module_utils._internal import _traceback, _deprecator
11
10
  from ansible.module_utils.common import messages as _messages
12
11
  from ansible.module_utils import _internal
13
12
 
14
- _UNSET = _t.cast(_t.Any, object())
15
-
16
13
 
17
14
  def warn(warning: str) -> None:
18
15
  """Record a warning to be returned with the module result."""
@@ -28,22 +25,23 @@ def warn(warning: str) -> None:
28
25
  def deprecate(
29
26
  msg: str,
30
27
  version: str | None = None,
31
- date: str | _datetime.date | None = None,
32
- collection_name: str | None = _UNSET,
28
+ date: str | None = None,
29
+ collection_name: str | None = None,
33
30
  *,
31
+ deprecator: _messages.PluginInfo | None = None,
34
32
  help_text: str | None = None,
35
33
  obj: object | None = None,
36
34
  ) -> None:
37
35
  """
38
- Record a deprecation warning to be returned with the module result.
36
+ Record a deprecation warning.
39
37
  The `obj` argument is only useful in a controller context; it is ignored for target-side callers.
38
+ Most callers do not need to provide `collection_name` or `deprecator` -- but provide only one if needed.
39
+ Specify `version` or `date`, but not both.
40
+ If `date` is a string, it must be in the form `YYYY-MM-DD`.
40
41
  """
41
- if isinstance(date, _datetime.date):
42
- date = str(date)
42
+ _skip_stackwalk = True
43
43
 
44
- # deprecated: description='enable the deprecation message for collection_name' core_version='2.23'
45
- # if collection_name is not _UNSET:
46
- # deprecate('The `collection_name` argument to `deprecate` is deprecated.', version='2.27')
44
+ deprecator = _deprecator.get_best_deprecator(deprecator=deprecator, collection_name=collection_name)
47
45
 
48
46
  if _internal.is_controller:
49
47
  _display = _internal.import_controller_module('ansible.utils.display').Display()
@@ -53,6 +51,8 @@ def deprecate(
53
51
  date=date,
54
52
  help_text=help_text,
55
53
  obj=obj,
54
+ # skip passing collection_name; get_best_deprecator already accounted for it when present
55
+ deprecator=deprecator,
56
56
  )
57
57
 
58
58
  return
@@ -64,7 +64,7 @@ def deprecate(
64
64
  formatted_traceback=_traceback.maybe_capture_traceback(_traceback.TracebackEvent.DEPRECATED),
65
65
  version=version,
66
66
  date=date,
67
- plugin=_plugin_exec_context.PluginExecContext.get_current_plugin_info(),
67
+ deprecator=deprecator,
68
68
  )] = None
69
69
 
70
70
 
@@ -1,11 +1,11 @@
1
1
  """Public API for data tagging."""
2
2
  from __future__ import annotations as _annotations
3
3
 
4
- import datetime as _datetime
5
4
  import typing as _t
6
5
 
7
- from ._internal import _plugin_exec_context, _datatag
6
+ from ._internal import _datatag, _deprecator
8
7
  from ._internal._datatag import _tags
8
+ from .common import messages as _messages
9
9
 
10
10
  _T = _t.TypeVar('_T')
11
11
 
@@ -14,28 +14,28 @@ def deprecate_value(
14
14
  value: _T,
15
15
  msg: str,
16
16
  *,
17
+ version: str | None = None,
18
+ date: str | None = None,
19
+ collection_name: str | None = None,
20
+ deprecator: _messages.PluginInfo | None = None,
17
21
  help_text: str | None = None,
18
- removal_date: str | _datetime.date | None = None,
19
- removal_version: str | None = None,
20
22
  ) -> _T:
21
23
  """
22
24
  Return `value` tagged with the given deprecation details.
23
25
  The types `None` and `bool` cannot be deprecated and are returned unmodified.
24
26
  Raises a `TypeError` if `value` is not a supported type.
25
- If `removal_date` is a string, it must be in the form `YYYY-MM-DD`.
26
- This function is only supported in contexts where an Ansible plugin/module is executing.
27
+ Most callers do not need to provide `collection_name` or `deprecator` -- but provide only one if needed.
28
+ Specify `version` or `date`, but not both.
29
+ If `date` is provided, it should be in the form `YYYY-MM-DD`.
27
30
  """
28
- if isinstance(removal_date, str):
29
- # The `fromisoformat` method accepts other ISO 8601 formats than `YYYY-MM-DD` starting with Python 3.11.
30
- # That should be considered undocumented behavior of `deprecate_value` rather than an intentional feature.
31
- removal_date = _datetime.date.fromisoformat(removal_date)
31
+ _skip_stackwalk = True
32
32
 
33
33
  deprecated = _tags.Deprecated(
34
34
  msg=msg,
35
35
  help_text=help_text,
36
- removal_date=removal_date,
37
- removal_version=removal_version,
38
- plugin=_plugin_exec_context.PluginExecContext.get_current_plugin_info(),
36
+ date=date,
37
+ version=version,
38
+ deprecator=_deprecator.get_best_deprecator(deprecator=deprecator, collection_name=collection_name),
39
39
  )
40
40
 
41
41
  return deprecated.tag(value)
@@ -151,7 +151,7 @@ class LinuxVirtual(Virtual):
151
151
  sys_vendor = get_file_content('/sys/devices/virtual/dmi/id/sys_vendor')
152
152
  product_family = get_file_content('/sys/devices/virtual/dmi/id/product_family')
153
153
 
154
- if product_name in ('KVM', 'KVM Server', 'Bochs', 'AHV'):
154
+ if product_name in ('KVM', 'KVM Server', 'Bochs', 'AHV', 'CloudStack KVM Hypervisor'):
155
155
  guest_tech.add('kvm')
156
156
  if not found_virt:
157
157
  virtual_facts['virtualization_type'] = 'kvm'
@@ -3,6 +3,8 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ import collections.abc as c
7
+
6
8
  from ansible.module_utils.six import binary_type, text_type
7
9
  from ansible.module_utils.common.text.converters import to_text
8
10
 
@@ -17,9 +19,13 @@ def boolean(value, strict=True):
17
19
  return value
18
20
 
19
21
  normalized_value = value
22
+
20
23
  if isinstance(value, (text_type, binary_type)):
21
24
  normalized_value = to_text(value, errors='surrogate_or_strict').lower().strip()
22
25
 
26
+ if not isinstance(value, c.Hashable):
27
+ normalized_value = None # prevent unhashable types from bombing, but keep the rest of the existing fallback/error behavior
28
+
23
29
  if normalized_value in BOOLEANS_TRUE:
24
30
  return True
25
31
  elif normalized_value in BOOLEANS_FALSE or not strict:
@@ -181,7 +181,7 @@ def assemble_from_fragments(src_path, delimiter=None, compiled_regexp=None, igno
181
181
  return temp_path
182
182
 
183
183
 
184
- def cleanup(path, result=None):
184
+ def cleanup(module, path, result=None):
185
185
  # cleanup just in case
186
186
  if os.path.exists(path):
187
187
  try:
@@ -189,7 +189,7 @@ def cleanup(path, result=None):
189
189
  except (IOError, OSError) as e:
190
190
  # don't error on possible race conditions, but keep warning
191
191
  if result is not None:
192
- result['warnings'] = ['Unable to remove temp file (%s): %s' % (path, to_native(e))]
192
+ module.warn('Unable to remove temp file (%s): %s' % (path, to_native(e)))
193
193
 
194
194
 
195
195
  def main():
@@ -261,7 +261,7 @@ def main():
261
261
  (rc, out, err) = module.run_command(validate % path)
262
262
  result['validation'] = dict(rc=rc, stdout=out, stderr=err)
263
263
  if rc != 0:
264
- cleanup(path)
264
+ cleanup(module, path)
265
265
  module.fail_json(msg="failed to validate: rc:%s error:%s" % (rc, err))
266
266
  if backup and dest_hash is not None:
267
267
  result['backup_file'] = module.backup_local(dest)
@@ -269,7 +269,7 @@ def main():
269
269
  module.atomic_move(path, dest, unsafe_writes=module.params['unsafe_writes'])
270
270
  changed = True
271
271
 
272
- cleanup(path, result)
272
+ cleanup(module, path, result)
273
273
 
274
274
  # handle file permissions
275
275
  file_args = module.load_file_common_arguments(module.params)
@@ -68,7 +68,7 @@ EXAMPLES = r"""
68
68
  ansible.builtin.async_status:
69
69
  jid: '{{ dnf_sleeper.ansible_job_id }}'
70
70
  register: job_result
71
- until: job_result.finished
71
+ until: job_result is finished
72
72
  retries: 100
73
73
  delay: 10
74
74
 
ansible/modules/cron.py CHANGED
@@ -618,7 +618,6 @@ def main():
618
618
 
619
619
  changed = False
620
620
  res_args = dict()
621
- warnings = list()
622
621
 
623
622
  if cron_file:
624
623
 
@@ -627,8 +626,8 @@ def main():
627
626
 
628
627
  cron_file_basename = os.path.basename(cron_file)
629
628
  if not re.search(r'^[A-Z0-9_-]+$', cron_file_basename, re.I):
630
- warnings.append('Filename portion of cron_file ("%s") should consist' % cron_file_basename +
631
- ' solely of upper- and lower-case letters, digits, underscores, and hyphens')
629
+ module.warn('Filename portion of cron_file ("%s") should consist' % cron_file_basename +
630
+ ' solely of upper- and lower-case letters, digits, underscores, and hyphens')
632
631
 
633
632
  # Ensure all files generated are only writable by the owning user. Primarily relevant for the cron_file option.
634
633
  os.umask(int('022', 8))
@@ -693,7 +692,7 @@ def main():
693
692
  if do_install:
694
693
  for char in ['\r', '\n']:
695
694
  if char in job.strip('\r\n'):
696
- warnings.append('Job should not contain line breaks')
695
+ module.warn('Job should not contain line breaks')
697
696
  break
698
697
 
699
698
  job = crontab.get_cron_job(minute, hour, day, month, weekday, job, special_time, disabled)
@@ -734,7 +733,6 @@ def main():
734
733
  res_args = dict(
735
734
  jobs=crontab.get_jobnames(),
736
735
  envs=crontab.get_envnames(),
737
- warnings=warnings,
738
736
  changed=changed
739
737
  )
740
738
 
ansible/modules/dnf5.py CHANGED
@@ -722,6 +722,7 @@ class Dnf5Module(YumDnf):
722
722
  if self.security:
723
723
  types.append("security")
724
724
  advisory_query.filter_type(types)
725
+ conf.skip_unavailable = True # ignore packages that are of a different type, for backwards compat
725
726
  settings.set_advisory_filter(advisory_query)
726
727
 
727
728
  goal = libdnf5.base.Goal(base)
@@ -797,7 +798,7 @@ class Dnf5Module(YumDnf):
797
798
  if self.module.check_mode:
798
799
  if results:
799
800
  msg = "Check mode: No changes made, but would have if not in check mode"
800
- else:
801
+ elif changed:
801
802
  transaction.download()
802
803
  if not self.download_only:
803
804
  transaction.set_description("ansible dnf5 module")
@@ -87,7 +87,7 @@ options:
87
87
  - 'If a checksum is passed to this parameter, the digest of the
88
88
  destination file will be calculated after it is downloaded to ensure
89
89
  its integrity and verify that the transfer completed successfully.
90
- Format: <algorithm>:<checksum|url>, for example C(checksum="sha256:D98291AC[...]B6DC7B97",
90
+ Format: <algorithm>:<checksum|url>, for example C(checksum="sha256:D98291AC[...]B6DC7B97"),
91
91
  C(checksum="sha256:http://example.com/path/sha256sum.txt").'
92
92
  - If you worry about portability, only the sha1 algorithm is available
93
93
  on all platforms and python versions.
ansible/modules/git.py CHANGED
@@ -317,11 +317,6 @@ remote_url_changed:
317
317
  returned: success
318
318
  type: bool
319
319
  sample: True
320
- warnings:
321
- description: List of warnings if requested features were not available due to a too old git version.
322
- returned: error
323
- type: str
324
- sample: git version is too old to fully support the depth argument. Falling back to full checkouts.
325
320
  git_dir_now:
326
321
  description: Contains the new path of .git directory if it is changed.
327
322
  returned: success
@@ -1240,7 +1235,7 @@ def main():
1240
1235
  archive_prefix = module.params['archive_prefix']
1241
1236
  separate_git_dir = module.params['separate_git_dir']
1242
1237
 
1243
- result = dict(changed=False, warnings=list())
1238
+ result = dict(changed=False)
1244
1239
 
1245
1240
  if module.params['accept_hostkey']:
1246
1241
  if ssh_opts is not None:
ansible/modules/pip.py CHANGED
@@ -814,10 +814,8 @@ def main():
814
814
  elif requirements:
815
815
  cmd.extend(['-r', requirements])
816
816
  else:
817
- module.exit_json(
818
- changed=False,
819
- warnings=["No valid name or requirements file found."],
820
- )
817
+ module.warn("No valid name or requirements file found.")
818
+ module.exit_json(changed=False)
821
819
 
822
820
  if module.check_mode:
823
821
  if extra_args or requirements or state == 'latest' or not name:
@@ -88,9 +88,9 @@ EXAMPLES = """
88
88
 
89
89
  - name: Sleep for 5 seconds between stop and start command of badly behaving service
90
90
  ansible.builtin.sysvinit:
91
- name: apache2
92
- state: restarted
93
- sleep: 5
91
+ name: apache2
92
+ state: restarted
93
+ sleep: 5
94
94
 
95
95
  - name: Make sure apache2 is started on runlevels 3 and 5
96
96
  ansible.builtin.sysvinit:
ansible/playbook/task.py CHANGED
@@ -227,8 +227,6 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
227
227
  raise AnsibleError("you must specify a value when using %s" % k, obj=ds)
228
228
  new_ds['loop_with'] = loop_name
229
229
  new_ds['loop'] = v
230
- # display.deprecated("with_ type loops are being phased out, use the 'loop' keyword instead",
231
- # version="2.10", collection_name='ansible.builtin')
232
230
 
233
231
  def preprocess_data(self, ds):
234
232
  """
@@ -20,24 +20,26 @@
20
20
  from __future__ import annotations
21
21
 
22
22
  import abc
23
+ import functools
23
24
  import types
24
25
  import typing as t
25
26
 
26
27
  from ansible import constants as C
27
28
  from ansible.errors import AnsibleError
28
29
  from ansible.utils.display import Display
30
+ from ansible.utils import display as _display
29
31
 
30
- from ansible.module_utils._internal import _plugin_exec_context
32
+ from ansible.module_utils._internal import _plugin_info
31
33
 
32
34
  display = Display()
33
35
 
34
36
  if t.TYPE_CHECKING:
35
- from .loader import PluginPathContext
37
+ from . import loader as _t_loader
36
38
 
37
39
  # Global so that all instances of a PluginLoader will share the caches
38
40
  MODULE_CACHE = {} # type: dict[str, dict[str, types.ModuleType]]
39
- PATH_CACHE = {} # type: dict[str, list[PluginPathContext] | None]
40
- PLUGIN_PATH_CACHE = {} # type: dict[str, dict[str, dict[str, PluginPathContext]]]
41
+ PATH_CACHE = {} # type: dict[str, list[_t_loader.PluginPathContext] | None]
42
+ PLUGIN_PATH_CACHE = {} # type: dict[str, dict[str, dict[str, _t_loader.PluginPathContext]]]
41
43
 
42
44
 
43
45
  def get_plugin_class(obj):
@@ -50,10 +52,10 @@ def get_plugin_class(obj):
50
52
  class _ConfigurablePlugin(t.Protocol):
51
53
  """Protocol to provide type-safe access to config for plugin-related mixins."""
52
54
 
53
- def get_option(self, option: str, hostvars: dict[str, object] | None = None) -> object: ...
55
+ def get_option(self, option: str, hostvars: dict[str, object] | None = None) -> t.Any: ...
54
56
 
55
57
 
56
- class _AnsiblePluginInfoMixin(_plugin_exec_context.HasPluginInfo):
58
+ class _AnsiblePluginInfoMixin(_plugin_info.HasPluginInfo):
57
59
  """Mixin to provide type annotations and default values for existing PluginLoader-set load-time attrs."""
58
60
  _original_path: str | None = None
59
61
  _load_name: str | None = None
@@ -102,6 +104,14 @@ class AnsiblePlugin(_AnsiblePluginInfoMixin, _ConfigurablePlugin, metaclass=abc.
102
104
  raise KeyError(str(e))
103
105
  return option_value, origin
104
106
 
107
+ @functools.cached_property
108
+ def __plugin_info(self):
109
+ """
110
+ Internal cached property to retrieve `PluginInfo` for this plugin instance.
111
+ Only for use by the `AnsiblePlugin` base class.
112
+ """
113
+ return _plugin_info.get_plugin_info(self)
114
+
105
115
  def get_option(self, option, hostvars=None):
106
116
 
107
117
  if option not in self._options:
@@ -117,7 +127,7 @@ class AnsiblePlugin(_AnsiblePluginInfoMixin, _ConfigurablePlugin, metaclass=abc.
117
127
 
118
128
  def set_option(self, option, value):
119
129
  self._options[option] = C.config.get_config_value(option, plugin_type=self.plugin_type, plugin_name=self._load_name, direct={option: value})
120
- C.handle_config_noise(display)
130
+ _display._report_config_warnings(self.__plugin_info)
121
131
 
122
132
  def set_options(self, task_keys=None, var_options=None, direct=None):
123
133
  """
@@ -134,7 +144,7 @@ class AnsiblePlugin(_AnsiblePluginInfoMixin, _ConfigurablePlugin, metaclass=abc.
134
144
  if self.allow_extras and var_options and '_extras' in var_options:
135
145
  # these are largely unvalidated passthroughs, either plugin or underlying API will validate
136
146
  self._options['_extras'] = var_options['_extras']
137
- C.handle_config_noise(display)
147
+ _display._report_config_warnings(self.__plugin_info)
138
148
 
139
149
  def has_option(self, option):
140
150
  if not self._options:
@@ -318,13 +318,6 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
318
318
  final_environment: dict[str, t.Any] = {}
319
319
  self._compute_environment_string(final_environment)
320
320
 
321
- # `modify_module` adapts PluginInfo to allow target-side use of `PluginExecContext` since modules aren't plugins
322
- plugin = PluginInfo(
323
- requested_name=module_name,
324
- resolved_name=result.resolved_fqcn,
325
- type='module',
326
- )
327
-
328
321
  # modify_module will exit early if interpreter discovery is required; re-run after if necessary
329
322
  for _dummy in (1, 2):
330
323
  try:
@@ -338,7 +331,6 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
338
331
  async_timeout=self._task.async_val,
339
332
  environment=final_environment,
340
333
  remote_is_local=bool(getattr(self._connection, '_remote_is_local', False)),
341
- plugin=plugin,
342
334
  become_plugin=self._connection.become,
343
335
  )
344
336
 
@@ -649,12 +641,12 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
649
641
  # done. Make the files +x if we're asked to, and return.
650
642
  if not self._is_become_unprivileged():
651
643
  if execute:
652
- # Can't depend on the file being transferred with execute permissions.
644
+ # Can't depend on the file being transferred with required permissions.
653
645
  # Only need user perms because no become was used here
654
- res = self._remote_chmod(remote_paths, 'u+x')
646
+ res = self._remote_chmod(remote_paths, 'u+rwx')
655
647
  if res['rc'] != 0:
656
648
  raise AnsibleError(
657
- 'Failed to set execute bit on remote files '
649
+ 'Failed to set permissions on remote files '
658
650
  '(rc: {0}, err: {1})'.format(
659
651
  res['rc'],
660
652
  to_native(res['stderr'])))
@@ -695,10 +687,10 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
695
687
  return remote_paths
696
688
 
697
689
  # Step 3b: Set execute if we need to. We do this before anything else
698
- # because some of the methods below might work but not let us set +x
699
- # as part of them.
690
+ # because some of the methods below might work but not let us set
691
+ # permissions as part of them.
700
692
  if execute:
701
- res = self._remote_chmod(remote_paths, 'u+x')
693
+ res = self._remote_chmod(remote_paths, 'u+rwx')
702
694
  if res['rc'] != 0:
703
695
  raise AnsibleError(
704
696
  'Failed to set file mode or acl on remote temporary files '
@@ -1010,7 +1002,7 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
1010
1002
  # tells the module to ignore options that are not in its argspec.
1011
1003
  module_args['_ansible_ignore_unknown_opts'] = ignore_unknown_opts
1012
1004
 
1013
- # allow user to insert string to add context to remote loggging
1005
+ # allow user to insert string to add context to remote logging
1014
1006
  module_args['_ansible_target_log_info'] = C.config.get_config_value('TARGET_LOG_INFO', variables=task_vars)
1015
1007
 
1016
1008
  module_args['_ansible_tracebacks_for'] = _traceback.traceback_for()
@@ -28,10 +28,8 @@ class ActionModule(ActionBase):
28
28
 
29
29
  # TODO: remove in favor of controller side argspec detecting valid arguments
30
30
  # network facts modules must support gather_subset
31
- try:
32
- name = self._connection.ansible_name.removeprefix('ansible.netcommon.')
33
- except AttributeError:
34
- name = self._connection._load_name.split('.')[-1]
31
+ name = self._connection.ansible_name.removeprefix('ansible.netcommon.')
32
+
35
33
  if name not in ('network_cli', 'httpapi', 'netconf'):
36
34
  subset = mod_args.pop('gather_subset', None)
37
35
  if subset not in ('all', ['all'], None):
@@ -132,6 +132,9 @@ class ActionModule(ActionBase):
132
132
  data_templar = self._templar.copy_with_new_env(searchpath=searchpath, available_variables=temp_vars)
133
133
  resultant = data_templar.template(template_data, escape_backslashes=False, overrides=overrides)
134
134
 
135
+ if resultant is None:
136
+ resultant = ''
137
+
135
138
  new_task = self._task.copy()
136
139
  # mode is either the mode from task.args or the mode of the source file if the task.args
137
140
  # mode == 'preserve'
@@ -17,6 +17,7 @@ from ansible import constants as C
17
17
  from ansible.plugins.callback import CallbackBase
18
18
  from ansible.template import Templar
19
19
  from ansible.executor.task_result import CallbackTaskResult
20
+ from ansible.module_utils._internal import _deprecator
20
21
 
21
22
 
22
23
  class CallbackModule(CallbackBase):
@@ -32,7 +33,12 @@ class CallbackModule(CallbackBase):
32
33
 
33
34
  def __init__(self, *args, **kwargs):
34
35
  super().__init__(*args, **kwargs)
35
- self._display.deprecated('The oneline callback plugin is deprecated.', version='2.23')
36
+
37
+ self._display.deprecated( # pylint: disable=ansible-deprecated-unnecessary-collection-name
38
+ msg='The oneline callback plugin is deprecated.',
39
+ version='2.23',
40
+ deprecator=_deprecator.ANSIBLE_CORE_DEPRECATOR, # entire plugin being removed; this improves the messaging
41
+ )
36
42
 
37
43
  def _command_generic_msg(self, hostname, result, caption):
38
44
  stdout = result.get('stdout', '').replace('\n', '\\n').replace('\r', '\\r')
@@ -34,6 +34,7 @@ from ansible.executor.task_result import CallbackTaskResult
34
34
  from ansible.module_utils.common.text.converters import to_bytes, to_text
35
35
  from ansible.plugins.callback import CallbackBase
36
36
  from ansible.utils.path import makedirs_safe, unfrackpath
37
+ from ansible.module_utils._internal import _deprecator
37
38
 
38
39
 
39
40
  class CallbackModule(CallbackBase):
@@ -48,7 +49,12 @@ class CallbackModule(CallbackBase):
48
49
 
49
50
  def __init__(self, *args, **kwargs):
50
51
  super().__init__(*args, **kwargs)
51
- self._display.deprecated('The tree callback plugin is deprecated.', version='2.23')
52
+
53
+ self._display.deprecated( # pylint: disable=ansible-deprecated-unnecessary-collection-name
54
+ msg='The tree callback plugin is deprecated.',
55
+ version='2.23',
56
+ deprecator=_deprecator.ANSIBLE_CORE_DEPRECATOR, # entire plugin being removed; this improves the messaging
57
+ )
52
58
 
53
59
  def set_options(self, task_keys=None, var_options=None, direct=None):
54
60
  """ override to set self.tree """
@@ -252,7 +252,7 @@ class Connection(ConnectionBase):
252
252
  def _become_success_timeout(self) -> int:
253
253
  """Timeout value for become success in seconds."""
254
254
  if (timeout := self.get_option('become_success_timeout')) < 1:
255
- timeout = C.config.get_configuration_definitions('connection', 'local')['become_success_timeout']['default']
255
+ timeout = C.config.get_config_default('become_success_timeout', plugin_type='connection', plugin_name='local')
256
256
 
257
257
  return timeout
258
258
 
@@ -248,11 +248,13 @@ from ansible.errors import (
248
248
  AnsibleError,
249
249
  AnsibleFileNotFound,
250
250
  )
251
+
252
+ from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
251
253
  from ansible.module_utils.compat.paramiko import _PARAMIKO_IMPORT_ERR as PARAMIKO_IMPORT_ERR, _paramiko as paramiko
252
254
  from ansible.plugins.connection import ConnectionBase
253
255
  from ansible.utils.display import Display
254
256
  from ansible.utils.path import makedirs_safe
255
- from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
257
+ from ansible.module_utils._internal import _deprecator
256
258
 
257
259
  display = Display()
258
260
 
@@ -327,7 +329,12 @@ class Connection(ConnectionBase):
327
329
  _log_channel: str | None = None
328
330
 
329
331
  def __init__(self, *args, **kwargs):
330
- display.deprecated('The paramiko connection plugin is deprecated.', version='2.21')
332
+ display.deprecated( # pylint: disable=ansible-deprecated-unnecessary-collection-name
333
+ msg='The paramiko connection plugin is deprecated.',
334
+ version='2.21',
335
+ deprecator=_deprecator.ANSIBLE_CORE_DEPRECATOR, # entire plugin being removed; this improves the messaging
336
+ )
337
+
331
338
  super().__init__(*args, **kwargs)
332
339
 
333
340
  def _cache_key(self) -> str:
@@ -29,7 +29,7 @@ attributes:
29
29
  platforms: all
30
30
  until:
31
31
  description: Denotes if this action obeys until/retry/poll keywords
32
- support: full
32
+ support: none
33
33
  tags:
34
34
  description: Allows for the 'tags' keyword to control the selection of this action for execution
35
35
  support: full
@@ -115,7 +115,10 @@ def to_bool(value: object) -> bool:
115
115
  result = value_to_check == 1 # backwards compatibility with the old code which checked: value in ('yes', 'on', '1', 'true', 1)
116
116
 
117
117
  # NB: update the doc string to reflect reality once this fallback is removed
118
- display.deprecated(f'The `bool` filter coerced invalid value {value!r} ({native_type_name(value)}) to {result!r}.', version='2.23')
118
+ display.deprecated(
119
+ msg=f'The `bool` filter coerced invalid value {value!r} ({native_type_name(value)}) to {result!r}.',
120
+ version='2.23',
121
+ )
119
122
 
120
123
  return result
121
124
 
@@ -28,7 +28,7 @@ from collections.abc import Mapping
28
28
  from ansible import template as _template
29
29
  from ansible.errors import AnsibleError, AnsibleParserError, AnsibleValueOmittedError
30
30
  from ansible.inventory.group import to_safe_group_name as original_safe
31
- from ansible.module_utils._internal import _plugin_exec_context
31
+ from ansible.module_utils._internal import _plugin_info
32
32
  from ansible.parsing.utils.addresses import parse_address
33
33
  from ansible.parsing.dataloader import DataLoader
34
34
  from ansible.plugins import AnsiblePlugin, _ConfigurablePlugin
@@ -314,7 +314,7 @@ class BaseFileInventoryPlugin(_BaseInventoryPlugin):
314
314
  super(BaseFileInventoryPlugin, self).__init__()
315
315
 
316
316
 
317
- class Cacheable(_plugin_exec_context.HasPluginInfo, _ConfigurablePlugin):
317
+ class Cacheable(_plugin_info.HasPluginInfo, _ConfigurablePlugin):
318
318
  """Mixin for inventory plugins which support caching."""
319
319
 
320
320
  _cache: CachePluginAdjudicator