ansible-core 2.15.0__py3-none-any.whl → 2.15.2__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 (61) hide show
  1. ansible/config/manager.py +1 -1
  2. ansible/galaxy/collection/__init__.py +17 -30
  3. ansible/galaxy/collection/concrete_artifact_manager.py +12 -6
  4. ansible/galaxy/dependency_resolution/dataclasses.py +6 -3
  5. ansible/module_utils/ansible_release.py +1 -1
  6. ansible/modules/apt_key.py +8 -5
  7. ansible/modules/apt_repository.py +2 -0
  8. ansible/modules/deb822_repository.py +1 -1
  9. ansible/modules/dnf5.py +8 -8
  10. ansible/modules/find.py +3 -0
  11. ansible/modules/uri.py +9 -1
  12. ansible/modules/validate_argument_spec.py +1 -1
  13. ansible/plugins/action/template.py +26 -15
  14. ansible/plugins/connection/paramiko_ssh.py +8 -0
  15. ansible/plugins/connection/psrp.py +3 -3
  16. ansible/plugins/connection/ssh.py +19 -2
  17. ansible/plugins/filter/comment.yml +1 -1
  18. ansible/plugins/filter/split.yml +1 -1
  19. ansible/plugins/filter/to_yaml.yml +1 -1
  20. ansible/plugins/lookup/template.py +11 -6
  21. ansible/plugins/strategy/__init__.py +20 -12
  22. ansible/plugins/test/change.yml +1 -1
  23. ansible/plugins/test/changed.yml +1 -1
  24. ansible/plugins/test/reachable.yml +1 -1
  25. ansible/plugins/test/succeeded.yml +1 -1
  26. ansible/plugins/test/success.yml +1 -1
  27. ansible/plugins/test/successful.yml +1 -1
  28. ansible/plugins/test/unreachable.yml +1 -1
  29. ansible/release.py +1 -1
  30. ansible/template/__init__.py +42 -28
  31. ansible/utils/_junit_xml.py +5 -1
  32. {ansible_core-2.15.0.dist-info → ansible_core-2.15.2.dist-info}/METADATA +1 -1
  33. {ansible_core-2.15.0.dist-info → ansible_core-2.15.2.dist-info}/RECORD +61 -61
  34. ansible_test/_data/completion/remote.txt +2 -1
  35. ansible_test/_internal/__init__.py +11 -0
  36. ansible_test/_internal/cli/commands/env.py +1 -1
  37. ansible_test/_internal/commands/env/__init__.py +14 -17
  38. ansible_test/_internal/commands/integration/__init__.py +1 -1
  39. ansible_test/_internal/commands/integration/cloud/__init__.py +3 -3
  40. ansible_test/_internal/commands/sanity/pylint.py +4 -4
  41. ansible_test/_internal/commands/sanity/validate_modules.py +2 -2
  42. ansible_test/_internal/containers.py +2 -2
  43. ansible_test/_internal/coverage_util.py +2 -2
  44. ansible_test/_internal/data.py +2 -7
  45. ansible_test/_internal/git.py +1 -1
  46. ansible_test/_internal/junit_xml.py +5 -1
  47. ansible_test/_internal/payload.py +2 -2
  48. ansible_test/_internal/provider/layout/__init__.py +1 -1
  49. ansible_test/_internal/provider/layout/ansible.py +15 -0
  50. ansible_test/_internal/provider/layout/collection.py +9 -1
  51. ansible_test/_internal/provisioning.py +5 -2
  52. ansible_test/_internal/pypi_proxy.py +4 -4
  53. ansible_test/_internal/test.py +3 -5
  54. ansible_test/_internal/timeout.py +56 -19
  55. ansible_test/_internal/util.py +4 -0
  56. ansible_test/_internal/util_common.py +38 -6
  57. {ansible_core-2.15.0.data → ansible_core-2.15.2.data}/scripts/ansible-test +0 -0
  58. {ansible_core-2.15.0.dist-info → ansible_core-2.15.2.dist-info}/COPYING +0 -0
  59. {ansible_core-2.15.0.dist-info → ansible_core-2.15.2.dist-info}/WHEEL +0 -0
  60. {ansible_core-2.15.0.dist-info → ansible_core-2.15.2.dist-info}/entry_points.txt +0 -0
  61. {ansible_core-2.15.0.dist-info → ansible_core-2.15.2.dist-info}/top_level.txt +0 -0
@@ -50,10 +50,12 @@ DOCUMENTATION = """
50
50
  description: The string marking the beginning of a comment statement.
51
51
  version_added: '2.12'
52
52
  type: str
53
+ default: '{#'
53
54
  comment_end_string:
54
55
  description: The string marking the end of a comment statement.
55
56
  version_added: '2.12'
56
57
  type: str
58
+ default: '#}'
57
59
  """
58
60
 
59
61
  EXAMPLES = """
@@ -145,13 +147,16 @@ class LookupModule(LookupBase):
145
147
  vars.update(generate_ansible_template_vars(term, lookupfile))
146
148
  vars.update(lookup_template_vars)
147
149
 
148
- with templar.set_temporary_context(variable_start_string=variable_start_string,
149
- variable_end_string=variable_end_string,
150
- comment_start_string=comment_start_string,
151
- comment_end_string=comment_end_string,
152
- available_variables=vars, searchpath=searchpath):
150
+ with templar.set_temporary_context(available_variables=vars, searchpath=searchpath):
151
+ overrides = dict(
152
+ variable_start_string=variable_start_string,
153
+ variable_end_string=variable_end_string,
154
+ comment_start_string=comment_start_string,
155
+ comment_end_string=comment_end_string
156
+ )
153
157
  res = templar.template(template_data, preserve_trailing_newlines=True,
154
- convert_data=convert_data_p, escape_backslashes=False)
158
+ convert_data=convert_data_p, escape_backslashes=False,
159
+ overrides=overrides)
155
160
 
156
161
  if (C.DEFAULT_JINJA2_NATIVE and not jinja2_native) or not convert_data_p:
157
162
  # jinja2_native is true globally but off for the lookup, we need this text
@@ -652,20 +652,28 @@ class StrategyBase:
652
652
  # only ensure that notified handlers exist, if so save the notifications for when
653
653
  # handlers are actually flushed so the last defined handlers are exexcuted,
654
654
  # otherwise depending on the setting either error or warn
655
+ host_state = iterator.get_state_for_host(original_host.name)
655
656
  for notification in result_item['_ansible_notify']:
656
- if any(self.search_handlers_by_notification(notification, iterator)):
657
- iterator.add_notification(original_host.name, notification)
658
- display.vv(f"Notification for handler {notification} has been saved.")
659
- continue
660
-
661
- msg = (
662
- f"The requested handler '{notification}' was not found in either the main handlers"
663
- " list nor in the listening handlers list"
664
- )
665
- if C.ERROR_ON_MISSING_HANDLER:
666
- raise AnsibleError(msg)
657
+ for handler in self.search_handlers_by_notification(notification, iterator):
658
+ if host_state.run_state == IteratingStates.HANDLERS:
659
+ # we're currently iterating handlers, so we need to expand this now
660
+ if handler.notify_host(original_host):
661
+ # NOTE even with notifications deduplicated this can still happen in case of handlers being
662
+ # notified multiple times using different names, like role name or fqcn
663
+ self._tqm.send_callback('v2_playbook_on_notify', handler, original_host)
664
+ else:
665
+ iterator.add_notification(original_host.name, notification)
666
+ display.vv(f"Notification for handler {notification} has been saved.")
667
+ break
667
668
  else:
668
- display.warning(msg)
669
+ msg = (
670
+ f"The requested handler '{notification}' was not found in either the main handlers"
671
+ " list nor in the listening handlers list"
672
+ )
673
+ if C.ERROR_ON_MISSING_HANDLER:
674
+ raise AnsibleError(msg)
675
+ else:
676
+ display.warning(msg)
669
677
 
670
678
  if 'add_host' in result_item:
671
679
  # this task added a new host (add_host module)
@@ -14,7 +14,7 @@ DOCUMENTATION:
14
14
  required: True
15
15
  EXAMPLES: |
16
16
  # test 'status' to know how to respond
17
- {{ (taskresults is changed }}
17
+ {{ taskresults is changed }}
18
18
 
19
19
  RETURN:
20
20
  _value:
@@ -14,7 +14,7 @@ DOCUMENTATION:
14
14
  required: True
15
15
  EXAMPLES: |
16
16
  # test 'status' to know how to respond
17
- {{ (taskresults is changed }}
17
+ {{ taskresults is changed }}
18
18
 
19
19
  RETURN:
20
20
  _value:
@@ -13,7 +13,7 @@ DOCUMENTATION:
13
13
  required: True
14
14
  EXAMPLES: |
15
15
  # test 'status' to know how to respond
16
- {{ (taskresults is reachable }}
16
+ {{ taskresults is reachable }}
17
17
 
18
18
  RETURN:
19
19
  _value:
@@ -14,7 +14,7 @@ DOCUMENTATION:
14
14
  required: True
15
15
  EXAMPLES: |
16
16
  # test 'status' to know how to respond
17
- {{ (taskresults is success }}
17
+ {{ taskresults is success }}
18
18
 
19
19
  RETURN:
20
20
  _value:
@@ -14,7 +14,7 @@ DOCUMENTATION:
14
14
  required: True
15
15
  EXAMPLES: |
16
16
  # test 'status' to know how to respond
17
- {{ (taskresults is success }}
17
+ {{ taskresults is success }}
18
18
 
19
19
  RETURN:
20
20
  _value:
@@ -14,7 +14,7 @@ DOCUMENTATION:
14
14
  required: True
15
15
  EXAMPLES: |
16
16
  # test 'status' to know how to respond
17
- {{ (taskresults is success }}
17
+ {{ taskresults is success }}
18
18
 
19
19
  RETURN:
20
20
  _value:
@@ -13,7 +13,7 @@ DOCUMENTATION:
13
13
  required: True
14
14
  EXAMPLES: |
15
15
  # test 'status' to know how to respond
16
- {{ (taskresults is unreachable }}
16
+ {{ taskresults is unreachable }}
17
17
 
18
18
  RETURN:
19
19
  _value:
ansible/release.py CHANGED
@@ -19,6 +19,6 @@
19
19
  from __future__ import (absolute_import, division, print_function)
20
20
  __metaclass__ = type
21
21
 
22
- __version__ = '2.15.0'
22
+ __version__ = '2.15.2'
23
23
  __author__ = 'Ansible, Inc.'
24
24
  __codename__ = "Ten Years Gone"
@@ -153,6 +153,39 @@ def _escape_backslashes(data, jinja_env):
153
153
  return data
154
154
 
155
155
 
156
+ def _create_overlay(data, overrides, jinja_env):
157
+ if overrides is None:
158
+ overrides = {}
159
+
160
+ try:
161
+ has_override_header = data.startswith(JINJA2_OVERRIDE)
162
+ except (TypeError, AttributeError):
163
+ has_override_header = False
164
+
165
+ if overrides or has_override_header:
166
+ overlay = jinja_env.overlay(**overrides)
167
+ else:
168
+ overlay = jinja_env
169
+
170
+ # Get jinja env overrides from template
171
+ if has_override_header:
172
+ eol = data.find('\n')
173
+ line = data[len(JINJA2_OVERRIDE):eol]
174
+ data = data[eol + 1:]
175
+ for pair in line.split(','):
176
+ if ':' not in pair:
177
+ raise AnsibleError("failed to parse jinja2 override '%s'."
178
+ " Did you use something different from colon as key-value separator?" % pair.strip())
179
+ (key, val) = pair.split(':', 1)
180
+ key = key.strip()
181
+ if hasattr(overlay, key):
182
+ setattr(overlay, key, ast.literal_eval(val.strip()))
183
+ else:
184
+ display.warning(f"Could not find Jinja2 environment setting to override: '{key}'")
185
+
186
+ return data, overlay
187
+
188
+
156
189
  def is_possibly_template(data, jinja_env):
157
190
  """Determines if a string looks like a template, by seeing if it
158
191
  contains a jinja2 start delimiter. Does not guarantee that the string
@@ -705,7 +738,7 @@ class Templar:
705
738
  variable = self._convert_bare_variable(variable)
706
739
 
707
740
  if isinstance(variable, string_types):
708
- if not self.is_possibly_template(variable):
741
+ if not self.is_possibly_template(variable, overrides):
709
742
  return variable
710
743
 
711
744
  # Check to see if the string we are trying to render is just referencing a single
@@ -776,8 +809,9 @@ class Templar:
776
809
 
777
810
  templatable = is_template
778
811
 
779
- def is_possibly_template(self, data):
780
- return is_possibly_template(data, self.environment)
812
+ def is_possibly_template(self, data, overrides=None):
813
+ data, env = _create_overlay(data, overrides, self.environment)
814
+ return is_possibly_template(data, env)
781
815
 
782
816
  def _convert_bare_variable(self, variable):
783
817
  '''
@@ -918,31 +952,11 @@ class Templar:
918
952
  if fail_on_undefined is None:
919
953
  fail_on_undefined = self._fail_on_undefined_errors
920
954
 
921
- has_template_overrides = data.startswith(JINJA2_OVERRIDE)
922
-
923
955
  try:
924
956
  # NOTE Creating an overlay that lives only inside do_template means that overrides are not applied
925
957
  # when templating nested variables in AnsibleJ2Vars where Templar.environment is used, not the overlay.
926
958
  # This is historic behavior that is kept for backwards compatibility.
927
- if overrides:
928
- myenv = self.environment.overlay(overrides)
929
- elif has_template_overrides:
930
- myenv = self.environment.overlay()
931
- else:
932
- myenv = self.environment
933
-
934
- # Get jinja env overrides from template
935
- if has_template_overrides:
936
- eol = data.find('\n')
937
- line = data[len(JINJA2_OVERRIDE):eol]
938
- data = data[eol + 1:]
939
- for pair in line.split(','):
940
- if ':' not in pair:
941
- raise AnsibleError("failed to parse jinja2 override '%s'."
942
- " Did you use something different from colon as key-value separator?" % pair.strip())
943
- (key, val) = pair.split(':', 1)
944
- key = key.strip()
945
- setattr(myenv, key, ast.literal_eval(val.strip()))
959
+ data, myenv = _create_overlay(data, overrides, self.environment)
946
960
 
947
961
  if escape_backslashes:
948
962
  # Allow users to specify backslashes in playbooks as "\\" instead of as "\\\\".
@@ -970,7 +984,7 @@ class Templar:
970
984
  # In case this is a recursive call and we set different concat
971
985
  # function up the stack, reset it in case the value of convert_data
972
986
  # changed in this call
973
- self.environment.concat = self.environment.__class__.concat
987
+ myenv.concat = myenv.__class__.concat
974
988
  # the concat function is set for each Ansible environment,
975
989
  # however for convert_data=False we need to use the concat
976
990
  # function that avoids any evaluation and set it temporarily
@@ -978,13 +992,13 @@ class Templar:
978
992
  # the concat function is called internally in Jinja,
979
993
  # most notably for macro execution
980
994
  if not self.jinja2_native and not convert_data:
981
- self.environment.concat = ansible_concat
995
+ myenv.concat = ansible_concat
982
996
 
983
997
  self.cur_context = t.new_context(jvars, shared=True)
984
998
  rf = t.root_render_func(self.cur_context)
985
999
 
986
1000
  try:
987
- res = self.environment.concat(rf)
1001
+ res = myenv.concat(rf)
988
1002
  unsafe = getattr(self.cur_context, 'unsafe', False)
989
1003
  if unsafe:
990
1004
  res = wrap_var(res)
@@ -1012,7 +1026,7 @@ class Templar:
1012
1026
  # "Hello world\n!\n" instead of "Hello world!\n".
1013
1027
  res_newlines = _count_newlines_from_end(res)
1014
1028
  if data_newlines > res_newlines:
1015
- res += self.environment.newline_sequence * (data_newlines - res_newlines)
1029
+ res += myenv.newline_sequence * (data_newlines - res_newlines)
1016
1030
  if unsafe:
1017
1031
  res = wrap_var(res)
1018
1032
  return res
@@ -144,6 +144,10 @@ class TestSuite:
144
144
  system_out: str | None = None
145
145
  system_err: str | None = None
146
146
 
147
+ def __post_init__(self):
148
+ if self.timestamp and self.timestamp.tzinfo != datetime.timezone.utc:
149
+ raise ValueError(f'timestamp.tzinfo must be {datetime.timezone.utc!r}')
150
+
147
151
  @property
148
152
  def disabled(self) -> int:
149
153
  """The number of disabled test cases."""
@@ -187,7 +191,7 @@ class TestSuite:
187
191
  skipped=self.skipped,
188
192
  tests=self.tests,
189
193
  time=self.time,
190
- timestamp=self.timestamp.isoformat(timespec='seconds') if self.timestamp else None,
194
+ timestamp=self.timestamp.replace(tzinfo=None).isoformat(timespec='seconds') if self.timestamp else None,
191
195
  )
192
196
 
193
197
  def get_xml_element(self) -> ET.Element:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ansible-core
3
- Version: 2.15.0
3
+ Version: 2.15.2
4
4
  Summary: Radically simple IT automation
5
5
  Home-page: https://ansible.com/
6
6
  Author: Ansible, Inc.