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