ansible-core 2.19.4rc1__py3-none-any.whl → 2.20.0__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 (215) hide show
  1. ansible/_internal/__init__.py +1 -4
  2. ansible/_internal/_ansiballz/_builder.py +1 -3
  3. ansible/_internal/_collection_proxy.py +7 -9
  4. ansible/_internal/_json/__init__.py +3 -4
  5. ansible/_internal/_templating/_engine.py +1 -1
  6. ansible/_internal/_templating/_jinja_plugins.py +1 -2
  7. ansible/_internal/_wrapt.py +105 -301
  8. ansible/cli/__init__.py +11 -10
  9. ansible/cli/adhoc.py +1 -2
  10. ansible/cli/arguments/option_helpers.py +1 -1
  11. ansible/cli/config.py +5 -6
  12. ansible/cli/doc.py +67 -67
  13. ansible/cli/galaxy.py +15 -24
  14. ansible/cli/inventory.py +0 -1
  15. ansible/cli/playbook.py +0 -1
  16. ansible/cli/pull.py +0 -1
  17. ansible/cli/scripts/ansible_connection_cli_stub.py +1 -1
  18. ansible/config/base.yml +1 -25
  19. ansible/config/manager.py +0 -2
  20. ansible/executor/play_iterator.py +42 -20
  21. ansible/executor/playbook_executor.py +0 -9
  22. ansible/executor/task_executor.py +26 -18
  23. ansible/executor/task_queue_manager.py +1 -3
  24. ansible/galaxy/api.py +33 -80
  25. ansible/galaxy/collection/__init__.py +11 -21
  26. ansible/galaxy/dependency_resolution/__init__.py +10 -9
  27. ansible/galaxy/dependency_resolution/dataclasses.py +86 -70
  28. ansible/galaxy/dependency_resolution/providers.py +54 -134
  29. ansible/galaxy/dependency_resolution/versioning.py +2 -4
  30. ansible/galaxy/role.py +1 -33
  31. ansible/inventory/manager.py +2 -3
  32. ansible/keyword_desc.yml +0 -3
  33. ansible/module_utils/_internal/_datatag/__init__.py +2 -10
  34. ansible/module_utils/_internal/_no_six.py +86 -0
  35. ansible/module_utils/_text.py +28 -8
  36. ansible/module_utils/ansible_release.py +2 -2
  37. ansible/module_utils/basic.py +26 -23
  38. ansible/module_utils/common/_collections_compat.py +11 -2
  39. ansible/module_utils/common/collections.py +8 -3
  40. ansible/module_utils/common/dict_transformations.py +1 -2
  41. ansible/module_utils/common/network.py +4 -2
  42. ansible/module_utils/common/parameters.py +32 -41
  43. ansible/module_utils/common/text/converters.py +109 -23
  44. ansible/module_utils/common/text/formatters.py +6 -2
  45. ansible/module_utils/common/validation.py +11 -9
  46. ansible/module_utils/connection.py +8 -3
  47. ansible/module_utils/facts/hardware/linux.py +23 -7
  48. ansible/module_utils/facts/hardware/netbsd.py +1 -1
  49. ansible/module_utils/facts/hardware/sunos.py +2 -1
  50. ansible/module_utils/facts/packages.py +6 -2
  51. ansible/module_utils/facts/system/distribution.py +2 -1
  52. ansible/module_utils/facts/system/env.py +6 -3
  53. ansible/module_utils/facts/system/local.py +3 -1
  54. ansible/module_utils/parsing/convert_bool.py +6 -2
  55. ansible/module_utils/service.py +2 -3
  56. ansible/module_utils/six/__init__.py +11 -6
  57. ansible/module_utils/yumdnf.py +0 -5
  58. ansible/modules/apt.py +18 -13
  59. ansible/modules/apt_repository.py +1 -1
  60. ansible/modules/assemble.py +5 -9
  61. ansible/modules/blockinfile.py +39 -23
  62. ansible/modules/cron.py +26 -35
  63. ansible/modules/deb822_repository.py +83 -12
  64. ansible/modules/dnf.py +3 -7
  65. ansible/modules/dnf5.py +4 -6
  66. ansible/modules/expect.py +0 -3
  67. ansible/modules/find.py +1 -2
  68. ansible/modules/get_url.py +1 -1
  69. ansible/modules/git.py +4 -5
  70. ansible/modules/include_vars.py +1 -1
  71. ansible/modules/known_hosts.py +7 -1
  72. ansible/modules/lineinfile.py +71 -63
  73. ansible/modules/package_facts.py +1 -1
  74. ansible/modules/pip.py +8 -2
  75. ansible/modules/replace.py +6 -6
  76. ansible/modules/service.py +3 -4
  77. ansible/modules/stat.py +20 -0
  78. ansible/modules/uri.py +9 -10
  79. ansible/modules/user.py +1 -2
  80. ansible/modules/wait_for.py +2 -2
  81. ansible/modules/wait_for_connection.py +2 -1
  82. ansible/modules/yum_repository.py +1 -16
  83. ansible/parsing/dataloader.py +24 -31
  84. ansible/parsing/vault/__init__.py +1 -2
  85. ansible/playbook/base.py +8 -56
  86. ansible/playbook/block.py +0 -60
  87. ansible/playbook/collectionsearch.py +1 -2
  88. ansible/playbook/handler.py +1 -7
  89. ansible/playbook/helpers.py +0 -7
  90. ansible/playbook/included_file.py +1 -1
  91. ansible/playbook/play.py +102 -36
  92. ansible/playbook/play_context.py +4 -0
  93. ansible/playbook/role/__init__.py +10 -65
  94. ansible/playbook/role/definition.py +3 -4
  95. ansible/playbook/role/include.py +2 -3
  96. ansible/playbook/role/metadata.py +1 -12
  97. ansible/playbook/role/requirement.py +1 -2
  98. ansible/playbook/role_include.py +1 -2
  99. ansible/playbook/taggable.py +16 -5
  100. ansible/playbook/task.py +11 -50
  101. ansible/plugins/action/__init__.py +20 -19
  102. ansible/plugins/action/add_host.py +1 -2
  103. ansible/plugins/action/fetch.py +3 -5
  104. ansible/plugins/action/group_by.py +1 -2
  105. ansible/plugins/action/include_vars.py +20 -22
  106. ansible/plugins/action/script.py +1 -3
  107. ansible/plugins/action/template.py +1 -2
  108. ansible/plugins/action/uri.py +4 -2
  109. ansible/plugins/cache/__init__.py +1 -0
  110. ansible/plugins/callback/__init__.py +13 -6
  111. ansible/plugins/connection/__init__.py +3 -7
  112. ansible/plugins/connection/local.py +2 -3
  113. ansible/plugins/connection/psrp.py +0 -2
  114. ansible/plugins/connection/ssh.py +2 -7
  115. ansible/plugins/connection/winrm.py +0 -2
  116. ansible/plugins/doc_fragments/result_format_callback.py +15 -0
  117. ansible/plugins/filter/core.py +4 -5
  118. ansible/plugins/filter/encryption.py +3 -27
  119. ansible/plugins/filter/mathstuff.py +1 -2
  120. ansible/plugins/filter/to_nice_yaml.yml +31 -3
  121. ansible/plugins/filter/to_yaml.yml +29 -12
  122. ansible/plugins/inventory/__init__.py +1 -2
  123. ansible/plugins/inventory/toml.py +3 -6
  124. ansible/plugins/inventory/yaml.py +1 -2
  125. ansible/plugins/loader.py +3 -4
  126. ansible/plugins/lookup/password.py +1 -2
  127. ansible/plugins/lookup/subelements.py +2 -3
  128. ansible/plugins/lookup/url.py +1 -1
  129. ansible/plugins/lookup/varnames.py +1 -2
  130. ansible/plugins/shell/__init__.py +9 -4
  131. ansible/plugins/shell/powershell.py +8 -24
  132. ansible/plugins/strategy/__init__.py +5 -2
  133. ansible/plugins/test/core.py +4 -1
  134. ansible/plugins/test/falsy.yml +1 -1
  135. ansible/plugins/test/regex.yml +18 -6
  136. ansible/plugins/test/truthy.yml +1 -1
  137. ansible/release.py +2 -2
  138. ansible/template/__init__.py +3 -7
  139. ansible/utils/collection_loader/_collection_config.py +5 -0
  140. ansible/utils/collection_loader/_collection_finder.py +11 -14
  141. ansible/utils/context_objects.py +7 -4
  142. ansible/utils/display.py +7 -6
  143. ansible/utils/encrypt.py +0 -5
  144. ansible/utils/helpers.py +6 -2
  145. ansible/utils/jsonrpc.py +7 -3
  146. ansible/utils/plugin_docs.py +49 -38
  147. ansible/utils/ssh_functions.py +0 -19
  148. ansible/utils/unsafe_proxy.py +7 -7
  149. ansible/vars/clean.py +2 -3
  150. ansible/vars/manager.py +28 -22
  151. ansible/vars/plugins.py +1 -31
  152. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/METADATA +4 -4
  153. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/RECORD +213 -214
  154. ansible_test/_data/completion/docker.txt +7 -7
  155. ansible_test/_data/completion/network.txt +0 -1
  156. ansible_test/_data/completion/remote.txt +4 -4
  157. ansible_test/_data/requirements/ansible-test.txt +1 -1
  158. ansible_test/_data/requirements/ansible.txt +1 -1
  159. ansible_test/_data/requirements/sanity.ansible-doc.txt +2 -2
  160. ansible_test/_data/requirements/sanity.changelog.txt +2 -2
  161. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
  162. ansible_test/_data/requirements/sanity.import.txt +1 -1
  163. ansible_test/_data/requirements/sanity.integration-aliases.txt +1 -1
  164. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  165. ansible_test/_data/requirements/sanity.pylint.txt +6 -6
  166. ansible_test/_data/requirements/sanity.runtime-metadata.txt +1 -1
  167. ansible_test/_data/requirements/sanity.validate-modules.txt +2 -2
  168. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  169. ansible_test/_internal/cache.py +2 -5
  170. ansible_test/_internal/cli/compat.py +1 -1
  171. ansible_test/_internal/commands/coverage/combine.py +1 -3
  172. ansible_test/_internal/commands/integration/__init__.py +3 -7
  173. ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
  174. ansible_test/_internal/commands/integration/coverage.py +1 -3
  175. ansible_test/_internal/commands/integration/filters.py +5 -10
  176. ansible_test/_internal/commands/sanity/pylint.py +11 -0
  177. ansible_test/_internal/commands/sanity/validate_modules.py +1 -5
  178. ansible_test/_internal/commands/units/__init__.py +1 -13
  179. ansible_test/_internal/compat/packaging.py +2 -2
  180. ansible_test/_internal/compat/yaml.py +2 -2
  181. ansible_test/_internal/completion.py +2 -5
  182. ansible_test/_internal/config.py +2 -7
  183. ansible_test/_internal/coverage_util.py +1 -1
  184. ansible_test/_internal/delegation.py +2 -0
  185. ansible_test/_internal/docker_util.py +1 -1
  186. ansible_test/_internal/host_profiles.py +6 -11
  187. ansible_test/_internal/provider/__init__.py +2 -5
  188. ansible_test/_internal/provisioning.py +2 -5
  189. ansible_test/_internal/pypi_proxy.py +1 -1
  190. ansible_test/_internal/python_requirements.py +1 -1
  191. ansible_test/_internal/target.py +2 -6
  192. ansible_test/_internal/thread.py +1 -4
  193. ansible_test/_internal/util.py +9 -14
  194. ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +14 -19
  195. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +48 -45
  196. ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +9 -7
  197. ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +51 -37
  198. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +31 -18
  199. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +1 -2
  200. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +59 -71
  201. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -2
  202. ansible_test/_util/target/cli/ansible_test_cli_stub.py +4 -2
  203. ansible_test/_util/target/common/constants.py +2 -2
  204. ansible_test/_util/target/setup/bootstrap.sh +0 -6
  205. ansible/utils/py3compat.py +0 -27
  206. ansible_test/_data/pytest/config/legacy.ini +0 -4
  207. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/WHEEL +0 -0
  208. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/entry_points.txt +0 -0
  209. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/COPYING +0 -0
  210. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  211. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  212. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  213. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  214. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  215. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/top_level.txt +0 -0
@@ -6,12 +6,12 @@
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ import collections.abc as _c
9
10
  import os
10
11
  import pathlib
11
12
  import typing as t
12
13
 
13
14
  from collections import namedtuple
14
- from collections.abc import MutableSequence, MutableMapping
15
15
  from glob import iglob
16
16
  from urllib.parse import urlparse
17
17
  from yaml import safe_load
@@ -26,9 +26,6 @@ if t.TYPE_CHECKING:
26
26
  '_ComputedReqKindsMixin',
27
27
  )
28
28
 
29
- import ansible
30
- import ansible.release
31
-
32
29
  from ansible.errors import AnsibleError, AnsibleAssertionError
33
30
  from ansible.galaxy.api import GalaxyAPI
34
31
  from ansible.galaxy.collection import HAS_PACKAGING, PkgReq
@@ -42,12 +39,16 @@ _ALLOW_CONCRETE_POINTER_IN_SOURCE = False # NOTE: This is a feature flag
42
39
  _GALAXY_YAML = b'galaxy.yml'
43
40
  _MANIFEST_JSON = b'MANIFEST.json'
44
41
  _SOURCE_METADATA_FILE = b'GALAXY.yml'
45
- _ANSIBLE_PACKAGE_PATH = pathlib.Path(ansible.__file__).parent
46
42
 
47
43
  display = Display()
48
44
 
49
45
 
50
- def get_validated_source_info(b_source_info_path, namespace, name, version):
46
+ def get_validated_source_info(
47
+ b_source_info_path: bytes,
48
+ namespace: str,
49
+ name: str,
50
+ version: str,
51
+ ) -> dict[str, object] | None:
51
52
  source_info_path = to_text(b_source_info_path, errors='surrogate_or_strict')
52
53
 
53
54
  if not os.path.isfile(b_source_info_path):
@@ -62,7 +63,7 @@ def get_validated_source_info(b_source_info_path, namespace, name, version):
62
63
  )
63
64
  return None
64
65
 
65
- if not isinstance(metadata, MutableMapping):
66
+ if not isinstance(metadata, dict):
66
67
  display.warning(f"Error getting collection source information at '{source_info_path}': expected a YAML dictionary")
67
68
  return None
68
69
 
@@ -76,7 +77,12 @@ def get_validated_source_info(b_source_info_path, namespace, name, version):
76
77
  return metadata
77
78
 
78
79
 
79
- def _validate_v1_source_info_schema(namespace, name, version, provided_arguments):
80
+ def _validate_v1_source_info_schema(
81
+ namespace: str,
82
+ name: str,
83
+ version: str,
84
+ provided_arguments: dict[str, object],
85
+ ) -> list[str]:
80
86
  argument_spec_data = dict(
81
87
  format_version=dict(choices=["1.0.0"]),
82
88
  download_url=dict(),
@@ -106,24 +112,24 @@ def _validate_v1_source_info_schema(namespace, name, version, provided_arguments
106
112
  return validation_result.error_messages
107
113
 
108
114
 
109
- def _is_collection_src_dir(dir_path):
115
+ def _is_collection_src_dir(dir_path: bytes | str) -> bool:
110
116
  b_dir_path = to_bytes(dir_path, errors='surrogate_or_strict')
111
117
  return os.path.isfile(os.path.join(b_dir_path, _GALAXY_YAML))
112
118
 
113
119
 
114
- def _is_installed_collection_dir(dir_path):
120
+ def _is_installed_collection_dir(dir_path: bytes | str) -> bool:
115
121
  b_dir_path = to_bytes(dir_path, errors='surrogate_or_strict')
116
122
  return os.path.isfile(os.path.join(b_dir_path, _MANIFEST_JSON))
117
123
 
118
124
 
119
- def _is_collection_dir(dir_path):
125
+ def _is_collection_dir(dir_path: bytes | str) -> bool:
120
126
  return (
121
127
  _is_installed_collection_dir(dir_path) or
122
128
  _is_collection_src_dir(dir_path)
123
129
  )
124
130
 
125
131
 
126
- def _find_collections_in_subdirs(dir_path):
132
+ def _find_collections_in_subdirs(dir_path: str) -> _c.Iterator[bytes]:
127
133
  b_dir_path = to_bytes(dir_path, errors='surrogate_or_strict')
128
134
 
129
135
  subdir_glob_pattern = os.path.join(
@@ -139,23 +145,23 @@ def _find_collections_in_subdirs(dir_path):
139
145
  yield subdir
140
146
 
141
147
 
142
- def _is_collection_namespace_dir(tested_str):
148
+ def _is_collection_namespace_dir(tested_str: str) -> bool:
143
149
  return any(_find_collections_in_subdirs(tested_str))
144
150
 
145
151
 
146
- def _is_file_path(tested_str):
152
+ def _is_file_path(tested_str: str) -> bool:
147
153
  return os.path.isfile(to_bytes(tested_str, errors='surrogate_or_strict'))
148
154
 
149
155
 
150
- def _is_http_url(tested_str):
156
+ def _is_http_url(tested_str: str) -> bool:
151
157
  return urlparse(tested_str).scheme.lower() in {'http', 'https'}
152
158
 
153
159
 
154
- def _is_git_url(tested_str):
160
+ def _is_git_url(tested_str: str) -> bool:
155
161
  return tested_str.startswith(('git+', 'git@'))
156
162
 
157
163
 
158
- def _is_concrete_artifact_pointer(tested_str):
164
+ def _is_concrete_artifact_pointer(tested_str: str) -> bool:
159
165
  return any(
160
166
  predicate(tested_str)
161
167
  for predicate in (
@@ -172,7 +178,7 @@ def _is_concrete_artifact_pointer(tested_str):
172
178
  class _ComputedReqKindsMixin:
173
179
  UNIQUE_ATTRS = ('fqcn', 'ver', 'src', 'type')
174
180
 
175
- def __init__(self, *args, **kwargs):
181
+ def __init__(self, *args, **kwargs) -> None:
176
182
  if not self.may_have_offline_galaxy_info:
177
183
  self._source_info = None
178
184
  else:
@@ -185,18 +191,18 @@ class _ComputedReqKindsMixin:
185
191
  self.ver
186
192
  )
187
193
 
188
- def __hash__(self):
194
+ def __hash__(self) -> int:
189
195
  return hash(tuple(getattr(self, attr) for attr in _ComputedReqKindsMixin.UNIQUE_ATTRS))
190
196
 
191
- def __eq__(self, candidate):
197
+ def __eq__(self, candidate: _c.Hashable) -> bool:
192
198
  return hash(self) == hash(candidate)
193
199
 
194
200
  @classmethod
195
- def from_dir_path_as_unknown( # type: ignore[misc]
196
- cls, # type: t.Type[Collection]
197
- dir_path, # type: bytes
198
- art_mgr, # type: ConcreteArtifactsManager
199
- ): # type: (...) -> Collection
201
+ def from_dir_path_as_unknown(
202
+ cls,
203
+ dir_path: bytes,
204
+ art_mgr: ConcreteArtifactsManager,
205
+ ) -> t.Self:
200
206
  """Make collection from an unspecified dir type.
201
207
 
202
208
  This alternative constructor attempts to grab metadata from the
@@ -219,22 +225,16 @@ class _ComputedReqKindsMixin:
219
225
  return cls.from_dir_path_implicit(dir_path)
220
226
 
221
227
  @classmethod
222
- def from_dir_path( # type: ignore[misc]
223
- cls, # type: t.Type[Collection]
224
- dir_path, # type: bytes
225
- art_mgr, # type: ConcreteArtifactsManager
226
- ): # type: (...) -> Collection
228
+ def from_dir_path(
229
+ cls,
230
+ dir_path: bytes,
231
+ art_mgr: ConcreteArtifactsManager,
232
+ ) -> t.Self:
227
233
  """Make collection from an directory with metadata."""
228
234
  if dir_path.endswith(to_bytes(os.path.sep)):
229
235
  dir_path = dir_path.rstrip(to_bytes(os.path.sep))
230
236
  if not _is_collection_dir(dir_path):
231
237
  dir_pathlib = pathlib.Path(to_text(dir_path))
232
-
233
- # special handling for bundled collections without manifests, e.g., ansible._protomatter
234
- if dir_pathlib.is_relative_to(_ANSIBLE_PACKAGE_PATH):
235
- req_name = f'{dir_pathlib.parent.name}.{dir_pathlib.name}'
236
- return cls(req_name, ansible.release.__version__, dir_path, 'dir', None)
237
-
238
238
  display.warning(
239
239
  u"Collection at '{path!s}' does not have a {manifest_json!s} "
240
240
  u'file, nor has it {galaxy_yml!s}: cannot detect version.'.
@@ -272,10 +272,10 @@ class _ComputedReqKindsMixin:
272
272
  return cls(req_name, req_version, dir_path, 'dir', None)
273
273
 
274
274
  @classmethod
275
- def from_dir_path_implicit( # type: ignore[misc]
276
- cls, # type: t.Type[Collection]
277
- dir_path, # type: bytes
278
- ): # type: (...) -> Collection
275
+ def from_dir_path_implicit(
276
+ cls,
277
+ dir_path: bytes,
278
+ ) -> t.Self:
279
279
  """Construct a collection instance based on an arbitrary dir.
280
280
 
281
281
  This alternative constructor infers the FQCN based on the parent
@@ -288,11 +288,16 @@ class _ComputedReqKindsMixin:
288
288
  u_dir_path = to_text(dir_path, errors='surrogate_or_strict')
289
289
  path_list = u_dir_path.split(os.path.sep)
290
290
  req_name = '.'.join(path_list[-2:])
291
- return cls(req_name, '*', dir_path, 'dir', None) # type: ignore[call-arg]
291
+ return cls(req_name, '*', dir_path, 'dir', None)
292
292
 
293
293
  @classmethod
294
- def from_string(cls, collection_input, artifacts_manager, supplemental_signatures):
295
- req = {}
294
+ def from_string(
295
+ cls,
296
+ collection_input: str,
297
+ artifacts_manager: ConcreteArtifactsManager,
298
+ supplemental_signatures: list[str] | None,
299
+ ) -> t.Self:
300
+ req: dict[str, str | list[str] | None] = {}
296
301
  if _is_concrete_artifact_pointer(collection_input) or AnsibleCollectionRef.is_valid_collection_name(collection_input):
297
302
  # Arg is a file path or URL to a collection, or just a collection
298
303
  req['name'] = collection_input
@@ -317,7 +322,14 @@ class _ComputedReqKindsMixin:
317
322
  return cls.from_requirement_dict(req, artifacts_manager)
318
323
 
319
324
  @classmethod
320
- def from_requirement_dict(cls, collection_req, art_mgr, validate_signature_options=True):
325
+ def from_requirement_dict(
326
+ cls,
327
+ # NOTE: The actual `collection_req` shape is supposed to be
328
+ # NOTE: `dict[str, str | list[str] | None]`
329
+ collection_req: dict[str, t.Any],
330
+ art_mgr: ConcreteArtifactsManager,
331
+ validate_signature_options: bool = True,
332
+ ) -> t.Self:
321
333
  req_name = collection_req.get('name', None)
322
334
  req_version = collection_req.get('version', '*')
323
335
  req_type = collection_req.get('type')
@@ -330,7 +342,7 @@ class _ComputedReqKindsMixin:
330
342
  f"Signatures were provided to verify {req_name} but no keyring was configured."
331
343
  )
332
344
 
333
- if not isinstance(req_signature_sources, MutableSequence):
345
+ if not isinstance(req_signature_sources, _c.MutableSequence):
334
346
  req_signature_sources = [req_signature_sources]
335
347
  req_signature_sources = frozenset(req_signature_sources)
336
348
 
@@ -444,7 +456,11 @@ class _ComputedReqKindsMixin:
444
456
  format(not_url=req_source.api_server),
445
457
  )
446
458
 
447
- if req_type == 'dir' and req_source.endswith(os.path.sep):
459
+ if (
460
+ req_type == 'dir'
461
+ and isinstance(req_source, str)
462
+ and req_source.endswith(os.path.sep)
463
+ ):
448
464
  req_source = req_source.rstrip(os.path.sep)
449
465
 
450
466
  tmp_inst_req = cls(req_name, req_version, req_source, req_type, req_signature_sources)
@@ -461,16 +477,16 @@ class _ComputedReqKindsMixin:
461
477
  req_signature_sources,
462
478
  )
463
479
 
464
- def __repr__(self):
480
+ def __repr__(self) -> str:
465
481
  return (
466
482
  '<{self!s} of type {coll_type!r} from {src!s}>'.
467
483
  format(self=self, coll_type=self.type, src=self.src or 'Galaxy')
468
484
  )
469
485
 
470
- def __str__(self):
486
+ def __str__(self) -> str:
471
487
  return to_native(self.__unicode__())
472
488
 
473
- def __unicode__(self):
489
+ def __unicode__(self) -> str:
474
490
  if self.fqcn is None:
475
491
  return (
476
492
  f'{self.type} collection from a Git repo' if self.is_scm
@@ -483,7 +499,7 @@ class _ComputedReqKindsMixin:
483
499
  )
484
500
 
485
501
  @property
486
- def may_have_offline_galaxy_info(self):
502
+ def may_have_offline_galaxy_info(self) -> bool:
487
503
  if self.fqcn is None:
488
504
  # Virtual collection
489
505
  return False
@@ -492,7 +508,7 @@ class _ComputedReqKindsMixin:
492
508
  return False
493
509
  return True
494
510
 
495
- def construct_galaxy_info_path(self, b_collection_path):
511
+ def construct_galaxy_info_path(self, b_collection_path: bytes) -> bytes:
496
512
  if not self.may_have_offline_galaxy_info and not self.type == 'galaxy':
497
513
  raise TypeError('Only installed collections from a Galaxy server have offline Galaxy info')
498
514
 
@@ -512,21 +528,21 @@ class _ComputedReqKindsMixin:
512
528
  return self.fqcn.split('.')
513
529
 
514
530
  @property
515
- def namespace(self):
531
+ def namespace(self) -> str:
516
532
  if self.is_virtual:
517
533
  raise TypeError(f'{self.type} collections do not have a namespace')
518
534
 
519
535
  return self._get_separate_ns_n_name()[0]
520
536
 
521
537
  @property
522
- def name(self):
538
+ def name(self) -> str:
523
539
  if self.is_virtual:
524
540
  raise TypeError(f'{self.type} collections do not have a name')
525
541
 
526
542
  return self._get_separate_ns_n_name()[-1]
527
543
 
528
544
  @property
529
- def canonical_package_id(self):
545
+ def canonical_package_id(self) -> str:
530
546
  if not self.is_virtual:
531
547
  return to_native(self.fqcn)
532
548
 
@@ -536,46 +552,46 @@ class _ComputedReqKindsMixin:
536
552
  )
537
553
 
538
554
  @property
539
- def is_virtual(self):
555
+ def is_virtual(self) -> bool:
540
556
  return self.is_scm or self.is_subdirs
541
557
 
542
558
  @property
543
- def is_file(self):
559
+ def is_file(self) -> bool:
544
560
  return self.type == 'file'
545
561
 
546
562
  @property
547
- def is_dir(self):
563
+ def is_dir(self) -> bool:
548
564
  return self.type == 'dir'
549
565
 
550
566
  @property
551
- def namespace_collection_paths(self):
567
+ def namespace_collection_paths(self) -> list[str]:
552
568
  return [
553
569
  to_native(path)
554
570
  for path in _find_collections_in_subdirs(self.src)
555
571
  ]
556
572
 
557
573
  @property
558
- def is_subdirs(self):
574
+ def is_subdirs(self) -> bool:
559
575
  return self.type == 'subdirs'
560
576
 
561
577
  @property
562
- def is_url(self):
578
+ def is_url(self) -> bool:
563
579
  return self.type == 'url'
564
580
 
565
581
  @property
566
- def is_scm(self):
582
+ def is_scm(self) -> bool:
567
583
  return self.type == 'git'
568
584
 
569
585
  @property
570
- def is_concrete_artifact(self):
586
+ def is_concrete_artifact(self) -> bool:
571
587
  return self.type in {'git', 'url', 'file', 'dir', 'subdirs'}
572
588
 
573
589
  @property
574
- def is_online_index_pointer(self):
590
+ def is_online_index_pointer(self) -> bool:
575
591
  return not self.is_concrete_artifact
576
592
 
577
593
  @property
578
- def is_pinned(self):
594
+ def is_pinned(self) -> bool:
579
595
  """Indicate if the version set is considered pinned.
580
596
 
581
597
  This essentially computes whether the version field of the current
@@ -595,7 +611,7 @@ class _ComputedReqKindsMixin:
595
611
  )
596
612
 
597
613
  @property
598
- def source_info(self):
614
+ def source_info(self) -> dict[str, object] | None:
599
615
  return self._source_info
600
616
 
601
617
 
@@ -611,11 +627,11 @@ class Requirement(
611
627
  ):
612
628
  """An abstract requirement request."""
613
629
 
614
- def __new__(cls, *args, **kwargs):
630
+ def __new__(cls, *args: object, **kwargs: object) -> t.Self:
615
631
  self = RequirementNamedTuple.__new__(cls, *args, **kwargs)
616
632
  return self
617
633
 
618
- def __init__(self, *args, **kwargs):
634
+ def __init__(self, *args: object, **kwargs: object) -> None:
619
635
  super(Requirement, self).__init__()
620
636
 
621
637
 
@@ -625,14 +641,14 @@ class Candidate(
625
641
  ):
626
642
  """A concrete collection candidate with its version resolved."""
627
643
 
628
- def __new__(cls, *args, **kwargs):
644
+ def __new__(cls, *args: object, **kwargs: object) -> t.Self:
629
645
  self = CandidateNamedTuple.__new__(cls, *args, **kwargs)
630
646
  return self
631
647
 
632
- def __init__(self, *args, **kwargs):
648
+ def __init__(self, *args: object, **kwargs: object) -> None:
633
649
  super(Candidate, self).__init__()
634
650
 
635
- def with_signatures_repopulated(self): # type: (Candidate) -> Candidate
651
+ def with_signatures_repopulated(self) -> Candidate:
636
652
  """Populate a new Candidate instance with Galaxy signatures.
637
653
  :raises AnsibleAssertionError: If the supplied candidate is not sourced from a Galaxy-like index.
638
654
  """