ansible-core 2.19.0b5__py3-none-any.whl → 2.19.0b7__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.
- ansible/_internal/_ansiballz/__init__.py +0 -0
- ansible/_internal/_ansiballz/_builder.py +101 -0
- ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
- ansible/_internal/_templating/_jinja_bits.py +22 -4
- ansible/_internal/_templating/_jinja_common.py +1 -1
- ansible/_internal/_templating/_jinja_plugins.py +5 -2
- ansible/_internal/_templating/_template_vars.py +72 -0
- ansible/_internal/_templating/_transform.py +6 -0
- ansible/_internal/_yaml/_constructor.py +4 -4
- ansible/_internal/_yaml/_dumper.py +26 -18
- ansible/cli/__init__.py +9 -14
- ansible/cli/adhoc.py +6 -3
- ansible/cli/arguments/option_helpers.py +1 -1
- ansible/cli/console.py +2 -2
- ansible/cli/doc.py +4 -4
- ansible/cli/inventory.py +5 -7
- ansible/config/base.yml +33 -6
- ansible/errors/__init__.py +2 -1
- ansible/executor/module_common.py +75 -44
- ansible/executor/powershell/psrp_put_file.ps1 +1 -1
- ansible/executor/process/worker.py +2 -2
- ansible/executor/task_executor.py +2 -2
- ansible/executor/task_queue_manager.py +34 -70
- ansible/executor/task_result.py +1 -1
- ansible/galaxy/api.py +3 -6
- ansible/galaxy/collection/__init__.py +1 -6
- ansible/galaxy/collection/concrete_artifact_manager.py +4 -10
- ansible/galaxy/dependency_resolution/providers.py +3 -3
- ansible/galaxy/role.py +2 -2
- ansible/inventory/group.py +6 -1
- ansible/inventory/host.py +6 -1
- ansible/module_utils/_internal/__init__.py +7 -4
- ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
- ansible/module_utils/_internal/{_ansiballz.py → _ansiballz/_loader.py} +10 -38
- ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
- ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
- ansible/module_utils/_internal/_datatag/__init__.py +23 -1
- ansible/module_utils/_internal/_deprecator.py +39 -34
- ansible/module_utils/_internal/_json/_profiles/__init__.py +1 -0
- ansible/module_utils/_internal/_messages.py +26 -2
- ansible/module_utils/_internal/_plugin_info.py +14 -1
- ansible/module_utils/ansible_release.py +1 -1
- ansible/module_utils/basic.py +58 -70
- ansible/module_utils/common/respawn.py +4 -41
- ansible/module_utils/common/yaml.py +1 -1
- ansible/module_utils/connection.py +8 -11
- ansible/module_utils/csharp/Ansible.Basic.cs +1 -1
- ansible/module_utils/csharp/Ansible.Privilege.cs +2 -2
- ansible/module_utils/facts/hardware/base.py +1 -1
- ansible/module_utils/facts/hardware/linux.py +1 -1
- ansible/module_utils/facts/other/facter.py +1 -1
- ansible/module_utils/facts/sysctl.py +4 -6
- ansible/module_utils/facts/system/caps.py +2 -2
- ansible/module_utils/facts/system/distribution.py +2 -2
- ansible/module_utils/facts/system/local.py +1 -1
- ansible/module_utils/facts/virtual/linux.py +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.WebRequest.psm1 +1 -1
- ansible/module_utils/service.py +1 -1
- ansible/module_utils/urls.py +5 -5
- ansible/modules/apt.py +9 -3
- ansible/modules/apt_repository.py +10 -10
- ansible/modules/assemble.py +7 -5
- ansible/modules/async_wrapper.py +7 -17
- ansible/modules/command.py +3 -3
- ansible/modules/copy.py +4 -4
- ansible/modules/cron.py +1 -1
- ansible/modules/expect.py +5 -5
- ansible/modules/file.py +16 -17
- ansible/modules/find.py +3 -3
- ansible/modules/get_url.py +17 -0
- ansible/modules/git.py +9 -7
- ansible/modules/hostname.py +2 -2
- ansible/modules/known_hosts.py +12 -14
- ansible/modules/package.py +6 -0
- ansible/modules/pip.py +9 -11
- ansible/modules/raw.py +2 -2
- ansible/modules/replace.py +2 -2
- ansible/modules/slurp.py +10 -13
- ansible/modules/stat.py +6 -8
- ansible/modules/unarchive.py +6 -6
- ansible/modules/user.py +1 -1
- ansible/modules/wait_for.py +38 -33
- ansible/modules/yum_repository.py +4 -3
- ansible/parsing/dataloader.py +2 -2
- ansible/parsing/mod_args.py +38 -20
- ansible/parsing/vault/__init__.py +9 -13
- ansible/playbook/base.py +7 -4
- ansible/playbook/helpers.py +1 -1
- ansible/playbook/included_file.py +3 -1
- ansible/playbook/play_context.py +2 -0
- ansible/playbook/playbook_include.py +23 -56
- ansible/playbook/role/__init__.py +38 -21
- ansible/playbook/taggable.py +19 -5
- ansible/playbook/task.py +2 -0
- ansible/plugins/action/__init__.py +2 -2
- ansible/plugins/action/assemble.py +2 -1
- ansible/plugins/action/assert.py +2 -2
- ansible/plugins/action/fetch.py +3 -3
- ansible/plugins/action/script.py +5 -4
- ansible/plugins/action/template.py +9 -3
- ansible/plugins/cache/__init__.py +17 -19
- ansible/plugins/callback/__init__.py +77 -87
- ansible/plugins/callback/default.py +0 -3
- ansible/plugins/callback/junit.py +0 -6
- ansible/plugins/callback/tree.py +5 -5
- ansible/plugins/connection/local.py +4 -4
- ansible/plugins/connection/paramiko_ssh.py +5 -5
- ansible/plugins/connection/ssh.py +9 -7
- ansible/plugins/connection/winrm.py +1 -1
- ansible/plugins/filter/core.py +19 -21
- ansible/plugins/filter/encryption.py +10 -2
- ansible/plugins/filter/pow.yml +1 -1
- ansible/plugins/filter/root.yml +1 -1
- ansible/plugins/filter/strftime.yml +3 -3
- ansible/plugins/filter/to_uuid.yml +1 -1
- ansible/plugins/inventory/script.py +1 -1
- ansible/plugins/list.py +5 -4
- ansible/plugins/loader.py +5 -0
- ansible/plugins/lookup/password.py +4 -6
- ansible/plugins/lookup/template.py +9 -4
- ansible/plugins/shell/powershell.py +3 -2
- ansible/plugins/shell/sh.py +3 -2
- ansible/plugins/strategy/__init__.py +3 -3
- ansible/plugins/test/core.py +2 -2
- ansible/release.py +1 -1
- ansible/template/__init__.py +9 -53
- ansible/utils/collection_loader/_collection_finder.py +3 -3
- ansible/utils/display.py +38 -37
- ansible/utils/galaxy.py +2 -2
- ansible/utils/hashing.py +6 -7
- ansible/utils/path.py +6 -8
- ansible/utils/py3compat.py +2 -1
- ansible/utils/ssh_functions.py +3 -2
- ansible/utils/vars.py +4 -1
- ansible/vars/manager.py +6 -3
- ansible/vars/plugins.py +3 -3
- ansible/vars/reserved.py +6 -4
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/METADATA +1 -1
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/RECORD +184 -173
- ansible_test/_internal/__init__.py +5 -0
- ansible_test/_internal/ansible_util.py +1 -1
- ansible_test/_internal/classification/python.py +6 -0
- ansible_test/_internal/cli/commands/__init__.py +0 -5
- ansible_test/_internal/cli/environments.py +51 -5
- ansible_test/_internal/commands/coverage/__init__.py +1 -1
- ansible_test/_internal/commands/integration/__init__.py +18 -5
- ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
- ansible_test/_internal/commands/integration/coverage.py +7 -2
- ansible_test/_internal/commands/sanity/__init__.py +3 -1
- ansible_test/_internal/commands/sanity/integration_aliases.py +11 -0
- ansible_test/_internal/commands/shell/__init__.py +43 -4
- ansible_test/_internal/commands/units/__init__.py +4 -1
- ansible_test/_internal/config.py +21 -13
- ansible_test/_internal/debugging.py +166 -0
- ansible_test/_internal/delegation.py +21 -13
- ansible_test/_internal/host_profiles.py +259 -16
- ansible_test/_internal/inventory.py +4 -0
- ansible_test/_internal/metadata.py +94 -4
- ansible_test/_internal/processes.py +80 -0
- ansible_test/_internal/provisioning.py +10 -4
- ansible_test/_internal/python_requirements.py +27 -0
- ansible_test/_internal/ssh.py +1 -5
- ansible_test/_internal/target.py +8 -0
- ansible_test/_internal/thread.py +2 -1
- ansible_test/_internal/timeout.py +1 -1
- ansible_test/_internal/util.py +20 -12
- ansible_test/_internal/util_common.py +13 -3
- ansible_test/_util/target/injector/python.py +8 -0
- ansible_test/_util/target/setup/requirements.py +3 -9
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/WHEEL +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
"""Wrappers around `ps` for querying running processes."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import collections
|
6
|
+
import dataclasses
|
7
|
+
import os
|
8
|
+
import pathlib
|
9
|
+
import shlex
|
10
|
+
|
11
|
+
from ansible_test._internal.util import raw_command
|
12
|
+
|
13
|
+
|
14
|
+
@dataclasses.dataclass(frozen=True)
|
15
|
+
class ProcessData:
|
16
|
+
"""Data about a running process."""
|
17
|
+
|
18
|
+
pid: int
|
19
|
+
ppid: int
|
20
|
+
command: str
|
21
|
+
|
22
|
+
|
23
|
+
@dataclasses.dataclass(frozen=True)
|
24
|
+
class Process:
|
25
|
+
"""A process in the process tree."""
|
26
|
+
|
27
|
+
pid: int
|
28
|
+
command: str
|
29
|
+
parent: Process | None = None
|
30
|
+
children: tuple[Process, ...] = dataclasses.field(default_factory=tuple)
|
31
|
+
|
32
|
+
@property
|
33
|
+
def args(self) -> list[str]:
|
34
|
+
"""The list of arguments that make up `command`."""
|
35
|
+
return shlex.split(self.command)
|
36
|
+
|
37
|
+
@property
|
38
|
+
def path(self) -> pathlib.Path:
|
39
|
+
"""The path to the process."""
|
40
|
+
return pathlib.Path(self.args[0])
|
41
|
+
|
42
|
+
|
43
|
+
def get_process_data(pids: list[int] | None = None) -> list[ProcessData]:
|
44
|
+
"""Return a list of running processes."""
|
45
|
+
if pids:
|
46
|
+
args = ['-p', ','.join(map(str, pids))]
|
47
|
+
else:
|
48
|
+
args = ['-A']
|
49
|
+
|
50
|
+
lines = raw_command(['ps'] + args + ['-o', 'pid,ppid,command'], capture=True)[0].splitlines()[1:]
|
51
|
+
processes = [ProcessData(pid=int(pid), ppid=int(ppid), command=command) for pid, ppid, command in (line.split(maxsplit=2) for line in lines)]
|
52
|
+
|
53
|
+
return processes
|
54
|
+
|
55
|
+
|
56
|
+
def get_process_tree() -> dict[int, Process]:
|
57
|
+
"""Return the process tree."""
|
58
|
+
processes = get_process_data()
|
59
|
+
pid_to_process: dict[int, Process] = {}
|
60
|
+
pid_to_children: dict[int, list[Process]] = collections.defaultdict(list)
|
61
|
+
|
62
|
+
for data in processes:
|
63
|
+
pid_to_process[data.pid] = process = Process(pid=data.pid, command=data.command)
|
64
|
+
|
65
|
+
if data.ppid:
|
66
|
+
pid_to_children[data.ppid].append(process)
|
67
|
+
|
68
|
+
for data in processes:
|
69
|
+
pid_to_process[data.pid] = dataclasses.replace(
|
70
|
+
pid_to_process[data.pid],
|
71
|
+
parent=pid_to_process.get(data.ppid),
|
72
|
+
children=tuple(pid_to_children[data.pid]),
|
73
|
+
)
|
74
|
+
|
75
|
+
return pid_to_process
|
76
|
+
|
77
|
+
|
78
|
+
def get_current_process() -> Process:
|
79
|
+
"""Return the current process along with its ancestors and descendants."""
|
80
|
+
return get_process_tree()[os.getpid()]
|
@@ -116,9 +116,11 @@ def prepare_profiles(
|
|
116
116
|
else:
|
117
117
|
run_pypi_proxy(args, targets_use_pypi)
|
118
118
|
|
119
|
+
controller_host_profile = t.cast(ControllerHostProfile, create_host_profile(args, args.controller, None))
|
120
|
+
|
119
121
|
host_state = HostState(
|
120
|
-
controller_profile=
|
121
|
-
target_profiles=[create_host_profile(args, target,
|
122
|
+
controller_profile=controller_host_profile,
|
123
|
+
target_profiles=[create_host_profile(args, target, controller_host_profile) for target in args.targets],
|
122
124
|
)
|
123
125
|
|
124
126
|
if args.prime_containers:
|
@@ -137,7 +139,9 @@ def prepare_profiles(
|
|
137
139
|
if not skip_setup:
|
138
140
|
profile.setup()
|
139
141
|
|
140
|
-
dispatch_jobs(
|
142
|
+
dispatch_jobs(
|
143
|
+
[(profile, WrappedThread(functools.partial(provision, profile), f'Provision: {profile}')) for profile in host_state.profiles]
|
144
|
+
)
|
141
145
|
|
142
146
|
host_state.controller_profile.configure()
|
143
147
|
|
@@ -157,7 +161,9 @@ def prepare_profiles(
|
|
157
161
|
if requirements:
|
158
162
|
requirements(profile)
|
159
163
|
|
160
|
-
dispatch_jobs(
|
164
|
+
dispatch_jobs(
|
165
|
+
[(profile, WrappedThread(functools.partial(configure, profile), f'Configure: {profile}')) for profile in host_state.target_profiles]
|
166
|
+
)
|
161
167
|
|
162
168
|
return host_state
|
163
169
|
|
@@ -55,6 +55,11 @@ from .coverage_util import (
|
|
55
55
|
get_coverage_version,
|
56
56
|
)
|
57
57
|
|
58
|
+
if t.TYPE_CHECKING:
|
59
|
+
from .host_profiles import (
|
60
|
+
HostProfile,
|
61
|
+
)
|
62
|
+
|
58
63
|
QUIET_PIP_SCRIPT_PATH = os.path.join(ANSIBLE_TEST_TARGET_ROOT, 'setup', 'quiet_pip.py')
|
59
64
|
REQUIREMENTS_SCRIPT_PATH = os.path.join(ANSIBLE_TEST_TARGET_ROOT, 'setup', 'requirements.py')
|
60
65
|
|
@@ -122,6 +127,7 @@ class PipBootstrap(PipCommand):
|
|
122
127
|
|
123
128
|
def install_requirements(
|
124
129
|
args: EnvironmentConfig,
|
130
|
+
host_profile: HostProfile | None,
|
125
131
|
python: PythonConfig,
|
126
132
|
ansible: bool = False,
|
127
133
|
command: bool = False,
|
@@ -133,6 +139,7 @@ def install_requirements(
|
|
133
139
|
create_result_directories(args)
|
134
140
|
|
135
141
|
if not requirements_allowed(args, controller):
|
142
|
+
post_install(host_profile)
|
136
143
|
return
|
137
144
|
|
138
145
|
if command and isinstance(args, (UnitsConfig, IntegrationConfig)) and args.coverage:
|
@@ -161,7 +168,17 @@ def install_requirements(
|
|
161
168
|
sanity=None,
|
162
169
|
)
|
163
170
|
|
171
|
+
from .host_profiles import DebuggableProfile
|
172
|
+
|
173
|
+
if isinstance(host_profile, DebuggableProfile) and host_profile.debugging_enabled and args.metadata.debugger_settings.package:
|
174
|
+
commands.append(PipInstall(
|
175
|
+
requirements=[],
|
176
|
+
constraints=[],
|
177
|
+
packages=[args.metadata.debugger_settings.package],
|
178
|
+
))
|
179
|
+
|
164
180
|
if not commands:
|
181
|
+
post_install(host_profile)
|
165
182
|
return
|
166
183
|
|
167
184
|
run_pip(args, python, commands, connection)
|
@@ -170,6 +187,16 @@ def install_requirements(
|
|
170
187
|
if any(isinstance(command, PipInstall) and command.has_package('pyyaml') for command in commands):
|
171
188
|
check_pyyaml(python)
|
172
189
|
|
190
|
+
post_install(host_profile)
|
191
|
+
|
192
|
+
|
193
|
+
def post_install(host_profile: HostProfile) -> None:
|
194
|
+
"""Operations to perform after requirements are installed."""
|
195
|
+
from .host_profiles import DebuggableProfile
|
196
|
+
|
197
|
+
if isinstance(host_profile, DebuggableProfile):
|
198
|
+
host_profile.activate_debugger()
|
199
|
+
|
173
200
|
|
174
201
|
def collect_bootstrap(python: PythonConfig) -> list[PipCommand]:
|
175
202
|
"""Return the details necessary to bootstrap pip into an empty virtual environment."""
|
ansible_test/_internal/ssh.py
CHANGED
@@ -13,7 +13,6 @@ import shlex
|
|
13
13
|
import typing as t
|
14
14
|
|
15
15
|
from .encoding import (
|
16
|
-
to_bytes,
|
17
16
|
to_text,
|
18
17
|
)
|
19
18
|
|
@@ -223,13 +222,10 @@ def run_ssh_command(
|
|
223
222
|
cmd_show = shlex.join(cmd)
|
224
223
|
display.info('Run background command: %s' % cmd_show, verbosity=1, truncate=True)
|
225
224
|
|
226
|
-
cmd_bytes = [to_bytes(arg) for arg in cmd]
|
227
|
-
env_bytes = dict((to_bytes(k), to_bytes(v)) for k, v in env.items())
|
228
|
-
|
229
225
|
if args.explain:
|
230
226
|
process = SshProcess(None)
|
231
227
|
else:
|
232
|
-
process = SshProcess(subprocess.Popen(
|
228
|
+
process = SshProcess(subprocess.Popen(cmd, env=env, bufsize=-1, # pylint: disable=consider-using-with
|
233
229
|
stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
|
234
230
|
|
235
231
|
return process
|
ansible_test/_internal/target.py
CHANGED
@@ -582,6 +582,14 @@ class IntegrationTarget(CompletionTarget):
|
|
582
582
|
else:
|
583
583
|
static_aliases = tuple()
|
584
584
|
|
585
|
+
# non-group aliases which need to be extracted before group mangling occurs
|
586
|
+
|
587
|
+
self.env_set: dict[str, str] = {
|
588
|
+
match.group('key'): match.group('value') for match in (
|
589
|
+
re.match(r'env/set/(?P<key>[^/]+)/(?P<value>.*)', alias) for alias in static_aliases
|
590
|
+
) if match
|
591
|
+
}
|
592
|
+
|
585
593
|
# modules
|
586
594
|
|
587
595
|
if self.name in modules:
|
ansible_test/_internal/thread.py
CHANGED
@@ -17,11 +17,12 @@ TCallable = t.TypeVar('TCallable', bound=t.Callable[..., t.Any])
|
|
17
17
|
class WrappedThread(threading.Thread):
|
18
18
|
"""Wrapper around Thread which captures results and exceptions."""
|
19
19
|
|
20
|
-
def __init__(self, action: c.Callable[[], t.Any]) -> None:
|
20
|
+
def __init__(self, action: c.Callable[[], t.Any], name: str) -> None:
|
21
21
|
super().__init__()
|
22
22
|
self._result: queue.Queue[t.Any] = queue.Queue()
|
23
23
|
self.action = action
|
24
24
|
self.result = None
|
25
|
+
self.name = name
|
25
26
|
|
26
27
|
def run(self) -> None:
|
27
28
|
"""
|
@@ -126,6 +126,6 @@ def configure_test_timeout(args: TestConfig) -> None:
|
|
126
126
|
|
127
127
|
signal.signal(signal.SIGUSR1, timeout_handler)
|
128
128
|
|
129
|
-
instance = WrappedThread(functools.partial(timeout_waiter, timeout_remaining.total_seconds()))
|
129
|
+
instance = WrappedThread(functools.partial(timeout_waiter, timeout_remaining.total_seconds()), 'Timeout Watchdog')
|
130
130
|
instance.daemon = True
|
131
131
|
instance.start()
|
ansible_test/_internal/util.py
CHANGED
@@ -533,16 +533,23 @@ def raw_command(
|
|
533
533
|
|
534
534
|
try:
|
535
535
|
try:
|
536
|
-
|
537
|
-
env_bytes = dict((to_bytes(k), to_bytes(v)) for k, v in env.items())
|
538
|
-
process = subprocess.Popen(cmd_bytes, env=env_bytes, stdin=stdin, stdout=stdout, stderr=stderr, cwd=cwd) # pylint: disable=consider-using-with
|
536
|
+
process = subprocess.Popen(cmd, env=env, stdin=stdin, stdout=stdout, stderr=stderr, cwd=cwd) # pylint: disable=consider-using-with
|
539
537
|
except FileNotFoundError as ex:
|
540
538
|
raise ApplicationError('Required program "%s" not found.' % cmd[0]) from ex
|
541
539
|
|
542
540
|
if communicate:
|
543
541
|
data_bytes = to_optional_bytes(data)
|
544
|
-
|
545
|
-
|
542
|
+
|
543
|
+
stdout_bytes, stderr_bytes = communicate_with_process(
|
544
|
+
name=cmd[0],
|
545
|
+
process=process,
|
546
|
+
stdin=data_bytes,
|
547
|
+
stdout=stdout == subprocess.PIPE,
|
548
|
+
stderr=stderr == subprocess.PIPE,
|
549
|
+
capture=capture,
|
550
|
+
output_stream=output_stream,
|
551
|
+
)
|
552
|
+
|
546
553
|
stdout_text = to_optional_text(stdout_bytes, str_errors) or ''
|
547
554
|
stderr_text = to_optional_text(stderr_bytes, str_errors) or ''
|
548
555
|
else:
|
@@ -566,6 +573,7 @@ def raw_command(
|
|
566
573
|
|
567
574
|
|
568
575
|
def communicate_with_process(
|
576
|
+
name: str,
|
569
577
|
process: subprocess.Popen,
|
570
578
|
stdin: t.Optional[bytes],
|
571
579
|
stdout: bool,
|
@@ -583,16 +591,16 @@ def communicate_with_process(
|
|
583
591
|
reader = OutputThread
|
584
592
|
|
585
593
|
if stdin is not None:
|
586
|
-
threads.append(WriterThread(process.stdin, stdin))
|
594
|
+
threads.append(WriterThread(process.stdin, stdin, name))
|
587
595
|
|
588
596
|
if stdout:
|
589
|
-
stdout_reader = reader(process.stdout, output_stream.get_buffer(sys.stdout.buffer))
|
597
|
+
stdout_reader = reader(process.stdout, output_stream.get_buffer(sys.stdout.buffer), name)
|
590
598
|
threads.append(stdout_reader)
|
591
599
|
else:
|
592
600
|
stdout_reader = None
|
593
601
|
|
594
602
|
if stderr:
|
595
|
-
stderr_reader = reader(process.stderr, output_stream.get_buffer(sys.stderr.buffer))
|
603
|
+
stderr_reader = reader(process.stderr, output_stream.get_buffer(sys.stderr.buffer), name)
|
596
604
|
threads.append(stderr_reader)
|
597
605
|
else:
|
598
606
|
stderr_reader = None
|
@@ -624,8 +632,8 @@ def communicate_with_process(
|
|
624
632
|
class WriterThread(WrappedThread):
|
625
633
|
"""Thread to write data to stdin of a subprocess."""
|
626
634
|
|
627
|
-
def __init__(self, handle: t.IO[bytes], data: bytes) -> None:
|
628
|
-
super().__init__(self._run)
|
635
|
+
def __init__(self, handle: t.IO[bytes], data: bytes, name: str) -> None:
|
636
|
+
super().__init__(self._run, f'{self.__class__.__name__}: {name}')
|
629
637
|
|
630
638
|
self.handle = handle
|
631
639
|
self.data = data
|
@@ -642,8 +650,8 @@ class WriterThread(WrappedThread):
|
|
642
650
|
class ReaderThread(WrappedThread, metaclass=abc.ABCMeta):
|
643
651
|
"""Thread to read stdout from a subprocess."""
|
644
652
|
|
645
|
-
def __init__(self, handle: t.IO[bytes], buffer: t.BinaryIO) -> None:
|
646
|
-
super().__init__(self._run)
|
653
|
+
def __init__(self, handle: t.IO[bytes], buffer: t.BinaryIO, name: str) -> None:
|
654
|
+
super().__init__(self._run, f'{self.__class__.__name__}: {name}')
|
647
655
|
|
648
656
|
self.handle = handle
|
649
657
|
self.buffer = buffer
|
@@ -451,10 +451,20 @@ def intercept_python(
|
|
451
451
|
"""
|
452
452
|
Run a command while intercepting invocations of Python to control the version used.
|
453
453
|
If the specified Python is an ansible-test managed virtual environment, it will be added to PATH to activate it.
|
454
|
-
Otherwise a temporary directory will be created to ensure the correct Python can be found in PATH.
|
454
|
+
Otherwise, a temporary directory will be created to ensure the correct Python can be found in PATH.
|
455
455
|
"""
|
456
|
-
env = env.copy()
|
457
456
|
cmd = list(cmd)
|
457
|
+
env = get_injector_env(python, env)
|
458
|
+
|
459
|
+
return run_command(args, cmd, capture=capture, env=env, data=data, cwd=cwd, always=always)
|
460
|
+
|
461
|
+
|
462
|
+
def get_injector_env(
|
463
|
+
python: PythonConfig,
|
464
|
+
env: dict[str, str],
|
465
|
+
) -> dict[str, str]:
|
466
|
+
"""Get the environment variables needed to inject the given Python interpreter into the environment."""
|
467
|
+
env = env.copy()
|
458
468
|
inject_path = get_injector_path()
|
459
469
|
|
460
470
|
# make sure scripts (including injector.py) find the correct Python interpreter
|
@@ -467,7 +477,7 @@ def intercept_python(
|
|
467
477
|
env['ANSIBLE_TEST_PYTHON_VERSION'] = python.version
|
468
478
|
env['ANSIBLE_TEST_PYTHON_INTERPRETER'] = python.path
|
469
479
|
|
470
|
-
return
|
480
|
+
return env
|
471
481
|
|
472
482
|
|
473
483
|
def run_command(
|
@@ -15,6 +15,7 @@ def main():
|
|
15
15
|
args = [sys.executable]
|
16
16
|
|
17
17
|
ansible_lib_root = os.environ.get('ANSIBLE_TEST_ANSIBLE_LIB_ROOT')
|
18
|
+
debugger_config = os.environ.get('ANSIBLE_TEST_DEBUGGER_CONFIG')
|
18
19
|
coverage_config = os.environ.get('COVERAGE_CONF')
|
19
20
|
coverage_output = os.environ.get('COVERAGE_FILE')
|
20
21
|
|
@@ -28,6 +29,13 @@ def main():
|
|
28
29
|
sys.exit('ERROR: Could not find `coverage` module. '
|
29
30
|
'Did you use a virtualenv created without --system-site-packages or with the wrong interpreter?')
|
30
31
|
|
32
|
+
if debugger_config:
|
33
|
+
import json
|
34
|
+
|
35
|
+
debugger_options = json.loads(debugger_config)
|
36
|
+
os.environ.update(debugger_options['env'])
|
37
|
+
args += debugger_options['args']
|
38
|
+
|
31
39
|
if name == 'python.py':
|
32
40
|
if sys.argv[1] == '-c':
|
33
41
|
# prevent simple misuse of python.py with -c which does not work with coverage
|
@@ -19,7 +19,6 @@ if DESIRED_RLIMIT_NOFILE < CURRENT_RLIMIT_NOFILE:
|
|
19
19
|
|
20
20
|
import base64
|
21
21
|
import contextlib
|
22
|
-
import errno
|
23
22
|
import io
|
24
23
|
import json
|
25
24
|
import os
|
@@ -349,18 +348,13 @@ def remove_tree(path): # type: (str) -> None
|
|
349
348
|
"""Remove the specified directory tree."""
|
350
349
|
try:
|
351
350
|
shutil.rmtree(to_bytes(path))
|
352
|
-
except
|
353
|
-
|
354
|
-
raise
|
351
|
+
except FileNotFoundError:
|
352
|
+
pass
|
355
353
|
|
356
354
|
|
357
355
|
def make_dirs(path): # type: (str) -> None
|
358
356
|
"""Create a directory at path, including any necessary parent directories."""
|
359
|
-
|
360
|
-
os.makedirs(to_bytes(path))
|
361
|
-
except OSError as ex:
|
362
|
-
if ex.errno != errno.EEXIST:
|
363
|
-
raise
|
357
|
+
os.makedirs(to_bytes(path), exist_ok=True)
|
364
358
|
|
365
359
|
|
366
360
|
def open_binary_file(path, mode='rb'): # type: (str, str) -> t.IO[bytes]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|