ansible-core 2.19.0b4__py3-none-any.whl → 2.19.0b6__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.
Files changed (225) hide show
  1. ansible/_internal/__init__.py +1 -1
  2. ansible/_internal/_ansiballz/__init__.py +0 -0
  3. ansible/_internal/_ansiballz/_builder.py +101 -0
  4. ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
  5. ansible/_internal/_collection_proxy.py +1 -1
  6. ansible/_internal/_errors/_alarm_timeout.py +66 -0
  7. ansible/_internal/_errors/_captured.py +25 -30
  8. ansible/_internal/_errors/_error_factory.py +89 -0
  9. ansible/_internal/_errors/_error_utils.py +240 -0
  10. ansible/_internal/_errors/_task_timeout.py +28 -0
  11. ansible/_internal/_event_formatting.py +127 -0
  12. ansible/_internal/_json/__init__.py +5 -5
  13. ansible/_internal/_json/_profiles/_cache_persistence.py +2 -0
  14. ansible/_internal/_json/_profiles/_inventory_legacy.py +1 -1
  15. ansible/_internal/_json/_profiles/_legacy.py +3 -11
  16. ansible/_internal/_ssh/__init__.py +0 -0
  17. ansible/_internal/_ssh/_agent_launch.py +91 -0
  18. ansible/{utils → _internal/_ssh}/_ssh_agent.py +55 -93
  19. ansible/_internal/_templating/__init__.py +5 -3
  20. ansible/_internal/_templating/_datatag.py +2 -1
  21. ansible/_internal/_templating/_engine.py +3 -4
  22. ansible/_internal/_templating/_jinja_bits.py +28 -20
  23. ansible/_internal/_templating/_jinja_common.py +18 -27
  24. ansible/_internal/_templating/_jinja_plugins.py +36 -5
  25. ansible/_internal/_templating/_lazy_containers.py +5 -5
  26. ansible/_internal/_templating/_template_vars.py +72 -0
  27. ansible/_internal/_templating/_transform.py +26 -19
  28. ansible/_internal/_templating/_utils.py +1 -1
  29. ansible/_internal/_yaml/_constructor.py +4 -4
  30. ansible/_internal/_yaml/_dumper.py +26 -18
  31. ansible/_internal/_yaml/_errors.py +7 -7
  32. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +1 -1
  33. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +1 -1
  34. ansible/cli/__init__.py +11 -93
  35. ansible/cli/arguments/option_helpers.py +3 -4
  36. ansible/cli/console.py +1 -1
  37. ansible/cli/doc.py +86 -30
  38. ansible/cli/inventory.py +5 -7
  39. ansible/compat/importlib_resources.py +9 -12
  40. ansible/config/base.yml +46 -0
  41. ansible/errors/__init__.py +98 -50
  42. ansible/executor/module_common.py +75 -49
  43. ansible/executor/powershell/async_watchdog.ps1 +2 -2
  44. ansible/executor/powershell/async_wrapper.ps1 +3 -3
  45. ansible/executor/powershell/become_wrapper.ps1 +20 -2
  46. ansible/executor/powershell/bootstrap_wrapper.ps1 +28 -6
  47. ansible/executor/powershell/coverage_wrapper.ps1 +15 -6
  48. ansible/executor/powershell/exec_wrapper.ps1 +219 -6
  49. ansible/executor/powershell/module_manifest.py +52 -0
  50. ansible/executor/powershell/module_wrapper.ps1 +47 -21
  51. ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
  52. ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
  53. ansible/executor/process/worker.py +40 -115
  54. ansible/executor/task_executor.py +26 -61
  55. ansible/executor/task_result.py +2 -4
  56. ansible/galaxy/api.py +1 -4
  57. ansible/galaxy/collection/__init__.py +2 -10
  58. ansible/galaxy/collection/concrete_artifact_manager.py +2 -8
  59. ansible/galaxy/role.py +2 -2
  60. ansible/inventory/manager.py +1 -1
  61. ansible/module_utils/_internal/__init__.py +7 -7
  62. ansible/module_utils/_internal/_ambient_context.py +3 -3
  63. ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
  64. ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
  65. ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
  66. ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
  67. ansible/module_utils/_internal/{_ansiballz.py → _ansiballz/_loader.py} +13 -39
  68. ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
  69. ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
  70. ansible/module_utils/_internal/_datatag/__init__.py +43 -15
  71. ansible/module_utils/_internal/_datatag/_tags.py +2 -2
  72. ansible/module_utils/_internal/_deprecator.py +67 -55
  73. ansible/module_utils/_internal/_errors.py +88 -17
  74. ansible/module_utils/_internal/_event_utils.py +61 -0
  75. ansible/module_utils/_internal/_json/_profiles/__init__.py +22 -4
  76. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +2 -0
  77. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +2 -0
  78. ansible/module_utils/_internal/_json/_profiles/_tagless.py +3 -1
  79. ansible/module_utils/{common/messages.py → _internal/_messages.py} +54 -49
  80. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
  81. ansible/module_utils/_internal/_plugin_info.py +15 -2
  82. ansible/module_utils/_internal/_stack.py +22 -0
  83. ansible/module_utils/_internal/_text_utils.py +6 -0
  84. ansible/module_utils/_internal/_traceback.py +11 -8
  85. ansible/module_utils/ansible_release.py +1 -1
  86. ansible/module_utils/basic.py +95 -71
  87. ansible/module_utils/common/arg_spec.py +2 -2
  88. ansible/module_utils/common/collections.py +6 -0
  89. ansible/module_utils/common/json.py +2 -2
  90. ansible/module_utils/common/respawn.py +4 -41
  91. ansible/module_utils/common/text/converters.py +3 -3
  92. ansible/module_utils/common/validation.py +1 -1
  93. ansible/module_utils/common/warnings.py +80 -23
  94. ansible/module_utils/common/yaml.py +1 -1
  95. ansible/module_utils/connection.py +8 -11
  96. ansible/module_utils/datatag.py +5 -2
  97. ansible/module_utils/facts/hardware/linux.py +1 -1
  98. ansible/module_utils/facts/sysctl.py +4 -6
  99. ansible/module_utils/facts/system/caps.py +2 -2
  100. ansible/module_utils/facts/system/distribution.py +16 -3
  101. ansible/module_utils/facts/system/local.py +1 -1
  102. ansible/module_utils/facts/virtual/linux.py +2 -2
  103. ansible/module_utils/service.py +3 -10
  104. ansible/module_utils/urls.py +4 -4
  105. ansible/modules/apt_repository.py +17 -39
  106. ansible/modules/assemble.py +2 -2
  107. ansible/modules/async_status.py +13 -11
  108. ansible/modules/async_wrapper.py +12 -22
  109. ansible/modules/command.py +3 -3
  110. ansible/modules/copy.py +4 -4
  111. ansible/modules/cron.py +1 -1
  112. ansible/modules/dnf5.py +14 -22
  113. ansible/modules/file.py +16 -17
  114. ansible/modules/find.py +3 -3
  115. ansible/modules/get_url.py +17 -0
  116. ansible/modules/git.py +9 -7
  117. ansible/modules/hostname.py +0 -1
  118. ansible/modules/known_hosts.py +12 -14
  119. ansible/modules/package.py +6 -0
  120. ansible/modules/replace.py +2 -2
  121. ansible/modules/service.py +3 -9
  122. ansible/modules/slurp.py +10 -13
  123. ansible/modules/stat.py +5 -7
  124. ansible/modules/unarchive.py +6 -6
  125. ansible/modules/user.py +1 -1
  126. ansible/modules/wait_for.py +28 -30
  127. ansible/modules/yum_repository.py +4 -3
  128. ansible/parsing/ajson.py +3 -5
  129. ansible/parsing/dataloader.py +6 -6
  130. ansible/parsing/mod_args.py +1 -1
  131. ansible/parsing/plugin_docs.py +2 -2
  132. ansible/parsing/utils/yaml.py +3 -3
  133. ansible/parsing/vault/__init__.py +10 -14
  134. ansible/playbook/base.py +7 -2
  135. ansible/playbook/included_file.py +3 -1
  136. ansible/playbook/play_context.py +2 -0
  137. ansible/playbook/playbook_include.py +1 -1
  138. ansible/playbook/taggable.py +19 -8
  139. ansible/playbook/task.py +2 -0
  140. ansible/plugins/__init__.py +0 -25
  141. ansible/plugins/action/__init__.py +8 -31
  142. ansible/plugins/action/add_host.py +1 -1
  143. ansible/plugins/action/assemble.py +8 -16
  144. ansible/plugins/action/async_status.py +7 -2
  145. ansible/plugins/action/copy.py +8 -7
  146. ansible/plugins/action/fetch.py +3 -3
  147. ansible/plugins/action/gather_facts.py +8 -8
  148. ansible/plugins/action/package.py +5 -8
  149. ansible/plugins/action/script.py +8 -15
  150. ansible/plugins/action/service.py +3 -7
  151. ansible/plugins/action/template.py +11 -10
  152. ansible/plugins/action/unarchive.py +5 -15
  153. ansible/plugins/action/uri.py +9 -20
  154. ansible/plugins/cache/__init__.py +17 -19
  155. ansible/plugins/callback/__init__.py +4 -6
  156. ansible/plugins/callback/junit.py +4 -2
  157. ansible/plugins/callback/tree.py +5 -5
  158. ansible/plugins/connection/local.py +6 -6
  159. ansible/plugins/connection/paramiko_ssh.py +5 -5
  160. ansible/plugins/connection/ssh.py +25 -15
  161. ansible/plugins/connection/winrm.py +6 -3
  162. ansible/plugins/doc_fragments/constructed.py +2 -2
  163. ansible/plugins/filter/core.py +32 -27
  164. ansible/plugins/filter/encryption.py +14 -6
  165. ansible/plugins/inventory/__init__.py +11 -10
  166. ansible/plugins/inventory/script.py +1 -1
  167. ansible/plugins/list.py +73 -19
  168. ansible/plugins/loader.py +7 -7
  169. ansible/plugins/lookup/csvfile.py +16 -71
  170. ansible/plugins/lookup/first_found.py +2 -1
  171. ansible/plugins/lookup/template.py +9 -4
  172. ansible/plugins/shell/__init__.py +56 -2
  173. ansible/plugins/shell/powershell.py +67 -9
  174. ansible/plugins/shell/sh.py +10 -5
  175. ansible/plugins/strategy/__init__.py +3 -3
  176. ansible/plugins/test/core.py +22 -16
  177. ansible/plugins/test/finished.yml +1 -1
  178. ansible/plugins/test/uri.py +2 -5
  179. ansible/release.py +1 -1
  180. ansible/template/__init__.py +38 -54
  181. ansible/utils/collection_loader/_collection_finder.py +3 -3
  182. ansible/utils/display.py +124 -138
  183. ansible/utils/galaxy.py +2 -2
  184. ansible/utils/hashing.py +6 -8
  185. ansible/utils/listify.py +6 -4
  186. ansible/utils/path.py +5 -7
  187. ansible/utils/py3compat.py +2 -1
  188. ansible/utils/ssh_functions.py +3 -2
  189. ansible/utils/unsafe_proxy.py +1 -1
  190. ansible/vars/hostvars.py +1 -1
  191. ansible/vars/plugins.py +3 -3
  192. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/METADATA +1 -1
  193. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/RECORD +224 -204
  194. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/WHEEL +1 -1
  195. ansible_test/_data/completion/docker.txt +3 -3
  196. ansible_test/_data/completion/remote.txt +1 -0
  197. ansible_test/_data/requirements/sanity.ansible-doc.txt +1 -1
  198. ansible_test/_data/requirements/sanity.changelog.txt +2 -2
  199. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  200. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  201. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  202. ansible_test/_internal/commands/integration/coverage.py +7 -2
  203. ansible_test/_internal/host_profiles.py +62 -10
  204. ansible_test/_internal/provisioning.py +10 -4
  205. ansible_test/_internal/ssh.py +1 -5
  206. ansible_test/_internal/thread.py +2 -1
  207. ansible_test/_internal/timeout.py +1 -1
  208. ansible_test/_internal/util.py +40 -12
  209. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +1 -0
  210. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
  211. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +1 -0
  212. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
  213. ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
  214. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +61 -7
  215. ansible_test/_util/target/setup/bootstrap.sh +31 -0
  216. ansible_test/_util/target/setup/requirements.py +3 -9
  217. ansible/_internal/_errors/_utils.py +0 -310
  218. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/entry_points.txt +0 -0
  219. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/COPYING +0 -0
  220. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  221. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  222. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  223. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  224. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  225. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/top_level.txt +0 -0
@@ -106,21 +106,19 @@ class SshAgentFailure(RuntimeError):
106
106
  # NOTE: Classes below somewhat represent "Data Type Representations Used in the SSH Protocols"
107
107
  # as specified by RFC4251
108
108
 
109
+
109
110
  @t.runtime_checkable
110
111
  class SupportsToBlob(t.Protocol):
111
- def to_blob(self) -> bytes:
112
- ...
112
+ def to_blob(self) -> bytes: ...
113
113
 
114
114
 
115
115
  @t.runtime_checkable
116
116
  class SupportsFromBlob(t.Protocol):
117
117
  @classmethod
118
- def from_blob(cls, blob: memoryview | bytes) -> t.Self:
119
- ...
118
+ def from_blob(cls, blob: memoryview | bytes) -> t.Self: ...
120
119
 
121
120
  @classmethod
122
- def consume_from_blob(cls, blob: memoryview | bytes) -> tuple[t.Self, memoryview | bytes]:
123
- ...
121
+ def consume_from_blob(cls, blob: memoryview | bytes) -> tuple[t.Self, memoryview | bytes]: ...
124
122
 
125
123
 
126
124
  def _split_blob(blob: memoryview | bytes, length: int) -> tuple[memoryview | bytes, memoryview | bytes]:
@@ -304,10 +302,12 @@ class PrivateKeyMsg(Msg):
304
302
  return EcdsaPrivateKeyMsg(
305
303
  getattr(KeyAlgo, f'ECDSA{key_size}'),
306
304
  unicode_string(f'nistp{key_size}'),
307
- binary_string(private_key.public_key().public_bytes(
308
- encoding=serialization.Encoding.X962,
309
- format=serialization.PublicFormat.UncompressedPoint
310
- )),
305
+ binary_string(
306
+ private_key.public_key().public_bytes(
307
+ encoding=serialization.Encoding.X962,
308
+ format=serialization.PublicFormat.UncompressedPoint,
309
+ )
310
+ ),
311
311
  mpint(ecdsa_pn.private_value),
312
312
  )
313
313
  case Ed25519PrivateKey():
@@ -318,7 +318,7 @@ class PrivateKeyMsg(Msg):
318
318
  private_bytes = private_key.private_bytes(
319
319
  encoding=serialization.Encoding.Raw,
320
320
  format=serialization.PrivateFormat.Raw,
321
- encryption_algorithm=serialization.NoEncryption()
321
+ encryption_algorithm=serialization.NoEncryption(),
322
322
  )
323
323
  return Ed25519PrivateKeyMsg(
324
324
  KeyAlgo.ED25519,
@@ -376,14 +376,14 @@ class Ed25519PrivateKeyMsg(PrivateKeyMsg):
376
376
  @dataclasses.dataclass
377
377
  class PublicKeyMsg(Msg):
378
378
  @staticmethod
379
- def get_dataclass(
380
- type: KeyAlgo
381
- ) -> type[t.Union[
379
+ def get_dataclass(type: KeyAlgo) -> type[
380
+ t.Union[
382
381
  RSAPublicKeyMsg,
383
382
  EcdsaPublicKeyMsg,
384
383
  Ed25519PublicKeyMsg,
385
- DSAPublicKeyMsg
386
- ]]:
384
+ DSAPublicKeyMsg,
385
+ ]
386
+ ]:
387
387
  match type:
388
388
  case KeyAlgo.RSA:
389
389
  return RSAPublicKeyMsg
@@ -401,29 +401,14 @@ class PublicKeyMsg(Msg):
401
401
  type: KeyAlgo = self.type
402
402
  match type:
403
403
  case KeyAlgo.RSA:
404
- return RSAPublicNumbers(
405
- self.e,
406
- self.n
407
- ).public_key()
404
+ return RSAPublicNumbers(self.e, self.n).public_key()
408
405
  case KeyAlgo.ECDSA256 | KeyAlgo.ECDSA384 | KeyAlgo.ECDSA521:
409
406
  curve = _ECDSA_KEY_TYPE[KeyAlgo(type)]
410
- return EllipticCurvePublicKey.from_encoded_point(
411
- curve(),
412
- self.Q
413
- )
407
+ return EllipticCurvePublicKey.from_encoded_point(curve(), self.Q)
414
408
  case KeyAlgo.ED25519:
415
- return Ed25519PublicKey.from_public_bytes(
416
- self.enc_a
417
- )
409
+ return Ed25519PublicKey.from_public_bytes(self.enc_a)
418
410
  case KeyAlgo.DSA:
419
- return DSAPublicNumbers(
420
- self.y,
421
- DSAParameterNumbers(
422
- self.p,
423
- self.q,
424
- self.g
425
- )
426
- ).public_key()
411
+ return DSAPublicNumbers(self.y, DSAParameterNumbers(self.p, self.q, self.g)).public_key()
427
412
  case _:
428
413
  raise NotImplementedError(type)
429
414
 
@@ -437,32 +422,32 @@ class PublicKeyMsg(Msg):
437
422
  mpint(dsa_pn.parameter_numbers.p),
438
423
  mpint(dsa_pn.parameter_numbers.q),
439
424
  mpint(dsa_pn.parameter_numbers.g),
440
- mpint(dsa_pn.y)
425
+ mpint(dsa_pn.y),
441
426
  )
442
427
  case EllipticCurvePublicKey():
443
428
  return EcdsaPublicKeyMsg(
444
429
  getattr(KeyAlgo, f'ECDSA{public_key.curve.key_size}'),
445
430
  unicode_string(f'nistp{public_key.curve.key_size}'),
446
- binary_string(public_key.public_bytes(
447
- encoding=serialization.Encoding.X962,
448
- format=serialization.PublicFormat.UncompressedPoint
449
- ))
431
+ binary_string(
432
+ public_key.public_bytes(
433
+ encoding=serialization.Encoding.X962,
434
+ format=serialization.PublicFormat.UncompressedPoint,
435
+ )
436
+ ),
450
437
  )
451
438
  case Ed25519PublicKey():
452
439
  return Ed25519PublicKeyMsg(
453
440
  KeyAlgo.ED25519,
454
- binary_string(public_key.public_bytes(
455
- encoding=serialization.Encoding.Raw,
456
- format=serialization.PublicFormat.Raw,
457
- ))
441
+ binary_string(
442
+ public_key.public_bytes(
443
+ encoding=serialization.Encoding.Raw,
444
+ format=serialization.PublicFormat.Raw,
445
+ )
446
+ ),
458
447
  )
459
448
  case RSAPublicKey():
460
449
  rsa_pn: RSAPublicNumbers = public_key.public_numbers()
461
- return RSAPublicKeyMsg(
462
- KeyAlgo.RSA,
463
- mpint(rsa_pn.e),
464
- mpint(rsa_pn.n)
465
- )
450
+ return RSAPublicKeyMsg(KeyAlgo.RSA, mpint(rsa_pn.e), mpint(rsa_pn.n))
466
451
  case _:
467
452
  raise NotImplementedError(public_key)
468
453
 
@@ -473,10 +458,7 @@ class PublicKeyMsg(Msg):
473
458
  msg.comments = unicode_string('')
474
459
  k = msg.to_blob()
475
460
  digest.update(k)
476
- return binascii.b2a_base64(
477
- digest.digest(),
478
- newline=False
479
- ).rstrip(b'=').decode('utf-8')
461
+ return binascii.b2a_base64(digest.digest(), newline=False).rstrip(b'=').decode('utf-8')
480
462
 
481
463
 
482
464
  @dataclasses.dataclass(order=True, slots=True)
@@ -519,9 +501,7 @@ class KeyList(Msg):
519
501
 
520
502
  def __post_init__(self) -> None:
521
503
  if self.nkeys != len(self.keys):
522
- raise SshAgentFailure(
523
- "agent: invalid number of keys received for identities list"
524
- )
504
+ raise SshAgentFailure("agent: invalid number of keys received for identities list")
525
505
 
526
506
 
527
507
  @dataclasses.dataclass(order=True, slots=True)
@@ -535,8 +515,7 @@ class PublicKeyMsgList(Msg):
535
515
  return len(self.keys)
536
516
 
537
517
  @classmethod
538
- def from_blob(cls, blob: memoryview | bytes) -> t.Self:
539
- ...
518
+ def from_blob(cls, blob: memoryview | bytes) -> t.Self: ...
540
519
 
541
520
  @classmethod
542
521
  def consume_from_blob(cls, blob: memoryview | bytes) -> tuple[t.Self, memoryview | bytes]:
@@ -546,22 +525,16 @@ class PublicKeyMsgList(Msg):
546
525
  key_blob, key_blob_length, comment_blob = cls._consume_field(blob)
547
526
 
548
527
  peek_key_algo, _length, _blob = cls._consume_field(key_blob)
549
- pub_key_msg_cls = PublicKeyMsg.get_dataclass(
550
- KeyAlgo(bytes(peek_key_algo).decode('utf-8'))
551
- )
528
+ pub_key_msg_cls = PublicKeyMsg.get_dataclass(KeyAlgo(bytes(peek_key_algo).decode('utf-8')))
552
529
 
553
530
  _fv, comment_blob_length, blob = cls._consume_field(comment_blob)
554
- key_plus_comment = (
555
- prev_blob[4: (4 + key_blob_length) + (4 + comment_blob_length)]
556
- )
531
+ key_plus_comment = prev_blob[4 : (4 + key_blob_length) + (4 + comment_blob_length)]
557
532
 
558
533
  args.append(pub_key_msg_cls.from_blob(key_plus_comment))
559
534
  return cls(args), b""
560
535
 
561
536
  @staticmethod
562
- def _consume_field(
563
- blob: memoryview | bytes
564
- ) -> tuple[memoryview | bytes, uint32, memoryview | bytes]:
537
+ def _consume_field(blob: memoryview | bytes) -> tuple[memoryview | bytes, uint32, memoryview | bytes]:
565
538
  length = uint32.from_blob(blob[:4])
566
539
  blob = blob[4:]
567
540
  data, rest = _split_blob(blob, length)
@@ -581,10 +554,10 @@ class SshAgentClient:
581
554
  return self
582
555
 
583
556
  def __exit__(
584
- self,
585
- exc_type: type[BaseException] | None,
586
- exc_value: BaseException | None,
587
- traceback: types.TracebackType | None
557
+ self,
558
+ exc_type: type[BaseException] | None,
559
+ exc_value: BaseException | None,
560
+ traceback: types.TracebackType | None,
588
561
  ) -> None:
589
562
  self.close()
590
563
 
@@ -598,34 +571,25 @@ class SshAgentClient:
598
571
  return resp
599
572
 
600
573
  def remove_all(self) -> None:
601
- self.send(
602
- ProtocolMsgNumbers.SSH_AGENTC_REMOVE_ALL_IDENTITIES.to_blob()
603
- )
574
+ self.send(ProtocolMsgNumbers.SSH_AGENTC_REMOVE_ALL_IDENTITIES.to_blob())
604
575
 
605
576
  def remove(self, public_key: CryptoPublicKey) -> None:
606
577
  key_blob = PublicKeyMsg.from_public_key(public_key).to_blob()
607
- self.send(
608
- ProtocolMsgNumbers.SSH_AGENTC_REMOVE_IDENTITY.to_blob() +
609
- uint32(len(key_blob)).to_blob() + key_blob
610
- )
578
+ self.send(ProtocolMsgNumbers.SSH_AGENTC_REMOVE_IDENTITY.to_blob() + uint32(len(key_blob)).to_blob() + key_blob)
611
579
 
612
580
  def add(
613
- self,
614
- private_key: CryptoPrivateKey,
615
- comments: str | None = None,
616
- lifetime: int | None = None,
617
- confirm: bool | None = None,
581
+ self,
582
+ private_key: CryptoPrivateKey,
583
+ comments: str | None = None,
584
+ lifetime: int | None = None,
585
+ confirm: bool | None = None,
618
586
  ) -> None:
619
587
  key_msg = PrivateKeyMsg.from_private_key(private_key)
620
588
  key_msg.comments = unicode_string(comments or '')
621
589
  if lifetime:
622
- key_msg.constraints += constraints(
623
- [ProtocolMsgNumbers.SSH_AGENT_CONSTRAIN_LIFETIME]
624
- ).to_blob() + uint32(lifetime).to_blob()
590
+ key_msg.constraints += constraints([ProtocolMsgNumbers.SSH_AGENT_CONSTRAIN_LIFETIME]).to_blob() + uint32(lifetime).to_blob()
625
591
  if confirm:
626
- key_msg.constraints += constraints(
627
- [ProtocolMsgNumbers.SSH_AGENT_CONSTRAIN_CONFIRM]
628
- ).to_blob()
592
+ key_msg.constraints += constraints([ProtocolMsgNumbers.SSH_AGENT_CONSTRAIN_CONFIRM]).to_blob()
629
593
 
630
594
  if key_msg.constraints:
631
595
  msg = ProtocolMsgNumbers.SSH_AGENTC_ADD_ID_CONSTRAINED.to_blob()
@@ -638,9 +602,7 @@ class SshAgentClient:
638
602
  req = ProtocolMsgNumbers.SSH_AGENTC_REQUEST_IDENTITIES.to_blob()
639
603
  r = memoryview(bytearray(self.send(req)))
640
604
  if r[0] != ProtocolMsgNumbers.SSH_AGENT_IDENTITIES_ANSWER:
641
- raise SshAgentFailure(
642
- 'agent: non-identities answer received for identities list'
643
- )
605
+ raise SshAgentFailure('agent: non-identities answer received for identities list')
644
606
  return KeyList.from_blob(r[1:])
645
607
 
646
608
  def __contains__(self, public_key: CryptoPublicKey) -> bool:
@@ -649,7 +611,7 @@ class SshAgentClient:
649
611
 
650
612
 
651
613
  @functools.cache
652
- def _key_data_into_crypto_objects(key_data: bytes, passphrase: bytes | None) -> tuple[CryptoPrivateKey, CryptoPublicKey, str]:
614
+ def key_data_into_crypto_objects(key_data: bytes, passphrase: bytes | None) -> tuple[CryptoPrivateKey, CryptoPublicKey, str]:
653
615
  private_key = serialization.ssh.load_ssh_private_key(key_data, passphrase)
654
616
  public_key = private_key.public_key()
655
617
  fingerprint = PublicKeyMsg.from_public_key(public_key).fingerprint
@@ -1,10 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
- from jinja2 import __version__ as _jinja2_version
3
+ import importlib.metadata
4
+
5
+ jinja2_version = importlib.metadata.version('jinja2')
4
6
 
5
7
  # DTFIX-FUTURE: sanity test to ensure this doesn't drift from requirements
6
8
  _MINIMUM_JINJA_VERSION = (3, 1)
7
- _CURRENT_JINJA_VERSION = tuple(map(int, _jinja2_version.split('.', maxsplit=2)[:2]))
9
+ _CURRENT_JINJA_VERSION = tuple(map(int, jinja2_version.split('.', maxsplit=2)[:2]))
8
10
 
9
11
  if _CURRENT_JINJA_VERSION < _MINIMUM_JINJA_VERSION:
10
- raise RuntimeError(f'Jinja version {".".join(map(str, _MINIMUM_JINJA_VERSION))} or higher is required (current version {_jinja2_version}).')
12
+ raise RuntimeError(f'Jinja version {".".join(map(str, _MINIMUM_JINJA_VERSION))} or higher is required (current version {jinja2_version}).')
@@ -60,6 +60,7 @@ class DeprecatedAccessAuditContext(NotifiableAccessContextBase):
60
60
  date=item.deprecated.date,
61
61
  obj=item.template,
62
62
  deprecator=item.deprecated.deprecator,
63
+ formatted_traceback=item.deprecated.formatted_traceback,
63
64
  )
64
65
 
65
66
  return result
@@ -79,7 +80,7 @@ class DeprecatedAccessAuditContext(NotifiableAccessContextBase):
79
80
  # DTFIX-FUTURE: ascend the template stack to try and find the nearest string source template
80
81
  origin = Origin.get_tag(template)
81
82
 
82
- # DTFIX-RELEASE: this should probably use a synthesized description value on the tag
83
+ # DTFIX-FUTURE: this should probably use a synthesized description value on the tag
83
84
  # it is reachable from the data_tagging_controller test: ../playbook_output_validator/filter.py actual_stdout.txt actual_stderr.txt
84
85
  # -[DEPRECATION WARNING]: `something_old` is deprecated, don't use it! This feature will be removed in version 1.2.3.
85
86
  # +[DEPRECATION WARNING]: While processing '<<container>>': `something_old` is deprecated, don't use it! This feature will be removed in ...
@@ -75,7 +75,7 @@ class TemplateOptions:
75
75
  value_for_omit: object = Omit
76
76
  escape_backslashes: bool = True
77
77
  preserve_trailing_newlines: bool = True
78
- # DTFIX-RELEASE: these aren't really overrides anymore, rename the dataclass and this field
78
+ # DTFIX-FUTURE: these aren't really overrides anymore, rename the dataclass and this field
79
79
  # also mention in docstring this has no effect unless used to template a string
80
80
  overrides: TemplateOverrides = TemplateOverrides.DEFAULT
81
81
 
@@ -122,7 +122,6 @@ class TemplateEngine:
122
122
  return new_engine
123
123
 
124
124
  def extend(self, marker_behavior: MarkerBehavior | None = None) -> t.Self:
125
- # DTFIX-RELEASE: bikeshed name, supported features
126
125
  new_templar = type(self)(
127
126
  loader=self._loader,
128
127
  variables=self._variables,
@@ -187,7 +186,7 @@ class TemplateEngine:
187
186
  @property
188
187
  def available_variables(self) -> dict[str, t.Any] | ChainMap[str, t.Any]:
189
188
  """Available variables this instance will use when templating."""
190
- # DTFIX-RELEASE: ensure that we're always accessing this as a shallow container-level snapshot, and eliminate uses of anything
189
+ # DTFIX3: ensure that we're always accessing this as a shallow container-level snapshot, and eliminate uses of anything
191
190
  # that directly mutates this value. _new_context may resolve this for us?
192
191
  if self._variables is None:
193
192
  self._variables = self._variables_factory() if self._variables_factory else {}
@@ -235,7 +234,7 @@ class TemplateEngine:
235
234
 
236
235
  def template(
237
236
  self,
238
- variable: t.Any, # DTFIX-RELEASE: once we settle the new/old API boundaries, rename this (here and in other methods)
237
+ variable: t.Any, # DTFIX-FUTURE: once we settle the new/old API boundaries, rename this (here and in other methods)
239
238
  *,
240
239
  options: TemplateOptions = TemplateOptions.DEFAULT,
241
240
  mode: TemplateMode = TemplateMode.DEFAULT,
@@ -19,7 +19,7 @@ from jinja2.compiler import Frame
19
19
  from jinja2.lexer import TOKEN_VARIABLE_BEGIN, TOKEN_VARIABLE_END, TOKEN_STRING, Lexer
20
20
  from jinja2.nativetypes import NativeCodeGenerator
21
21
  from jinja2.nodes import Const, EvalContext
22
- from jinja2.runtime import Context
22
+ from jinja2.runtime import Context, Macro
23
23
  from jinja2.sandbox import ImmutableSandboxedEnvironment
24
24
  from jinja2.utils import missing, LRUCache
25
25
 
@@ -49,6 +49,7 @@ from ._jinja_common import (
49
49
  TruncationMarker,
50
50
  validate_arg_type,
51
51
  JinjaCallContext,
52
+ _SandboxMode,
52
53
  )
53
54
  from ._jinja_plugins import JinjaPluginIntercept, _query, _lookup, _now, _wrap_plugin_output, get_first_marker_arg, _DirectCall, _jinja_const_template_warning
54
55
  from ._lazy_containers import (
@@ -71,6 +72,11 @@ from ansible.vars.hostvars import HostVars, HostVarsVars
71
72
  from ...module_utils.datatag import native_type_name
72
73
 
73
74
  JINJA2_OVERRIDE = '#jinja2:'
75
+ """
76
+ String values prefixed with this sequence are interpreted as templates, even without template delimiters.
77
+ The values following this prefix up to the first newline are parsed as Jinja2 template overrides.
78
+ To include this literal value at the start of a string, a space or other character must precede it.
79
+ """
74
80
 
75
81
  display = Display()
76
82
 
@@ -304,7 +310,7 @@ class AnsibleTemplate(Template):
304
310
  _python_source_temp_path: pathlib.Path | None = None
305
311
 
306
312
  def __del__(self):
307
- # DTFIX-RELEASE: this still isn't working reliably; something else must be keeping the template object alive
313
+ # DTFIX-FUTURE: this still isn't working reliably; something else must be keeping the template object alive
308
314
  if self._python_source_temp_path:
309
315
  self._python_source_temp_path.unlink(missing_ok=True)
310
316
 
@@ -497,7 +503,7 @@ def create_template_error(ex: Exception, variable: t.Any, is_expression: bool) -
497
503
  return exception_to_raise
498
504
 
499
505
 
500
- # DTFIX-RELEASE: implement CapturedExceptionMarker deferral support on call (and lookup), filter/test plugins, etc.
506
+ # DTFIX3: implement CapturedExceptionMarker deferral support on call (and lookup), filter/test plugins, etc.
501
507
  # also update the protomatter integration test once this is done (the test was written differently since this wasn't done yet)
502
508
 
503
509
  _BUILTIN_FILTER_ALIASES: dict[str, str] = {}
@@ -583,10 +589,17 @@ class AnsibleEnvironment(ImmutableSandboxedEnvironment):
583
589
 
584
590
  return template_obj
585
591
 
592
+ def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
593
+ # deprecated: description="remove relaxed template sandbox mode support" core_version="2.23"
594
+ if _TemplateConfig.sandbox_mode == _SandboxMode.ALLOW_UNSAFE_ATTRIBUTES:
595
+ return True
596
+
597
+ return super().is_safe_attribute(obj, attr, value)
598
+
586
599
  @property
587
600
  def lexer(self) -> AnsibleLexer:
588
601
  """Return/cache an AnsibleLexer with settings from the current AnsibleEnvironment"""
589
- # DTFIX-RELEASE: optimization - we should pre-generate the default cached lexer before forking, not leave it to chance (e.g. simple playbooks)
602
+ # DTFIX-FUTURE: optimization - we should pre-generate the default cached lexer before forking, not leave it to chance (e.g. simple playbooks)
590
603
  key = tuple(getattr(self, name) for name in _TEMPLATE_OVERRIDE_FIELD_NAMES)
591
604
 
592
605
  lex = self._lexer_cache.get(key)
@@ -610,7 +623,7 @@ class AnsibleEnvironment(ImmutableSandboxedEnvironment):
610
623
  Without this, `_wrap_filter` will wrap `args` and `kwargs` in templating lazy containers.
611
624
  This provides consistency with plugin output handling by preventing auto-templating of trusted templates passed in native containers.
612
625
  """
613
- # DTFIX-RELEASE: need better logic to handle non-list/non-dict inputs for args/kwargs
626
+ # DTFIX-FUTURE: need better logic to handle non-list/non-dict inputs for args/kwargs
614
627
  args = _AnsibleLazyTemplateMixin._try_create(list(args or []), LazyOptions.SKIP_TEMPLATES)
615
628
  kwargs = _AnsibleLazyTemplateMixin._try_create(kwargs, LazyOptions.SKIP_TEMPLATES)
616
629
 
@@ -630,7 +643,7 @@ class AnsibleEnvironment(ImmutableSandboxedEnvironment):
630
643
  Without this, `_wrap_test` will wrap `args` and `kwargs` in templating lazy containers.
631
644
  This provides consistency with plugin output handling by preventing auto-templating of trusted templates passed in native containers.
632
645
  """
633
- # DTFIX-RELEASE: need better logic to handle non-list/non-dict inputs for args/kwargs
646
+ # DTFIX-FUTURE: need better logic to handle non-list/non-dict inputs for args/kwargs
634
647
  args = _AnsibleLazyTemplateMixin._try_create(list(args or []), LazyOptions.SKIP_TEMPLATES)
635
648
  kwargs = _AnsibleLazyTemplateMixin._try_create(kwargs, LazyOptions.SKIP_TEMPLATES)
636
649
 
@@ -701,7 +714,6 @@ class AnsibleEnvironment(ImmutableSandboxedEnvironment):
701
714
  # this code is complemented by our tweaked CodeGenerator _output_const_repr that ensures that literal constants
702
715
  # in templates aren't double-repr'd in the generated code
703
716
  if len(node_list) == 1:
704
- # DTFIX-RELEASE: determine if we should do managed access here (we *should* have hit them all during templating/resolve, but ?)
705
717
  return node_list[0]
706
718
 
707
719
  # In order to ensure that all markers are tripped, do a recursive finalize before we repr (otherwise we can end up
@@ -787,11 +799,14 @@ class AnsibleEnvironment(ImmutableSandboxedEnvironment):
787
799
  # Performing either before calling them will interfere with that processing.
788
800
  return super().call(__context, __obj, *args, **kwargs)
789
801
 
790
- if (first_marker := get_first_marker_arg(args, kwargs)) is not None:
802
+ # Jinja's generated macro code handles Markers, so pre-emptive raise on Marker args and lazy retrieval should be disabled for the macro invocation.
803
+ is_macro = isinstance(__obj, Macro)
804
+
805
+ if not is_macro and (first_marker := get_first_marker_arg(args, kwargs)) is not None:
791
806
  return first_marker
792
807
 
793
808
  try:
794
- with JinjaCallContext(accept_lazy_markers=False):
809
+ with JinjaCallContext(accept_lazy_markers=is_macro):
795
810
  call_res = super().call(__context, __obj, *lazify_container_args(args), **lazify_container_kwargs(kwargs))
796
811
 
797
812
  if __obj is range:
@@ -814,7 +829,7 @@ _sentinel: t.Final[object] = object()
814
829
 
815
830
 
816
831
  @_DirectCall.mark
817
- def _undef(hint=None):
832
+ def _undef(hint: str | None = None) -> UndefinedMarker:
818
833
  """Jinja2 global function (undef) for creating getting a `UndefinedMarker` instance, optionally with a custom hint."""
819
834
  validate_arg_type('hint', hint, (str, type(None)))
820
835
 
@@ -856,9 +871,6 @@ def _flatten_and_lazify_vars(mapping: c.Mapping) -> t.Iterable[c.Mapping]:
856
871
  for m in mapping.maps:
857
872
  yield from _flatten_and_lazify_vars(m)
858
873
  elif mapping_type is _AnsibleLazyTemplateDict:
859
- if not mapping:
860
- # DTFIX-RELEASE: handle or remove?
861
- raise Exception("we didn't think it was possible to have an empty lazy here...")
862
874
  yield mapping
863
875
  elif mapping_type in (dict, _AnsibleTaggedDict):
864
876
  # don't propagate empty dictionary layers
@@ -882,10 +894,6 @@ def _new_context(
882
894
  layers = []
883
895
 
884
896
  if jinja_locals:
885
- # DTFIX-RELEASE: if we can't trip this in coverage, kill it off?
886
- if type(jinja_locals) is not dict: # pylint: disable=unidiomatic-typecheck
887
- raise NotImplementedError("locals must be a dict")
888
-
889
897
  # Omit values set to Jinja's internal `missing` sentinel; they are locals that have not yet been
890
898
  # initialized in the current context, and should not be exposed to child contexts. e.g.: {% import 'a' as b with context %}.
891
899
  # The `b` local will be `missing` in the `a` context and should not be propagated as a local to the child context we're creating.
@@ -978,7 +986,7 @@ def _finalize_list(o: t.Any, mode: FinalizeMode) -> t.Iterator[t.Any]:
978
986
 
979
987
 
980
988
  def _maybe_finalize_scalar(o: t.Any) -> t.Any:
981
- # DTFIX-RELEASE: this should check all supported scalar subclasses, not just JSON ones (also, does the JSON serializer handle these cases?)
989
+ # DTFIX5: this should check all supported scalar subclasses, not just JSON ones (also, does the JSON serializer handle these cases?)
982
990
  for target_type in _json_subclassable_scalar_types:
983
991
  if not isinstance(o, target_type):
984
992
  continue
@@ -1028,7 +1036,7 @@ def _finalize_collection(
1028
1036
 
1029
1037
  def _finalize_template_result(o: t.Any, mode: FinalizeMode) -> t.Any:
1030
1038
  """Recurse the template result, rendering any encountered templates, converting containers to non-lazy versions."""
1031
- # DTFIX-RELEASE: add tests to ensure this method doesn't drift from allowed types
1039
+ # DTFIX5: add tests to ensure this method doesn't drift from allowed types
1032
1040
  o_type = type(o)
1033
1041
 
1034
1042
  # DTFIX-FUTURE: provide an optional way to check for trusted templates leaking out of templating (injected, but not passed through templar.template)
@@ -1045,7 +1053,7 @@ def _finalize_template_result(o: t.Any, mode: FinalizeMode) -> t.Any:
1045
1053
  if o_type in _FINALIZE_FAST_PATH_EXACT_ITERABLE_TYPES: # silently convert known sequence types to list
1046
1054
  return _finalize_collection(o, mode, _finalize_list, list)
1047
1055
 
1048
- if o_type in Marker.concrete_subclasses: # this early return assumes handle_marker follows our variable type rules
1056
+ if o_type in Marker._concrete_subclasses: # this early return assumes handle_marker follows our variable type rules
1049
1057
  return TemplateContext.current().templar.marker_behavior.handle_marker(o)
1050
1058
 
1051
1059
  if mode is not FinalizeMode.TOP_LEVEL: # unsupported type (do not raise)
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import abc
4
4
  import collections.abc as c
5
+ import enum
5
6
  import inspect
6
7
  import itertools
7
8
  import typing as t
@@ -9,7 +10,7 @@ import typing as t
9
10
  from jinja2 import UndefinedError, StrictUndefined, TemplateRuntimeError
10
11
  from jinja2.utils import missing
11
12
 
12
- from ansible.module_utils.common.messages import ErrorSummary, Detail
13
+ from ...module_utils._internal import _messages
13
14
  from ansible.constants import config
14
15
  from ansible.errors import AnsibleUndefinedVariable, AnsibleTypeError
15
16
  from ansible._internal._errors._handler import ErrorHandler
@@ -24,10 +25,16 @@ from ...module_utils.datatag import native_type_name
24
25
  _patch_jinja() # apply Jinja2 patches before types are declared that are dependent on the changes
25
26
 
26
27
 
28
+ class _SandboxMode(enum.Enum):
29
+ DEFAULT = enum.auto()
30
+ ALLOW_UNSAFE_ATTRIBUTES = enum.auto()
31
+
32
+
27
33
  class _TemplateConfig:
28
34
  allow_embedded_templates: bool = config.get_config_value("ALLOW_EMBEDDED_TEMPLATES")
29
35
  allow_broken_conditionals: bool = config.get_config_value('ALLOW_BROKEN_CONDITIONALS')
30
36
  jinja_extensions: list[str] = config.get_config_value('DEFAULT_JINJA2_EXTENSIONS')
37
+ sandbox_mode: _SandboxMode = _SandboxMode.__members__[config.get_config_value('_TEMPLAR_SANDBOX_MODE').upper()]
31
38
 
32
39
  unknown_type_encountered_handler = ErrorHandler.from_config('_TEMPLAR_UNKNOWN_TYPE_ENCOUNTERED')
33
40
  unknown_type_conversion_handler = ErrorHandler.from_config('_TEMPLAR_UNKNOWN_TYPE_CONVERSION')
@@ -55,7 +62,7 @@ class Marker(StrictUndefined, Tripwire):
55
62
 
56
63
  __slots__ = ('_marker_template_source',)
57
64
 
58
- concrete_subclasses: t.ClassVar[set[type[Marker]]] = set()
65
+ _concrete_subclasses: t.ClassVar[set[type[Marker]]] = set()
59
66
 
60
67
  def __init__(
61
68
  self,
@@ -129,7 +136,7 @@ class Marker(StrictUndefined, Tripwire):
129
136
  def __init_subclass__(cls, **kwargs) -> None:
130
137
  if not inspect.isabstract(cls):
131
138
  _untaggable_types.add(cls)
132
- cls.concrete_subclasses.add(cls)
139
+ cls._concrete_subclasses.add(cls)
133
140
 
134
141
  @classmethod
135
142
  def _init_class(cls):
@@ -197,8 +204,6 @@ class TruncationMarker(Marker):
197
204
  It will only be visible if the previous `Marker` was ignored/replaced instead of being tripped, which would raise an exception.
198
205
  """
199
206
 
200
- # DTFIX-RELEASE: make this a singleton?
201
-
202
207
  __slots__ = ()
203
208
 
204
209
  def __init__(self) -> None:
@@ -252,28 +257,18 @@ class UndecryptableVaultError(_captured.AnsibleCapturedError):
252
257
  class VaultExceptionMarker(ExceptionMarker):
253
258
  """A `Marker` value that represents an error accessing a vaulted value during templating."""
254
259
 
255
- __slots__ = ('_marker_undecryptable_ciphertext', '_marker_undecryptable_reason', '_marker_undecryptable_traceback')
260
+ __slots__ = ('_marker_undecryptable_ciphertext', '_marker_event')
256
261
 
257
- def __init__(self, ciphertext: str, reason: str, traceback: str | None) -> None:
258
- # DTFIX-RELEASE: when does this show up, should it contain more details?
259
- # see also CapturedExceptionMarker for a similar issue
262
+ def __init__(self, ciphertext: str, event: _messages.Event) -> None:
260
263
  super().__init__(hint='A vault exception marker was tripped.')
261
264
 
262
265
  self._marker_undecryptable_ciphertext = ciphertext
263
- self._marker_undecryptable_reason = reason
264
- self._marker_undecryptable_traceback = traceback
266
+ self._marker_event = event
265
267
 
266
268
  def _as_exception(self) -> Exception:
267
269
  return UndecryptableVaultError(
268
270
  obj=self._marker_undecryptable_ciphertext,
269
- error_summary=ErrorSummary(
270
- details=(
271
- Detail(
272
- msg=self._marker_undecryptable_reason,
273
- ),
274
- ),
275
- formatted_traceback=self._marker_undecryptable_traceback,
276
- ),
271
+ event=self._marker_event,
277
272
  )
278
273
 
279
274
  def _disarm(self) -> str:
@@ -282,16 +277,12 @@ class VaultExceptionMarker(ExceptionMarker):
282
277
 
283
278
  def get_first_marker_arg(args: c.Sequence, kwargs: dict[str, t.Any]) -> Marker | None:
284
279
  """Utility method to inspect plugin args and return the first `Marker` encountered, otherwise `None`."""
285
- # DTFIX-RELEASE: this may or may not need to be public API, move back to utils or once usage is wrapped in a decorator?
286
- for arg in iter_marker_args(args, kwargs):
287
- return arg
288
-
289
- return None
280
+ # CAUTION: This function is exposed in public API as ansible.template.get_first_marker_arg.
281
+ return next(iter_marker_args(args, kwargs), None)
290
282
 
291
283
 
292
284
  def iter_marker_args(args: c.Sequence, kwargs: dict[str, t.Any]) -> t.Generator[Marker]:
293
285
  """Utility method to iterate plugin args and yield any `Marker` encountered."""
294
- # DTFIX-RELEASE: this may or may not need to be public API, move back to utils or once usage is wrapped in a decorator?
295
286
  for arg in itertools.chain(args, kwargs.values()):
296
287
  if isinstance(arg, Marker):
297
288
  yield arg
@@ -306,7 +297,7 @@ class JinjaCallContext(NotifiableAccessContextBase):
306
297
  _mask = True
307
298
 
308
299
  def __init__(self, accept_lazy_markers: bool) -> None:
309
- self._type_interest = frozenset() if accept_lazy_markers else frozenset(Marker.concrete_subclasses)
300
+ self._type_interest = frozenset() if accept_lazy_markers else frozenset(Marker._concrete_subclasses)
310
301
 
311
302
  def _notify(self, o: Marker) -> t.NoReturn:
312
303
  o.trip()
@@ -314,7 +305,7 @@ class JinjaCallContext(NotifiableAccessContextBase):
314
305
 
315
306
  def validate_arg_type(name: str, value: t.Any, allowed_type_or_types: type | tuple[type, ...], /) -> None:
316
307
  """Validate the type of the given argument while preserving context for Marker values."""
317
- # DTFIX-RELEASE: find a home for this as a general-purpose utliity method and expose it after some API review
308
+ # DTFIX-FUTURE: find a home for this as a general-purpose utliity method and expose it after some API review
318
309
  if isinstance(value, allowed_type_or_types):
319
310
  return
320
311