ansible-core 2.14.4rc1__py3-none-any.whl → 2.14.5rc1__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 (33) hide show
  1. ansible/cli/arguments/option_helpers.py +0 -2
  2. ansible/cli/doc.py +10 -4
  3. ansible/cli/galaxy.py +1 -1
  4. ansible/cli/playbook.py +2 -0
  5. ansible/executor/powershell/module_manifest.py +1 -1
  6. ansible/galaxy/api.py +29 -10
  7. ansible/galaxy/collection/__init__.py +3 -0
  8. ansible/galaxy/collection/concrete_artifact_manager.py +34 -17
  9. ansible/galaxy/dependency_resolution/dataclasses.py +11 -1
  10. ansible/galaxy/dependency_resolution/providers.py +0 -1
  11. ansible/module_utils/ansible_release.py +1 -1
  12. ansible/module_utils/api.py +14 -1
  13. ansible/module_utils/csharp/Ansible.Basic.cs +265 -7
  14. ansible/modules/uri.py +10 -3
  15. ansible/playbook/helpers.py +1 -0
  16. ansible/playbook/included_file.py +1 -0
  17. ansible/playbook/role_include.py +3 -3
  18. ansible/plugins/action/__init__.py +1 -1
  19. ansible/plugins/filter/basename.yml +3 -1
  20. ansible/plugins/filter/dirname.yml +3 -1
  21. ansible/plugins/lookup/password.py +78 -39
  22. ansible/release.py +1 -1
  23. ansible/utils/encrypt.py +9 -6
  24. {ansible_core-2.14.4rc1.dist-info → ansible_core-2.14.5rc1.dist-info}/METADATA +1 -1
  25. {ansible_core-2.14.4rc1.dist-info → ansible_core-2.14.5rc1.dist-info}/RECORD +33 -33
  26. ansible_test/_internal/classification/__init__.py +13 -5
  27. ansible_test/_internal/cli/argparsing/argcompletion.py +20 -5
  28. ansible_test/_util/controller/sanity/mypy/ansible-test.ini +3 -0
  29. {ansible_core-2.14.4rc1.data → ansible_core-2.14.5rc1.data}/scripts/ansible-test +0 -0
  30. {ansible_core-2.14.4rc1.dist-info → ansible_core-2.14.5rc1.dist-info}/COPYING +0 -0
  31. {ansible_core-2.14.4rc1.dist-info → ansible_core-2.14.5rc1.dist-info}/WHEEL +0 -0
  32. {ansible_core-2.14.4rc1.dist-info → ansible_core-2.14.5rc1.dist-info}/entry_points.txt +0 -0
  33. {ansible_core-2.14.4rc1.dist-info → ansible_core-2.14.5rc1.dist-info}/top_level.txt +0 -0
@@ -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/doc.py CHANGED
@@ -1241,10 +1241,16 @@ class DocCLI(CLI, RoleMixin):
1241
1241
  if 'module' in item:
1242
1242
  text.append(textwrap.fill(DocCLI.tty_ify('Module %s' % item['module']),
1243
1243
  limit - 6, initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
1244
- description = item.get('description', 'The official documentation on the %s module.' % item['module'])
1245
- text.append(textwrap.fill(DocCLI.tty_ify(description), limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
1246
- text.append(textwrap.fill(DocCLI.tty_ify(get_versioned_doclink('modules/%s_module.html' % item['module'])),
1247
- limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent))
1244
+ description = item.get('description')
1245
+ if description is None and item['module'].startswith('ansible.builtin.'):
1246
+ description = 'The official documentation on the %s module.' % item['module']
1247
+ if description is not None:
1248
+ text.append(textwrap.fill(DocCLI.tty_ify(description),
1249
+ limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent + ' '))
1250
+ if item['module'].startswith('ansible.builtin.'):
1251
+ relative_url = 'collections/%s_module.html' % item['module'].replace('.', '/', 2)
1252
+ text.append(textwrap.fill(DocCLI.tty_ify(get_versioned_doclink(relative_url)),
1253
+ limit - 6, initial_indent=opt_indent + ' ', subsequent_indent=opt_indent))
1248
1254
  elif 'name' in item and 'link' in item and 'description' in item:
1249
1255
  text.append(textwrap.fill(DocCLI.tty_ify(item['name']),
1250
1256
  limit - 6, initial_indent=opt_indent[:-2] + "* ", subsequent_indent=opt_indent))
ansible/cli/galaxy.py CHANGED
@@ -242,7 +242,7 @@ class GalaxyCLI(CLI):
242
242
  help='The Ansible Galaxy API key which can be found at '
243
243
  'https://galaxy.ansible.com/me/preferences.')
244
244
  common.add_argument('-c', '--ignore-certs', action='store_true', dest='ignore_certs', help='Ignore SSL certificate validation errors.', default=None)
245
- common.add_argument('--timeout', dest='timeout', type=int,
245
+ common.add_argument('--timeout', dest='timeout', type=int, default=60,
246
246
  help="The time to wait for operations against the galaxy server, defaults to 60s.")
247
247
 
248
248
  opt_help.add_verbosity_options(common)
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',
@@ -319,7 +319,7 @@ def _create_powershell_wrapper(b_module_data, module_path, module_args,
319
319
 
320
320
  exec_manifest["actions"].insert(0, 'async_watchdog')
321
321
  exec_manifest["actions"].insert(0, 'async_wrapper')
322
- exec_manifest["async_jid"] = str(random.randint(0, 999999999999))
322
+ exec_manifest["async_jid"] = f'j{random.randint(0, 999999999999)}'
323
323
  exec_manifest["async_timeout_sec"] = async_timeout
324
324
  exec_manifest["async_startup_timeout"] = C.config.get_config_value("WIN_ASYNC_STARTUP_TIMEOUT", variables=task_vars)
325
325
 
ansible/galaxy/api.py CHANGED
@@ -11,12 +11,15 @@ import functools
11
11
  import hashlib
12
12
  import json
13
13
  import os
14
+ import socket
14
15
  import stat
15
16
  import tarfile
16
17
  import time
17
18
  import threading
18
19
 
19
- from urllib.error import HTTPError
20
+ from http import HTTPStatus
21
+ from http.client import BadStatusLine, IncompleteRead
22
+ from urllib.error import HTTPError, URLError
20
23
  from urllib.parse import quote as urlquote, urlencode, urlparse, parse_qs, urljoin
21
24
 
22
25
  from ansible import constants as C
@@ -34,10 +37,11 @@ from ansible.utils.path import makedirs_safe
34
37
  display = Display()
35
38
  _CACHE_LOCK = threading.Lock()
36
39
  COLLECTION_PAGE_SIZE = 100
37
- RETRY_HTTP_ERROR_CODES = [ # TODO: Allow user-configuration
38
- 429, # Too Many Requests
40
+ RETRY_HTTP_ERROR_CODES = { # TODO: Allow user-configuration
41
+ HTTPStatus.TOO_MANY_REQUESTS,
39
42
  520, # Galaxy rate limit error code (Cloudflare unknown error)
40
- ]
43
+ HTTPStatus.BAD_GATEWAY, # Common error from galaxy that may represent any number of transient backend issues
44
+ }
41
45
 
42
46
 
43
47
  def cache_lock(func):
@@ -48,11 +52,24 @@ def cache_lock(func):
48
52
  return wrapped
49
53
 
50
54
 
51
- def is_rate_limit_exception(exception):
55
+ def should_retry_error(exception):
52
56
  # Note: cloud.redhat.com masks rate limit errors with 403 (Forbidden) error codes.
53
57
  # Since 403 could reflect the actual problem (such as an expired token), we should
54
58
  # not retry by default.
55
- return isinstance(exception, GalaxyError) and exception.http_code in RETRY_HTTP_ERROR_CODES
59
+ if isinstance(exception, GalaxyError) and exception.http_code in RETRY_HTTP_ERROR_CODES:
60
+ return True
61
+
62
+ if isinstance(exception, AnsibleError) and (orig_exc := getattr(exception, 'orig_exc', None)):
63
+ # URLError is often a proxy for an underlying error, handle wrapped exceptions
64
+ if isinstance(orig_exc, URLError):
65
+ orig_exc = orig_exc.reason
66
+
67
+ # Handle common URL related errors such as TimeoutError, and BadStatusLine
68
+ # Note: socket.timeout is only required for Py3.9
69
+ if isinstance(orig_exc, (TimeoutError, BadStatusLine, IncompleteRead, socket.timeout)):
70
+ return True
71
+
72
+ return False
56
73
 
57
74
 
58
75
  def g_connect(versions):
@@ -327,7 +344,7 @@ class GalaxyAPI:
327
344
 
328
345
  @retry_with_delays_and_condition(
329
346
  backoff_iterator=generate_jittered_backoff(retries=6, delay_base=2, delay_threshold=40),
330
- should_retry_error=is_rate_limit_exception
347
+ should_retry_error=should_retry_error
331
348
  )
332
349
  def _call_galaxy(self, url, args=None, headers=None, method=None, auth_required=False, error_context_msg=None,
333
350
  cache=False, cache_key=None):
@@ -385,7 +402,10 @@ class GalaxyAPI:
385
402
  except HTTPError as e:
386
403
  raise GalaxyError(e, error_context_msg)
387
404
  except Exception as e:
388
- raise AnsibleError("Unknown error when attempting to call Galaxy at '%s': %s" % (url, to_native(e)))
405
+ raise AnsibleError(
406
+ "Unknown error when attempting to call Galaxy at '%s': %s" % (url, to_native(e)),
407
+ orig_exc=e
408
+ )
389
409
 
390
410
  resp_data = to_text(resp.read(), errors='surrogate_or_strict')
391
411
  try:
@@ -906,8 +926,7 @@ class GalaxyAPI:
906
926
  try:
907
927
  signatures = data["signatures"]
908
928
  except KeyError:
909
- # Noisy since this is used by the dep resolver, so require more verbosity than Galaxy calls
910
- display.vvvvvv(f"Server {self.api_server} has not signed {namespace}.{name}:{version}")
929
+ display.vvvv(f"Server {self.api_server} has not signed {namespace}.{name}:{version}")
911
930
  return []
912
931
  else:
913
932
  return [signature_info["signature"] for signature_info in signatures]
@@ -767,6 +767,9 @@ def install_collections(
767
767
  "Skipping signature verification."
768
768
  )
769
769
 
770
+ if concrete_coll_pin.type == 'galaxy':
771
+ concrete_coll_pin = concrete_coll_pin.with_signatures_repopulated()
772
+
770
773
  try:
771
774
  install(concrete_coll_pin, output_path, artifacts_manager)
772
775
  except AnsibleError as err:
@@ -27,9 +27,12 @@ if t.TYPE_CHECKING:
27
27
 
28
28
  from ansible.errors import AnsibleError
29
29
  from ansible.galaxy import get_collections_galaxy_meta_info
30
+ from ansible.galaxy.api import should_retry_error
30
31
  from ansible.galaxy.dependency_resolution.dataclasses import _GALAXY_YAML
31
32
  from ansible.galaxy.user_agent import user_agent
32
33
  from ansible.module_utils._text import to_bytes, to_native, to_text
34
+ from ansible.module_utils.api import retry_with_delays_and_condition
35
+ from ansible.module_utils.api import generate_jittered_backoff
33
36
  from ansible.module_utils.common.process import get_bin_path
34
37
  from ansible.module_utils.common._collections_compat import MutableMapping
35
38
  from ansible.module_utils.common.yaml import yaml_load
@@ -160,17 +163,24 @@ class ConcreteArtifactsManager:
160
163
  token=token,
161
164
  ) # type: bytes
162
165
  except URLError as err:
163
- raise_from(
164
- AnsibleError(
165
- 'Failed to download collection tar '
166
- "from '{coll_src!s}': {download_err!s}".
167
- format(
168
- coll_src=to_native(collection.src),
169
- download_err=to_native(err),
170
- ),
166
+ raise AnsibleError(
167
+ 'Failed to download collection tar '
168
+ "from '{coll_src!s}': {download_err!s}".
169
+ format(
170
+ coll_src=to_native(collection.src),
171
+ download_err=to_native(err),
171
172
  ),
172
- err,
173
- )
173
+ ) from err
174
+ except Exception as err:
175
+ raise AnsibleError(
176
+ 'Failed to download collection tar '
177
+ "from '{coll_src!s}' due to the following unforeseen error: "
178
+ '{download_err!s}'.
179
+ format(
180
+ coll_src=to_native(collection.src),
181
+ download_err=to_native(err),
182
+ ),
183
+ ) from err
174
184
  else:
175
185
  display.vvv(
176
186
  "Collection '{coll!s}' obtained from "
@@ -460,6 +470,10 @@ def _extract_collection_from_git(repo_url, coll_ver, b_path):
460
470
 
461
471
 
462
472
  # FIXME: use random subdirs while preserving the file names
473
+ @retry_with_delays_and_condition(
474
+ backoff_iterator=generate_jittered_backoff(retries=6, delay_base=2, delay_threshold=40),
475
+ should_retry_error=should_retry_error
476
+ )
463
477
  def _download_file(url, b_path, expected_hash, validate_certs, token=None, timeout=60):
464
478
  # type: (str, bytes, t.Optional[str], bool, GalaxyToken, int) -> bytes
465
479
  # ^ NOTE: used in download and verify_collections ^
@@ -478,13 +492,16 @@ def _download_file(url, b_path, expected_hash, validate_certs, token=None, timeo
478
492
  display.display("Downloading %s to %s" % (url, to_text(b_tarball_dir)))
479
493
  # NOTE: Galaxy redirects downloads to S3 which rejects the request
480
494
  # NOTE: if an Authorization header is attached so don't redirect it
481
- resp = open_url(
482
- to_native(url, errors='surrogate_or_strict'),
483
- validate_certs=validate_certs,
484
- headers=None if token is None else token.headers(),
485
- unredirected_headers=['Authorization'], http_agent=user_agent(),
486
- timeout=timeout
487
- )
495
+ try:
496
+ resp = open_url(
497
+ to_native(url, errors='surrogate_or_strict'),
498
+ validate_certs=validate_certs,
499
+ headers=None if token is None else token.headers(),
500
+ unredirected_headers=['Authorization'], http_agent=user_agent(),
501
+ timeout=timeout
502
+ )
503
+ except Exception as err:
504
+ raise AnsibleError(to_native(err), orig_exc=err)
488
505
 
489
506
  with open(b_file_path, 'wb') as download_file: # type: t.BinaryIO
490
507
  actual_hash = _consume_file(resp, write_to=download_file)
@@ -27,7 +27,7 @@ if t.TYPE_CHECKING:
27
27
  )
28
28
 
29
29
 
30
- from ansible.errors import AnsibleError
30
+ from ansible.errors import AnsibleError, AnsibleAssertionError
31
31
  from ansible.galaxy.api import GalaxyAPI
32
32
  from ansible.module_utils._text import to_bytes, to_native, to_text
33
33
  from ansible.module_utils.common.arg_spec import ArgumentSpecValidator
@@ -571,3 +571,13 @@ class Candidate(
571
571
 
572
572
  def __init__(self, *args, **kwargs):
573
573
  super(Candidate, self).__init__()
574
+
575
+ def with_signatures_repopulated(self): # type: (Candidate) -> Candidate
576
+ """Populate a new Candidate instance with Galaxy signatures.
577
+ :raises AnsibleAssertionError: If the supplied candidate is not sourced from a Galaxy-like index.
578
+ """
579
+ if self.type != 'galaxy':
580
+ raise AnsibleAssertionError(f"Invalid collection type for {self!r}: unable to get signatures from a galaxy server.")
581
+
582
+ signatures = self.src.get_collection_signatures(self.namespace, self.name, self.ver)
583
+ return self.__class__(self.fqcn, self.ver, self.src, self.type, frozenset([*self.signatures, *signatures]))
@@ -392,7 +392,6 @@ class CollectionDependencyProviderBase(AbstractProvider):
392
392
 
393
393
  if not unsatisfied:
394
394
  if self._include_signatures:
395
- signatures = src_server.get_collection_signatures(first_req.namespace, first_req.name, version)
396
395
  for extra_source in extra_signature_sources:
397
396
  signatures.append(get_signature_from_source(extra_source))
398
397
  latest_matches.append(
@@ -19,6 +19,6 @@
19
19
  from __future__ import (absolute_import, division, print_function)
20
20
  __metaclass__ = type
21
21
 
22
- __version__ = '2.14.4rc1'
22
+ __version__ = '2.14.5rc1'
23
23
  __author__ = 'Ansible, Inc.'
24
24
  __codename__ = "C'mon Everybody"
@@ -26,11 +26,15 @@ The 'api' module provides the following common argument specs:
26
26
  from __future__ import (absolute_import, division, print_function)
27
27
  __metaclass__ = type
28
28
 
29
+ import copy
29
30
  import functools
31
+ import itertools
30
32
  import random
31
33
  import sys
32
34
  import time
33
35
 
36
+ import ansible.module_utils.compat.typing as t
37
+
34
38
 
35
39
  def rate_limit_argument_spec(spec=None):
36
40
  """Creates an argument spec for working with rate limiting"""
@@ -141,6 +145,15 @@ def retry_with_delays_and_condition(backoff_iterator, should_retry_error=None):
141
145
  :param backoff_iterator: An iterable of delays in seconds.
142
146
  :param should_retry_error: A callable that takes an exception of the decorated function and decides whether to retry or not (returns a bool).
143
147
  """
148
+ def _emit_isolated_iterator_copies(original_iterator): # type: (t.Iterable[t.Any]) -> t.Generator
149
+ # Ref: https://stackoverflow.com/a/30232619/595220
150
+ _copiable_iterator, _first_iterator_copy = itertools.tee(original_iterator)
151
+ yield _first_iterator_copy
152
+ while True:
153
+ yield copy.copy(_copiable_iterator)
154
+ backoff_iterator_generator = _emit_isolated_iterator_copies(backoff_iterator)
155
+ del backoff_iterator # prevent accidental use elsewhere
156
+
144
157
  if should_retry_error is None:
145
158
  should_retry_error = retry_never
146
159
 
@@ -152,7 +165,7 @@ def retry_with_delays_and_condition(backoff_iterator, should_retry_error=None):
152
165
  """
153
166
  call_retryable_function = functools.partial(function, *args, **kwargs)
154
167
 
155
- for delay in backoff_iterator:
168
+ for delay in next(backoff_iterator_generator):
156
169
  try:
157
170
  return call_retryable_function()
158
171
  except Exception as e:
@@ -1,6 +1,8 @@
1
+ using Microsoft.Win32.SafeHandles;
1
2
  using System;
2
3
  using System.Collections;
3
4
  using System.Collections.Generic;
5
+ using System.ComponentModel;
4
6
  using System.Diagnostics;
5
7
  using System.IO;
6
8
  using System.Linq;
@@ -176,7 +178,8 @@ namespace Ansible.Basic
176
178
  }
177
179
 
178
180
  string dateTime = DateTime.Now.ToFileTime().ToString();
179
- string dirName = String.Format("ansible-moduletmp-{0}-{1}", dateTime, new Random().Next(0, int.MaxValue));
181
+ string dirName = String.Format("ansible-moduletmp-{0}-{1}-{2}", dateTime, System.Diagnostics.Process.GetCurrentProcess().Id,
182
+ new Random().Next(0, int.MaxValue));
180
183
  string newTmpdir = Path.Combine(baseDir, dirName);
181
184
  #if CORECLR
182
185
  DirectoryInfo tmpdirInfo = Directory.CreateDirectory(newTmpdir);
@@ -309,8 +312,8 @@ namespace Ansible.Basic
309
312
 
310
313
  public void ExitJson()
311
314
  {
312
- WriteLine(GetFormattedResults(Result));
313
315
  CleanupFiles(null, null);
316
+ WriteLine(GetFormattedResults(Result));
314
317
  Exit(0);
315
318
  }
316
319
 
@@ -337,8 +340,8 @@ namespace Ansible.Basic
337
340
  Result["exception"] = exception.ToString();
338
341
  }
339
342
 
340
- WriteLine(GetFormattedResults(Result));
341
343
  CleanupFiles(null, null);
344
+ WriteLine(GetFormattedResults(Result));
342
345
  Exit(1);
343
346
  }
344
347
 
@@ -1444,10 +1447,22 @@ namespace Ansible.Basic
1444
1447
  {
1445
1448
  foreach (string path in cleanupFiles)
1446
1449
  {
1447
- if (File.Exists(path))
1448
- File.Delete(path);
1449
- else if (Directory.Exists(path))
1450
- Directory.Delete(path, true);
1450
+ try
1451
+ {
1452
+ #if WINDOWS
1453
+ FileCleaner.Delete(path);
1454
+ #else
1455
+ if (File.Exists(path))
1456
+ File.Delete(path);
1457
+ else if (Directory.Exists(path))
1458
+ Directory.Delete(path, true);
1459
+ #endif
1460
+ }
1461
+ catch (Exception e)
1462
+ {
1463
+ Warn(string.Format("Failure cleaning temp path '{0}': {1} {2}",
1464
+ path, e.GetType().Name, e.Message));
1465
+ }
1451
1466
  }
1452
1467
  cleanupFiles = new List<string>();
1453
1468
  }
@@ -1486,4 +1501,247 @@ namespace Ansible.Basic
1486
1501
  Console.WriteLine(line);
1487
1502
  }
1488
1503
  }
1504
+
1505
+ #if WINDOWS
1506
+ // Windows is tricky as AVs and other software might still
1507
+ // have an open handle to files causing a failure. Use a
1508
+ // custom deletion mechanism to remove the files/dirs.
1509
+ // https://github.com/ansible/ansible/pull/80247
1510
+ internal static class FileCleaner
1511
+ {
1512
+ private const int FileDispositionInformation = 13;
1513
+ private const int FileDispositionInformationEx = 64;
1514
+
1515
+ private const int ERROR_INVALID_PARAMETER = 0x00000057;
1516
+ private const int ERROR_DIR_NOT_EMPTY = 0x00000091;
1517
+
1518
+ private static bool? _supportsPosixDelete = null;
1519
+
1520
+ [Flags()]
1521
+ public enum DispositionFlags : uint
1522
+ {
1523
+ FILE_DISPOSITION_DO_NOT_DELETE = 0x00000000,
1524
+ FILE_DISPOSITION_DELETE = 0x00000001,
1525
+ FILE_DISPOSITION_POSIX_SEMANTICS = 0x00000002,
1526
+ FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK = 0x00000004,
1527
+ FILE_DISPOSITION_ON_CLOSE = 0x00000008,
1528
+ FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE = 0x00000010,
1529
+ }
1530
+
1531
+ [Flags()]
1532
+ public enum FileFlags : uint
1533
+ {
1534
+ FILE_FLAG_OPEN_NO_RECALL = 0x00100000,
1535
+ FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000,
1536
+ FILE_FLAG_SESSION_AWARE = 0x00800000,
1537
+ FILE_FLAG_POSIX_SEMANTICS = 0x01000000,
1538
+ FILE_FLAG_BACKUP_SEMANTICS = 0x02000000,
1539
+ FILE_FLAG_DELETE_ON_CLOSE = 0x04000000,
1540
+ FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000,
1541
+ FILE_FLAG_RANDOM_ACCESS = 0x10000000,
1542
+ FILE_FLAG_NO_BUFFERING = 0x20000000,
1543
+ FILE_FLAG_OVERLAPPED = 0x40000000,
1544
+ FILE_FLAG_WRITE_THROUGH = 0x80000000,
1545
+ }
1546
+
1547
+ [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
1548
+ private static extern SafeFileHandle CreateFileW(
1549
+ [MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
1550
+ FileSystemRights dwDesiredAccess,
1551
+ FileShare dwShareMode,
1552
+ IntPtr lpSecurityAttributes,
1553
+ FileMode dwCreationDisposition,
1554
+ uint dwFlagsAndAttributes,
1555
+ IntPtr hTemplateFile);
1556
+
1557
+ private static SafeFileHandle CreateFile(string path, FileSystemRights access, FileShare share, FileMode mode,
1558
+ FileAttributes attributes, FileFlags flags)
1559
+ {
1560
+ uint flagsAndAttributes = (uint)attributes | (uint)flags;
1561
+ SafeFileHandle handle = CreateFileW(path, access, share, IntPtr.Zero, mode, flagsAndAttributes,
1562
+ IntPtr.Zero);
1563
+ if (handle.IsInvalid)
1564
+ {
1565
+ int errCode = Marshal.GetLastWin32Error();
1566
+ string msg = string.Format("CreateFileW({0}) failed 0x{1:X8}: {2}",
1567
+ path, errCode, new Win32Exception(errCode).Message);
1568
+ throw new Win32Exception(errCode, msg);
1569
+ }
1570
+
1571
+ return handle;
1572
+ }
1573
+
1574
+ [DllImport("Ntdll.dll")]
1575
+ private static extern int NtSetInformationFile(
1576
+ SafeFileHandle FileHandle,
1577
+ out IntPtr IoStatusBlock,
1578
+ ref int FileInformation,
1579
+ int Length,
1580
+ int FileInformationClass);
1581
+
1582
+ [DllImport("Ntdll.dll")]
1583
+ private static extern int RtlNtStatusToDosError(
1584
+ int Status);
1585
+
1586
+ public static void Delete(string path)
1587
+ {
1588
+ if (File.Exists(path))
1589
+ {
1590
+ DeleteEntry(path, FileAttributes.ReadOnly);
1591
+ }
1592
+ else if (Directory.Exists(path))
1593
+ {
1594
+ Queue<DirectoryInfo> dirQueue = new Queue<DirectoryInfo>();
1595
+ dirQueue.Enqueue(new DirectoryInfo(path));
1596
+ bool nonEmptyDirs = false;
1597
+ HashSet<string> processedDirs = new HashSet<string>();
1598
+
1599
+ while (dirQueue.Count > 0)
1600
+ {
1601
+ DirectoryInfo currentDir = dirQueue.Dequeue();
1602
+
1603
+ bool deleteDir = true;
1604
+ if (processedDirs.Add(currentDir.FullName))
1605
+ {
1606
+ foreach (FileSystemInfo entry in currentDir.EnumerateFileSystemInfos())
1607
+ {
1608
+ // Tries to delete each entry. Failures are ignored
1609
+ // as they will be picked up when the dir is
1610
+ // deleted and not empty.
1611
+ if (entry is DirectoryInfo)
1612
+ {
1613
+ if ((entry.Attributes & FileAttributes.ReparsePoint) != 0)
1614
+ {
1615
+ // If it's a reparse point, just delete it directly.
1616
+ DeleteEntry(entry.FullName, entry.Attributes, ignoreFailure: true);
1617
+ }
1618
+ else
1619
+ {
1620
+ // Add the dir to the queue to delete and it will be processed next round.
1621
+ dirQueue.Enqueue((DirectoryInfo)entry);
1622
+ deleteDir = false;
1623
+ }
1624
+ }
1625
+ else
1626
+ {
1627
+ DeleteEntry(entry.FullName, entry.Attributes, ignoreFailure: true);
1628
+ }
1629
+ }
1630
+ }
1631
+
1632
+ if (deleteDir)
1633
+ {
1634
+ try
1635
+ {
1636
+ DeleteEntry(currentDir.FullName, FileAttributes.Directory);
1637
+ }
1638
+ catch (Win32Exception e)
1639
+ {
1640
+ if (e.NativeErrorCode == ERROR_DIR_NOT_EMPTY)
1641
+ {
1642
+ nonEmptyDirs = true;
1643
+ }
1644
+ else
1645
+ {
1646
+ throw;
1647
+ }
1648
+ }
1649
+ }
1650
+ else
1651
+ {
1652
+ dirQueue.Enqueue(currentDir);
1653
+ }
1654
+ }
1655
+
1656
+ if (nonEmptyDirs)
1657
+ {
1658
+ throw new IOException("Directory contains files still open by other processes");
1659
+ }
1660
+ }
1661
+ }
1662
+
1663
+ private static void DeleteEntry(string path, FileAttributes attr, bool ignoreFailure = false)
1664
+ {
1665
+ try
1666
+ {
1667
+ if ((attr & FileAttributes.ReadOnly) != 0)
1668
+ {
1669
+ // Windows does not allow files set with ReadOnly to be
1670
+ // deleted. Pre-emptively unset the attribute.
1671
+ // FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE is quite new,
1672
+ // look at using that flag with POSIX delete once Server 2019
1673
+ // is the baseline.
1674
+ File.SetAttributes(path, FileAttributes.Normal);
1675
+ }
1676
+
1677
+ // REPARSE - Only touch the symlink itself and not the target
1678
+ // BACKUP - Needed for dir handles, bypasses access checks for admins
1679
+ // DELETE_ON_CLOSE is not used as it interferes with the POSIX delete
1680
+ FileFlags flags = FileFlags.FILE_FLAG_OPEN_REPARSE_POINT |
1681
+ FileFlags.FILE_FLAG_BACKUP_SEMANTICS;
1682
+
1683
+ using (SafeFileHandle fileHandle = CreateFile(path, FileSystemRights.Delete,
1684
+ FileShare.ReadWrite | FileShare.Delete, FileMode.Open, FileAttributes.Normal, flags))
1685
+ {
1686
+ if (_supportsPosixDelete == null || _supportsPosixDelete == true)
1687
+ {
1688
+ // A POSIX delete will delete the filesystem entry even if
1689
+ // it's still opened by another process so favour that if
1690
+ // available.
1691
+ DispositionFlags deleteFlags = DispositionFlags.FILE_DISPOSITION_DELETE |
1692
+ DispositionFlags.FILE_DISPOSITION_POSIX_SEMANTICS;
1693
+
1694
+ SetInformationFile(fileHandle, FileDispositionInformationEx, (int)deleteFlags);
1695
+ if (_supportsPosixDelete == true)
1696
+ {
1697
+ return;
1698
+ }
1699
+ }
1700
+
1701
+ // FileDispositionInformation takes in a struct with only a BOOLEAN value.
1702
+ // Using an int will also do the same thing to set that flag to true.
1703
+ SetInformationFile(fileHandle, FileDispositionInformation, Int32.MaxValue);
1704
+ }
1705
+ }
1706
+ catch
1707
+ {
1708
+ if (!ignoreFailure)
1709
+ {
1710
+ throw;
1711
+ }
1712
+ }
1713
+ }
1714
+
1715
+ private static void SetInformationFile(SafeFileHandle handle, int infoClass, int value)
1716
+ {
1717
+ IntPtr ioStatusBlock = IntPtr.Zero;
1718
+
1719
+ int ntStatus = NtSetInformationFile(handle, out ioStatusBlock, ref value,
1720
+ Marshal.SizeOf(typeof(int)), infoClass);
1721
+
1722
+ if (ntStatus != 0)
1723
+ {
1724
+ int errCode = RtlNtStatusToDosError(ntStatus);
1725
+
1726
+ // The POSIX delete was added in Server 2016 (Win 10 14393/Redstone 1)
1727
+ // Mark this flag so we don't try again.
1728
+ if (infoClass == FileDispositionInformationEx && _supportsPosixDelete == null &&
1729
+ errCode == ERROR_INVALID_PARAMETER)
1730
+ {
1731
+ _supportsPosixDelete = false;
1732
+ return;
1733
+ }
1734
+
1735
+ string msg = string.Format("NtSetInformationFile() failed 0x{0:X8}: {1}",
1736
+ errCode, new Win32Exception(errCode).Message);
1737
+ throw new Win32Exception(errCode, msg);
1738
+ }
1739
+
1740
+ if (infoClass == FileDispositionInformationEx)
1741
+ {
1742
+ _supportsPosixDelete = true;
1743
+ }
1744
+ }
1745
+ }
1746
+ #endif
1489
1747
  }
ansible/modules/uri.py CHANGED
@@ -94,9 +94,16 @@ options:
94
94
  force_basic_auth:
95
95
  description:
96
96
  - Force the sending of the Basic authentication header upon initial request.
97
- - The library used by the uri module only sends authentication information when a webservice
98
- responds to an initial request with a 401 status. Since some basic auth services do not properly
99
- send a 401, logins will fail.
97
+ - When this setting is C(false), this module will first try an unauthenticated request, and when the server replies
98
+ with an C(HTTP 401) error, it will submit the Basic authentication header.
99
+ - When this setting is C(true), this module will immediately send a Basic authentication header on the first
100
+ request.
101
+ - "Use this setting in any of the following scenarios:"
102
+ - You know the webservice endpoint always requires HTTP Basic authentication, and you want to speed up your
103
+ requests by eliminating the first roundtrip.
104
+ - The web service does not properly send an HTTP 401 error to your client, so Ansible's HTTP library will not
105
+ properly respond with HTTP credentials, and logins will fail.
106
+ - The webservice bans or rate-limits clients that cause any HTTP 401 errors.
100
107
  type: bool
101
108
  default: no
102
109
  follow_redirects:
@@ -308,6 +308,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
308
308
  # template the role name now, if needed
309
309
  all_vars = variable_manager.get_vars(play=play, task=ir)
310
310
  templar = Templar(loader=loader, variables=all_vars)
311
+ ir.post_validate(templar=templar)
311
312
  ir._role_name = templar.template(ir._role_name)
312
313
 
313
314
  # uses compiled list from object
@@ -187,6 +187,7 @@ class IncludedFile:
187
187
  role_name = templar.template(role_name)
188
188
 
189
189
  new_task = original_task.copy()
190
+ new_task.post_validate(templar=templar)
190
191
  new_task._role_name = role_name
191
192
  for from_arg in new_task.FROM_ARGS:
192
193
  if from_arg in include_args:
@@ -52,9 +52,9 @@ class IncludeRole(TaskInclude):
52
52
  # ATTRIBUTES
53
53
 
54
54
  # private as this is a 'module options' vs a task property
55
- allow_duplicates = NonInheritableFieldAttribute(isa='bool', default=True, private=True)
56
- public = NonInheritableFieldAttribute(isa='bool', default=False, private=True)
57
- rolespec_validate = NonInheritableFieldAttribute(isa='bool', default=True)
55
+ allow_duplicates = NonInheritableFieldAttribute(isa='bool', default=True, private=True, always_post_validate=True)
56
+ public = NonInheritableFieldAttribute(isa='bool', default=False, private=True, always_post_validate=True)
57
+ rolespec_validate = NonInheritableFieldAttribute(isa='bool', default=True, private=True, always_post_validate=True)
58
58
 
59
59
  def __init__(self, block=None, role=None, task_include=None):
60
60
 
@@ -1125,7 +1125,7 @@ class ActionBase(ABC):
1125
1125
  remote_files.append(remote_async_module_path)
1126
1126
 
1127
1127
  async_limit = self._task.async_val
1128
- async_jid = str(random.randint(0, 999999999999))
1128
+ async_jid = f'j{random.randint(0, 999999999999)}'
1129
1129
 
1130
1130
  # call the interpreter for async_wrapper directly
1131
1131
  # this permits use of a script for an interpreter on non-Linux platforms
@@ -5,6 +5,8 @@ DOCUMENTATION:
5
5
  short_description: get a path's base name
6
6
  description:
7
7
  - Returns the last name component of a path, what is left in the string that is not 'dirname'.
8
+ notes:
9
+ - The result of this filter is different from the Unix basename program; where basename for C(/foo/bar/) returns C(bar), the basename filter returns an empty string (C('')).
8
10
  options:
9
11
  _input:
10
12
  description: A path.
@@ -15,7 +17,7 @@ DOCUMENTATION:
15
17
  plugin: ansible.builtin.dirname
16
18
  EXAMPLES: |
17
19
 
18
- # To get the last name of a file path, like 'foo.txt' out of '/etc/asdf/foo.txt'
20
+ # To get the last name of a file path, like 'foo.txt' out of '/etc/asdf/foo.txt'.
19
21
  {{ mypath | basename }}
20
22
 
21
23
  RETURN:
@@ -5,6 +5,8 @@ DOCUMENTATION:
5
5
  short_description: get a path's directory name
6
6
  description:
7
7
  - Returns the 'head' component of a path, basically everything that is not the 'basename'.
8
+ notes:
9
+ - The result of this filter is different from the Unix dirname program; where dirname for C(/foo/bar/) returns C(/foo), the dirname filter returns the full path (C(/foo/bar/)).
8
10
  options:
9
11
  _input:
10
12
  description: A path.
@@ -15,7 +17,7 @@ DOCUMENTATION:
15
17
  plugin_type: filter
16
18
  EXAMPLES: |
17
19
 
18
- # To get the dir name of a file path, like '/etc/asdf' out of '/etc/asdf/foo.txt'
20
+ # To get the dir name of a file path, like '/etc/asdf' out of '/etc/asdf/foo.txt'.
19
21
  {{ mypath | dirname }}
20
22
 
21
23
  RETURN:
@@ -197,18 +197,31 @@ def _parse_content(content):
197
197
  '''
198
198
  password = content
199
199
  salt = None
200
+ ident = None
200
201
 
201
202
  salt_slug = u' salt='
203
+ ident_slug = u' ident='
204
+ rem = u''
202
205
  try:
203
206
  sep = content.rindex(salt_slug)
204
207
  except ValueError:
205
208
  # No salt
206
209
  pass
207
210
  else:
208
- salt = password[sep + len(salt_slug):]
211
+ rem = content[sep + len(salt_slug):]
209
212
  password = content[:sep]
210
213
 
211
- return password, salt
214
+ if rem:
215
+ try:
216
+ sep = rem.rindex(ident_slug)
217
+ except ValueError:
218
+ # no ident
219
+ salt = rem
220
+ else:
221
+ ident = rem[sep + len(ident_slug):]
222
+ salt = rem[:sep]
223
+
224
+ return password, salt, ident
212
225
 
213
226
 
214
227
  def _format_content(password, salt, encrypt=None, ident=None):
@@ -338,47 +351,73 @@ class LookupModule(LookupBase):
338
351
  self.set_options(var_options=variables, direct=kwargs)
339
352
 
340
353
  for term in terms:
354
+
355
+ changed = None
341
356
  relpath, params = self._parse_parameters(term)
342
357
  path = self._loader.path_dwim(relpath)
343
358
  b_path = to_bytes(path, errors='surrogate_or_strict')
344
359
  chars = _gen_candidate_chars(params['chars'])
345
-
346
- changed = None
347
- # make sure only one process finishes all the job first
348
- first_process, lockfile = _get_lock(b_path)
349
-
350
- content = _read_password_file(b_path)
351
-
352
- if content is None or b_path == to_bytes('/dev/null'):
353
- plaintext_password = random_password(params['length'], chars, params['seed'])
354
- salt = None
355
- changed = True
356
- else:
357
- plaintext_password, salt = _parse_content(content)
358
-
359
- encrypt = params['encrypt']
360
- if encrypt and not salt:
361
- changed = True
362
- try:
363
- salt = random_salt(BaseHash.algorithms[encrypt].salt_size)
364
- except KeyError:
365
- salt = random_salt()
366
-
367
- ident = params['ident']
368
- if encrypt and not ident:
369
- changed = True
370
- try:
371
- ident = BaseHash.algorithms[encrypt].implicit_ident
372
- except KeyError:
373
- ident = None
374
-
375
- if changed and b_path != to_bytes('/dev/null'):
376
- content = _format_content(plaintext_password, salt, encrypt=encrypt, ident=ident)
377
- _write_password_file(b_path, content)
378
-
379
- if first_process:
380
- # let other processes continue
381
- _release_lock(lockfile)
360
+ ident = None
361
+ first_process = None
362
+ lockfile = None
363
+
364
+ try:
365
+ # make sure only one process finishes all the job first
366
+ first_process, lockfile = _get_lock(b_path)
367
+ content = _read_password_file(b_path)
368
+
369
+ if content is None or b_path == to_bytes('/dev/null'):
370
+ plaintext_password = random_password(params['length'], chars, params['seed'])
371
+ salt = None
372
+ changed = True
373
+ else:
374
+ plaintext_password, salt, ident = _parse_content(content)
375
+
376
+ encrypt = params['encrypt']
377
+ if encrypt and not salt:
378
+ changed = True
379
+ try:
380
+ salt = random_salt(BaseHash.algorithms[encrypt].salt_size)
381
+ except KeyError:
382
+ salt = random_salt()
383
+
384
+ ident = params['ident']
385
+ if encrypt and not ident:
386
+ changed = True
387
+ try:
388
+ ident = BaseHash.algorithms[encrypt].implicit_ident
389
+ except KeyError:
390
+ ident = None
391
+
392
+ encrypt = params['encrypt']
393
+ if encrypt and not salt:
394
+ changed = True
395
+ try:
396
+ salt = random_salt(BaseHash.algorithms[encrypt].salt_size)
397
+ except KeyError:
398
+ salt = random_salt()
399
+
400
+ if not ident:
401
+ ident = params['ident']
402
+ elif params['ident'] and ident != params['ident']:
403
+ raise AnsibleError('The ident parameter provided (%s) does not match the stored one (%s).' % (ident, params['ident']))
404
+
405
+ if encrypt and not ident:
406
+ try:
407
+ ident = BaseHash.algorithms[encrypt].implicit_ident
408
+ except KeyError:
409
+ ident = None
410
+ if ident:
411
+ changed = True
412
+
413
+ if changed and b_path != to_bytes('/dev/null'):
414
+ content = _format_content(plaintext_password, salt, encrypt=encrypt, ident=ident)
415
+ _write_password_file(b_path, content)
416
+
417
+ finally:
418
+ if first_process:
419
+ # let other processes continue
420
+ _release_lock(lockfile)
382
421
 
383
422
  if encrypt:
384
423
  password = do_encrypt(plaintext_password, encrypt, salt=salt, ident=ident)
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.14.4rc1'
22
+ __version__ = '2.14.5rc1'
23
23
  __author__ = 'Ansible, Inc.'
24
24
  __codename__ = "C'mon Everybody"
ansible/utils/encrypt.py CHANGED
@@ -240,12 +240,15 @@ class PasslibHash(BaseHash):
240
240
  settings['ident'] = ident
241
241
 
242
242
  # starting with passlib 1.7 'using' and 'hash' should be used instead of 'encrypt'
243
- if hasattr(self.crypt_algo, 'hash'):
244
- result = self.crypt_algo.using(**settings).hash(secret)
245
- elif hasattr(self.crypt_algo, 'encrypt'):
246
- result = self.crypt_algo.encrypt(secret, **settings)
247
- else:
248
- raise AnsibleError("installed passlib version %s not supported" % passlib.__version__)
243
+ try:
244
+ if hasattr(self.crypt_algo, 'hash'):
245
+ result = self.crypt_algo.using(**settings).hash(secret)
246
+ elif hasattr(self.crypt_algo, 'encrypt'):
247
+ result = self.crypt_algo.encrypt(secret, **settings)
248
+ else:
249
+ raise AnsibleError("installed passlib version %s not supported" % passlib.__version__)
250
+ except ValueError as e:
251
+ raise AnsibleError("Could not hash the secret.", orig_exc=e)
249
252
 
250
253
  # passlib.hash should always return something or raise an exception.
251
254
  # Still ensure that there is always a result.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ansible-core
3
- Version: 2.14.4rc1
3
+ Version: 2.14.5rc1
4
4
  Summary: Radically simple IT automation
5
5
  Home-page: https://ansible.com/
6
6
  Author: Ansible, Inc.
@@ -3,20 +3,20 @@ 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=djTStOaiHKudMpMGgX-G2rj8yOfqFesusOSYEThIy2I,922
6
+ ansible/release.py,sha256=MN3F2lsQhXwkSVF7FWDp8Ub67eOlE9GPccTYxfvMFrI,922
7
7
  ansible/_vendor/__init__.py,sha256=wJRKH7kI9OzYVY9hgSchOsTNTmTnugpPLGYj9Y5akX0,2086
8
8
  ansible/cli/__init__.py,sha256=yDt8_ny7HauC9Aj-MMHQr3Y6gDQADfdEU0O1BfzkSwA,27957
9
9
  ansible/cli/adhoc.py,sha256=pGW6eysaireovp4sVsUuntg-l1o7DSujuhxVhVC2zsM,8230
10
10
  ansible/cli/config.py,sha256=zH9hNrXyd_E3WAbBSQ6_RqZDFchcMP9ZbkkpDWVXnSY,22109
11
11
  ansible/cli/console.py,sha256=rc-6s-Exf9b8lead40RyfugZdU1-cMoN-kA1iI8Uhs8,21941
12
- ansible/cli/doc.py,sha256=JLXNlmFofrkVLV--VussPZfBybNZy3JhWFS7bRdFwx4,60058
13
- ansible/cli/galaxy.py,sha256=hidKiuM2e0Q45oD9amHS5Cffo_3e5zf26rLw0iV7c8Q,92441
12
+ ansible/cli/doc.py,sha256=PerGaymHYb5TtKKbcuXs93VXMSYVapDEapQGZ4qYoxY,60432
13
+ ansible/cli/galaxy.py,sha256=NaHPqyzE6fHLGyS0gl_ofn-v0LAcN2tNJjF-KkvUpso,92453
14
14
  ansible/cli/inventory.py,sha256=Qh0QSMqG8Y8Wy6XXRJvP0HBlMntuRBVkq4ip2edExWo,16417
15
- ansible/cli/playbook.py,sha256=Of69ZAdQw4TYaydZaW5TpYu4VWrU95LwPG_o8qGTaOU,10305
15
+ ansible/cli/playbook.py,sha256=EqpEZ8Rb1q-0HBcnCb7QxP7OzkoJXw9P33uiQBtUmMM,10495
16
16
  ansible/cli/pull.py,sha256=TI3xfqcO-f8I60gRvVdiAEELghSq5Mlb8YPX6SdiitM,17010
17
17
  ansible/cli/vault.py,sha256=1x9Mn6KEdtGhyT9f8Z5ESWp52ft51uJjceinPcEObq4,22400
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=zaet96eOYPUk4v5p4AwPiDn3RAKtDWl31Z0L_By-4SM,13476
22
22
  ansible/collections/__init__.py,sha256=t8x1TZiQQa9vaQAm7ECYoP3MCdzquEtAlPzG9HzEV50,814
@@ -48,19 +48,19 @@ ansible/executor/powershell/become_wrapper.ps1,sha256=c0a4nYlFbpoZHzRpRHKgndZbYB
48
48
  ansible/executor/powershell/bootstrap_wrapper.ps1,sha256=8xudggKTMGibFCspF5m2BrJALARd67Rj062qCbKM2eY,487
49
49
  ansible/executor/powershell/coverage_wrapper.ps1,sha256=EM4X0tEWw2vMrNVQoxen6y8rYxTB8kEjx3PHoH4Mx5c,8296
50
50
  ansible/executor/powershell/exec_wrapper.ps1,sha256=6jHQZntr_WTD6tRlVV3mxgsvSZksdVtJM95HC_b9N2E,10128
51
- ansible/executor/powershell/module_manifest.py,sha256=f7Iyq2hXCtWgTQ3GrNvnzJXmIjU6VNFCpgi9NsdjNwg,17431
51
+ ansible/executor/powershell/module_manifest.py,sha256=GgOc4a03ryNAP1Tzm9wl4p9MSoTtc_md93ERCpTGnC0,17432
52
52
  ansible/executor/powershell/module_powershell_wrapper.ps1,sha256=6dPo5tSV6B-KipCBrz3iTvOM2Y_yj-gn_u7OKKmfjAs,3179
53
53
  ansible/executor/powershell/module_script_wrapper.ps1,sha256=Th1KTjKE3aOul6kRMunUj0EXFf7Jn7WOH-byCap9pqU,872
54
54
  ansible/executor/powershell/module_wrapper.ps1,sha256=JkCL_6aAZoXgQmFjU5hgEzTCMdzzh46G44rAJ_tfSzU,9038
55
55
  ansible/executor/process/__init__.py,sha256=1lMXN1i2fFqslda4BmeI5tpYMFP95D5Wpr1AjDJi-SQ,833
56
56
  ansible/executor/process/worker.py,sha256=mO9-GR7bDVxgDh65ROBhEBNg8qb9FWkkaP1vb_sklxY,8843
57
57
  ansible/galaxy/__init__.py,sha256=_ccTedn8dUGGtkmHcQLIkeje_YD0TYSXlvCl1AOY5fE,2533
58
- ansible/galaxy/api.py,sha256=MDtZGpFBAQlc5mE3Z47SJzr3CXXBBTvDF5www0DPxHw,39140
58
+ ansible/galaxy/api.py,sha256=deSYsFinaJodT2Y9-XnOerWIwYY8V2AWQ_9kZI0pWCE,39872
59
59
  ansible/galaxy/role.py,sha256=roEhuloz2-UHLdNwK7pqRCYsOLpu_Xg6sC_nyE5A30w,19086
60
60
  ansible/galaxy/token.py,sha256=K0dAwD3Fjkn3Zs2N9sG98UesSWfAukie47QGyYpIf0M,6167
61
61
  ansible/galaxy/user_agent.py,sha256=x7cJzzpnTngHcwqSUd2hg0i28Dv0tbAyBdke5CSiNhM,813
62
- ansible/galaxy/collection/__init__.py,sha256=3ncG3LBZXIxF9dGIuAQmoT-lQ7nxmap3guUbo43mPVI,75688
63
- ansible/galaxy/collection/concrete_artifact_manager.py,sha256=PXo1n9ldppWqp-qK4Dw2K2AhOd7jDkN1hq6nLNXHymQ,28713
62
+ ansible/galaxy/collection/__init__.py,sha256=VeKnmVeCyg0PFFus_5xmkt0kAsUiX59NRQB5kQVUUMc,75824
63
+ ansible/galaxy/collection/concrete_artifact_manager.py,sha256=btOQ__jt6gQgmHGitGoOQTQ3Ezvc1BssPaJLEs7nKl8,29515
64
64
  ansible/galaxy/collection/galaxy_api_proxy.py,sha256=HWnMiWIEt1YW7srbnFXjRsgpSC-3Iwj7-wkrkmVtXkA,7972
65
65
  ansible/galaxy/collection/gpg.py,sha256=1wk22RJnX--FsB-4h_EdaT05PWlx9AMxhfH3H7db1i4,7312
66
66
  ansible/galaxy/data/collections_galaxy_meta.yml,sha256=UNkGJWZEWSRbyiPbpoqLQdBR4DbAg9DeIB38HSZycoI,4018
@@ -125,9 +125,9 @@ ansible/galaxy/data/network/tests/inventory,sha256=4CIzgZsaCYREEFSRkYE_fMe6Ng8hK
125
125
  ansible/galaxy/data/network/tests/test.yml.j2,sha256=rjvtKhlkGT_CI_dgBLHX14eI41S6jFPypqESN8Uztgg,199
126
126
  ansible/galaxy/data/network/vars/main.yml.j2,sha256=3qtsgmeZTZ37OdmQNpmI9R6XxOEzJcOhjjGQYGFb85w,36
127
127
  ansible/galaxy/dependency_resolution/__init__.py,sha256=HDqJKqsDP4UHTCnoGq6c3rGpVwOI2DdYaH-4s7tkL0A,2196
128
- ansible/galaxy/dependency_resolution/dataclasses.py,sha256=BTVlxByqEKUntAxvBdyLcW6Xn-7i84U5JNVf9hfuCXs,21038
128
+ ansible/galaxy/dependency_resolution/dataclasses.py,sha256=qta8D8F-1uyaWh9SujIfPFGfYB3RYowQsNdKw4zJplk,21699
129
129
  ansible/galaxy/dependency_resolution/errors.py,sha256=7QSmgfeGrespnlr-eqKRQP3fopl4G_wRIiTSDILBrKs,717
130
- ansible/galaxy/dependency_resolution/providers.py,sha256=7vgs1SilEa50e0QpfaUnkRGrKYeh6pii6CT0ulrz9rI,25062
130
+ ansible/galaxy/dependency_resolution/providers.py,sha256=aY2FQgkucYMMGGjlK8v0V8bzR6pL1PAcaCner_vHIS8,24946
131
131
  ansible/galaxy/dependency_resolution/reporters.py,sha256=q-jyfsRu5qve8nikZ0_cBQar3dOX4_WMSAUXZd9xlqg,687
132
132
  ansible/galaxy/dependency_resolution/resolvers.py,sha256=XHBYqltTS7AV4mSiAF2ImXP53W6Yadog_rrqKxrpZU0,676
133
133
  ansible/galaxy/dependency_resolution/versioning.py,sha256=fGmuNhgwHaCKfHb6uH91A7ztuB86nDgiMW3htSI06DY,1779
@@ -139,8 +139,8 @@ ansible/inventory/host.py,sha256=wXJp6kpSaZtDr4JNsgdAuhi5MzQ9LTQzaAH10zoVbIA,505
139
139
  ansible/inventory/manager.py,sha256=tGwhBR6poLuG_i4jZ5RGOG-rH4gu4DBfT0-4iLLZZMs,29490
140
140
  ansible/module_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
141
141
  ansible/module_utils/_text.py,sha256=VCjTJovTxGfLahnzyrvOehpwTXLpRzMUug6wheirt4A,565
142
- ansible/module_utils/ansible_release.py,sha256=djTStOaiHKudMpMGgX-G2rj8yOfqFesusOSYEThIy2I,922
143
- ansible/module_utils/api.py,sha256=TKacH22Afdud1NWFSbmKwrO3mU1Br-KnnEh7cSJvRYU,5251
142
+ ansible/module_utils/ansible_release.py,sha256=MN3F2lsQhXwkSVF7FWDp8Ub67eOlE9GPccTYxfvMFrI,922
143
+ ansible/module_utils/api.py,sha256=BTo7stVOANbtd-ngZslaqx70r9t5gfvo44cKyu5SFjU,5837
144
144
  ansible/module_utils/basic.py,sha256=dGTJD-84x2a0hqKgoqB6PNhjmOEqYuuf2EgWyX5zVC8,86621
145
145
  ansible/module_utils/connection.py,sha256=XHxMlyAdwLiXDSo8jBMkV61-lz_0FDJUYH1B152UGJU,8430
146
146
  ansible/module_utils/errors.py,sha256=LYv9EWkvBRAmYW6qQY4Vz2hMULqkAGkMG2Opf86Ep4M,3396
@@ -180,7 +180,7 @@ ansible/module_utils/compat/selinux.py,sha256=FDJE4H5flBECz6k11dI0BObuCZkhToAEcN
180
180
  ansible/module_utils/compat/typing.py,sha256=w8Yy7Rm5MtsgTFA6rVe0YTRSY3FaatZeLCyS-rAOokM,741
181
181
  ansible/module_utils/compat/version.py,sha256=UKmpFhMk-Y9vuYfgmaT5NYctS3WFU-Xn9ycEzl01Nlc,12787
182
182
  ansible/module_utils/csharp/Ansible.AccessToken.cs,sha256=bGKFk92EEiG4omGPdUk3YZ_GzrDhzn0mO5yu_qC2rVs,16066
183
- ansible/module_utils/csharp/Ansible.Basic.cs,sha256=hhipp9LdbSKuNHnRvLFTHaV-uXhXf_vXnL19fKa3fi0,67135
183
+ ansible/module_utils/csharp/Ansible.Basic.cs,sha256=slTfWp_cpHuxlzjSQMT5VMqIBKg8XE0Ynsa_w0WoX7Q,77734
184
184
  ansible/module_utils/csharp/Ansible.Become.cs,sha256=py7VKd4XjFahf8blwX_Zj5pUvVVCA547SaGDCpMJfME,30728
185
185
  ansible/module_utils/csharp/Ansible.Privilege.cs,sha256=xdreXzRqhvp2kpGWVqFrDrnZozb_JANVVhH_7MDZ4fw,19592
186
186
  ansible/module_utils/csharp/Ansible.Process.cs,sha256=YTTK2LNwxlUa1Xdvf3KER71JFyrZSyrsrZtEX5vkoqo,19568
@@ -338,7 +338,7 @@ ansible/modules/sysvinit.py,sha256=xFhfQLwTdcbV7xVdlItHeLFDdU0t3rjiWoY1qocCIs0,1
338
338
  ansible/modules/tempfile.py,sha256=D4l0CHjp9AIC0o1BBDvw8UWQkWpilnc9VdmxNChxq6E,3502
339
339
  ansible/modules/template.py,sha256=k0h7j9n9v2efC0f1boCsTq2NwgTLkFuQxgxmUgq4nZE,3171
340
340
  ansible/modules/unarchive.py,sha256=77kv3buZezvXMb-8KmFvV-rtiyVdcvynaOw84bzk2Js,43815
341
- ansible/modules/uri.py,sha256=Vk5tqJAg7Q7UasgF65v6-sEB8zGI_5vK3-UHpNUjL40,27565
341
+ ansible/modules/uri.py,sha256=jU4WykeTWqa0KAkxF5hrmfJ19hbsn5-ALpZAorkT6fY,28158
342
342
  ansible/modules/user.py,sha256=qOnO_lxilSM--HAuO7UtC5g8PKaMS8hiD7QNpnTbyrY,116313
343
343
  ansible/modules/validate_argument_spec.py,sha256=nnRXyK1LmSSLxr2wsHnKdz8hbtvRft2_ifoTDhMm71k,3067
344
344
  ansible/modules/wait_for.py,sha256=8xLXpbwlXiL4Wwan0cQgpHoQaB6xaqn9Oc5TM4W9Xis,26530
@@ -370,13 +370,13 @@ ansible/playbook/collectionsearch.py,sha256=dfTz79Af_3iurr0YZDcNWsAffaRlbrffXz_K
370
370
  ansible/playbook/conditional.py,sha256=0uRGEegPxrKxZEEFNl50VcIsFrhQBScb7MsMdbaDsHg,9982
371
371
  ansible/playbook/handler.py,sha256=-gh7wMi0ZGLgVt3otzcA1ChyYOPYOchnVJREuuxHtZo,2249
372
372
  ansible/playbook/handler_task_include.py,sha256=grMLsGCeEb2YG4f_-Q6n7YZTRfoKFmXvSdLKivQJ1FA,1475
373
- ansible/playbook/helpers.py,sha256=FfAh89FFtG6xnSE7-NZm2f8LlOXKHyWlW_l8Cr_Z154,16690
374
- ansible/playbook/included_file.py,sha256=qEcMgbJ80TYO0_wWBqzeG-ua6sUmcjixc8kU7H-Vd_o,11527
373
+ ansible/playbook/helpers.py,sha256=jotNlRQmN4SoHpbuMv0Mx_pfVjIjAGpf3TYwc9Iw7xQ,16744
374
+ ansible/playbook/included_file.py,sha256=r3A19Iy_9BtUBfIMeZL_Ep755MfkzVJAYE3uSvHc4KA,11591
375
375
  ansible/playbook/loop_control.py,sha256=BJv5j6jFNo_M5nK6HaXCsNdaM-JClMcCv9D21TCRku4,1764
376
376
  ansible/playbook/play.py,sha256=CI6pJarF19Mcc7vAeGsgBSPXRH4x2jzXF3kPXTF7pOE,15717
377
377
  ansible/playbook/play_context.py,sha256=1YK_3nV5OPY8vnqiuC1JWb640KitWZaATSMEAUpylcs,14710
378
378
  ansible/playbook/playbook_include.py,sha256=QK6rccZUtKra60_VvF0Cd4LZ9yaH_t4Sq9evmald6jA,7565
379
- ansible/playbook/role_include.py,sha256=pHp2Ua_OMsg9DFMgP2WxZhDj4Q5my9B414ZIPCQYqoE,7809
379
+ ansible/playbook/role_include.py,sha256=F5gpKxdkJunyuxLTRUxMbiRujZUTlaFl02WnSvU8lEE,7904
380
380
  ansible/playbook/taggable.py,sha256=7pCyNHhAs2TBTV6h4LT9gd9v2syY_gJZqg5x6p0w9C4,3169
381
381
  ansible/playbook/task.py,sha256=e0AV5WzEWQ9QimuBryZeJ75qqysGbioScAZ4pTtxHOc,19997
382
382
  ansible/playbook/task_include.py,sha256=Qs6CpbwRcuyZeVaDGDclR5V-ZjZErncLpvmAGywRIro,6043
@@ -388,7 +388,7 @@ ansible/playbook/role/requirement.py,sha256=T_PljD0hgVstV325iALyCUKkAZxzLLlcQjpp
388
388
  ansible/plugins/__init__.py,sha256=i984DJbHLaZ1rirIIGSk3mS-dRAYeCWMmS6jPWkK2bw,5117
389
389
  ansible/plugins/list.py,sha256=RBasSwr51nQLK9I3NDmLGmUo1ntzGiprMpdc2Mny64c,8635
390
390
  ansible/plugins/loader.py,sha256=OfKF01mtOgr787iiHvd-l7FvyN6eJpGIe5L5gyNGE-U,71936
391
- ansible/plugins/action/__init__.py,sha256=ybZCES9-SOZa6-mOe0NrP59vkzwcH9MtZ1jm_L80zE4,68789
391
+ ansible/plugins/action/__init__.py,sha256=8JJS8PD4yhrn02sp60_qqW6VRoXEXkYFHMUecp_znxM,68790
392
392
  ansible/plugins/action/add_host.py,sha256=5r_HKyiIyiKAj3PZMshuepYliTppjBp6-o8b9_aFTJA,3667
393
393
  ansible/plugins/action/assemble.py,sha256=YlgV3zbB_gOtVFycTB9eC1u16essMRqYMDhb6ur4UVg,6573
394
394
  ansible/plugins/action/assert.py,sha256=l3BIU0ZfG7z832NpfvfvP8RfJwkBe-T5_PRsTAbyFZo,3891
@@ -460,7 +460,7 @@ ansible/plugins/doc_fragments/vars_plugin_staging.py,sha256=zsCh9HKY_FUVrs5VJF7_
460
460
  ansible/plugins/filter/__init__.py,sha256=c0E2OnVoIwjxAxvF-H9WkvN_MnJSSVdbHRTEoztEdEY,510
461
461
  ansible/plugins/filter/b64decode.yml,sha256=nkL5EqrNcDdQ7H8R1TlkPs9oThtcuJ4L-6pSV2tG7tM,927
462
462
  ansible/plugins/filter/b64encode.yml,sha256=QgnRLocoe5rIBXQkyCRJp0afbaDObVUxe5gKJ4io9C8,543
463
- ansible/plugins/filter/basename.yml,sha256=ZqG56yGv9KpyUcN8-am8E4Qhyl8z51YuA4_NvdhdAlU,621
463
+ ansible/plugins/filter/basename.yml,sha256=_Q5IxOAepLsSL8ENDd3MmgCXxbjazfSmE8gBSWJAlOU,809
464
464
  ansible/plugins/filter/bool.yml,sha256=PkoiSy7pwnkiyOEN59cRNaY56TAWEtzFhIuYxfLQTnc,631
465
465
  ansible/plugins/filter/checksum.yml,sha256=CMk4pVtvOWGyXoqH98KlvGOjMzMBDLTYl82SkzlOC0k,524
466
466
  ansible/plugins/filter/combinations.yml,sha256=LttrIICjapNtZHWnvJD-C9Pv3PIKP16i8WAXcU7TSLI,780
@@ -469,7 +469,7 @@ ansible/plugins/filter/comment.yml,sha256=3oYuPwp_stX1rOkkVinSFXoZLcpMvk93oj9RpF
469
469
  ansible/plugins/filter/core.py,sha256=7Bezfm4-w2mmeCsJZXIktLWhJyd-nGSfPuBjZ14nONs,21370
470
470
  ansible/plugins/filter/dict2items.yml,sha256=tUWJLVdZQ4sO0GYrJaC9wALSitTactFTdsrI9xb09KM,1314
471
471
  ansible/plugins/filter/difference.yml,sha256=a72tTaJswM3SS2NhFG2lQ5kRUnf5yYaouwS19f97bQA,1024
472
- ansible/plugins/filter/dirname.yml,sha256=WYY4yu5G8Dt2zXaKFaTp0_aqgDuKgfGqi0nEJT1p8Zw,629
472
+ ansible/plugins/filter/dirname.yml,sha256=Z7p7ay8s3_Zee6gIu7qr4wUC-an7lwLwuoVmgHQCKyg,820
473
473
  ansible/plugins/filter/encryption.py,sha256=dFznqIS2Q0XPnT-3yc77BHzF3sdfDXTVN_8AtfUdyCo,2693
474
474
  ansible/plugins/filter/expanduser.yml,sha256=P_AD4oBYbADe2f7pzHjbnkM95t8P1OzN0S3AHLT6C9o,506
475
475
  ansible/plugins/filter/expandvars.yml,sha256=KqtMP_oHLXsKFxHn_3iKX5PPGV5l-yhWX1jPD-sJOeE,569
@@ -556,7 +556,7 @@ ansible/plugins/lookup/items.py,sha256=TIeLaiW2PXHxZQ7Rd2cvoNg2blHtYkzSP8uI783xa
556
556
  ansible/plugins/lookup/lines.py,sha256=InvhZbBIIKpz-y0sTzCQRQmoG0RuOwGyvdpWXh_bmxk,2214
557
557
  ansible/plugins/lookup/list.py,sha256=7BylyTlSdmafiMXCVG7DIwqF2I5nAiDj9ot3nE_r3Ok,1142
558
558
  ansible/plugins/lookup/nested.py,sha256=pYhPRzrJ8JeILXAiBMvVK1-WXinvuus47SVqQopILnc,2702
559
- ansible/plugins/lookup/password.py,sha256=GlB39VDoUFZ2dzgWPggebII9g5Np1ScR7yXB7Ec-Ifk,16605
559
+ ansible/plugins/lookup/password.py,sha256=DIgDQXGqnHTS6YaSxaBzyH-5UL1xevJ_Jl5NXo9Ny-E,18098
560
560
  ansible/plugins/lookup/pipe.py,sha256=542Mcosq93UAwJlv7oE5JjpS3KKQTPfBfyZX7qE89tQ,3004
561
561
  ansible/plugins/lookup/random_choice.py,sha256=c7Cq2ufuDX1cOlMqvbjeoDT0B4r6jllprnl0FysUSgA,1574
562
562
  ansible/plugins/lookup/sequence.py,sha256=klnr8ewcq7gz5S_Z-CMSR8OsSR2KgLSUrhDQ4xRV1zU,9171
@@ -642,7 +642,7 @@ ansible/utils/cmd_functions.py,sha256=rh7_j2C93vVt-nueP9L2Ouz3Y_QAEQ7kI4dZTu2S6M
642
642
  ansible/utils/color.py,sha256=sBoONbLleSeCVPJMzyP4pvwSs1tKiSYwpCmAUq1Tzy0,4110
643
643
  ansible/utils/context_objects.py,sha256=1-j7SRLgVJU3J5b2PkXMcXS391z435SsKsYIU1m0eCw,3119
644
644
  ansible/utils/display.py,sha256=xKJFz0qLq2e4pXYCUUnQEmYSD0cIhm7Fi0QypVWHLFQ,19188
645
- ansible/utils/encrypt.py,sha256=OLn3dxTps8EVR05H_rO2jNbpGf9_jQmRrVsmMAaM2_s,10366
645
+ ansible/utils/encrypt.py,sha256=PRH8UG485_4PFH7B4VxXvz12iwprx8qWFbgBSTmlZEQ,10508
646
646
  ansible/utils/fqcn.py,sha256=bGTXbsj7wt0zfIf2c_mpLPTj8bGnjs1ACiRZAZpEs58,1268
647
647
  ansible/utils/galaxy.py,sha256=KkaFwPFCcn8S50R4jWDYzYm2H-2b95JUOxY2dAYPGIg,3914
648
648
  ansible/utils/hashing.py,sha256=YwnljDgZv2pJ82aydTSBGqmYWUujcOLHuwb-3S4AWfc,2904
@@ -674,7 +674,7 @@ ansible/vars/hostvars.py,sha256=dg3jpVmNwSg8EJ4SIvYGT80uxMgRtrOW6vvtDfrQzDU,5152
674
674
  ansible/vars/manager.py,sha256=9d-9uD9x1X35QgLOZRyJtAxg3b1j4sp8Ivc5C78viOk,36178
675
675
  ansible/vars/plugins.py,sha256=B7L3fXoSOoBZSXqJ2ulk0adx1g5SpAb8BxyLGPNA7d4,4695
676
676
  ansible/vars/reserved.py,sha256=FBD7n2dnA0CW4I0J1LtWwk2hQqvGW0KTRPcxaRtMKWo,2615
677
- ansible_core-2.14.4rc1.data/scripts/ansible-test,sha256=CYIYL99IxWdVTtDIj3avilIJXhGAmtjuKPPWNuLWuc8,1690
677
+ ansible_core-2.14.5rc1.data/scripts/ansible-test,sha256=CYIYL99IxWdVTtDIj3avilIJXhGAmtjuKPPWNuLWuc8,1690
678
678
  ansible_test/__init__.py,sha256=6e721yAyyyocRKzbCKtQXloAfFP7Aqv0L3zG70uh-4A,190
679
679
  ansible_test/_data/ansible.cfg,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
680
680
  ansible_test/_data/coveragerc,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -769,7 +769,7 @@ ansible_test/_internal/venv.py,sha256=DPHAt4tuoIdP7BOXa75-i4T7Paild8eGDsV2UUKOZ7
769
769
  ansible_test/_internal/ci/__init__.py,sha256=QOaC_8_wUzqFEbsFCXYAnElWoUo6gB40CXvP9RJ-Iyo,7738
770
770
  ansible_test/_internal/ci/azp.py,sha256=5ev2kSfqTHWVvluPbu7vV52gaXuBYt3mRGistKv4Kcs,10097
771
771
  ansible_test/_internal/ci/local.py,sha256=E4nnerMKdBoVEbsT8IBkl0nSdXyxO2gT8WAaxyzA1EY,6739
772
- ansible_test/_internal/classification/__init__.py,sha256=7GurCQDJezbQGw3ca2Xm5UDkHwVOuclIZPK84mU0V-k,34294
772
+ ansible_test/_internal/classification/__init__.py,sha256=MIYq_hhKRbZ9L31p7tRzeGlCPuX0M4IP1aH6f29K1UE,34475
773
773
  ansible_test/_internal/classification/common.py,sha256=jd5VLRegcOX-GNTZqN_7PBzwKF6akFQYsPEltfynGtU,894
774
774
  ansible_test/_internal/classification/csharp.py,sha256=3QpVZjamTTG7h86oeVm7d4UMbyojPbBALHVqCpxS1ic,3241
775
775
  ansible_test/_internal/classification/powershell.py,sha256=i8t8LxG_-wDPpz1VlnvqAPpRGgVJsuA_xglMtCpibCc,3053
@@ -783,7 +783,7 @@ ansible_test/_internal/cli/environments.py,sha256=M5Vcolpz-GBBUVlv8gVBl7_54RTSJL
783
783
  ansible_test/_internal/cli/epilog.py,sha256=kzCYlmqDccMZnSCV57iXUITo6Z9FMMUIagjWJHHA0yY,658
784
784
  ansible_test/_internal/cli/argparsing/__init__.py,sha256=ravr0Yv7tEOBFv2s2DuZtEl9BPAQNy-KMKcJNSk4dmc,8922
785
785
  ansible_test/_internal/cli/argparsing/actions.py,sha256=VplAf5K9G-loJmLXMAZwbRbIsuFJ-yDrRrP4El5p4RM,606
786
- ansible_test/_internal/cli/argparsing/argcompletion.py,sha256=bC2E7sBREAUufBSNTrrkSqJ9QtaDfLtaepugLhX9lvs,4562
786
+ ansible_test/_internal/cli/argparsing/argcompletion.py,sha256=zOZtYVDkqWIdbmuASkyJuMUKrFh4w3MJzYS2O9DoIQA,5166
787
787
  ansible_test/_internal/cli/argparsing/parsers.py,sha256=i7bEPWy7q2mcgiBb3sZ0EN5wQ0G5SetOMQKsOMSsw4M,21490
788
788
  ansible_test/_internal/cli/commands/__init__.py,sha256=d8FNvVbSVR2JlnyDUxnS-lZDIQqbdEEPU0cqJA9663Q,5436
789
789
  ansible_test/_internal/cli/commands/env.py,sha256=-3zKICX4STeo_lObMh6EmvBy3s2ORL53idmLKN2vCHk,1397
@@ -930,7 +930,7 @@ ansible_test/_util/controller/sanity/code-smell/use-compat-six.py,sha256=CkYomOt
930
930
  ansible_test/_util/controller/sanity/code-smell/changelog/sphinx.py,sha256=M3aEK_XugBtVJjfUZbeoVc10hzRylxRxNfEiNq1JVWQ,193
931
931
  ansible_test/_util/controller/sanity/integration-aliases/yaml_to_json.py,sha256=qxXHZboRVEqISZYOIXrutsAgobEyh6fiUibk133fzhI,299
932
932
  ansible_test/_util/controller/sanity/mypy/ansible-core.ini,sha256=B13dYyd5PGoN-BrFShPMhGBCGbV2oiTsBD8TdRDAh3Q,2327
933
- ansible_test/_util/controller/sanity/mypy/ansible-test.ini,sha256=5YFX-FxSKR3yVrOLxaP0jvpT380RubYZvMC2PG9NphA,854
933
+ ansible_test/_util/controller/sanity/mypy/ansible-test.ini,sha256=BFX5QmOpZiEpRf5zVcLvuIl00NtsJhTFXMAPsZ8sLyg,912
934
934
  ansible_test/_util/controller/sanity/mypy/modules.ini,sha256=48N2I3ubw3yAuE8layHQ_d0CTfH_eATuXt-K5Bq-ifw,1694
935
935
  ansible_test/_util/controller/sanity/pep8/current-ignore.txt,sha256=7J79Z7ypUs0AOEYy4didumaJI0z5Bo69V5u8eZ1M2Bc,20
936
936
  ansible_test/_util/controller/sanity/pslint/pslint.ps1,sha256=h0fLdkwF7JhGGjApvqAsCU87BKy0E_UiFJ_O7MARz6U,1089
@@ -991,9 +991,9 @@ ansible_test/config/cloud-config-vultr.ini.template,sha256=yO2Xz76Ay3kbG54jX7y8-
991
991
  ansible_test/config/config.yml,sha256=wb3knoBmZewG3GWOMnRHoVPQWW4vPixKLPMNS6vJmTc,2620
992
992
  ansible_test/config/inventory.networking.template,sha256=vQ7x1-u5Q4Y5CqZU-7eMSEJOSCzAbPOL1rmK_AQOQuY,1262
993
993
  ansible_test/config/inventory.winrm.template,sha256=_M2i1B9RYfwSjwvgf3M-H_5Br5FO_kney_kMRPmoggk,1025
994
- ansible_core-2.14.4rc1.dist-info/COPYING,sha256=CuBIWlvTemPmNgNZZBfk6w5lMzT6bH-TLKOg6F1K8ic,35148
995
- ansible_core-2.14.4rc1.dist-info/METADATA,sha256=NrnrR10FTLKKrdl7M858iex3UxIZOsRKnDfmHtqVIIc,7433
996
- ansible_core-2.14.4rc1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
997
- ansible_core-2.14.4rc1.dist-info/entry_points.txt,sha256=0mpmsrIhODChxKl3eS-NcVQCaMetBn8KdPLtVxQgR64,453
998
- ansible_core-2.14.4rc1.dist-info/top_level.txt,sha256=IFbRLjAvih1DYzJWg3_F6t4sCzEMxRO7TOMNs6GkYHo,21
999
- ansible_core-2.14.4rc1.dist-info/RECORD,,
994
+ ansible_core-2.14.5rc1.dist-info/COPYING,sha256=CuBIWlvTemPmNgNZZBfk6w5lMzT6bH-TLKOg6F1K8ic,35148
995
+ ansible_core-2.14.5rc1.dist-info/METADATA,sha256=anDEOrJmuSJQno8QE6F5ZtOuxDORy_FGFvYwKr8OrfA,7433
996
+ ansible_core-2.14.5rc1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
997
+ ansible_core-2.14.5rc1.dist-info/entry_points.txt,sha256=0mpmsrIhODChxKl3eS-NcVQCaMetBn8KdPLtVxQgR64,453
998
+ ansible_core-2.14.5rc1.dist-info/top_level.txt,sha256=IFbRLjAvih1DYzJWg3_F6t4sCzEMxRO7TOMNs6GkYHo,21
999
+ ansible_core-2.14.5rc1.dist-info/RECORD,,
@@ -668,6 +668,10 @@ class PathMapper:
668
668
 
669
669
  minimal: dict[str, str] = {}
670
670
 
671
+ packaging = {
672
+ 'integration': 'packaging/',
673
+ }
674
+
671
675
  # Early classification that needs to occur before common classification belongs here.
672
676
 
673
677
  if path.startswith('test/units/compat/'):
@@ -749,6 +753,9 @@ class PathMapper:
749
753
  return minimal
750
754
 
751
755
  if path.startswith('packaging/'):
756
+ if path.startswith('packaging/pep517_backend/'):
757
+ return packaging
758
+
752
759
  return minimal
753
760
 
754
761
  if path.startswith('test/ansible_test/'):
@@ -836,16 +843,17 @@ class PathMapper:
836
843
  return minimal
837
844
 
838
845
  if path in (
839
- 'setup.py',
846
+ 'MANIFEST.in',
847
+ 'pyproject.toml',
848
+ 'requirements.txt',
849
+ 'setup.cfg',
850
+ 'setup.py',
840
851
  ):
841
- return all_tests(self.args) # broad impact, run all tests
852
+ return packaging
842
853
 
843
854
  if ext in (
844
- '.in',
845
855
  '.md',
846
856
  '.rst',
847
- '.toml',
848
- '.txt',
849
857
  ):
850
858
  return minimal
851
859
 
@@ -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 = misc
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