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