|
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