|
7 | 7 | import warnings
|
8 | 8 | from contextlib import ExitStack
|
9 | 9 | from functools import partial
|
10 |
| -from typing import TYPE_CHECKING, Final, Literal, Protocol, Union, overload |
| 10 | +from typing import ( |
| 11 | + TYPE_CHECKING, |
| 12 | + Final, |
| 13 | + Literal, |
| 14 | + Protocol, |
| 15 | + TypedDict, |
| 16 | + Union, |
| 17 | + overload, |
| 18 | +) |
11 | 19 |
|
12 | 20 | import trio
|
13 | 21 |
|
|
23 | 31 |
|
24 | 32 | if TYPE_CHECKING:
|
25 | 33 | import signal
|
26 |
| - from collections.abc import Awaitable, Callable, Mapping, Sequence |
| 34 | + from collections.abc import Awaitable, Callable, Iterable, Mapping, Sequence |
27 | 35 | from io import TextIOWrapper
|
28 | 36 |
|
29 |
| - from typing_extensions import TypeAlias |
| 37 | + from typing_extensions import TypeAlias, Unpack |
30 | 38 |
|
31 | 39 | from ._abc import ReceiveStream, SendStream
|
32 | 40 |
|
@@ -779,29 +787,46 @@ async def killer() -> None:
|
779 | 787 | # There's a lot of duplication here because type checkers don't
|
780 | 788 | # have a good way to represent overloads that differ only
|
781 | 789 | # slightly. A cheat sheet:
|
| 790 | +# |
782 | 791 | # - on Windows, command is Union[str, Sequence[str]];
|
783 | 792 | # on Unix, command is str if shell=True and Sequence[str] otherwise
|
| 793 | +# |
784 | 794 | # - on Windows, there are startupinfo and creationflags options;
|
785 |
| -# on Unix, there are preexec_fn, restore_signals, start_new_session, and pass_fds |
| 795 | +# on Unix, there are preexec_fn, restore_signals, start_new_session, |
| 796 | +# pass_fds, group (3.9+), extra_groups (3.9+), user (3.9+), |
| 797 | +# umask (3.9+), pipesize (3.10+), process_group (3.11+) |
| 798 | +# |
786 | 799 | # - run_process() has the signature of open_process() plus arguments
|
787 |
| -# capture_stdout, capture_stderr, check, deliver_cancel, and the ability to pass |
788 |
| -# bytes as stdin |
| 800 | +# capture_stdout, capture_stderr, check, deliver_cancel, the ability |
| 801 | +# to pass bytes as stdin, and the ability to run in `nursery.start` |
| 802 | + |
| 803 | + |
| 804 | +class GeneralProcessArgs(TypedDict, total=False): |
| 805 | + """Arguments shared between all runs.""" |
| 806 | + |
| 807 | + stdout: int | HasFileno | None |
| 808 | + stderr: int | HasFileno | None |
| 809 | + close_fds: bool |
| 810 | + cwd: StrOrBytesPath | None |
| 811 | + env: Mapping[str, str] | None |
| 812 | + executable: StrOrBytesPath | None |
| 813 | + |
789 | 814 |
|
790 | 815 | if TYPE_CHECKING:
|
791 | 816 | if sys.platform == "win32":
|
792 | 817 |
|
| 818 | + class WindowsProcessArgs(GeneralProcessArgs, total=False): |
| 819 | + """Arguments shared between all Windows runs.""" |
| 820 | + |
| 821 | + shell: bool |
| 822 | + startupinfo: subprocess.STARTUPINFO | None |
| 823 | + creationflags: int |
| 824 | + |
793 | 825 | async def open_process(
|
794 | 826 | command: StrOrBytesPath | Sequence[StrOrBytesPath],
|
795 | 827 | *,
|
796 | 828 | stdin: int | HasFileno | None = None,
|
797 |
| - stdout: int | HasFileno | None = None, |
798 |
| - stderr: int | HasFileno | None = None, |
799 |
| - close_fds: bool = True, |
800 |
| - shell: bool = False, |
801 |
| - cwd: StrOrBytesPath | None = None, |
802 |
| - env: Mapping[str, str] | None = None, |
803 |
| - startupinfo: subprocess.STARTUPINFO | None = None, |
804 |
| - creationflags: int = 0, |
| 829 | + **kwargs: Unpack[WindowsProcessArgs], |
805 | 830 | ) -> trio.Process:
|
806 | 831 | r"""Execute a child program in a new process.
|
807 | 832 |
|
@@ -864,14 +889,7 @@ async def run_process(
|
864 | 889 | capture_stderr: bool = False,
|
865 | 890 | check: bool = True,
|
866 | 891 | deliver_cancel: Callable[[Process], Awaitable[object]] | None = None,
|
867 |
| - stdout: int | HasFileno | None = None, |
868 |
| - stderr: int | HasFileno | None = None, |
869 |
| - close_fds: bool = True, |
870 |
| - shell: bool = False, |
871 |
| - cwd: StrOrBytesPath | None = None, |
872 |
| - env: Mapping[str, str] | None = None, |
873 |
| - startupinfo: subprocess.STARTUPINFO | None = None, |
874 |
| - creationflags: int = 0, |
| 892 | + **kwargs: Unpack[WindowsProcessArgs], |
875 | 893 | ) -> subprocess.CompletedProcess[bytes]:
|
876 | 894 | """Run ``command`` in a subprocess and wait for it to complete.
|
877 | 895 |
|
@@ -1067,85 +1085,97 @@ async def my_deliver_cancel(process):
|
1067 | 1085 | ...
|
1068 | 1086 |
|
1069 | 1087 | else: # Unix
|
1070 |
| - # pyright doesn't give any error about these missing docstrings as they're |
| 1088 | + # pyright doesn't give any error about overloads missing docstrings as they're |
1071 | 1089 | # overloads. But might still be a problem for other static analyzers / docstring
|
1072 | 1090 | # readers (?)
|
| 1091 | + |
| 1092 | + class UnixProcessArgs3_9(GeneralProcessArgs, total=False): |
| 1093 | + """Arguments shared between all Unix runs.""" |
| 1094 | + |
| 1095 | + preexec_fn: Callable[[], object] | None |
| 1096 | + restore_signals: bool |
| 1097 | + start_new_session: bool |
| 1098 | + pass_fds: Sequence[int] |
| 1099 | + |
| 1100 | + # 3.9+ |
| 1101 | + group: str | int | None |
| 1102 | + extra_groups: Iterable[str | int] | None |
| 1103 | + user: str | int | None |
| 1104 | + umask: int |
| 1105 | + |
| 1106 | + class UnixProcessArgs3_10(UnixProcessArgs3_9, total=False): |
| 1107 | + """Arguments shared between all Unix runs on 3.10+.""" |
| 1108 | + |
| 1109 | + pipesize: int |
| 1110 | + |
| 1111 | + class UnixProcessArgs3_11(UnixProcessArgs3_10, total=False): |
| 1112 | + """Arguments shared between all Unix runs on 3.11+.""" |
| 1113 | + |
| 1114 | + process_group: int | None |
| 1115 | + |
| 1116 | + class UnixRunProcessMixin(TypedDict, total=False): |
| 1117 | + """Arguments unique to run_process on Unix.""" |
| 1118 | + |
| 1119 | + task_status: TaskStatus[Process] |
| 1120 | + capture_stdout: bool |
| 1121 | + capture_stderr: bool |
| 1122 | + check: bool |
| 1123 | + deliver_cancel: Callable[[Process], Awaitable[None]] | None |
| 1124 | + |
| 1125 | + # TODO: once https://github.com/python/mypy/issues/18692 is |
| 1126 | + # fixed, move the `UnixRunProcessArgs` definition down. |
| 1127 | + if sys.version_info >= (3, 11): |
| 1128 | + UnixProcessArgs = UnixProcessArgs3_11 |
| 1129 | + |
| 1130 | + class UnixRunProcessArgs(UnixProcessArgs3_11, UnixRunProcessMixin): |
| 1131 | + """Arguments for run_process on Unix with 3.11+""" |
| 1132 | + |
| 1133 | + elif sys.version_info >= (3, 10): |
| 1134 | + UnixProcessArgs = UnixProcessArgs3_10 |
| 1135 | + |
| 1136 | + class UnixRunProcessArgs(UnixProcessArgs3_10, UnixRunProcessMixin): |
| 1137 | + """Arguments for run_process on Unix with 3.10+""" |
| 1138 | + |
| 1139 | + else: |
| 1140 | + UnixProcessArgs = UnixProcessArgs3_9 |
| 1141 | + |
| 1142 | + class UnixRunProcessArgs(UnixProcessArgs3_9, UnixRunProcessMixin): |
| 1143 | + """Arguments for run_process on Unix with 3.9+""" |
| 1144 | + |
1073 | 1145 | @overload # type: ignore[no-overload-impl]
|
1074 | 1146 | async def open_process(
|
1075 | 1147 | command: StrOrBytesPath,
|
1076 | 1148 | *,
|
1077 | 1149 | stdin: int | HasFileno | None = None,
|
1078 |
| - stdout: int | HasFileno | None = None, |
1079 |
| - stderr: int | HasFileno | None = None, |
1080 |
| - close_fds: bool = True, |
1081 | 1150 | shell: Literal[True],
|
1082 |
| - cwd: StrOrBytesPath | None = None, |
1083 |
| - env: Mapping[str, str] | None = None, |
1084 |
| - preexec_fn: Callable[[], object] | None = None, |
1085 |
| - restore_signals: bool = True, |
1086 |
| - start_new_session: bool = False, |
1087 |
| - pass_fds: Sequence[int] = (), |
| 1151 | + **kwargs: Unpack[UnixProcessArgs], |
1088 | 1152 | ) -> trio.Process: ...
|
1089 | 1153 |
|
1090 | 1154 | @overload
|
1091 | 1155 | async def open_process(
|
1092 | 1156 | command: Sequence[StrOrBytesPath],
|
1093 | 1157 | *,
|
1094 | 1158 | stdin: int | HasFileno | None = None,
|
1095 |
| - stdout: int | HasFileno | None = None, |
1096 |
| - stderr: int | HasFileno | None = None, |
1097 |
| - close_fds: bool = True, |
1098 | 1159 | shell: bool = False,
|
1099 |
| - cwd: StrOrBytesPath | None = None, |
1100 |
| - env: Mapping[str, str] | None = None, |
1101 |
| - preexec_fn: Callable[[], object] | None = None, |
1102 |
| - restore_signals: bool = True, |
1103 |
| - start_new_session: bool = False, |
1104 |
| - pass_fds: Sequence[int] = (), |
| 1160 | + **kwargs: Unpack[UnixProcessArgs], |
1105 | 1161 | ) -> trio.Process: ...
|
1106 | 1162 |
|
1107 | 1163 | @overload # type: ignore[no-overload-impl]
|
1108 | 1164 | async def run_process(
|
1109 | 1165 | command: StrOrBytesPath,
|
1110 | 1166 | *,
|
1111 |
| - task_status: TaskStatus[Process] = trio.TASK_STATUS_IGNORED, |
1112 | 1167 | stdin: bytes | bytearray | memoryview | int | HasFileno | None = None,
|
1113 |
| - capture_stdout: bool = False, |
1114 |
| - capture_stderr: bool = False, |
1115 |
| - check: bool = True, |
1116 |
| - deliver_cancel: Callable[[Process], Awaitable[object]] | None = None, |
1117 |
| - stdout: int | HasFileno | None = None, |
1118 |
| - stderr: int | HasFileno | None = None, |
1119 |
| - close_fds: bool = True, |
1120 | 1168 | shell: Literal[True],
|
1121 |
| - cwd: StrOrBytesPath | None = None, |
1122 |
| - env: Mapping[str, str] | None = None, |
1123 |
| - preexec_fn: Callable[[], object] | None = None, |
1124 |
| - restore_signals: bool = True, |
1125 |
| - start_new_session: bool = False, |
1126 |
| - pass_fds: Sequence[int] = (), |
| 1169 | + **kwargs: Unpack[UnixRunProcessArgs], |
1127 | 1170 | ) -> subprocess.CompletedProcess[bytes]: ...
|
1128 | 1171 |
|
1129 | 1172 | @overload
|
1130 | 1173 | async def run_process(
|
1131 | 1174 | command: Sequence[StrOrBytesPath],
|
1132 | 1175 | *,
|
1133 |
| - task_status: TaskStatus[Process] = trio.TASK_STATUS_IGNORED, |
1134 | 1176 | stdin: bytes | bytearray | memoryview | int | HasFileno | None = None,
|
1135 |
| - capture_stdout: bool = False, |
1136 |
| - capture_stderr: bool = False, |
1137 |
| - check: bool = True, |
1138 |
| - deliver_cancel: Callable[[Process], Awaitable[None]] | None = None, |
1139 |
| - stdout: int | HasFileno | None = None, |
1140 |
| - stderr: int | HasFileno | None = None, |
1141 |
| - close_fds: bool = True, |
1142 | 1177 | shell: bool = False,
|
1143 |
| - cwd: StrOrBytesPath | None = None, |
1144 |
| - env: Mapping[str, str] | None = None, |
1145 |
| - preexec_fn: Callable[[], object] | None = None, |
1146 |
| - restore_signals: bool = True, |
1147 |
| - start_new_session: bool = False, |
1148 |
| - pass_fds: Sequence[int] = (), |
| 1178 | + **kwargs: Unpack[UnixRunProcessArgs], |
1149 | 1179 | ) -> subprocess.CompletedProcess[bytes]: ...
|
1150 | 1180 |
|
1151 | 1181 | else:
|
|
0 commit comments