Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/anyio/_core/_subprocesses.py: 28%
36 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 07:19 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 07:19 +0000
1from __future__ import annotations
3from io import BytesIO
4from os import PathLike
5from subprocess import DEVNULL, PIPE, CalledProcessError, CompletedProcess
6from typing import (
7 IO,
8 Any,
9 AsyncIterable,
10 Mapping,
11 Sequence,
12 cast,
13)
15from ..abc import Process
16from ._eventloop import get_asynclib
17from ._tasks import create_task_group
20async def run_process(
21 command: str | bytes | Sequence[str | bytes],
22 *,
23 input: bytes | None = None,
24 stdout: int | IO[Any] | None = PIPE,
25 stderr: int | IO[Any] | None = PIPE,
26 check: bool = True,
27 cwd: str | bytes | PathLike[str] | None = None,
28 env: Mapping[str, str] | None = None,
29 start_new_session: bool = False,
30) -> CompletedProcess[bytes]:
31 """
32 Run an external command in a subprocess and wait until it completes.
34 .. seealso:: :func:`subprocess.run`
36 :param command: either a string to pass to the shell, or an iterable of strings containing the
37 executable name or path and its arguments
38 :param input: bytes passed to the standard input of the subprocess
39 :param stdout: either :data:`subprocess.PIPE` or :data:`subprocess.DEVNULL`
40 :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL` or
41 :data:`subprocess.STDOUT`
42 :param check: if ``True``, raise :exc:`~subprocess.CalledProcessError` if the process
43 terminates with a return code other than 0
44 :param cwd: If not ``None``, change the working directory to this before running the command
45 :param env: if not ``None``, this mapping replaces the inherited environment variables from the
46 parent process
47 :param start_new_session: if ``true`` the setsid() system call will be made in the child
48 process prior to the execution of the subprocess. (POSIX only)
49 :return: an object representing the completed process
50 :raises ~subprocess.CalledProcessError: if ``check`` is ``True`` and the process exits with a
51 nonzero return code
53 """
55 async def drain_stream(stream: AsyncIterable[bytes], index: int) -> None:
56 buffer = BytesIO()
57 async for chunk in stream:
58 buffer.write(chunk)
60 stream_contents[index] = buffer.getvalue()
62 async with await open_process(
63 command,
64 stdin=PIPE if input else DEVNULL,
65 stdout=stdout,
66 stderr=stderr,
67 cwd=cwd,
68 env=env,
69 start_new_session=start_new_session,
70 ) as process:
71 stream_contents: list[bytes | None] = [None, None]
72 try:
73 async with create_task_group() as tg:
74 if process.stdout:
75 tg.start_soon(drain_stream, process.stdout, 0)
76 if process.stderr:
77 tg.start_soon(drain_stream, process.stderr, 1)
78 if process.stdin and input:
79 await process.stdin.send(input)
80 await process.stdin.aclose()
82 await process.wait()
83 except BaseException:
84 process.kill()
85 raise
87 output, errors = stream_contents
88 if check and process.returncode != 0:
89 raise CalledProcessError(cast(int, process.returncode), command, output, errors)
91 return CompletedProcess(command, cast(int, process.returncode), output, errors)
94async def open_process(
95 command: str | bytes | Sequence[str | bytes],
96 *,
97 stdin: int | IO[Any] | None = PIPE,
98 stdout: int | IO[Any] | None = PIPE,
99 stderr: int | IO[Any] | None = PIPE,
100 cwd: str | bytes | PathLike[str] | None = None,
101 env: Mapping[str, str] | None = None,
102 start_new_session: bool = False,
103) -> Process:
104 """
105 Start an external command in a subprocess.
107 .. seealso:: :class:`subprocess.Popen`
109 :param command: either a string to pass to the shell, or an iterable of strings containing the
110 executable name or path and its arguments
111 :param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, a
112 file-like object, or ``None``
113 :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
114 a file-like object, or ``None``
115 :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
116 :data:`subprocess.STDOUT`, a file-like object, or ``None``
117 :param cwd: If not ``None``, the working directory is changed before executing
118 :param env: If env is not ``None``, it must be a mapping that defines the environment
119 variables for the new process
120 :param start_new_session: if ``true`` the setsid() system call will be made in the child
121 process prior to the execution of the subprocess. (POSIX only)
122 :return: an asynchronous process object
124 """
125 shell = isinstance(command, str)
126 return await get_asynclib().open_process(
127 command,
128 shell=shell,
129 stdin=stdin,
130 stdout=stdout,
131 stderr=stderr,
132 cwd=cwd,
133 env=env,
134 start_new_session=start_new_session,
135 )