ansible-core 2.15.0b2__py3-none-any.whl → 2.15.0b3__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.

@@ -235,8 +235,6 @@ def add_check_options(parser):
235
235
  """Add options for commands which can run with diagnostic information of tasks"""
236
236
  parser.add_argument("-C", "--check", default=False, dest='check', action='store_true',
237
237
  help="don't make any changes; instead, try to predict some of the changes that may occur")
238
- parser.add_argument('--syntax-check', dest='syntax', action='store_true',
239
- help="perform a syntax check on the playbook, but do not execute it")
240
238
  parser.add_argument("-D", "--diff", default=C.DIFF_ALWAYS, dest='diff', action='store_true',
241
239
  help="when changing (small) files and templates, show the differences in those"
242
240
  " files; works great with --check")
ansible/cli/playbook.py CHANGED
@@ -54,6 +54,8 @@ class PlaybookCLI(CLI):
54
54
  opt_help.add_module_options(self.parser)
55
55
 
56
56
  # ansible playbook specific opts
57
+ self.parser.add_argument('--syntax-check', dest='syntax', action='store_true',
58
+ help="perform a syntax check on the playbook, but do not execute it")
57
59
  self.parser.add_argument('--list-tasks', dest='listtasks', action='store_true',
58
60
  help="list all tasks that would be executed")
59
61
  self.parser.add_argument('--list-tags', dest='listtags', action='store_true',
@@ -60,6 +60,8 @@ class HostState:
60
60
  self._blocks = blocks[:]
61
61
  self.handlers = []
62
62
 
63
+ self.handler_notifications = []
64
+
63
65
  self.cur_block = 0
64
66
  self.cur_regular_task = 0
65
67
  self.cur_rescue_task = 0
@@ -120,6 +122,7 @@ class HostState:
120
122
  def copy(self):
121
123
  new_state = HostState(self._blocks)
122
124
  new_state.handlers = self.handlers[:]
125
+ new_state.handler_notifications = self.handler_notifications[:]
123
126
  new_state.cur_block = self.cur_block
124
127
  new_state.cur_regular_task = self.cur_regular_task
125
128
  new_state.cur_rescue_task = self.cur_rescue_task
@@ -650,3 +653,12 @@ class PlayIterator:
650
653
  if not isinstance(fail_state, FailedStates):
651
654
  raise AnsibleAssertionError('Expected fail_state to be a FailedStates but was %s' % (type(fail_state)))
652
655
  self._host_states[hostname].fail_state = fail_state
656
+
657
+ def add_notification(self, hostname: str, notification: str) -> None:
658
+ # preserve order
659
+ host_state = self._host_states[hostname]
660
+ if notification not in host_state.handler_notifications:
661
+ host_state.handler_notifications.append(notification)
662
+
663
+ def clear_notification(self, hostname: str, notification: str) -> None:
664
+ self._host_states[hostname].handler_notifications.remove(notification)
@@ -137,6 +137,12 @@ class TaskExecutor:
137
137
  self._task.ignore_errors = item_ignore
138
138
  elif self._task.ignore_errors and not item_ignore:
139
139
  self._task.ignore_errors = item_ignore
140
+ if 'unreachable' in item and item['unreachable']:
141
+ item_ignore_unreachable = item.pop('_ansible_ignore_unreachable')
142
+ if not res.get('unreachable'):
143
+ self._task.ignore_unreachable = item_ignore_unreachable
144
+ elif self._task.ignore_unreachable and not item_ignore_unreachable:
145
+ self._task.ignore_unreachable = item_ignore_unreachable
140
146
 
141
147
  # ensure to accumulate these
142
148
  for array in ['warnings', 'deprecations']:
@@ -277,6 +283,7 @@ class TaskExecutor:
277
283
  u" to something else to avoid variable collisions and unexpected behavior." % (self._task, loop_var))
278
284
 
279
285
  ran_once = False
286
+ task_fields = None
280
287
  no_log = False
281
288
  items_len = len(items)
282
289
  results = []
@@ -348,6 +355,7 @@ class TaskExecutor:
348
355
 
349
356
  res['_ansible_item_result'] = True
350
357
  res['_ansible_ignore_errors'] = task_fields.get('ignore_errors')
358
+ res['_ansible_ignore_unreachable'] = task_fields.get('ignore_unreachable')
351
359
 
352
360
  # gets templated here unlike rest of loop_control fields, depends on loop_var above
353
361
  try:
@@ -392,6 +400,10 @@ class TaskExecutor:
392
400
  del task_vars[var]
393
401
 
394
402
  self._task.no_log = no_log
403
+ # NOTE: run_once cannot contain loop vars because it's templated earlier also
404
+ # This is saving the post-validated field from the last loop so the strategy can use the templated value post task execution
405
+ self._task.run_once = task_fields.get('run_once')
406
+ self._task.action = task_fields.get('action')
395
407
 
396
408
  return results
397
409
 
@@ -19,6 +19,6 @@
19
19
  from __future__ import (absolute_import, division, print_function)
20
20
  __metaclass__ = type
21
21
 
22
- __version__ = '2.15.0b2'
22
+ __version__ = '2.15.0b3'
23
23
  __author__ = 'Ansible, Inc.'
24
24
  __codename__ = "Ten Years Gone"
@@ -27,6 +27,7 @@ import queue
27
27
  import sys
28
28
  import threading
29
29
  import time
30
+ import typing as t
30
31
 
31
32
  from collections import deque
32
33
  from multiprocessing import Lock
@@ -37,7 +38,7 @@ from ansible import constants as C
37
38
  from ansible import context
38
39
  from ansible.errors import AnsibleError, AnsibleFileNotFound, AnsibleUndefinedVariable, AnsibleParserError
39
40
  from ansible.executor import action_write_locks
40
- from ansible.executor.play_iterator import IteratingStates
41
+ from ansible.executor.play_iterator import IteratingStates, PlayIterator
41
42
  from ansible.executor.process.worker import WorkerProcess
42
43
  from ansible.executor.task_result import TaskResult
43
44
  from ansible.executor.task_queue_manager import CallbackSend, DisplaySend, PromptSend
@@ -506,6 +507,57 @@ class StrategyBase:
506
507
 
507
508
  return task_result
508
509
 
510
+ def search_handlers_by_notification(self, notification: str, iterator: PlayIterator) -> t.Generator[Handler, None, None]:
511
+ templar = Templar(None)
512
+ # iterate in reversed order since last handler loaded with the same name wins
513
+ for handler in (h for b in reversed(iterator._play.handlers) for h in b.block if h.name):
514
+ if not handler.cached_name:
515
+ if templar.is_template(handler.name):
516
+ templar.available_variables = self._variable_manager.get_vars(
517
+ play=iterator._play,
518
+ task=handler,
519
+ _hosts=self._hosts_cache,
520
+ _hosts_all=self._hosts_cache_all
521
+ )
522
+ try:
523
+ handler.name = templar.template(handler.name)
524
+ except (UndefinedError, AnsibleUndefinedVariable) as e:
525
+ # We skip this handler due to the fact that it may be using
526
+ # a variable in the name that was conditionally included via
527
+ # set_fact or some other method, and we don't want to error
528
+ # out unnecessarily
529
+ if not handler.listen:
530
+ display.warning(
531
+ "Handler '%s' is unusable because it has no listen topics and "
532
+ "the name could not be templated (host-specific variables are "
533
+ "not supported in handler names). The error: %s" % (handler.name, to_text(e))
534
+ )
535
+ continue
536
+ handler.cached_name = True
537
+
538
+ # first we check with the full result of get_name(), which may
539
+ # include the role name (if the handler is from a role). If that
540
+ # is not found, we resort to the simple name field, which doesn't
541
+ # have anything extra added to it.
542
+ if notification in {
543
+ handler.name,
544
+ handler.get_name(include_role_fqcn=False),
545
+ handler.get_name(include_role_fqcn=True),
546
+ }:
547
+ yield handler
548
+ break
549
+
550
+ templar.available_variables = {}
551
+ for handler in (h for b in iterator._play.handlers for h in b.block):
552
+ if listeners := handler.listen:
553
+ if notification in handler.get_validated_value(
554
+ 'listen',
555
+ handler.fattributes.get('listen'),
556
+ listeners,
557
+ templar,
558
+ ):
559
+ yield handler
560
+
509
561
  @debug_closure
510
562
  def _process_pending_results(self, iterator, one_pass=False, max_passes=None):
511
563
  '''
@@ -516,46 +568,6 @@ class StrategyBase:
516
568
  ret_results = []
517
569
  handler_templar = Templar(self._loader)
518
570
 
519
- def search_handler_blocks_by_name(handler_name, handler_blocks):
520
- # iterate in reversed order since last handler loaded with the same name wins
521
- for handler_block in reversed(handler_blocks):
522
- for handler_task in handler_block.block:
523
- if handler_task.name:
524
- try:
525
- if not handler_task.cached_name:
526
- if handler_templar.is_template(handler_task.name):
527
- handler_templar.available_variables = self._variable_manager.get_vars(play=iterator._play,
528
- task=handler_task,
529
- _hosts=self._hosts_cache,
530
- _hosts_all=self._hosts_cache_all)
531
- handler_task.name = handler_templar.template(handler_task.name)
532
- handler_task.cached_name = True
533
-
534
- # first we check with the full result of get_name(), which may
535
- # include the role name (if the handler is from a role). If that
536
- # is not found, we resort to the simple name field, which doesn't
537
- # have anything extra added to it.
538
- candidates = (
539
- handler_task.name,
540
- handler_task.get_name(include_role_fqcn=False),
541
- handler_task.get_name(include_role_fqcn=True),
542
- )
543
-
544
- if handler_name in candidates:
545
- return handler_task
546
- except (UndefinedError, AnsibleUndefinedVariable) as e:
547
- # We skip this handler due to the fact that it may be using
548
- # a variable in the name that was conditionally included via
549
- # set_fact or some other method, and we don't want to error
550
- # out unnecessarily
551
- if not handler_task.listen:
552
- display.warning(
553
- "Handler '%s' is unusable because it has no listen topics and "
554
- "the name could not be templated (host-specific variables are "
555
- "not supported in handler names). The error: %s" % (handler_task.name, to_text(e))
556
- )
557
- continue
558
-
559
571
  cur_pass = 0
560
572
  while True:
561
573
  try:
@@ -636,49 +648,24 @@ class StrategyBase:
636
648
  result_items = [task_result._result]
637
649
 
638
650
  for result_item in result_items:
639
- if '_ansible_notify' in result_item:
640
- if task_result.is_changed():
641
- # The shared dictionary for notified handlers is a proxy, which
642
- # does not detect when sub-objects within the proxy are modified.
643
- # So, per the docs, we reassign the list so the proxy picks up and
644
- # notifies all other threads
645
- for handler_name in result_item['_ansible_notify']:
646
- found = False
647
- # Find the handler using the above helper. First we look up the
648
- # dependency chain of the current task (if it's from a role), otherwise
649
- # we just look through the list of handlers in the current play/all
650
- # roles and use the first one that matches the notify name
651
- target_handler = search_handler_blocks_by_name(handler_name, iterator._play.handlers)
652
- if target_handler is not None:
653
- found = True
654
- if target_handler.notify_host(original_host):
655
- self._tqm.send_callback('v2_playbook_on_notify', target_handler, original_host)
656
-
657
- for listening_handler_block in iterator._play.handlers:
658
- for listening_handler in listening_handler_block.block:
659
- listeners = getattr(listening_handler, 'listen', []) or []
660
- if not listeners:
661
- continue
662
-
663
- listeners = listening_handler.get_validated_value(
664
- 'listen', listening_handler.fattributes.get('listen'), listeners, handler_templar
665
- )
666
- if handler_name not in listeners:
667
- continue
668
- else:
669
- found = True
670
-
671
- if listening_handler.notify_host(original_host):
672
- self._tqm.send_callback('v2_playbook_on_notify', listening_handler, original_host)
673
-
674
- # and if none were found, then we raise an error
675
- if not found:
676
- msg = ("The requested handler '%s' was not found in either the main handlers list nor in the listening "
677
- "handlers list" % handler_name)
678
- if C.ERROR_ON_MISSING_HANDLER:
679
- raise AnsibleError(msg)
680
- else:
681
- display.warning(msg)
651
+ if '_ansible_notify' in result_item and task_result.is_changed():
652
+ # only ensure that notified handlers exist, if so save the notifications for when
653
+ # handlers are actually flushed so the last defined handlers are exexcuted,
654
+ # otherwise depending on the setting either error or warn
655
+ 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)
667
+ else:
668
+ display.warning(msg)
682
669
 
683
670
  if 'add_host' in result_item:
684
671
  # this task added a new host (add_host module)
@@ -957,6 +944,15 @@ class StrategyBase:
957
944
  elif meta_action == 'flush_handlers':
958
945
  if _evaluate_conditional(target_host):
959
946
  host_state = iterator.get_state_for_host(target_host.name)
947
+ # actually notify proper handlers based on all notifications up to this point
948
+ for notification in list(host_state.handler_notifications):
949
+ for handler in self.search_handlers_by_notification(notification, iterator):
950
+ if not handler.notify_host(target_host):
951
+ # NOTE even with notifications deduplicated this can still happen in case of handlers being
952
+ # notified multiple times using different names, like role name or fqcn
953
+ self._tqm.send_callback('v2_playbook_on_notify', handler, target_host)
954
+ iterator.clear_notification(target_host.name, notification)
955
+
960
956
  if host_state.run_state == IteratingStates.HANDLERS:
961
957
  raise AnsibleError('flush_handlers cannot be used as a handler')
962
958
  if target_host.name not in self._tqm._unreachable_hosts:
@@ -35,7 +35,6 @@ from ansible import constants as C
35
35
  from ansible.errors import AnsibleError, AnsibleAssertionError, AnsibleParserError
36
36
  from ansible.executor.play_iterator import IteratingStates, FailedStates
37
37
  from ansible.module_utils._text import to_text
38
- from ansible.module_utils.parsing.convert_bool import boolean
39
38
  from ansible.playbook.handler import Handler
40
39
  from ansible.playbook.included_file import IncludedFile
41
40
  from ansible.playbook.task import Task
@@ -214,10 +213,7 @@ class StrategyModule(StrategyBase):
214
213
  skip_rest = True
215
214
  break
216
215
 
217
- if templar.is_template(task.run_once):
218
- setattr(task, 'run_once', boolean(templar.template(task.run_once), strict=True))
219
-
220
- run_once = task.run_once or action and getattr(action, 'BYPASS_HOST_LOOP', False)
216
+ run_once = templar.template(task.run_once) or action and getattr(action, 'BYPASS_HOST_LOOP', False)
221
217
 
222
218
  if (task.any_errors_fatal or run_once) and not task.ignore_errors:
223
219
  any_errors_fatal = True
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.0b2'
22
+ __version__ = '2.15.0b3'
23
23
  __author__ = 'Ansible, Inc.'
24
24
  __codename__ = "Ten Years Gone"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ansible-core
3
- Version: 2.15.0b2
3
+ Version: 2.15.0b3
4
4
  Summary: Radically simple IT automation
5
5
  Home-page: https://ansible.com/
6
6
  Author: Ansible, Inc.
@@ -3,7 +3,7 @@ ansible/__main__.py,sha256=IvyRvY64pT0on94qCLibxgDJ0-7_2CRoaZ5kfGOl54Q,1395
3
3
  ansible/constants.py,sha256=JLIDnuSz3_PbtXWsL4vnvVBbxlh3lSrJREd7T73atEI,8293
4
4
  ansible/context.py,sha256=OzSlaA_GgGRyyf5I209sy19_eGOX6HXn441W9w_FcvU,2018
5
5
  ansible/keyword_desc.yml,sha256=FYY0Ld1Xc3AxJ_Tefz78kRSYzIKGS8qcPtVk370J118,7367
6
- ansible/release.py,sha256=mbp4I1EHas-bcXA8S0gQIxjOi4Vuog65VJ2m_BM8suk,920
6
+ ansible/release.py,sha256=P-fXbdy9zVITuZdHA46usN4gSEjuWpRFm8Vf8lcZFtc,920
7
7
  ansible/_vendor/__init__.py,sha256=wJRKH7kI9OzYVY9hgSchOsTNTmTnugpPLGYj9Y5akX0,2086
8
8
  ansible/cli/__init__.py,sha256=ZK8bKuMmeRqeAcePriGtJ0tMuoDur3sN-ySBmOzAF3c,28687
9
9
  ansible/cli/adhoc.py,sha256=pGW6eysaireovp4sVsUuntg-l1o7DSujuhxVhVC2zsM,8230
@@ -12,11 +12,11 @@ ansible/cli/console.py,sha256=rc-6s-Exf9b8lead40RyfugZdU1-cMoN-kA1iI8Uhs8,21941
12
12
  ansible/cli/doc.py,sha256=x7LNU10RiJJejxHxbZg0xd6cdJarxTEK5LfWmQMc3y0,64153
13
13
  ansible/cli/galaxy.py,sha256=BwKVIeErmdPIN7V77vzrGtlbO0n72_PYvh1bvVSuSLk,91107
14
14
  ansible/cli/inventory.py,sha256=6aZ9n8GrRHVPSYbrEezfIJO62pcdH8RUNIayA-iNTKs,17693
15
- ansible/cli/playbook.py,sha256=ttNHWeHUKvaF2u3ep2Q6O1IALru_RUGxJnYMjpy2LQk,10711
15
+ ansible/cli/playbook.py,sha256=2MNTSu99nKVO7b7ZeyA0PkR5ML8kuBCeCDMjd5YPh4g,10901
16
16
  ansible/cli/pull.py,sha256=TI3xfqcO-f8I60gRvVdiAEELghSq5Mlb8YPX6SdiitM,17010
17
17
  ansible/cli/vault.py,sha256=8od9BPi570xO7CqiG82G5HHa_6oFRGDxlQ5bdCZkkjQ,22645
18
18
  ansible/cli/arguments/__init__.py,sha256=CL2cOeYgVnD4r0iJTzEjjldSkJjGKPZc_t44UKSF4n8,221
19
- ansible/cli/arguments/option_helpers.py,sha256=cvnu8jwKm29SXV5_54KEBRjvVkwFqkwNjfNSjHia-MM,18279
19
+ ansible/cli/arguments/option_helpers.py,sha256=zu0VTuuUHCvw5X_nvcNe5qjCPmjJPNdHVmOUgDRsa7E,18107
20
20
  ansible/cli/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  ansible/cli/scripts/ansible_connection_cli_stub.py,sha256=bpaNmBnYGiTXVtZsM-Rw-jOE0LR9M5rL9WiZdX5X4vI,13505
22
22
  ansible/collections/__init__.py,sha256=t8x1TZiQQa9vaQAm7ECYoP3MCdzquEtAlPzG9HzEV50,814
@@ -34,10 +34,10 @@ ansible/executor/__init__.py,sha256=1lMXN1i2fFqslda4BmeI5tpYMFP95D5Wpr1AjDJi-SQ,
34
34
  ansible/executor/action_write_locks.py,sha256=Up2n3cwFCr4T4IvttHpe3QOxRBF_9NgWJ1tFm9CHpfM,1915
35
35
  ansible/executor/interpreter_discovery.py,sha256=0Pad_qYo8OSfA0U0AqHOXGt1HPKcqxBw-NfTKq4HmfQ,9926
36
36
  ansible/executor/module_common.py,sha256=6R58IqfOLzg0aDQWRWsi0cbohWMSf_Lvdhf_5hTavWg,65820
37
- ansible/executor/play_iterator.py,sha256=FUxYjkFU8s9RessNxdey66TWfXP1NvWXZbzBDFAJYdY,31013
37
+ ansible/executor/play_iterator.py,sha256=WmByZKIcBYx1gT5ybsIv9yiwkimmljR_NnbBVtYs2X8,31562
38
38
  ansible/executor/playbook_executor.py,sha256=VQHEIvZbfOFzp388XFD0KjG0e8Ye8yuNPnnHAZmi898,15069
39
39
  ansible/executor/stats.py,sha256=757UK8wDzLCXq4ltI9PqpoMNAdtRsd9D9-GS-5Al_Hs,3264
40
- ansible/executor/task_executor.py,sha256=Yfxrsd387IH3aEJgeXNCQSZhg5YVor-9PMaXkq2o5wI,59059
40
+ ansible/executor/task_executor.py,sha256=8LUxmZs5Ak3ciU_LayWB3LbCrzu4VkpCGnRkKGBCa-U,60003
41
41
  ansible/executor/task_queue_manager.py,sha256=DxmfDMeWAClNvp85qvc1uATor-hilv8KsYno3Pl_Ztk,18758
42
42
  ansible/executor/task_result.py,sha256=DvshMci5i9-qCXs0m_vScSa6BJMbPwwNQBV7L2DTCzE,5748
43
43
  ansible/executor/discovery/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -140,7 +140,7 @@ ansible/inventory/host.py,sha256=wXJp6kpSaZtDr4JNsgdAuhi5MzQ9LTQzaAH10zoVbIA,505
140
140
  ansible/inventory/manager.py,sha256=tGwhBR6poLuG_i4jZ5RGOG-rH4gu4DBfT0-4iLLZZMs,29490
141
141
  ansible/module_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
142
  ansible/module_utils/_text.py,sha256=F_YfeaxhwmTI16HICAzQS9ZmlKgBDdQ4mqR-Kh--okg,597
143
- ansible/module_utils/ansible_release.py,sha256=mbp4I1EHas-bcXA8S0gQIxjOi4Vuog65VJ2m_BM8suk,920
143
+ ansible/module_utils/ansible_release.py,sha256=P-fXbdy9zVITuZdHA46usN4gSEjuWpRFm8Vf8lcZFtc,920
144
144
  ansible/module_utils/api.py,sha256=BTo7stVOANbtd-ngZslaqx70r9t5gfvo44cKyu5SFjU,5837
145
145
  ansible/module_utils/basic.py,sha256=KwFTKMws6bPfSEP1fIc7Srvan6b34EEbZ5nedfhhiTw,87493
146
146
  ansible/module_utils/connection.py,sha256=XHxMlyAdwLiXDSo8jBMkV61-lz_0FDJUYH1B152UGJU,8430
@@ -580,11 +580,11 @@ ansible/plugins/shell/__init__.py,sha256=Rj-H2AhfBZAWZ_Hy8D-1ypvjXTMP3pTbByOYlPM
580
580
  ansible/plugins/shell/cmd.py,sha256=fswLtU2XVNb1T5tF0BIM9msViObs5dXzo9k6sNN4dao,2207
581
581
  ansible/plugins/shell/powershell.py,sha256=qnpEZ9uOJF_4gExheFCAYT_tSR_KQtQeilU1WKXJymA,11376
582
582
  ansible/plugins/shell/sh.py,sha256=1nhiMv0_c8zu2MaDHvOCr--dG8b-iUVEPPnpMh_Hx8I,3952
583
- ansible/plugins/strategy/__init__.py,sha256=pxs6OaFwYm5dN995kaqH_hrK8nzePD5MRPYIiLG2C9A,57490
583
+ ansible/plugins/strategy/__init__.py,sha256=ecMkuKVsIZyZHJzcPiFqq-RU74_Ub8bYptw5njge9uI,56268
584
584
  ansible/plugins/strategy/debug.py,sha256=GxUS0bSiaWInIK8zgB7rMREEqvgrZhVlFUzOCJtnjFo,1258
585
585
  ansible/plugins/strategy/free.py,sha256=eXAvxTFloyb5x6VYANXDMdloWUYxMhbPr1lyY6UPBps,15773
586
586
  ansible/plugins/strategy/host_pinned.py,sha256=3-q5l-tpheMlU-BXGm6ZQNgHvQv5IMvOCDZBLibl1L4,1959
587
- ansible/plugins/strategy/linear.py,sha256=maTN_kZpC3UYg_ZW7wsim7WecG8lr6puK7i2CnHj2Ow,20389
587
+ ansible/plugins/strategy/linear.py,sha256=pTdVG7cWrcDK1pXViLmiGtEPKMrOcgBWaGUENkd2RYg,20172
588
588
  ansible/plugins/terminal/__init__.py,sha256=zGIuxlntye0FHk6Zbl57snHB5d3-w_pr0osRpCRy4co,4438
589
589
  ansible/plugins/test/__init__.py,sha256=6DY18LxzSdtO7-fDS6957bo61fg-xG3TDWvtFkhGYOQ,471
590
590
  ansible/plugins/test/abs.yml,sha256=-caY4vAMXbhukUTdMQvBa2WYvg6w1AWr8raEfAv0qa8,764
@@ -682,7 +682,7 @@ ansible/vars/hostvars.py,sha256=dg3jpVmNwSg8EJ4SIvYGT80uxMgRtrOW6vvtDfrQzDU,5152
682
682
  ansible/vars/manager.py,sha256=qsF6PgAYcon5n7HmXG56P4pmKLyrniuFpAtKWnNaFpw,38284
683
683
  ansible/vars/plugins.py,sha256=B7L3fXoSOoBZSXqJ2ulk0adx1g5SpAb8BxyLGPNA7d4,4695
684
684
  ansible/vars/reserved.py,sha256=FBD7n2dnA0CW4I0J1LtWwk2hQqvGW0KTRPcxaRtMKWo,2615
685
- ansible_core-2.15.0b2.data/scripts/ansible-test,sha256=CYIYL99IxWdVTtDIj3avilIJXhGAmtjuKPPWNuLWuc8,1690
685
+ ansible_core-2.15.0b3.data/scripts/ansible-test,sha256=CYIYL99IxWdVTtDIj3avilIJXhGAmtjuKPPWNuLWuc8,1690
686
686
  ansible_test/__init__.py,sha256=6e721yAyyyocRKzbCKtQXloAfFP7Aqv0L3zG70uh-4A,190
687
687
  ansible_test/_data/ansible.cfg,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
688
688
  ansible_test/_data/coveragerc,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -791,7 +791,7 @@ ansible_test/_internal/cli/environments.py,sha256=M5Vcolpz-GBBUVlv8gVBl7_54RTSJL
791
791
  ansible_test/_internal/cli/epilog.py,sha256=kzCYlmqDccMZnSCV57iXUITo6Z9FMMUIagjWJHHA0yY,658
792
792
  ansible_test/_internal/cli/argparsing/__init__.py,sha256=ravr0Yv7tEOBFv2s2DuZtEl9BPAQNy-KMKcJNSk4dmc,8922
793
793
  ansible_test/_internal/cli/argparsing/actions.py,sha256=VplAf5K9G-loJmLXMAZwbRbIsuFJ-yDrRrP4El5p4RM,606
794
- ansible_test/_internal/cli/argparsing/argcompletion.py,sha256=bC2E7sBREAUufBSNTrrkSqJ9QtaDfLtaepugLhX9lvs,4562
794
+ ansible_test/_internal/cli/argparsing/argcompletion.py,sha256=zOZtYVDkqWIdbmuASkyJuMUKrFh4w3MJzYS2O9DoIQA,5166
795
795
  ansible_test/_internal/cli/argparsing/parsers.py,sha256=i7bEPWy7q2mcgiBb3sZ0EN5wQ0G5SetOMQKsOMSsw4M,21490
796
796
  ansible_test/_internal/cli/commands/__init__.py,sha256=d8FNvVbSVR2JlnyDUxnS-lZDIQqbdEEPU0cqJA9663Q,5436
797
797
  ansible_test/_internal/cli/commands/env.py,sha256=-3zKICX4STeo_lObMh6EmvBy3s2ORL53idmLKN2vCHk,1397
@@ -938,7 +938,7 @@ ansible_test/_util/controller/sanity/code-smell/use-compat-six.py,sha256=CkYomOt
938
938
  ansible_test/_util/controller/sanity/code-smell/changelog/sphinx.py,sha256=M3aEK_XugBtVJjfUZbeoVc10hzRylxRxNfEiNq1JVWQ,193
939
939
  ansible_test/_util/controller/sanity/integration-aliases/yaml_to_json.py,sha256=qxXHZboRVEqISZYOIXrutsAgobEyh6fiUibk133fzhI,299
940
940
  ansible_test/_util/controller/sanity/mypy/ansible-core.ini,sha256=B13dYyd5PGoN-BrFShPMhGBCGbV2oiTsBD8TdRDAh3Q,2327
941
- ansible_test/_util/controller/sanity/mypy/ansible-test.ini,sha256=g412ZxuLBxN22FM-jS49RA0JoT_M7q4S4g7x2LW9hf8,850
941
+ ansible_test/_util/controller/sanity/mypy/ansible-test.ini,sha256=lbBGGRhM-sL7iUdt5f0ctGSAvPmjCXIFeogm3Z22qkA,908
942
942
  ansible_test/_util/controller/sanity/mypy/modules.ini,sha256=48N2I3ubw3yAuE8layHQ_d0CTfH_eATuXt-K5Bq-ifw,1694
943
943
  ansible_test/_util/controller/sanity/pep8/current-ignore.txt,sha256=9VSaFOsdxN4_8GJVhnmpl5kXos2TPU3M08eC_NRI2Ks,196
944
944
  ansible_test/_util/controller/sanity/pslint/pslint.ps1,sha256=h0fLdkwF7JhGGjApvqAsCU87BKy0E_UiFJ_O7MARz6U,1089
@@ -999,9 +999,9 @@ ansible_test/config/cloud-config-vultr.ini.template,sha256=XLKHk3lg_8ReQMdWfZzhh
999
999
  ansible_test/config/config.yml,sha256=wb3knoBmZewG3GWOMnRHoVPQWW4vPixKLPMNS6vJmTc,2620
1000
1000
  ansible_test/config/inventory.networking.template,sha256=bFNSk8zNQOaZ_twaflrY0XZ9mLwUbRLuNT0BdIFwvn4,1335
1001
1001
  ansible_test/config/inventory.winrm.template,sha256=1QU8W-GFLnYEw8yY9bVIvUAVvJYPM3hyoijf6-M7T00,1098
1002
- ansible_core-2.15.0b2.dist-info/COPYING,sha256=CuBIWlvTemPmNgNZZBfk6w5lMzT6bH-TLKOg6F1K8ic,35148
1003
- ansible_core-2.15.0b2.dist-info/METADATA,sha256=UCl-9pHzhW6oKB0Sx9bwulPKIU5-TncCYaFlVVxbxzM,7506
1004
- ansible_core-2.15.0b2.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
1005
- ansible_core-2.15.0b2.dist-info/entry_points.txt,sha256=0mpmsrIhODChxKl3eS-NcVQCaMetBn8KdPLtVxQgR64,453
1006
- ansible_core-2.15.0b2.dist-info/top_level.txt,sha256=IFbRLjAvih1DYzJWg3_F6t4sCzEMxRO7TOMNs6GkYHo,21
1007
- ansible_core-2.15.0b2.dist-info/RECORD,,
1002
+ ansible_core-2.15.0b3.dist-info/COPYING,sha256=CuBIWlvTemPmNgNZZBfk6w5lMzT6bH-TLKOg6F1K8ic,35148
1003
+ ansible_core-2.15.0b3.dist-info/METADATA,sha256=XrERIJGjWiZy6gmN7oZxVTmEko90rTP5JsgV9L17KAI,7506
1004
+ ansible_core-2.15.0b3.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
1005
+ ansible_core-2.15.0b3.dist-info/entry_points.txt,sha256=0mpmsrIhODChxKl3eS-NcVQCaMetBn8KdPLtVxQgR64,453
1006
+ ansible_core-2.15.0b3.dist-info/top_level.txt,sha256=IFbRLjAvih1DYzJWg3_F6t4sCzEMxRO7TOMNs6GkYHo,21
1007
+ ansible_core-2.15.0b3.dist-info/RECORD,,
@@ -17,10 +17,19 @@ class Substitute:
17
17
  try:
18
18
  import argcomplete
19
19
 
20
- from argcomplete import (
21
- CompletionFinder,
22
- default_validator,
23
- )
20
+ try:
21
+ # argcomplete 3+
22
+ # see: https://github.com/kislyuk/argcomplete/commit/bd781cb08512b94966312377186ebc5550f46ae0
23
+ from argcomplete.finders import (
24
+ CompletionFinder,
25
+ default_validator,
26
+ )
27
+ except ImportError:
28
+ # argcomplete <3
29
+ from argcomplete import (
30
+ CompletionFinder,
31
+ default_validator,
32
+ )
24
33
 
25
34
  warn = argcomplete.warn # pylint: disable=invalid-name
26
35
  except ImportError:
@@ -72,7 +81,13 @@ class CompType(enum.Enum):
72
81
  def register_safe_action(action_type: t.Type[argparse.Action]) -> None:
73
82
  """Register the given action as a safe action for argcomplete to use during completion if it is not already registered."""
74
83
  if argcomplete and action_type not in argcomplete.safe_actions:
75
- argcomplete.safe_actions += (action_type,)
84
+ if isinstance(argcomplete.safe_actions, set):
85
+ # argcomplete 3+
86
+ # see: https://github.com/kislyuk/argcomplete/commit/bd781cb08512b94966312377186ebc5550f46ae0
87
+ argcomplete.safe_actions.add(action_type)
88
+ else:
89
+ # argcomplete <3
90
+ argcomplete.safe_actions += (action_type,)
76
91
 
77
92
 
78
93
  def get_comp_type() -> t.Optional[CompType]:
@@ -14,6 +14,9 @@ disable_error_code = type-abstract
14
14
  [mypy-argcomplete]
15
15
  ignore_missing_imports = True
16
16
 
17
+ [mypy-argcomplete.finders]
18
+ ignore_missing_imports = True
19
+
17
20
  [mypy-coverage]
18
21
  ignore_missing_imports = True
19
22