ansible-core 2.19.0rc1__py3-none-any.whl → 2.19.1rc1__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 (48) hide show
  1. ansible/_internal/_ansiballz/_builder.py +25 -14
  2. ansible/_internal/_templating/_engine.py +6 -4
  3. ansible/_internal/_templating/_jinja_bits.py +3 -1
  4. ansible/_internal/_templating/_jinja_plugins.py +7 -2
  5. ansible/_internal/_templating/_lazy_containers.py +5 -5
  6. ansible/config/base.yml +16 -6
  7. ansible/config/manager.py +37 -16
  8. ansible/executor/task_executor.py +5 -2
  9. ansible/executor/task_queue_manager.py +2 -2
  10. ansible/module_utils/_internal/_ansiballz/_extensions/_debugpy.py +97 -0
  11. ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +2 -4
  12. ansible/module_utils/_internal/_traceback.py +1 -1
  13. ansible/module_utils/ansible_release.py +1 -1
  14. ansible/module_utils/basic.py +10 -2
  15. ansible/module_utils/common/validation.py +4 -1
  16. ansible/modules/dnf.py +36 -50
  17. ansible/modules/dnf5.py +36 -29
  18. ansible/modules/meta.py +2 -1
  19. ansible/modules/service_facts.py +5 -1
  20. ansible/playbook/helpers.py +1 -0
  21. ansible/playbook/taggable.py +1 -2
  22. ansible/plugins/__init__.py +18 -10
  23. ansible/plugins/callback/__init__.py +6 -1
  24. ansible/plugins/filter/to_json.yml +8 -4
  25. ansible/plugins/filter/to_nice_json.yml +3 -2
  26. ansible/plugins/lookup/template.py +6 -1
  27. ansible/release.py +1 -1
  28. ansible/utils/encrypt.py +2 -0
  29. {ansible_core-2.19.0rc1.dist-info → ansible_core-2.19.1rc1.dist-info}/METADATA +1 -1
  30. {ansible_core-2.19.0rc1.dist-info → ansible_core-2.19.1rc1.dist-info}/RECORD +48 -47
  31. ansible_test/_internal/commands/integration/coverage.py +2 -2
  32. ansible_test/_internal/commands/shell/__init__.py +67 -28
  33. ansible_test/_internal/coverage_util.py +28 -25
  34. ansible_test/_internal/debugging.py +337 -49
  35. ansible_test/_internal/host_profiles.py +43 -43
  36. ansible_test/_internal/metadata.py +7 -42
  37. ansible_test/_internal/python_requirements.py +2 -2
  38. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
  39. ansible_test/_util/target/setup/bootstrap.sh +37 -16
  40. {ansible_core-2.19.0rc1.dist-info → ansible_core-2.19.1rc1.dist-info}/WHEEL +0 -0
  41. {ansible_core-2.19.0rc1.dist-info → ansible_core-2.19.1rc1.dist-info}/entry_points.txt +0 -0
  42. {ansible_core-2.19.0rc1.dist-info → ansible_core-2.19.1rc1.dist-info}/licenses/COPYING +0 -0
  43. {ansible_core-2.19.0rc1.dist-info → ansible_core-2.19.1rc1.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  44. {ansible_core-2.19.0rc1.dist-info → ansible_core-2.19.1rc1.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  45. {ansible_core-2.19.0rc1.dist-info → ansible_core-2.19.1rc1.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  46. {ansible_core-2.19.0rc1.dist-info → ansible_core-2.19.1rc1.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  47. {ansible_core-2.19.0rc1.dist-info → ansible_core-2.19.1rc1.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  48. {ansible_core-2.19.0rc1.dist-info → ansible_core-2.19.1rc1.dist-info}/top_level.txt +0 -0
ansible/modules/dnf.py CHANGED
@@ -208,6 +208,8 @@ options:
208
208
  packages to install (because dependencies between the downgraded
209
209
  package and others can cause changes to the packages which were
210
210
  in the earlier transaction).
211
+ - Since this feature is not provided by C(dnf) itself but by M(ansible.builtin.dnf) module,
212
+ using this in combination with wildcard characters in O(name) may result in an unexpected results.
211
213
  type: bool
212
214
  default: "no"
213
215
  version_added: "2.7"
@@ -708,72 +710,56 @@ class DnfModule(YumDnf):
708
710
  self.module.exit_json(msg="", results=results)
709
711
 
710
712
  def _is_installed(self, pkg):
711
- installed_query = dnf.subject.Subject(pkg).get_best_query(sack=self.base.sack).installed()
712
- if dnf.util.is_glob_pattern(pkg):
713
- available_query = dnf.subject.Subject(pkg).get_best_query(sack=self.base.sack).available()
714
- return not (
715
- {p.name for p in available_query} - {p.name for p in installed_query}
716
- )
717
- else:
718
- return bool(installed_query)
713
+ return bool(dnf.subject.Subject(pkg).get_best_query(sack=self.base.sack).installed())
719
714
 
720
715
  def _is_newer_version_installed(self, pkg_spec):
716
+ # expects a versioned package spec
721
717
  try:
722
718
  if isinstance(pkg_spec, dnf.package.Package):
723
719
  installed = sorted(self.base.sack.query().installed().filter(name=pkg_spec.name, arch=pkg_spec.arch))[-1]
724
720
  return installed.evr_gt(pkg_spec)
725
721
  else:
726
- available = dnf.subject.Subject(pkg_spec).get_best_query(sack=self.base.sack).available()
727
- installed = self.base.sack.query().installed().filter(name=available[0].name)
728
- for arch in sorted(set(p.arch for p in installed)): # select only from already-installed arches for this case
729
- installed_pkg = sorted(installed.filter(arch=arch))[-1]
730
- try:
731
- available_pkg = sorted(available.filter(arch=arch))[-1]
732
- except IndexError:
733
- continue # nothing currently available for this arch; keep going
734
- if installed_pkg.evr_gt(available_pkg):
735
- return True
736
- return False
722
+ solution = dnf.subject.Subject(pkg_spec).get_best_solution(self.base.sack)
723
+ q = solution["query"]
724
+ if not q or not solution['nevra'] or solution['nevra'].has_just_name():
725
+ return False
726
+ installed = self.base.sack.query().installed().filter(name=solution['nevra'].name)
727
+ if not installed:
728
+ return False
729
+ return installed[0].evr_gt(q[0])
737
730
  except IndexError:
738
731
  return False
739
732
 
740
733
  def _mark_package_install(self, pkg_spec, upgrade=False):
741
734
  """Mark the package for install."""
742
- is_newer_version_installed = self._is_newer_version_installed(pkg_spec)
743
- is_installed = self._is_installed(pkg_spec)
744
735
  msg = ''
745
736
  try:
746
- if is_newer_version_installed:
737
+ if dnf.util.is_glob_pattern(pkg_spec):
738
+ # Special case for package specs that contain glob characters.
739
+ # For these we skip `is_installed` and `is_newer_version_installed` tests that allow for the
740
+ # allow_downgrade feature and pass the package specs to dnf.
741
+ # Since allow_downgrade is not available in dnf and while it is relatively easy to implement it for
742
+ # package specs that evaluate to a single package, trying to mimic what would the dnf machinery do
743
+ # for glob package specs and then filtering those for allow_downgrade appears to always
744
+ # result in naive/inferior solution.
745
+ # NOTE this has historically never worked even before https://github.com/ansible/ansible/pull/82725
746
+ # where our (buggy) custom code ignored wildcards for the installed checks.
747
+ # TODO reasearch how feasible it is to implement the above
748
+ if upgrade:
749
+ # for upgrade we pass the spec to both upgrade and install, to satisfy both available and installed
750
+ # packages evaluated from the glob spec
751
+ try:
752
+ self.base.upgrade(pkg_spec)
753
+ except dnf.exceptions.PackagesNotInstalledError:
754
+ pass
755
+ self.base.install(pkg_spec, strict=self.base.conf.strict)
756
+ elif self._is_newer_version_installed(pkg_spec):
747
757
  if self.allow_downgrade:
748
- # dnf only does allow_downgrade, we have to handle this ourselves
749
- # because it allows a possibility for non-idempotent transactions
750
- # on a system's package set (pending the yum repo has many old
751
- # NVRs indexed)
752
- if upgrade:
753
- if is_installed: # Case 1
754
- # TODO: Is this case reachable?
755
- #
756
- # _is_installed() demands a name (*not* NVR) or else is always False
757
- # (wildcards are treated literally).
758
- #
759
- # Meanwhile, _is_newer_version_installed() demands something versioned
760
- # or else is always false.
761
- #
762
- # I fail to see how they can both be true at the same time for any
763
- # given pkg_spec. -re
764
- self.base.upgrade(pkg_spec)
765
- else: # Case 2
766
- self.base.install(pkg_spec, strict=self.base.conf.strict)
767
- else: # Case 3
768
- self.base.install(pkg_spec, strict=self.base.conf.strict)
769
- else: # Case 4, Nothing to do, report back
770
- pass
771
- elif is_installed: # A potentially older (or same) version is installed
772
- if upgrade: # Case 5
758
+ self.base.install(pkg_spec, strict=self.base.conf.strict)
759
+ elif self._is_installed(pkg_spec):
760
+ if upgrade:
773
761
  self.base.upgrade(pkg_spec)
774
- else: # Case 6, Nothing to do, report back
775
- pass
776
- else: # Case 7, The package is not installed, simply install it
762
+ else:
777
763
  self.base.install(pkg_spec, strict=self.base.conf.strict)
778
764
  except dnf.exceptions.MarkingError as e:
779
765
  msg = "No package {0} available.".format(pkg_spec)
ansible/modules/dnf5.py CHANGED
@@ -178,6 +178,8 @@ options:
178
178
  packages to install (because dependencies between the downgraded
179
179
  package and others can cause changes to the packages which were
180
180
  in the earlier transaction).
181
+ - Since this feature is not provided by C(dnf5) itself but by M(ansible.builtin.dnf5) module,
182
+ using this in combination with wildcard characters in O(name) may result in an unexpected results.
181
183
  type: bool
182
184
  default: "no"
183
185
  install_repoquery:
@@ -368,7 +370,7 @@ libdnf5 = None
368
370
  LIBDNF5_ERRORS = RuntimeError
369
371
 
370
372
 
371
- def is_installed(base, spec):
373
+ def get_resolve_spec_settings():
372
374
  settings = libdnf5.base.ResolveSpecSettings()
373
375
  try:
374
376
  settings.set_group_with_name(True)
@@ -394,47 +396,34 @@ def is_installed(base, spec):
394
396
  settings.group_with_name = True
395
397
  settings.with_binaries = False
396
398
  settings.with_provides = False
399
+ return settings
400
+
401
+
402
+ def is_installed(base, spec):
403
+ settings = get_resolve_spec_settings()
397
404
 
398
405
  installed_query = libdnf5.rpm.PackageQuery(base)
399
406
  installed_query.filter_installed()
400
407
  match, nevra = installed_query.resolve_pkg_spec(spec, settings, True)
401
-
402
- # FIXME use `is_glob_pattern` function when available:
403
- # https://github.com/rpm-software-management/dnf5/issues/1563
404
- glob_patterns = set("*[?")
405
- if any(set(char) & glob_patterns for char in spec):
406
- available_query = libdnf5.rpm.PackageQuery(base)
407
- available_query.filter_available()
408
- available_query.resolve_pkg_spec(spec, settings, True)
409
-
410
- return not (
411
- {p.get_name() for p in available_query} - {p.get_name() for p in installed_query}
412
- )
413
- else:
414
- return match
408
+ return match
415
409
 
416
410
 
417
411
  def is_newer_version_installed(base, spec):
418
- # FIXME investigate whether this function can be replaced by dnf5's allow_downgrade option
412
+ # expects a versioned package spec
419
413
  if "/" in spec:
420
414
  spec = spec.split("/")[-1]
421
415
  if spec.endswith(".rpm"):
422
416
  spec = spec[:-4]
423
417
 
424
- try:
425
- spec_nevra = next(iter(libdnf5.rpm.Nevra.parse(spec)))
426
- except LIBDNF5_ERRORS:
427
- return False
428
- except StopIteration:
429
- return False
430
-
431
- spec_version = spec_nevra.get_version()
432
- if not spec_version:
418
+ settings = get_resolve_spec_settings()
419
+ match, spec_nevra = libdnf5.rpm.PackageQuery(base).resolve_pkg_spec(spec, settings, True)
420
+ if not match or spec_nevra.has_just_name():
433
421
  return False
422
+ spec_name = spec_nevra.get_name()
434
423
 
435
424
  installed = libdnf5.rpm.PackageQuery(base)
436
425
  installed.filter_installed()
437
- installed.filter_name([spec_nevra.get_name()])
426
+ installed.filter_name([spec_name])
438
427
  installed.filter_latest_evr()
439
428
  try:
440
429
  installed_package = list(installed)[-1]
@@ -442,8 +431,8 @@ def is_newer_version_installed(base, spec):
442
431
  return False
443
432
 
444
433
  target = libdnf5.rpm.PackageQuery(base)
445
- target.filter_name([spec_nevra.get_name()])
446
- target.filter_version([spec_version])
434
+ target.filter_name([spec_name])
435
+ target.filter_version([spec_nevra.get_version()])
447
436
  spec_release = spec_nevra.get_release()
448
437
  if spec_release:
449
438
  target.filter_release([spec_release])
@@ -725,8 +714,26 @@ class Dnf5Module(YumDnf):
725
714
  goal.add_rpm_upgrade(settings)
726
715
  elif self.state in {"installed", "present", "latest"}:
727
716
  upgrade = self.state == "latest"
717
+ # FIXME use `is_glob_pattern` function when available:
718
+ # https://github.com/rpm-software-management/dnf5/issues/1563
719
+ glob_patterns = set("*[?")
728
720
  for spec in self.names:
729
- if is_newer_version_installed(base, spec):
721
+ if any(set(char) & glob_patterns for char in spec):
722
+ # Special case for package specs that contain glob characters.
723
+ # For these we skip `is_installed` and `is_newer_version_installed` tests that allow for the
724
+ # allow_downgrade feature and pass the package specs to dnf.
725
+ # Since allow_downgrade is not available in dnf and while it is relatively easy to implement it for
726
+ # package specs that evaluate to a single package, trying to mimic what would the dnf machinery do
727
+ # for glob package specs and then filtering those for allow_downgrade appears to always
728
+ # result in naive/inferior solution.
729
+ # TODO reasearch how feasible it is to implement the above
730
+ if upgrade:
731
+ # for upgrade we pass the spec to both upgrade and install, to satisfy both available and installed
732
+ # packages evaluated from the glob spec
733
+ goal.add_upgrade(spec, settings)
734
+ if not self.update_only:
735
+ goal.add_install(spec, settings)
736
+ elif is_newer_version_installed(base, spec):
730
737
  if self.allow_downgrade:
731
738
  goal.add_install(spec, settings)
732
739
  elif is_installed(base, spec):
ansible/modules/meta.py CHANGED
@@ -33,6 +33,7 @@ options:
33
33
  - V(clear_facts) (added in Ansible 2.1) causes the gathered facts for the hosts specified in the play's list of hosts to be cleared,
34
34
  including the fact cache.
35
35
  - V(clear_host_errors) (added in Ansible 2.1) clears the failed state (if any) from hosts specified in the play's list of hosts.
36
+ This will make them available for targetting in subsequent plays, but not continue execution in the current play.
36
37
  - V(end_play) (added in Ansible 2.2) causes the play to end without failing the host(s). Note that this affects all hosts.
37
38
  - V(reset_connection) (added in Ansible 2.3) interrupts a persistent connection (i.e. ssh + control persist)
38
39
  - V(end_host) (added in Ansible 2.8) is a per-host variation of V(end_play). Causes the play to end for the current host without failing it.
@@ -108,7 +109,7 @@ EXAMPLES = r"""
108
109
  - name: Clear gathered facts from all currently targeted hosts
109
110
  ansible.builtin.meta: clear_facts
110
111
 
111
- # Example showing how to continue using a failed target
112
+ # Example showing how to continue using a failed target, for the next play
112
113
  - name: Bring host back to play after failure
113
114
  ansible.builtin.copy:
114
115
  src: file
@@ -232,7 +232,11 @@ class ServiceScanService(BaseService):
232
232
  if service_name == "*":
233
233
  continue
234
234
  service_state = line_data[1]
235
- service_runlevels = all_services_runlevels[service_name]
235
+ try:
236
+ service_runlevels = all_services_runlevels[service_name]
237
+ except KeyError:
238
+ self.module.warn(f"Service {service_name} not found in the service list")
239
+ continue
236
240
  service_data = {"name": service_name, "runlevels": service_runlevels, "state": service_state, "source": "openrc"}
237
241
  services[service_name] = service_data
238
242
 
@@ -169,6 +169,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
169
169
  if not isinstance(parent_include, TaskInclude):
170
170
  parent_include = parent_include._parent
171
171
  continue
172
+ parent_include.post_validate(templar=templar)
172
173
  parent_include_dir = os.path.dirname(parent_include.args.get('_raw_params'))
173
174
  if cumulative_path is None:
174
175
  cumulative_path = parent_include_dir
@@ -20,7 +20,6 @@ from __future__ import annotations
20
20
  import typing as t
21
21
 
22
22
  from ansible.errors import AnsibleError
23
- from ansible.module_utils.six import string_types
24
23
  from ansible.module_utils.common.sentinel import Sentinel
25
24
  from ansible.module_utils._internal._datatag import AnsibleTagHelper
26
25
  from ansible.playbook.attribute import FieldAttribute
@@ -40,7 +39,7 @@ def _flatten_tags(tags: list[str | int]) -> list[str | int]:
40
39
  class Taggable:
41
40
 
42
41
  untagged = frozenset(['untagged'])
43
- tags = FieldAttribute(isa='list', default=list, listof=(string_types, int), extend=True)
42
+ tags = FieldAttribute(isa='list', default=list, listof=(str, int), extend=True)
44
43
 
45
44
  def _load_tags(self, attr, ds):
46
45
  if isinstance(ds, list):
@@ -79,6 +79,7 @@ class AnsiblePlugin(_AnsiblePluginInfoMixin, _ConfigurablePlugin, metaclass=abc.
79
79
 
80
80
  def __init__(self):
81
81
  self._options = {}
82
+ self._origins = {}
82
83
  self._defs = None
83
84
 
84
85
  @property
@@ -98,11 +99,16 @@ class AnsiblePlugin(_AnsiblePluginInfoMixin, _ConfigurablePlugin, metaclass=abc.
98
99
  return bool(possible_fqcns.intersection(set(self.ansible_aliases)))
99
100
 
100
101
  def get_option_and_origin(self, option, hostvars=None):
101
- try:
102
- option_value, origin = C.config.get_config_value_and_origin(option, plugin_type=self.plugin_type, plugin_name=self._load_name, variables=hostvars)
103
- except AnsibleError as e:
104
- raise KeyError(str(e))
105
- return option_value, origin
102
+ if option not in self._options:
103
+ try:
104
+ # some plugins don't use set_option(s) and cannot use direct settings, so this populates the local copy for them
105
+ self._options[option], self._origins[option] = C.config.get_config_value_and_origin(option, plugin_type=self.plugin_type,
106
+ plugin_name=self._load_name, variables=hostvars)
107
+ except AnsibleError as e:
108
+ # callers expect key error on missing
109
+ raise KeyError() from e
110
+
111
+ return self._options[option], self._origins[option]
106
112
 
107
113
  @functools.cached_property
108
114
  def __plugin_info(self):
@@ -113,11 +119,10 @@ class AnsiblePlugin(_AnsiblePluginInfoMixin, _ConfigurablePlugin, metaclass=abc.
113
119
  return _plugin_info.get_plugin_info(self)
114
120
 
115
121
  def get_option(self, option, hostvars=None):
116
-
117
122
  if option not in self._options:
118
- option_value, dummy = self.get_option_and_origin(option, hostvars=hostvars)
119
- self.set_option(option, option_value)
120
- return self._options.get(option)
123
+ # let it populate _options
124
+ self.get_option_and_origin(option, hostvars=hostvars)
125
+ return self._options[option]
121
126
 
122
127
  def get_options(self, hostvars=None):
123
128
  options = {}
@@ -127,6 +132,7 @@ class AnsiblePlugin(_AnsiblePluginInfoMixin, _ConfigurablePlugin, metaclass=abc.
127
132
 
128
133
  def set_option(self, option, value):
129
134
  self._options[option] = C.config.get_config_value(option, plugin_type=self.plugin_type, plugin_name=self._load_name, direct={option: value})
135
+ self._origins[option] = 'Direct'
130
136
  _display._report_config_warnings(self.__plugin_info)
131
137
 
132
138
  def set_options(self, task_keys=None, var_options=None, direct=None):
@@ -137,12 +143,14 @@ class AnsiblePlugin(_AnsiblePluginInfoMixin, _ConfigurablePlugin, metaclass=abc.
137
143
  :arg var_options: Dict with either 'connection variables'
138
144
  :arg direct: Dict with 'direct assignment'
139
145
  """
140
- self._options = C.config.get_plugin_options(self.plugin_type, self._load_name, keys=task_keys, variables=var_options, direct=direct)
146
+ self._options, self._origins = C.config.get_plugin_options_and_origins(self.plugin_type, self._load_name, keys=task_keys,
147
+ variables=var_options, direct=direct)
141
148
 
142
149
  # allow extras/wildcards from vars that are not directly consumed in configuration
143
150
  # this is needed to support things like winrm that can have extended protocol options we don't directly handle
144
151
  if self.allow_extras and var_options and '_extras' in var_options:
145
152
  # these are largely unvalidated passthroughs, either plugin or underlying API will validate
153
+ # TODO: deprecate and remove, most plugins that needed this don't use this facility anymore
146
154
  self._options['_extras'] = var_options['_extras']
147
155
  _display._report_config_warnings(self.__plugin_info)
148
156
 
@@ -228,10 +228,14 @@ class CallbackBase(AnsiblePlugin):
228
228
 
229
229
  def set_option(self, k, v):
230
230
  self._plugin_options[k] = C.config.get_config_value(k, plugin_type=self.plugin_type, plugin_name=self._load_name, direct={k: v})
231
+ self._origins[k] = 'direct'
231
232
 
232
233
  def get_option(self, k, hostvars=None):
233
234
  return self._plugin_options[k]
234
235
 
236
+ def get_option_and_origin(self, k, hostvars=None):
237
+ return self._plugin_options[k], self._origins[k]
238
+
235
239
  def has_option(self, option):
236
240
  return (option in self._plugin_options)
237
241
 
@@ -241,7 +245,8 @@ class CallbackBase(AnsiblePlugin):
241
245
  """
242
246
 
243
247
  # load from config
244
- self._plugin_options = C.config.get_plugin_options(self.plugin_type, self._load_name, keys=task_keys, variables=var_options, direct=direct)
248
+ self._plugin_options, self._origins = C.config.get_plugin_options_and_origins(self.plugin_type, self._load_name,
249
+ keys=task_keys, variables=var_options, direct=direct)
245
250
 
246
251
  @staticmethod
247
252
  def host_label(result: CallbackTaskResult) -> str:
@@ -23,8 +23,9 @@ DOCUMENTATION:
23
23
  default: True
24
24
  version_added: '2.9'
25
25
  allow_nan:
26
- description: When V(False), strict adherence to float value limits of the JSON specifications, so C(nan), C(inf) and C(-inf) values will produce errors.
27
- When V(True), JavaScript equivalents will be used (C(NaN), C(Infinity), C(-Infinity)).
26
+ description:
27
+ - When V(False), out-of-range float values C(nan), C(inf) and C(-inf) will result in an error.
28
+ - When V(True), out-of-range float values will be represented using their JavaScript equivalents, C(NaN), C(Infinity) and C(-Infinity).
28
29
  default: True
29
30
  type: bool
30
31
  check_circular:
@@ -42,8 +43,11 @@ DOCUMENTATION:
42
43
  separators:
43
44
  description: The C(item) and C(key) separator to be used in the serialized output,
44
45
  default may change depending on O(indent) and Python version.
45
- default: "(', ', ': ')"
46
- type: tuple
46
+ default:
47
+ - ', '
48
+ - ': '
49
+ type: list
50
+ elements: str
47
51
  skipkeys:
48
52
  description: If V(True), keys that are not basic Python types will be skipped.
49
53
  default: False
@@ -23,8 +23,9 @@ DOCUMENTATION:
23
23
  default: True
24
24
  version_added: '2.9'
25
25
  allow_nan:
26
- description: When V(False), strict adherence to float value limits of the JSON specification, so C(nan), C(inf) and C(-inf) values will produce errors.
27
- When V(True), JavaScript equivalents will be used (C(NaN), C(Infinity), C(-Infinity)).
26
+ description:
27
+ - When V(False), out-of-range float values C(nan), C(inf) and C(-inf) will result in an error.
28
+ - When V(True), out-of-range float values will be represented using their JavaScript equivalents, C(NaN), C(Infinity) and C(-Infinity).
28
29
  default: True
29
30
  type: bool
30
31
  check_circular:
@@ -107,6 +107,7 @@ from ansible.errors import AnsibleError
107
107
  from ansible.plugins.lookup import LookupBase
108
108
  from ansible.template import trust_as_template
109
109
  from ansible._internal._templating import _template_vars
110
+ from ansible._internal._templating._engine import TemplateOptions, TemplateOverrides
110
111
  from ansible.utils.display import Display
111
112
 
112
113
 
@@ -174,7 +175,11 @@ class LookupModule(LookupBase):
174
175
  )
175
176
 
176
177
  data_templar = templar.copy_with_new_env(available_variables=vars, searchpath=searchpath)
177
- res = data_templar.template(template_data, escape_backslashes=False, overrides=overrides)
178
+ # use the internal template API to avoid forced top-level finalization behavior imposed by the public API
179
+ res = data_templar._engine.template(template_data, options=TemplateOptions(
180
+ escape_backslashes=False,
181
+ overrides=TemplateOverrides.from_kwargs(overrides),
182
+ ))
178
183
 
179
184
  ret.append(res)
180
185
  else:
ansible/release.py CHANGED
@@ -17,6 +17,6 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- __version__ = '2.19.0rc1'
20
+ __version__ = '2.19.1rc1'
21
21
  __author__ = 'Ansible, Inc.'
22
22
  __codename__ = "What Is and What Should Never Be"
ansible/utils/encrypt.py CHANGED
@@ -99,6 +99,8 @@ class PasslibHash(BaseHash):
99
99
  salt = self._clean_salt(salt)
100
100
  rounds = self._clean_rounds(rounds)
101
101
  ident = self._clean_ident(ident)
102
+ if salt_size is not None and not isinstance(salt_size, int):
103
+ raise TypeError("salt_size must be an integer")
102
104
  return self._hash(secret, salt=salt, salt_size=salt_size, rounds=rounds, ident=ident)
103
105
 
104
106
  def _clean_ident(self, ident):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ansible-core
3
- Version: 2.19.0rc1
3
+ Version: 2.19.1rc1
4
4
  Summary: Radically simple IT automation
5
5
  Author: Ansible Project
6
6
  Project-URL: Homepage, https://ansible.com/