• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

python-useful-helpers / exec-helpers / 14900591534

08 May 2025 06:58AM CUT coverage: 54.224%. Remained the same
14900591534

push

github

web-flow
Bump actions/download-artifact from 4.1.8 to 4.3.0 (#159)

Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.8 to 4.3.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4.1.8...v4.3.0)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 4.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

98 of 201 branches covered (48.76%)

Branch coverage included in aggregate %.

1083 of 1977 relevant lines covered (54.78%)

5.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/exec_helpers/async_api/subprocess.py
1
#    Copyright 2018 - 2023 Aleksei Stepanov aka penguinolog.
2

3
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
4
#    not use this file except in compliance with the License. You may obtain
5
#    a copy of the License at
6

7
#         http://www.apache.org/licenses/LICENSE-2.0
8

9
#    Unless required by applicable law or agreed to in writing, software
10
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
#    License for the specific language governing permissions and limitations
13
#    under the License.
14

15
"""Python asyncio.create_subprocess_shell wrapper.
16

17
.. versionadded:: 3.0.0
18
"""
19

20
from __future__ import annotations
×
21

22
import asyncio
×
23
import contextlib
×
24
import copy
×
25
import datetime
×
26
import errno
×
27
import logging
×
28
import os
×
29
import typing
×
30
import warnings
×
31

32
from exec_helpers import constants
×
33
from exec_helpers import exceptions
×
34
from exec_helpers import proc_enums
×
35
from exec_helpers import subprocess
×
36
from exec_helpers.async_api import api
×
37
from exec_helpers.async_api import exec_result
×
38

39
from .. import _log_templates
×
40
from .. import _subprocess_helpers
×
41

42
if typing.TYPE_CHECKING:
43
    import types
44
    from collections.abc import AsyncIterable
45

46
    from exec_helpers.api import CalledProcessErrorSubClassT
47
    from exec_helpers.api import CommandT
48
    from exec_helpers.api import ErrorInfoT
49
    from exec_helpers.api import ExpectedExitCodesT
50
    from exec_helpers.api import LogMaskReT
51
    from exec_helpers.api import OptionalTimeoutT
52
    from exec_helpers.exec_result import OptionalStdinT
53
    from exec_helpers.subprocess import CwdT
54
    from exec_helpers.subprocess import EnvT
55

56
__all__ = ("Subprocess", "SubprocessExecuteAsyncResult")
×
57

58

59
# noinspection PyTypeHints,PyTypeChecker
60
class SubprocessExecuteAsyncResult(subprocess.SubprocessExecuteAsyncResult):
×
61
    """Override original NamedTuple with proper typing."""
62

63
    __slots__ = ()
×
64

65
    @property
×
66
    def interface(self) -> asyncio.subprocess.Process:  # type: ignore[override]
×
67
        """Override original NamedTuple with proper typing.
68

69
        :return: Control interface.
70
        :rtype: asyncio.subprocess.Process
71
        """
72
        return super().interface  # type: ignore[return-value]
×
73

74
    # pylint: enable=no-member
75

76
    @property
×
77
    def stdin(self) -> asyncio.StreamWriter | None:  # type: ignore[override]
×
78
        """Override original NamedTuple with proper typing.
79

80
        :return: STDIN interface.
81
        :rtype: asyncio.StreamWriter | None
82
        """
83
        warnings.warn(
×
84
            "stdin access deprecated: FIFO is often closed on execution and direct access is not expected.",
85
            DeprecationWarning,
86
            stacklevel=2,
87
        )
88
        return super().stdin  # type: ignore[return-value]
×
89

90
    @property
×
91
    def stderr(self) -> AsyncIterable[bytes] | None:  # type: ignore[override]
×
92
        """Override original NamedTuple with proper typing.
93

94
        :return: STDERR interface.
95
        :rtype: AsyncIterable[bytes] | None
96
        """
97
        return super().stderr  # type: ignore[return-value]
×
98

99
    @property
×
100
    def stdout(self) -> AsyncIterable[bytes] | None:  # type: ignore[override]
×
101
        """Override original NamedTuple with proper typing.
102

103
        :return: STDOUT interface.
104
        :rtype: AsyncIterable[bytes] | None
105
        """
106
        return super().stdout  # type: ignore[return-value]
×
107

108

109
class _SubprocessExecuteContext(
×
110
    api.ExecuteContext, contextlib.AbstractAsyncContextManager[SubprocessExecuteAsyncResult]
111
):
112
    """Subprocess Execute context."""
113

114
    __slots__ = ("__cwd", "__env", "__process")
×
115

116
    def __init__(
×
117
        self,
118
        *,
119
        command: str,
120
        stdin: bytes | None = None,
121
        open_stdout: bool = True,
122
        open_stderr: bool = True,
123
        cwd: CwdT = None,
124
        env: EnvT = None,
125
        logger: logging.Logger,
126
        **kwargs: typing.Any,
127
    ) -> None:
128
        """Subprocess Execute context.
129

130
        :param command: Command for execution (fully formatted).
131
        :type command: str
132
        :param stdin: Pass STDIN text to the process (fully formatted).
133
        :type stdin: bytes
134
        :param open_stdout: Open STDOUT stream for read.
135
        :type open_stdout: bool
136
        :param open_stderr: Open STDERR stream for read.
137
        :type open_stderr: bool
138
        :param cwd: Sets the current directory before the child is executed.
139
        :type cwd: str | bytes | pathlib.Path | None
140
        :param env: Defines the environment variables for the new process.
141
        :type env: Mapping[str | bytes, str | bytes] | None
142
        :param logger: Logger instance.
143
        :type logger: logging.Logger
144
        :param kwargs: Additional parameters for call.
145
        :type kwargs: typing.Any
146
        """
147
        super().__init__(
×
148
            command=command,
149
            stdin=stdin,
150
            open_stdout=open_stdout,
151
            open_stderr=open_stderr,
152
            logger=logger,
153
            **kwargs,
154
        )
155
        self.__cwd = cwd
×
156
        self.__env = env
×
157
        self.__process: asyncio.subprocess.Process | None = None
×
158

159
    def __repr__(self) -> str:
160
        """Debug string.
161

162
        :return: Reproduce for debug.
163
        :rtype: str
164
        """
165
        return (
166
            f"<Subprocess().open_execute_context("
167
            f"command={self.command!r}, "
168
            f"stdin={self.stdin!r}, "
169
            f"open_stdout={self.open_stdout!r}, "
170
            f"open_stderr={self.open_stderr!r}, "
171
            f"cwd={self.__cwd!r}, "
172
            f"env={self.__env!r}, "
173
            f"logger={self.logger!r}) "
174
            f"at {id(self)}>"
175
        )
176

177
    async def __aenter__(self) -> SubprocessExecuteAsyncResult:
×
178
        """Context manager enter.
179

180
        :return: Raw execution information.
181
        :rtype: SshExecuteAsyncResult
182
        :raises OSError: STDIN write failed/STDIN close failed.
183

184
        The Command is executed only in the context manager to be sure that everything will be cleaned up properly.
185
        """
186
        started = datetime.datetime.now(tz=datetime.timezone.utc)
×
187

188
        self.__process = await asyncio.create_subprocess_shell(
×
189
            cmd=self.command,
190
            stdout=asyncio.subprocess.PIPE if self.open_stdout else asyncio.subprocess.DEVNULL,
191
            stderr=asyncio.subprocess.PIPE if self.open_stderr else asyncio.subprocess.DEVNULL,
192
            stdin=asyncio.subprocess.PIPE,
193
            cwd=self.__cwd,
194
            env=self.__env,
195
            universal_newlines=False,
196
            **_subprocess_helpers.subprocess_kw,
197
        )
198
        process = self.__process
×
199

200
        if self.stdin is not None:
×
201
            if process.stdin is None:
×
202
                self.logger.warning("STDIN pipe is not set, but STDIN data is available to send.")
×
203
            else:
204
                try:
×
205
                    process.stdin.write(self.stdin)
×
206
                    await process.stdin.drain()
×
207
                except BrokenPipeError:
×
208
                    self.logger.warning("STDIN Send failed: broken PIPE")
×
209
                except ConnectionResetError:
×
210
                    self.logger.warning("STDIN Send failed: closed PIPE")
×
211
                try:
×
212
                    process.stdin.close()
×
213
                except BrokenPipeError:
×
214
                    self.logger.warning("STDIN Send failed: broken PIPE")
×
215
                except OSError as exc:
×
216
                    if exc.errno != errno.EINVAL:
×
217
                        _subprocess_helpers.kill_proc_tree(process.pid)
×
218
                        process.kill()
×
219
                        raise
×
220

221
        # noinspection PyArgumentList
222
        return SubprocessExecuteAsyncResult(
×
223
            interface=process,
224
            stdin=None,
225
            stderr=process.stderr,
226
            stdout=process.stdout,
227
            started=started,
228
        )
229

230
    async def __aexit__(
×
231
        self,
232
        exc_type: type[BaseException] | None,
233
        exc_val: BaseException | None,
234
        exc_tb: types.TracebackType | None,
235
    ) -> None:
236
        process = self.__process
×
237
        if process is not None:  # pylint: disable=consider-using-assignment-expr
×
238
            _subprocess_helpers.kill_proc_tree(process.pid)
×
239
            with contextlib.suppress(ProcessLookupError):
×
240
                process.kill()
×
241
            self.__process = None
×
242

243

244
class Subprocess(api.ExecHelper):
×
245
    """Subprocess helper with timeouts and lock-free FIFO.
246

247
    :param log_mask_re: Regex lookup rule to mask command for logger.
248
                        All MATCHED groups will be replaced by '<*masked*>'.
249
    :type log_mask_re: str | re.Pattern[str] | None
250
    :param logger: Logger instance to use.
251
    :type logger: logging.Logger
252

253
    .. versionchanged:: 3.1.0 Not singleton anymore. Only lock is shared between all instances.
254
    .. versionchanged:: 3.2.0 Logger can be enforced.
255
    .. versionchanged:: 4.1.0 Support chroot
256
    .. versionchanged:: 4.3.0 Lock is not shared anymore: allow parallel call of different instances.
257
    """
258

259
    __slots__ = ()
×
260

261
    def __init__(
×
262
        self,
263
        log_mask_re: LogMaskReT = None,
264
        *,
265
        logger: logging.Logger = logging.getLogger(__name__),  # noqa: B008
266
    ) -> None:
267
        """Subprocess helper with timeouts and lock-free FIFO."""
268
        super().__init__(logger=logger, log_mask_re=log_mask_re)
×
269

270
    async def _exec_command(  # type: ignore[override]
×
271
        self,
272
        command: str,
273
        async_result: SubprocessExecuteAsyncResult,
274
        timeout: OptionalTimeoutT,
275
        *,
276
        verbose: bool = False,
277
        log_mask_re: LogMaskReT = None,
278
        stdin: OptionalStdinT = None,
279
        log_stdout: bool = True,
280
        log_stderr: bool = True,
281
        **kwargs: typing.Any,
282
    ) -> exec_result.ExecResult:
283
        """Get exit status from a channel with timeout.
284

285
        :param command: Command for execution.
286
        :type command: str
287
        :param async_result: execute_async result.
288
        :type async_result: SubprocessExecuteAsyncResult
289
        :param timeout: Timeout for command execution.
290
        :type timeout: int | float | None
291
        :param verbose: Produce verbose log record on command call.
292
        :type verbose: bool
293
        :param log_mask_re: Regex lookup rule to mask command for logger.
294
                            All MATCHED groups will be replaced by '<*masked*>'.
295
        :type log_mask_re: str | re.Pattern[str] | None
296
        :param stdin: pass STDIN text to the process.
297
        :type stdin: bytes | str | bytearray | None
298
        :param log_stdout: Log STDOUT during read.
299
        :type log_stdout: bool
300
        :param log_stderr: Log STDERR during read.
301
        :type log_stderr: bool
302
        :param kwargs: Additional parameters for call.
303
        :type kwargs: typing.Any
304
        :return: Execution result.
305
        :rtype: ExecResult
306
        :raises OSError: exception during process kill (and not regarding already closed process).
307
        :raises ExecHelperNoKillError: Process not dies on SIGTERM & SIGKILL.
308
        :raises ExecHelperTimeoutError: Timeout exceeded.
309
        """
310

311
        async def poll_stdout() -> None:
×
312
            """Sync stdout poll."""
313
            await result.read_stdout(src=async_result.stdout, log=self.logger if log_stdout else None, verbose=verbose)
×
314

315
        async def poll_stderr() -> None:
×
316
            """Sync stderr poll."""
317
            await result.read_stderr(src=async_result.stderr, log=self.logger if log_stderr else None, verbose=verbose)
×
318

319
        # Store command with hidden data
320
        cmd_for_log: str = self._mask_command(cmd=command, log_mask_re=log_mask_re)
×
321

322
        result = exec_result.ExecResult(cmd=cmd_for_log, stdin=stdin, started=async_result.started)
×
323

324
        stdout_task: asyncio.Task[None] = asyncio.create_task(poll_stdout())
×
325
        stderr_task: asyncio.Task[None] = asyncio.create_task(poll_stderr())
×
326

327
        try:
×
328
            # Wait real timeout here
329
            exit_code: int = await asyncio.wait_for(async_result.interface.wait(), timeout=timeout)
×
330
            result.exit_code = exit_code
×
331
        except asyncio.TimeoutError as exc:
×
332
            # kill -9 for all subprocesses
333
            _subprocess_helpers.kill_proc_tree(async_result.interface.pid)
×
334
            exit_signal: int | None = await asyncio.wait_for(async_result.interface.wait(), timeout=0.001)
×
335
            if exit_signal is None:  # pylint: disable=consider-using-assignment-expr
×
336
                raise exceptions.ExecHelperNoKillError(
×
337
                    result=result,
338
                    timeout=timeout,  # type: ignore[arg-type]
339
                ) from exc
340
            result.exit_code = exit_signal
×
341
        else:
342
            return result
×
343
        finally:
344
            stdout_task.cancel()
×
345
            stderr_task.cancel()
×
346
            result.set_timestamp()
×
347

348
        wait_err_msg: str = _log_templates.CMD_WAIT_ERROR.format(result=result, timeout=timeout)
×
349
        self.logger.debug(wait_err_msg)
×
350
        raise exceptions.ExecHelperTimeoutError(result=result, timeout=timeout)  # type: ignore[arg-type]
×
351

352
    def open_execute_context(
×
353
        self,
354
        command: str,
355
        *,
356
        stdin: OptionalStdinT = None,
357
        open_stdout: bool = True,
358
        open_stderr: bool = True,
359
        chroot_path: str | None = None,
360
        chroot_exe: str | None = None,
361
        cwd: CwdT = None,
362
        env: EnvT = None,
363
        env_patch: EnvT = None,
364
        **kwargs: typing.Any,
365
    ) -> _SubprocessExecuteContext:
366
        """Get execution context manager.
367

368
        :param command: Command for execution.
369
        :type command: str | Iterable[str]
370
        :param stdin: Pass STDIN text to the process.
371
        :type stdin: bytes | str | bytearray | None
372
        :param open_stdout: Open STDOUT stream for read.
373
        :type open_stdout: bool
374
        :param open_stderr: Open STDERR stream for read.
375
        :type open_stderr: bool
376
        :param chroot_path: chroot path override.
377
        :type chroot_path: str | None
378
        :param chroot_exe: chroot exe override.
379
        :type chroot_exe: str | None
380
        :param cwd: Sets the current directory before the child is executed.
381
        :type cwd: str | bytes | pathlib.Path | None
382
        :param env: Defines the environment variables for the new process.
383
        :type env: Mapping[str | bytes, str | bytes] | None
384
        :param env_patch: Defines the environment variables to ADD for the new process.
385
        :type env_patch: Mapping[str | bytes, str | bytes] | None
386
        :param kwargs: Additional parameters for call.
387
        :type kwargs: typing.Any
388
        :return: Execute context.
389
        :rtype: _SubprocessExecuteContext
390
        .. versionadded:: 8.0.0
391
        """
392
        if env_patch is not None:
×
393
            # make mutable copy
394
            env = dict(copy.deepcopy(os.environ) if env is None else copy.deepcopy(env))  # type: ignore[arg-type]
×
395
            env.update(env_patch)
×
396
        return _SubprocessExecuteContext(
×
397
            command=f"{self._prepare_command(cmd=command, chroot_path=chroot_path, chroot_exe=chroot_exe)}\n",
398
            stdin=None if stdin is None else self._string_bytes_bytearray_as_bytes(stdin),
399
            open_stdout=open_stdout,
400
            open_stderr=open_stderr,
401
            cwd=cwd,
402
            env=env,
403
            logger=self.logger,
404
            **kwargs,
405
        )
406

407
    async def execute(
×
408
        self,
409
        command: CommandT,
410
        verbose: bool = False,
411
        timeout: OptionalTimeoutT = constants.DEFAULT_TIMEOUT,
412
        *,
413
        log_mask_re: LogMaskReT = None,
414
        stdin: OptionalStdinT = None,
415
        open_stdout: bool = True,
416
        log_stdout: bool = True,
417
        open_stderr: bool = True,
418
        log_stderr: bool = True,
419
        chroot_path: str | None = None,
420
        chroot_exe: str | None = None,
421
        cwd: CwdT = None,
422
        env: EnvT = None,
423
        env_patch: EnvT = None,
424
        **kwargs: typing.Any,
425
    ) -> exec_result.ExecResult:
426
        """Execute command and wait for return code.
427

428
        :param command: Command for execution.
429
        :type command: str | Iterable[str]
430
        :param verbose: Produce log.info records for command call and output.
431
        :type verbose: bool
432
        :param timeout: Timeout for command execution.
433
        :type timeout: int | float | None
434
        :param log_mask_re: Regex lookup rule to mask command for logger.
435
                            All MATCHED groups will be replaced by '<*masked*>'.
436
        :type log_mask_re: str | re.Pattern[str] | None
437
        :param stdin: Pass STDIN text to the process.
438
        :type stdin: bytes | str | bytearray | None
439
        :param open_stdout: Open STDOUT stream for read.
440
        :type open_stdout: bool
441
        :param log_stdout: Log STDOUT during read.
442
        :type log_stdout: bool
443
        :param open_stderr: Open STDERR stream for read.
444
        :type open_stderr: bool
445
        :param log_stderr: Log STDERR during read.
446
        :type log_stderr: bool
447
        :param chroot_path: chroot path override.
448
        :type chroot_path: str | None
449
        :param chroot_exe: chroot exe override.
450
        :type chroot_exe: str | None
451
        :param cwd: Sets the current directory before the child is executed.
452
        :type cwd: str | bytes | pathlib.Path | None
453
        :param env: Defines the environment variables for the new process.
454
        :type env: Mapping[str | bytes, str | bytes] | None
455
        :param env_patch: Defines the environment variables to ADD for the new process.
456
        :type env_patch: Mapping[str | bytes, str | bytes] | None
457
        :param kwargs: Additional parameters for call.
458
        :type kwargs: typing.Any
459
        :return: Execution result.
460
        :rtype: ExecResult
461
        :raises ExecHelperTimeoutError: Timeout exceeded.
462

463
        .. versionchanged:: 1.2.0 Default timeout 1 hour.
464
        .. versionchanged:: 2.1.0 Allow parallel calls.
465
        .. versionchanged:: 7.0.0 Allow command as list of arguments. Command will be joined with components escaping.
466
        .. versionchanged:: 8.0.0 chroot path exposed.
467
        """
468
        return await super().execute(
×
469
            command=command,
470
            verbose=verbose,
471
            timeout=timeout,
472
            log_mask_re=log_mask_re,
473
            stdin=stdin,
474
            open_stdout=open_stdout,
475
            log_stdout=log_stdout,
476
            open_stderr=open_stderr,
477
            log_stderr=log_stderr,
478
            chroot_path=chroot_path,
479
            chroot_exe=chroot_exe,
480
            cwd=cwd,
481
            env=env,
482
            env_patch=env_patch,
483
            **kwargs,
484
        )
485

486
    async def __call__(
×
487
        self,
488
        command: CommandT,
489
        verbose: bool = False,
490
        timeout: OptionalTimeoutT = constants.DEFAULT_TIMEOUT,
491
        *,
492
        log_mask_re: LogMaskReT = None,
493
        stdin: OptionalStdinT = None,
494
        open_stdout: bool = True,
495
        log_stdout: bool = True,
496
        open_stderr: bool = True,
497
        log_stderr: bool = True,
498
        chroot_path: str | None = None,
499
        chroot_exe: str | None = None,
500
        cwd: CwdT = None,
501
        env: EnvT = None,
502
        env_patch: EnvT = None,
503
        **kwargs: typing.Any,
504
    ) -> exec_result.ExecResult:
505
        """Execute command and wait for return code.
506

507
        :param command: Command for execution.
508
        :type command: str | Iterable[str]
509
        :param verbose: Produce log.info records for command call and output.
510
        :type verbose: bool
511
        :param timeout: Timeout for command execution.
512
        :type timeout: int | float | None
513
        :param log_mask_re: Regex lookup rule to mask command for logger.
514
                            All MATCHED groups will be replaced by '<*masked*>'.
515
        :type log_mask_re: str | re.Pattern[str] | None
516
        :param stdin: pass STDIN text to the process.
517
        :type stdin: bytes | str | bytearray | None
518
        :param open_stdout: Open STDOUT stream for read.
519
        :type open_stdout: bool
520
        :param log_stdout: Log STDOUT during read.
521
        :type log_stdout: bool
522
        :param open_stderr: Open STDERR stream for read.
523
        :type open_stderr: bool
524
        :param log_stderr: Log STDERR during read.
525
        :type log_stderr: bool
526
        :param chroot_path: chroot path override.
527
        :type chroot_path: str | None
528
        :param chroot_exe: chroot exe override.
529
        :type chroot_exe: str | None
530
        :param cwd: Sets the current directory before the child is executed.
531
        :type cwd: str | bytes | pathlib.Path | None
532
        :param env: Defines the environment variables for the new process.
533
        :type env: Mapping[str | bytes, str | bytes] | None
534
        :param env_patch: Defines the environment variables to ADD for the new process.
535
        :type env_patch: Mapping[str | bytes, str | bytes] | None
536
        :param kwargs: Additional parameters for call.
537
        :type kwargs: typing.Any
538
        :return: Execution result.
539
        :rtype: ExecResult
540
        :raises ExecHelperTimeoutError: Timeout exceeded.
541

542
        .. versionchanged:: 1.2.0 Default timeout 1 hour.
543
        .. versionchanged:: 2.1.0 Allow parallel calls.
544
        """
545
        return await super().__call__(
×
546
            command=command,
547
            verbose=verbose,
548
            timeout=timeout,
549
            log_mask_re=log_mask_re,
550
            stdin=stdin,
551
            open_stdout=open_stdout,
552
            log_stdout=log_stdout,
553
            open_stderr=open_stderr,
554
            log_stderr=log_stderr,
555
            chroot_path=chroot_path,
556
            chroot_exe=chroot_exe,
557
            cwd=cwd,
558
            env=env,
559
            env_patch=env_patch,
560
            **kwargs,
561
        )
562

563
    async def check_call(
×
564
        self,
565
        command: CommandT,
566
        verbose: bool = False,
567
        timeout: OptionalTimeoutT = constants.DEFAULT_TIMEOUT,
568
        error_info: ErrorInfoT = None,
569
        expected: ExpectedExitCodesT = (proc_enums.EXPECTED,),
570
        raise_on_err: bool = True,
571
        *,
572
        log_mask_re: LogMaskReT = None,
573
        stdin: OptionalStdinT = None,
574
        open_stdout: bool = True,
575
        log_stdout: bool = True,
576
        open_stderr: bool = True,
577
        log_stderr: bool = True,
578
        cwd: CwdT = None,
579
        env: EnvT = None,
580
        env_patch: EnvT = None,
581
        exception_class: CalledProcessErrorSubClassT = exceptions.CalledProcessError,
582
        **kwargs: typing.Any,
583
    ) -> exec_result.ExecResult:
584
        """Execute command and check for return code.
585

586
        :param command: Command for execution.
587
        :type command: str | Iterable[str]
588
        :param verbose: Produce log.info records for command call and output.
589
        :type verbose: bool
590
        :param timeout: Timeout for command execution.
591
        :type timeout: int | float | None
592
        :param error_info: Text for error details, if fail happens.
593
        :type error_info: str | None
594
        :param expected: Expected return codes (0 by default).
595
        :type expected: Iterable[int | proc_enums.ExitCodes]
596
        :param raise_on_err: Raise exception on unexpected return code.
597
        :type raise_on_err: bool
598
        :param log_mask_re: Regex lookup rule to mask command for logger.
599
                            All MATCHED groups will be replaced by '<*masked*>'.
600
        :type log_mask_re: str | re.Pattern[str] | None
601
        :param stdin: Pass STDIN text to the process.
602
        :type stdin: bytes | str | bytearray | None
603
        :param open_stdout: Open STDOUT stream for read.
604
        :type open_stdout: bool
605
        :param log_stdout: Log STDOUT during read.
606
        :type log_stdout: bool
607
        :param open_stderr: Open STDERR stream for read.
608
        :type open_stderr: bool
609
        :param log_stderr: Log STDERR during read.
610
        :type log_stderr: bool
611
        :param cwd: Sets the current directory before the child is executed.
612
        :type cwd: str | bytes | pathlib.Path | None
613
        :param env: Defines the environment variables for the new process.
614
        :type env: Mapping[str | bytes, str | bytes] | None
615
        :param env_patch: Defines the environment variables to ADD for the new process.
616
        :type env_patch: Mapping[str | bytes, str | bytes] | None
617
        :param exception_class: Exception class for errors. Subclass of CalledProcessError is mandatory.
618
        :type exception_class: type[exceptions.CalledProcessError]
619
        :param kwargs: Additional parameters for call.
620
        :type kwargs: typing.Any
621
        :return: Execution result.
622
        :rtype: ExecResult
623
        :raises ExecHelperTimeoutError: Timeout exceeded.
624
        :raises CalledProcessError: Unexpected exit code.
625

626
        .. versionchanged:: 1.2.0 Default timeout 1 hour.
627
        .. versionchanged:: 3.2.0 Exception class can be substituted.
628
        .. versionchanged:: 3.4.0 Expected is not optional, defaults os dependent.
629
        """
630
        return await super().check_call(
×
631
            command=command,
632
            verbose=verbose,
633
            timeout=timeout,
634
            error_info=error_info,
635
            expected=expected,
636
            raise_on_err=raise_on_err,
637
            log_mask_re=log_mask_re,
638
            stdin=stdin,
639
            open_stdout=open_stdout,
640
            log_stdout=log_stdout,
641
            open_stderr=open_stderr,
642
            log_stderr=log_stderr,
643
            cwd=cwd,
644
            env=env,
645
            env_patch=env_patch,
646
            exception_class=exception_class,
647
            **kwargs,
648
        )
649

650
    async def check_stderr(
×
651
        self,
652
        command: CommandT,
653
        verbose: bool = False,
654
        timeout: OptionalTimeoutT = constants.DEFAULT_TIMEOUT,
655
        error_info: ErrorInfoT = None,
656
        raise_on_err: bool = True,
657
        *,
658
        expected: ExpectedExitCodesT = (proc_enums.EXPECTED,),
659
        log_mask_re: LogMaskReT = None,
660
        stdin: OptionalStdinT = None,
661
        open_stdout: bool = True,
662
        log_stdout: bool = True,
663
        open_stderr: bool = True,
664
        log_stderr: bool = True,
665
        cwd: CwdT = None,
666
        env: EnvT = None,
667
        env_patch: EnvT = None,
668
        exception_class: CalledProcessErrorSubClassT = exceptions.CalledProcessError,
669
        **kwargs: typing.Any,
670
    ) -> exec_result.ExecResult:
671
        """Execute command expecting return code 0 and empty STDERR.
672

673
        :param command: Command for execution.
674
        :type command: str | Iterable[str]
675
        :param verbose: Produce log.info records for command call and output.
676
        :type verbose: bool
677
        :param timeout: Timeout for command execution.
678
        :type timeout: int | float | None
679
        :param error_info: Text for error details, if fail happens.
680
        :type error_info: str | None
681
        :param raise_on_err: Raise exception on unexpected return code.
682
        :type raise_on_err: bool
683
        :param expected: Expected return codes (0 by default).
684
        :type expected: Iterable[int | proc_enums.ExitCodes]
685
        :param log_mask_re: Regex lookup rule to mask command for logger.
686
                            All MATCHED groups will be replaced by '<*masked*>'.
687
        :type log_mask_re: str | re.Pattern[str] | None
688
        :param stdin: Pass STDIN text to the process.
689
        :type stdin: bytes | str | bytearray | None
690
        :param open_stdout: Open STDOUT stream for read.
691
        :type open_stdout: bool
692
        :param log_stdout: Log STDOUT during read.
693
        :type log_stdout: bool
694
        :param open_stderr: Open STDERR stream for read.
695
        :type open_stderr: bool
696
        :param log_stderr: Log STDERR during read.
697
        :type log_stderr: bool
698
        :param cwd: Sets the current directory before the child is executed.
699
        :type cwd: str | bytes | pathlib.Path | None
700
        :param env: Defines the environment variables for the new process.
701
        :type env: Mapping[str | bytes, str | bytes] | None
702
        :param env_patch: Defines the environment variables to ADD for the new process.
703
        :type env_patch: Mapping[str | bytes, str | bytes] | None
704
        :param exception_class: Exception class for errors. Subclass of CalledProcessError is mandatory.
705
        :type exception_class: type[exceptions.CalledProcessError]
706
        :param kwargs: Additional parameters for call.
707
        :type kwargs: typing.Any
708
        :return: Execution result.
709
        :rtype: ExecResult
710
        :raises ExecHelperTimeoutError: Timeout exceeded.
711
        :raises CalledProcessError: Unexpected exit code or stderr presents.
712

713
        .. versionchanged:: 1.2.0 Default timeout 1 hour.
714
        .. versionchanged:: 3.2.0 Exception class can be substituted.
715
        .. versionchanged:: 3.4.0 Expected is not optional, defaults os dependent.
716
        """
717
        return await super().check_stderr(
×
718
            command=command,
719
            verbose=verbose,
720
            timeout=timeout,
721
            error_info=error_info,
722
            raise_on_err=raise_on_err,
723
            expected=expected,
724
            log_mask_re=log_mask_re,
725
            stdin=stdin,
726
            open_stdout=open_stdout,
727
            log_stdout=log_stdout,
728
            open_stderr=open_stderr,
729
            log_stderr=log_stderr,
730
            cwd=cwd,
731
            env=env,
732
            env_patch=env_patch,
733
            exception_class=exception_class,
734
            **kwargs,
735
        )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc