ansible-core 2.19.0rc2__py3-none-any.whl → 2.19.1rc1__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 (46) hide show
  1. ansible/_internal/_ansiballz/_builder.py +25 -14
  2. ansible/_internal/_templating/_engine.py +6 -4
  3. ansible/_internal/_templating/_jinja_bits.py +3 -1
  4. ansible/_internal/_templating/_jinja_plugins.py +7 -2
  5. ansible/_internal/_templating/_lazy_containers.py +5 -5
  6. ansible/config/base.yml +16 -6
  7. ansible/config/manager.py +7 -3
  8. ansible/executor/task_executor.py +4 -1
  9. ansible/executor/task_queue_manager.py +2 -2
  10. ansible/module_utils/_internal/_ansiballz/_extensions/_debugpy.py +97 -0
  11. ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +2 -4
  12. ansible/module_utils/_internal/_traceback.py +1 -1
  13. ansible/module_utils/ansible_release.py +1 -1
  14. ansible/module_utils/basic.py +10 -2
  15. ansible/module_utils/common/validation.py +4 -1
  16. ansible/modules/dnf.py +36 -50
  17. ansible/modules/dnf5.py +36 -29
  18. ansible/modules/meta.py +2 -1
  19. ansible/modules/service_facts.py +5 -1
  20. ansible/playbook/helpers.py +1 -0
  21. ansible/playbook/taggable.py +1 -2
  22. ansible/plugins/__init__.py +18 -10
  23. ansible/plugins/callback/__init__.py +6 -1
  24. ansible/plugins/lookup/template.py +6 -1
  25. ansible/release.py +1 -1
  26. ansible/utils/encrypt.py +2 -0
  27. {ansible_core-2.19.0rc2.dist-info → ansible_core-2.19.1rc1.dist-info}/METADATA +1 -1
  28. {ansible_core-2.19.0rc2.dist-info → ansible_core-2.19.1rc1.dist-info}/RECORD +46 -45
  29. ansible_test/_internal/commands/integration/coverage.py +2 -2
  30. ansible_test/_internal/commands/shell/__init__.py +67 -28
  31. ansible_test/_internal/coverage_util.py +28 -25
  32. ansible_test/_internal/debugging.py +337 -49
  33. ansible_test/_internal/host_profiles.py +43 -43
  34. ansible_test/_internal/metadata.py +7 -42
  35. ansible_test/_internal/python_requirements.py +2 -2
  36. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
  37. ansible_test/_util/target/setup/bootstrap.sh +37 -16
  38. {ansible_core-2.19.0rc2.dist-info → ansible_core-2.19.1rc1.dist-info}/WHEEL +0 -0
  39. {ansible_core-2.19.0rc2.dist-info → ansible_core-2.19.1rc1.dist-info}/entry_points.txt +0 -0
  40. {ansible_core-2.19.0rc2.dist-info → ansible_core-2.19.1rc1.dist-info}/licenses/COPYING +0 -0
  41. {ansible_core-2.19.0rc2.dist-info → ansible_core-2.19.1rc1.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  42. {ansible_core-2.19.0rc2.dist-info → ansible_core-2.19.1rc1.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  43. {ansible_core-2.19.0rc2.dist-info → ansible_core-2.19.1rc1.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  44. {ansible_core-2.19.0rc2.dist-info → ansible_core-2.19.1rc1.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  45. {ansible_core-2.19.0rc2.dist-info → ansible_core-2.19.1rc1.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  46. {ansible_core-2.19.0rc2.dist-info → ansible_core-2.19.1rc1.dist-info}/top_level.txt +0 -0
@@ -167,7 +167,7 @@ class PosixCoverageHandler(CoverageHandler[PosixConfig]):
167
167
  coverage_config_path = os.path.join(self.common_temp_path, COVERAGE_CONFIG_NAME)
168
168
  coverage_output_path = os.path.join(self.common_temp_path, ResultType.COVERAGE.name)
169
169
 
170
- coverage_config = generate_coverage_config(self.args)
170
+ coverage_config = generate_coverage_config()
171
171
 
172
172
  write_text_file(coverage_config_path, coverage_config, create_directories=True)
173
173
 
@@ -260,7 +260,7 @@ class PosixCoverageHandler(CoverageHandler[PosixConfig]):
260
260
  """Return a dictionary of variables for setup and teardown of POSIX coverage."""
261
261
  return dict(
262
262
  common_temp_dir=self.common_temp_path,
263
- coverage_config=generate_coverage_config(self.args),
263
+ coverage_config=generate_coverage_config(),
264
264
  coverage_config_path=os.path.join(self.common_temp_path, COVERAGE_CONFIG_NAME),
265
265
  coverage_output_path=os.path.join(self.common_temp_path, ResultType.COVERAGE.name),
266
266
  mode_directory=f'{MODE_DIRECTORY:04o}',
@@ -2,11 +2,16 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import contextlib
5
6
  import dataclasses
6
7
  import os
7
8
  import sys
8
9
  import typing as t
9
10
 
11
+ from ...data import (
12
+ data_context,
13
+ )
14
+
10
15
  from ...util import (
11
16
  ApplicationError,
12
17
  OutputStream,
@@ -101,19 +106,33 @@ def command_shell(args: ShellConfig) -> None:
101
106
  if args.export:
102
107
  return
103
108
 
104
- if args.cmd:
105
- # Running a command is assumed to be non-interactive. Only a shell (no command) is interactive.
106
- # If we want to support interactive commands in the future, we'll need an `--interactive` command line option.
107
- # Command stderr output is allowed to mix with our own output, which is all sent to stderr.
108
- con.run(args.cmd, capture=False, interactive=False, output_stream=OutputStream.ORIGINAL)
109
- return
110
-
111
109
  if isinstance(con, LocalConnection) and isinstance(target_profile, DebuggableProfile) and target_profile.debugging_enabled:
112
- # HACK: ensure the pydevd port visible in the shell is the forwarded port, not the original
113
- args.metadata.debugger_settings = dataclasses.replace(args.metadata.debugger_settings, port=target_profile.pydevd_port)
110
+ # HACK: ensure the debugger port visible in the shell is the forwarded port, not the original
111
+ args.metadata.debugger_settings = dataclasses.replace(args.metadata.debugger_settings, port=target_profile.debugger_port)
114
112
 
115
- with metadata_context(args):
116
- interactive_shell(args, target_profile, con)
113
+ with contextlib.nullcontext() if data_context().content.unsupported else metadata_context(args):
114
+ if args.cmd:
115
+ non_interactive_shell(args, target_profile, con)
116
+ else:
117
+ interactive_shell(args, target_profile, con)
118
+
119
+
120
+ def non_interactive_shell(
121
+ args: ShellConfig,
122
+ target_profile: SshTargetHostProfile,
123
+ con: Connection,
124
+ ) -> None:
125
+ """Run a non-interactive shell command."""
126
+ if isinstance(target_profile, PosixProfile):
127
+ env = get_environment_variables(args, target_profile, con)
128
+ cmd = get_env_command(env) + args.cmd
129
+ else:
130
+ cmd = args.cmd
131
+
132
+ # Running a command is assumed to be non-interactive. Only a shell (no command) is interactive.
133
+ # If we want to support interactive commands in the future, we'll need an `--interactive` command line option.
134
+ # Command stderr output is allowed to mix with our own output, which is all sent to stderr.
135
+ con.run(cmd, capture=False, interactive=False, output_stream=OutputStream.ORIGINAL)
117
136
 
118
137
 
119
138
  def interactive_shell(
@@ -135,23 +154,8 @@ def interactive_shell(
135
154
  python = target_profile.python # make sure the python interpreter has been initialized before opening a shell
136
155
  display.info(f'Target Python {python.version} is at: {python.path}')
137
156
 
138
- optional_vars = (
139
- 'TERM', # keep backspace working
140
- )
141
-
142
- env = {name: os.environ[name] for name in optional_vars if name in os.environ}
143
-
144
- if isinstance(con, LocalConnection): # configure the controller environment
145
- env.update(ansible_environment(args))
146
- env.update(get_injector_env(target_profile.python, env))
147
- env.update(ANSIBLE_TEST_METADATA_PATH=os.path.abspath(args.metadata_path))
148
-
149
- if isinstance(target_profile, DebuggableProfile):
150
- env.update(target_profile.get_ansiballz_environment_variables())
151
- env.update(target_profile.get_ansible_cli_environment_variables())
152
-
153
- if env:
154
- cmd = ['/usr/bin/env'] + [f'{name}={value}' for name, value in env.items()]
157
+ env = get_environment_variables(args, target_profile, con)
158
+ cmd = get_env_command(env)
155
159
 
156
160
  cmd += [shell, '-i']
157
161
  else:
@@ -175,3 +179,38 @@ def interactive_shell(
175
179
  raise HostConnectionError(f'SSH shell connection failed for host {target_profile.config}: {ex}', callback) from ex
176
180
 
177
181
  raise
182
+
183
+
184
+ def get_env_command(env: dict[str, str]) -> list[str]:
185
+ """Get an `env` command to set the given environment variables, if any."""
186
+ if not env:
187
+ return []
188
+
189
+ return ['/usr/bin/env'] + [f'{name}={value}' for name, value in env.items()]
190
+
191
+
192
+ def get_environment_variables(
193
+ args: ShellConfig,
194
+ target_profile: PosixProfile,
195
+ con: Connection,
196
+ ) -> dict[str, str]:
197
+ """Get the environment variables to expose to the shell."""
198
+ if data_context().content.unsupported:
199
+ return {}
200
+
201
+ optional_vars = (
202
+ 'TERM', # keep backspace working
203
+ )
204
+
205
+ env = {name: os.environ[name] for name in optional_vars if name in os.environ}
206
+
207
+ if isinstance(con, LocalConnection): # configure the controller environment
208
+ env.update(ansible_environment(args))
209
+ env.update(get_injector_env(target_profile.python, env))
210
+ env.update(ANSIBLE_TEST_METADATA_PATH=os.path.abspath(args.metadata_path))
211
+
212
+ if isinstance(target_profile, DebuggableProfile):
213
+ env.update(target_profile.get_ansiballz_environment_variables())
214
+ env.update(target_profile.get_ansible_cli_environment_variables())
215
+
216
+ return env
@@ -6,11 +6,10 @@ import dataclasses
6
6
  import os
7
7
  import sqlite3
8
8
  import tempfile
9
+ import textwrap
9
10
  import typing as t
10
11
 
11
12
  from .config import (
12
- IntegrationConfig,
13
- SanityConfig,
14
13
  TestConfig,
15
14
  )
16
15
 
@@ -217,7 +216,7 @@ def get_coverage_config(args: TestConfig) -> str:
217
216
  except AttributeError:
218
217
  pass
219
218
 
220
- coverage_config = generate_coverage_config(args)
219
+ coverage_config = generate_coverage_config()
221
220
 
222
221
  if args.explain:
223
222
  temp_dir = '/tmp/coverage-temp-dir'
@@ -235,10 +234,10 @@ def get_coverage_config(args: TestConfig) -> str:
235
234
  return path
236
235
 
237
236
 
238
- def generate_coverage_config(args: TestConfig) -> str:
237
+ def generate_coverage_config() -> str:
239
238
  """Generate code coverage configuration for tests."""
240
239
  if data_context().content.collection:
241
- coverage_config = generate_collection_coverage_config(args)
240
+ coverage_config = generate_collection_coverage_config()
242
241
  else:
243
242
  coverage_config = generate_ansible_coverage_config()
244
243
 
@@ -265,12 +264,29 @@ omit =
265
264
  */test/results/*
266
265
  """
267
266
 
267
+ coverage_config = coverage_config.lstrip()
268
+
268
269
  return coverage_config
269
270
 
270
271
 
271
- def generate_collection_coverage_config(args: TestConfig) -> str:
272
+ def generate_collection_coverage_config() -> str:
272
273
  """Generate code coverage configuration for Ansible Collection tests."""
273
- coverage_config = """
274
+ include_patterns = [
275
+ # {base}/ansible_collections/{ns}/{col}/*
276
+ os.path.join(data_context().content.root, '*'),
277
+ # */ansible_collections/{ns}/{col}/* (required to pick up AnsiballZ coverage)
278
+ os.path.join('*', data_context().content.collection.directory, '*'),
279
+ ]
280
+
281
+ omit_patterns = [
282
+ # {base}/ansible_collections/{ns}/{col}/tests/output/*
283
+ os.path.join(data_context().content.root, data_context().content.results_path, '*'),
284
+ ]
285
+
286
+ include = textwrap.indent('\n'.join(include_patterns), ' ' * 4)
287
+ omit = textwrap.indent('\n'.join(omit_patterns), ' ' * 4)
288
+
289
+ coverage_config = f"""
274
290
  [run]
275
291
  branch = True
276
292
  concurrency =
@@ -279,28 +295,15 @@ concurrency =
279
295
  parallel = True
280
296
  disable_warnings =
281
297
  no-data-collected
282
- """
283
298
 
284
- if isinstance(args, IntegrationConfig):
285
- coverage_config += """
286
- include =
287
- %s/*
288
- */%s/*
289
- """ % (data_context().content.root, data_context().content.collection.directory)
290
- elif isinstance(args, SanityConfig):
291
- # temporary work-around for import sanity test
292
- coverage_config += """
293
299
  include =
294
- %s/*
300
+ {include}
295
301
 
296
302
  omit =
297
- %s/*
298
- """ % (data_context().content.root, os.path.join(data_context().content.root, data_context().content.results_path))
299
- else:
300
- coverage_config += """
301
- include =
302
- %s/*
303
- """ % data_context().content.root
303
+ {omit}
304
+ """
305
+
306
+ coverage_config = coverage_config.lstrip()
304
307
 
305
308
  return coverage_config
306
309