ansible-core 2.19.0b5__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.
- 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 +7 -4
- 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 +7 -12
- ansible/cli/arguments/option_helpers.py +1 -1
- ansible/cli/console.py +1 -1
- ansible/cli/doc.py +2 -2
- ansible/cli/inventory.py +5 -7
- ansible/config/base.yml +24 -0
- ansible/errors/__init__.py +2 -1
- ansible/executor/module_common.py +67 -39
- ansible/executor/process/worker.py +2 -2
- ansible/galaxy/api.py +1 -4
- ansible/galaxy/collection/__init__.py +1 -6
- ansible/galaxy/collection/concrete_artifact_manager.py +2 -8
- ansible/galaxy/role.py +2 -2
- 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 +27 -33
- 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 +46 -56
- ansible/module_utils/common/respawn.py +4 -41
- ansible/module_utils/connection.py +8 -11
- ansible/module_utils/facts/hardware/linux.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/local.py +1 -1
- ansible/module_utils/facts/virtual/linux.py +1 -1
- ansible/module_utils/service.py +1 -1
- ansible/module_utils/urls.py +4 -4
- ansible/modules/apt_repository.py +10 -10
- ansible/modules/assemble.py +2 -2
- 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/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/known_hosts.py +12 -14
- ansible/modules/package.py +6 -0
- ansible/modules/replace.py +2 -2
- ansible/modules/slurp.py +10 -13
- ansible/modules/stat.py +5 -7
- ansible/modules/unarchive.py +6 -6
- ansible/modules/user.py +1 -1
- ansible/modules/wait_for.py +28 -30
- ansible/modules/yum_repository.py +4 -3
- ansible/parsing/dataloader.py +2 -2
- ansible/parsing/vault/__init__.py +6 -10
- ansible/playbook/base.py +7 -2
- ansible/playbook/included_file.py +3 -1
- ansible/playbook/play_context.py +2 -0
- ansible/playbook/taggable.py +19 -5
- ansible/playbook/task.py +2 -0
- ansible/plugins/action/fetch.py +3 -3
- ansible/plugins/action/template.py +8 -2
- ansible/plugins/cache/__init__.py +17 -19
- 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 +8 -6
- ansible/plugins/connection/winrm.py +1 -1
- ansible/plugins/filter/core.py +19 -21
- ansible/plugins/filter/encryption.py +10 -2
- ansible/plugins/list.py +5 -4
- 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 +23 -12
- ansible/utils/galaxy.py +2 -2
- ansible/utils/hashing.py +6 -7
- ansible/utils/path.py +5 -7
- ansible/utils/py3compat.py +2 -1
- ansible/utils/ssh_functions.py +3 -2
- ansible/vars/plugins.py +3 -3
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/METADATA +1 -1
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/RECORD +117 -108
- ansible_test/_internal/commands/integration/coverage.py +7 -2
- ansible_test/_internal/host_profiles.py +62 -10
- ansible_test/_internal/provisioning.py +10 -4
- ansible_test/_internal/ssh.py +1 -5
- ansible_test/_internal/thread.py +2 -1
- ansible_test/_internal/timeout.py +1 -1
- ansible_test/_internal/util.py +20 -12
- ansible_test/_util/target/setup/requirements.py +3 -9
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/WHEEL +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b6.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,7 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
import abc
|
6
|
+
import json
|
6
7
|
import os
|
7
8
|
import shutil
|
8
9
|
import tempfile
|
@@ -240,9 +241,13 @@ class PosixCoverageHandler(CoverageHandler[PosixConfig]):
|
|
240
241
|
# cause the 'coverage' module to be found, but not imported or enabled
|
241
242
|
coverage_file = ''
|
242
243
|
|
244
|
+
coverage_options = dict(
|
245
|
+
config=config_file,
|
246
|
+
output=coverage_file,
|
247
|
+
)
|
248
|
+
|
243
249
|
variables = dict(
|
244
|
-
|
245
|
-
_ANSIBLE_COVERAGE_OUTPUT=coverage_file,
|
250
|
+
_ANSIBLE_ANSIBALLZ_COVERAGE_CONFIG=json.dumps(coverage_options),
|
246
251
|
)
|
247
252
|
|
248
253
|
return variables
|
@@ -206,7 +206,7 @@ class Inventory:
|
|
206
206
|
inventory_text += f'[{group}]\n'
|
207
207
|
|
208
208
|
for host, variables in hosts.items():
|
209
|
-
kvp = ' '.join(f
|
209
|
+
kvp = ' '.join(f"{key}={value!r}" for key, value in variables.items())
|
210
210
|
inventory_text += f'{host} {kvp}\n'
|
211
211
|
|
212
212
|
inventory_text += '\n'
|
@@ -235,18 +235,24 @@ class HostProfile(t.Generic[THostConfig], metaclass=abc.ABCMeta):
|
|
235
235
|
*,
|
236
236
|
args: EnvironmentConfig,
|
237
237
|
config: THostConfig,
|
238
|
-
|
238
|
+
controller: ControllerHostProfile,
|
239
239
|
) -> None:
|
240
240
|
self.args = args
|
241
241
|
self.config = config
|
242
|
-
self.controller =
|
243
|
-
self.targets = targets
|
242
|
+
self.controller = not controller # this profile is a controller whenever the `controller` arg was not provided
|
243
|
+
self.targets = args.targets if self.controller else [] # only keep targets if this profile is a controller
|
244
|
+
self.controller_profile = controller if isinstance(self, ControllerProfile) else None
|
244
245
|
|
245
246
|
self.state: dict[str, t.Any] = {}
|
246
247
|
"""State that must be persisted across delegation."""
|
247
248
|
self.cache: dict[str, t.Any] = {}
|
248
249
|
"""Cache that must not be persisted across delegation."""
|
249
250
|
|
251
|
+
@property
|
252
|
+
@abc.abstractmethod
|
253
|
+
def name(self) -> str:
|
254
|
+
"""The name of the host profile."""
|
255
|
+
|
250
256
|
def provision(self) -> None:
|
251
257
|
"""Provision the host before delegation."""
|
252
258
|
|
@@ -274,6 +280,9 @@ class HostProfile(t.Generic[THostConfig], metaclass=abc.ABCMeta):
|
|
274
280
|
# args will be populated after the instances are restored
|
275
281
|
self.cache = {}
|
276
282
|
|
283
|
+
def __str__(self) -> str:
|
284
|
+
return f'{self.__class__.__name__}: {self.name}'
|
285
|
+
|
277
286
|
|
278
287
|
class PosixProfile(HostProfile[TPosixConfig], metaclass=abc.ABCMeta):
|
279
288
|
"""Base class for POSIX host profiles."""
|
@@ -320,6 +329,11 @@ class SshTargetHostProfile(HostProfile[THostConfig], metaclass=abc.ABCMeta):
|
|
320
329
|
class RemoteProfile(SshTargetHostProfile[TRemoteConfig], metaclass=abc.ABCMeta):
|
321
330
|
"""Base class for remote instance profiles."""
|
322
331
|
|
332
|
+
@property
|
333
|
+
def name(self) -> str:
|
334
|
+
"""The name of the host profile."""
|
335
|
+
return self.config.name
|
336
|
+
|
323
337
|
@property
|
324
338
|
def core_ci_state(self) -> t.Optional[dict[str, str]]:
|
325
339
|
"""The saved Ansible Core CI state."""
|
@@ -339,6 +353,8 @@ class RemoteProfile(SshTargetHostProfile[TRemoteConfig], metaclass=abc.ABCMeta):
|
|
339
353
|
|
340
354
|
def deprovision(self) -> None:
|
341
355
|
"""Deprovision the host after delegation has completed."""
|
356
|
+
super().deprovision()
|
357
|
+
|
342
358
|
if self.args.remote_terminate == TerminateMode.ALWAYS or (self.args.remote_terminate == TerminateMode.SUCCESS and self.args.success):
|
343
359
|
self.delete_instance()
|
344
360
|
|
@@ -397,6 +413,11 @@ class RemoteProfile(SshTargetHostProfile[TRemoteConfig], metaclass=abc.ABCMeta):
|
|
397
413
|
class ControllerProfile(SshTargetHostProfile[ControllerConfig], PosixProfile[ControllerConfig]):
|
398
414
|
"""Host profile for the controller as a target."""
|
399
415
|
|
416
|
+
@property
|
417
|
+
def name(self) -> str:
|
418
|
+
"""The name of the host profile."""
|
419
|
+
return self.controller_profile.name
|
420
|
+
|
400
421
|
def get_controller_target_connections(self) -> list[SshConnection]:
|
401
422
|
"""Return SSH connection(s) for accessing the host as a target from the controller."""
|
402
423
|
settings = SshConnectionDetail(
|
@@ -425,6 +446,11 @@ class DockerProfile(ControllerHostProfile[DockerConfig], SshTargetHostProfile[Do
|
|
425
446
|
command_privileged: bool
|
426
447
|
expected_mounts: tuple[CGroupMount, ...]
|
427
448
|
|
449
|
+
@property
|
450
|
+
def name(self) -> str:
|
451
|
+
"""The name of the host profile."""
|
452
|
+
return self.config.name
|
453
|
+
|
428
454
|
@property
|
429
455
|
def container_name(self) -> t.Optional[str]:
|
430
456
|
"""Return the stored container name, if any, otherwise None."""
|
@@ -976,6 +1002,8 @@ class DockerProfile(ControllerHostProfile[DockerConfig], SshTargetHostProfile[Do
|
|
976
1002
|
|
977
1003
|
def deprovision(self) -> None:
|
978
1004
|
"""Deprovision the host after delegation has completed."""
|
1005
|
+
super().deprovision()
|
1006
|
+
|
979
1007
|
container_exists = False
|
980
1008
|
|
981
1009
|
if self.container_name:
|
@@ -1025,10 +1053,10 @@ class DockerProfile(ControllerHostProfile[DockerConfig], SshTargetHostProfile[Do
|
|
1025
1053
|
|
1026
1054
|
raise HostConnectionError(f'Timeout waiting for {self.config.name} container {self.container_name}.', callback)
|
1027
1055
|
|
1028
|
-
def
|
1029
|
-
"""Return SSH connection
|
1056
|
+
def get_ssh_connection_detail(self, host_type: str) -> SshConnectionDetail:
|
1057
|
+
"""Return SSH connection detail for the specified host type."""
|
1030
1058
|
containers = get_container_database(self.args)
|
1031
|
-
access = containers.data[
|
1059
|
+
access = containers.data[host_type]['__test_hosts__'][self.container_name]
|
1032
1060
|
|
1033
1061
|
host = access.host_ip
|
1034
1062
|
port = dict(access.port_map())[22]
|
@@ -1046,7 +1074,11 @@ class DockerProfile(ControllerHostProfile[DockerConfig], SshTargetHostProfile[Do
|
|
1046
1074
|
enable_rsa_sha1='centos6' in self.config.image,
|
1047
1075
|
)
|
1048
1076
|
|
1049
|
-
return
|
1077
|
+
return settings
|
1078
|
+
|
1079
|
+
def get_controller_target_connections(self) -> list[SshConnection]:
|
1080
|
+
"""Return SSH connection(s) for accessing the host as a target from the controller."""
|
1081
|
+
return [SshConnection(self.args, self.get_ssh_connection_detail(HostType.control))]
|
1050
1082
|
|
1051
1083
|
def get_origin_controller_connection(self) -> DockerConnection:
|
1052
1084
|
"""Return a connection for accessing the host as a controller from the origin."""
|
@@ -1116,6 +1148,11 @@ class DockerProfile(ControllerHostProfile[DockerConfig], SshTargetHostProfile[Do
|
|
1116
1148
|
class NetworkInventoryProfile(HostProfile[NetworkInventoryConfig]):
|
1117
1149
|
"""Host profile for a network inventory."""
|
1118
1150
|
|
1151
|
+
@property
|
1152
|
+
def name(self) -> str:
|
1153
|
+
"""The name of the host profile."""
|
1154
|
+
return self.config.path
|
1155
|
+
|
1119
1156
|
|
1120
1157
|
class NetworkRemoteProfile(RemoteProfile[NetworkRemoteConfig]):
|
1121
1158
|
"""Host profile for a network remote instance."""
|
@@ -1197,6 +1234,11 @@ class NetworkRemoteProfile(RemoteProfile[NetworkRemoteConfig]):
|
|
1197
1234
|
class OriginProfile(ControllerHostProfile[OriginConfig]):
|
1198
1235
|
"""Host profile for origin."""
|
1199
1236
|
|
1237
|
+
@property
|
1238
|
+
def name(self) -> str:
|
1239
|
+
"""The name of the host profile."""
|
1240
|
+
return 'origin'
|
1241
|
+
|
1200
1242
|
def get_origin_controller_connection(self) -> LocalConnection:
|
1201
1243
|
"""Return a connection for accessing the host as a controller from the origin."""
|
1202
1244
|
return LocalConnection(self.args)
|
@@ -1317,6 +1359,11 @@ class PosixRemoteProfile(ControllerHostProfile[PosixRemoteConfig], RemoteProfile
|
|
1317
1359
|
class PosixSshProfile(SshTargetHostProfile[PosixSshConfig], PosixProfile[PosixSshConfig]):
|
1318
1360
|
"""Host profile for a POSIX SSH instance."""
|
1319
1361
|
|
1362
|
+
@property
|
1363
|
+
def name(self) -> str:
|
1364
|
+
"""The name of the host profile."""
|
1365
|
+
return self.config.host
|
1366
|
+
|
1320
1367
|
def get_controller_target_connections(self) -> list[SshConnection]:
|
1321
1368
|
"""Return SSH connection(s) for accessing the host as a target from the controller."""
|
1322
1369
|
settings = SshConnectionDetail(
|
@@ -1334,6 +1381,11 @@ class PosixSshProfile(SshTargetHostProfile[PosixSshConfig], PosixProfile[PosixSs
|
|
1334
1381
|
class WindowsInventoryProfile(SshTargetHostProfile[WindowsInventoryConfig]):
|
1335
1382
|
"""Host profile for a Windows inventory."""
|
1336
1383
|
|
1384
|
+
@property
|
1385
|
+
def name(self) -> str:
|
1386
|
+
"""The name of the host profile."""
|
1387
|
+
return self.config.path
|
1388
|
+
|
1337
1389
|
def get_controller_target_connections(self) -> list[SshConnection]:
|
1338
1390
|
"""Return SSH connection(s) for accessing the host as a target from the controller."""
|
1339
1391
|
inventory = parse_inventory(self.args, self.config.path)
|
@@ -1436,9 +1488,9 @@ def get_config_profile_type_map() -> dict[t.Type[HostConfig], t.Type[HostProfile
|
|
1436
1488
|
def create_host_profile(
|
1437
1489
|
args: EnvironmentConfig,
|
1438
1490
|
config: HostConfig,
|
1439
|
-
controller:
|
1491
|
+
controller: ControllerHostProfile | None,
|
1440
1492
|
) -> HostProfile:
|
1441
1493
|
"""Create and return a host profile from the given host configuration."""
|
1442
1494
|
profile_type = get_config_profile_type_map()[type(config)]
|
1443
|
-
profile = profile_type(args=args, config=config,
|
1495
|
+
profile = profile_type(args=args, config=config, controller=controller)
|
1444
1496
|
return profile
|
@@ -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
|
|
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/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
|
@@ -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
|