@ajksunkang-aios/kgraph-linux-x64 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/bin/kgraph-launcher +15 -3
  2. package/lib/kgraph/scripts/build-bundle.sh +17 -4
  3. package/lib/site-packages/outcome/__init__.py +20 -0
  4. package/lib/site-packages/outcome/_impl.py +239 -0
  5. package/lib/site-packages/outcome/_util.py +33 -0
  6. package/lib/site-packages/outcome/_version.py +7 -0
  7. package/lib/site-packages/outcome/py.typed +0 -0
  8. package/lib/site-packages/outcome-1.3.0.post0.dist-info/INSTALLER +1 -0
  9. package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE +3 -0
  10. package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.APACHE2 +202 -0
  11. package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.MIT +20 -0
  12. package/lib/site-packages/outcome-1.3.0.post0.dist-info/METADATA +63 -0
  13. package/lib/site-packages/outcome-1.3.0.post0.dist-info/RECORD +13 -0
  14. package/lib/site-packages/outcome-1.3.0.post0.dist-info/WHEEL +6 -0
  15. package/lib/site-packages/outcome-1.3.0.post0.dist-info/top_level.txt +1 -0
  16. package/lib/site-packages/sniffio/__init__.py +17 -0
  17. package/lib/site-packages/sniffio/_impl.py +95 -0
  18. package/lib/site-packages/sniffio/_tests/__init__.py +0 -0
  19. package/lib/site-packages/sniffio/_tests/test_sniffio.py +84 -0
  20. package/lib/site-packages/sniffio/_version.py +3 -0
  21. package/lib/site-packages/sniffio/py.typed +0 -0
  22. package/lib/site-packages/sniffio-1.3.1.dist-info/INSTALLER +1 -0
  23. package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE +3 -0
  24. package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 +202 -0
  25. package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT +20 -0
  26. package/lib/site-packages/sniffio-1.3.1.dist-info/METADATA +104 -0
  27. package/lib/site-packages/sniffio-1.3.1.dist-info/RECORD +14 -0
  28. package/lib/site-packages/sniffio-1.3.1.dist-info/WHEEL +5 -0
  29. package/lib/site-packages/sniffio-1.3.1.dist-info/top_level.txt +1 -0
  30. package/lib/site-packages/sortedcontainers/__init__.py +74 -0
  31. package/lib/site-packages/sortedcontainers/sorteddict.py +812 -0
  32. package/lib/site-packages/sortedcontainers/sortedlist.py +2646 -0
  33. package/lib/site-packages/sortedcontainers/sortedset.py +733 -0
  34. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/INSTALLER +1 -0
  35. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/LICENSE +13 -0
  36. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/METADATA +264 -0
  37. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/RECORD +10 -0
  38. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/WHEEL +6 -0
  39. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/top_level.txt +1 -0
  40. package/lib/site-packages/trio/__init__.py +133 -0
  41. package/lib/site-packages/trio/__main__.py +3 -0
  42. package/lib/site-packages/trio/_abc.py +714 -0
  43. package/lib/site-packages/trio/_channel.py +610 -0
  44. package/lib/site-packages/trio/_core/__init__.py +94 -0
  45. package/lib/site-packages/trio/_core/_asyncgens.py +243 -0
  46. package/lib/site-packages/trio/_core/_concat_tb.py +26 -0
  47. package/lib/site-packages/trio/_core/_entry_queue.py +223 -0
  48. package/lib/site-packages/trio/_core/_exceptions.py +169 -0
  49. package/lib/site-packages/trio/_core/_generated_instrumentation.py +50 -0
  50. package/lib/site-packages/trio/_core/_generated_io_epoll.py +98 -0
  51. package/lib/site-packages/trio/_core/_generated_io_kqueue.py +153 -0
  52. package/lib/site-packages/trio/_core/_generated_io_windows.py +204 -0
  53. package/lib/site-packages/trio/_core/_generated_run.py +269 -0
  54. package/lib/site-packages/trio/_core/_generated_windows_ffi.py +10 -0
  55. package/lib/site-packages/trio/_core/_instrumentation.py +117 -0
  56. package/lib/site-packages/trio/_core/_io_common.py +31 -0
  57. package/lib/site-packages/trio/_core/_io_epoll.py +385 -0
  58. package/lib/site-packages/trio/_core/_io_kqueue.py +292 -0
  59. package/lib/site-packages/trio/_core/_io_windows.py +1036 -0
  60. package/lib/site-packages/trio/_core/_ki.py +271 -0
  61. package/lib/site-packages/trio/_core/_local.py +104 -0
  62. package/lib/site-packages/trio/_core/_mock_clock.py +165 -0
  63. package/lib/site-packages/trio/_core/_parking_lot.py +317 -0
  64. package/lib/site-packages/trio/_core/_run.py +3148 -0
  65. package/lib/site-packages/trio/_core/_run_context.py +15 -0
  66. package/lib/site-packages/trio/_core/_tests/__init__.py +0 -0
  67. package/lib/site-packages/trio/_core/_tests/test_asyncgen.py +339 -0
  68. package/lib/site-packages/trio/_core/_tests/test_cancelled.py +222 -0
  69. package/lib/site-packages/trio/_core/_tests/test_exceptiongroup_gc.py +103 -0
  70. package/lib/site-packages/trio/_core/_tests/test_guest_mode.py +755 -0
  71. package/lib/site-packages/trio/_core/_tests/test_instrumentation.py +315 -0
  72. package/lib/site-packages/trio/_core/_tests/test_io.py +522 -0
  73. package/lib/site-packages/trio/_core/_tests/test_ki.py +703 -0
  74. package/lib/site-packages/trio/_core/_tests/test_local.py +118 -0
  75. package/lib/site-packages/trio/_core/_tests/test_mock_clock.py +193 -0
  76. package/lib/site-packages/trio/_core/_tests/test_parking_lot.py +389 -0
  77. package/lib/site-packages/trio/_core/_tests/test_run.py +3024 -0
  78. package/lib/site-packages/trio/_core/_tests/test_thread_cache.py +227 -0
  79. package/lib/site-packages/trio/_core/_tests/test_tutil.py +13 -0
  80. package/lib/site-packages/trio/_core/_tests/test_unbounded_queue.py +154 -0
  81. package/lib/site-packages/trio/_core/_tests/test_windows.py +305 -0
  82. package/lib/site-packages/trio/_core/_tests/tutil.py +117 -0
  83. package/lib/site-packages/trio/_core/_tests/type_tests/nursery_start.py +79 -0
  84. package/lib/site-packages/trio/_core/_tests/type_tests/run.py +51 -0
  85. package/lib/site-packages/trio/_core/_thread_cache.py +317 -0
  86. package/lib/site-packages/trio/_core/_traps.py +318 -0
  87. package/lib/site-packages/trio/_core/_unbounded_queue.py +163 -0
  88. package/lib/site-packages/trio/_core/_wakeup_socketpair.py +75 -0
  89. package/lib/site-packages/trio/_core/_windows_cffi.py +313 -0
  90. package/lib/site-packages/trio/_deprecate.py +171 -0
  91. package/lib/site-packages/trio/_dtls.py +1380 -0
  92. package/lib/site-packages/trio/_file_io.py +513 -0
  93. package/lib/site-packages/trio/_highlevel_generic.py +125 -0
  94. package/lib/site-packages/trio/_highlevel_open_tcp_listeners.py +251 -0
  95. package/lib/site-packages/trio/_highlevel_open_tcp_stream.py +397 -0
  96. package/lib/site-packages/trio/_highlevel_open_unix_stream.py +65 -0
  97. package/lib/site-packages/trio/_highlevel_serve_listeners.py +148 -0
  98. package/lib/site-packages/trio/_highlevel_socket.py +423 -0
  99. package/lib/site-packages/trio/_highlevel_ssl_helpers.py +180 -0
  100. package/lib/site-packages/trio/_path.py +289 -0
  101. package/lib/site-packages/trio/_repl.py +159 -0
  102. package/lib/site-packages/trio/_signals.py +185 -0
  103. package/lib/site-packages/trio/_socket.py +1326 -0
  104. package/lib/site-packages/trio/_ssl.py +964 -0
  105. package/lib/site-packages/trio/_subprocess.py +1178 -0
  106. package/lib/site-packages/trio/_subprocess_platform/__init__.py +123 -0
  107. package/lib/site-packages/trio/_subprocess_platform/kqueue.py +48 -0
  108. package/lib/site-packages/trio/_subprocess_platform/waitid.py +113 -0
  109. package/lib/site-packages/trio/_subprocess_platform/windows.py +11 -0
  110. package/lib/site-packages/trio/_sync.py +908 -0
  111. package/lib/site-packages/trio/_tests/__init__.py +0 -0
  112. package/lib/site-packages/trio/_tests/astrill-codesigning-cert.cer +0 -0
  113. package/lib/site-packages/trio/_tests/check_type_completeness.py +247 -0
  114. package/lib/site-packages/trio/_tests/module_with_deprecations.py +22 -0
  115. package/lib/site-packages/trio/_tests/pytest_plugin.py +54 -0
  116. package/lib/site-packages/trio/_tests/test_abc.py +72 -0
  117. package/lib/site-packages/trio/_tests/test_channel.py +750 -0
  118. package/lib/site-packages/trio/_tests/test_contextvars.py +56 -0
  119. package/lib/site-packages/trio/_tests/test_deprecate.py +277 -0
  120. package/lib/site-packages/trio/_tests/test_deprecate_strict_exception_groups_false.py +64 -0
  121. package/lib/site-packages/trio/_tests/test_dtls.py +950 -0
  122. package/lib/site-packages/trio/_tests/test_exports.py +626 -0
  123. package/lib/site-packages/trio/_tests/test_fakenet.py +317 -0
  124. package/lib/site-packages/trio/_tests/test_file_io.py +269 -0
  125. package/lib/site-packages/trio/_tests/test_highlevel_generic.py +98 -0
  126. package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_listeners.py +419 -0
  127. package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_stream.py +693 -0
  128. package/lib/site-packages/trio/_tests/test_highlevel_open_unix_stream.py +86 -0
  129. package/lib/site-packages/trio/_tests/test_highlevel_serve_listeners.py +186 -0
  130. package/lib/site-packages/trio/_tests/test_highlevel_socket.py +336 -0
  131. package/lib/site-packages/trio/_tests/test_highlevel_ssl_helpers.py +169 -0
  132. package/lib/site-packages/trio/_tests/test_path.py +279 -0
  133. package/lib/site-packages/trio/_tests/test_repl.py +428 -0
  134. package/lib/site-packages/trio/_tests/test_scheduler_determinism.py +47 -0
  135. package/lib/site-packages/trio/_tests/test_signals.py +186 -0
  136. package/lib/site-packages/trio/_tests/test_socket.py +1253 -0
  137. package/lib/site-packages/trio/_tests/test_ssl.py +1371 -0
  138. package/lib/site-packages/trio/_tests/test_subprocess.py +767 -0
  139. package/lib/site-packages/trio/_tests/test_sync.py +735 -0
  140. package/lib/site-packages/trio/_tests/test_testing.py +682 -0
  141. package/lib/site-packages/trio/_tests/test_testing_raisesgroup.py +1128 -0
  142. package/lib/site-packages/trio/_tests/test_threads.py +1173 -0
  143. package/lib/site-packages/trio/_tests/test_timeouts.py +281 -0
  144. package/lib/site-packages/trio/_tests/test_tracing.py +88 -0
  145. package/lib/site-packages/trio/_tests/test_trio.py +8 -0
  146. package/lib/site-packages/trio/_tests/test_unix_pipes.py +288 -0
  147. package/lib/site-packages/trio/_tests/test_util.py +349 -0
  148. package/lib/site-packages/trio/_tests/test_wait_for_object.py +225 -0
  149. package/lib/site-packages/trio/_tests/test_windows_pipes.py +112 -0
  150. package/lib/site-packages/trio/_tests/tools/__init__.py +0 -0
  151. package/lib/site-packages/trio/_tests/tools/test_gen_exports.py +179 -0
  152. package/lib/site-packages/trio/_tests/tools/test_mypy_annotate.py +140 -0
  153. package/lib/site-packages/trio/_tests/tools/test_sync_requirements.py +80 -0
  154. package/lib/site-packages/trio/_tests/type_tests/check_wraps.py +9 -0
  155. package/lib/site-packages/trio/_tests/type_tests/open_memory_channel.py +4 -0
  156. package/lib/site-packages/trio/_tests/type_tests/path.py +140 -0
  157. package/lib/site-packages/trio/_tests/type_tests/subprocesses.py +23 -0
  158. package/lib/site-packages/trio/_tests/type_tests/task_status.py +29 -0
  159. package/lib/site-packages/trio/_threads.py +610 -0
  160. package/lib/site-packages/trio/_timeouts.py +197 -0
  161. package/lib/site-packages/trio/_tools/__init__.py +0 -0
  162. package/lib/site-packages/trio/_tools/gen_exports.py +401 -0
  163. package/lib/site-packages/trio/_tools/mypy_annotate.py +126 -0
  164. package/lib/site-packages/trio/_tools/sync_requirements.py +98 -0
  165. package/lib/site-packages/trio/_tools/windows_ffi_build.py +220 -0
  166. package/lib/site-packages/trio/_unix_pipes.py +197 -0
  167. package/lib/site-packages/trio/_util.py +385 -0
  168. package/lib/site-packages/trio/_version.py +3 -0
  169. package/lib/site-packages/trio/_wait_for_object.py +67 -0
  170. package/lib/site-packages/trio/_windows_pipes.py +144 -0
  171. package/lib/site-packages/trio/abc.py +23 -0
  172. package/lib/site-packages/trio/from_thread.py +13 -0
  173. package/lib/site-packages/trio/lowlevel.py +95 -0
  174. package/lib/site-packages/trio/py.typed +0 -0
  175. package/lib/site-packages/trio/socket.py +602 -0
  176. package/lib/site-packages/trio/testing/__init__.py +58 -0
  177. package/lib/site-packages/trio/testing/_check_streams.py +570 -0
  178. package/lib/site-packages/trio/testing/_checkpoints.py +69 -0
  179. package/lib/site-packages/trio/testing/_fake_net.py +584 -0
  180. package/lib/site-packages/trio/testing/_memory_streams.py +633 -0
  181. package/lib/site-packages/trio/testing/_network.py +36 -0
  182. package/lib/site-packages/trio/testing/_raises_group.py +1015 -0
  183. package/lib/site-packages/trio/testing/_sequencer.py +87 -0
  184. package/lib/site-packages/trio/testing/_trio_test.py +50 -0
  185. package/lib/site-packages/trio/to_thread.py +4 -0
  186. package/lib/site-packages/trio-0.33.0.dist-info/INSTALLER +1 -0
  187. package/lib/site-packages/trio-0.33.0.dist-info/METADATA +186 -0
  188. package/lib/site-packages/trio-0.33.0.dist-info/RECORD +156 -0
  189. package/lib/site-packages/trio-0.33.0.dist-info/REQUESTED +0 -0
  190. package/lib/site-packages/trio-0.33.0.dist-info/WHEEL +5 -0
  191. package/lib/site-packages/trio-0.33.0.dist-info/entry_points.txt +2 -0
  192. package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE +3 -0
  193. package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.APACHE2 +202 -0
  194. package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.MIT +22 -0
  195. package/lib/site-packages/trio-0.33.0.dist-info/top_level.txt +1 -0
  196. package/package.json +1 -1
@@ -0,0 +1,1178 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import os
5
+ import subprocess
6
+ import sys
7
+ import warnings
8
+ from contextlib import ExitStack
9
+ from functools import partial
10
+ from typing import (
11
+ TYPE_CHECKING,
12
+ Final,
13
+ Literal,
14
+ Protocol,
15
+ TypeAlias,
16
+ TypedDict,
17
+ overload,
18
+ )
19
+
20
+ import trio
21
+
22
+ from ._core import ClosedResourceError, TaskStatus
23
+ from ._highlevel_generic import StapledStream
24
+ from ._subprocess_platform import (
25
+ create_pipe_from_child_output,
26
+ create_pipe_to_child_stdin,
27
+ wait_child_exiting,
28
+ )
29
+ from ._sync import Lock
30
+ from ._util import NoPublicConstructor, final
31
+
32
+ if TYPE_CHECKING:
33
+ import signal
34
+ from collections.abc import Awaitable, Callable, Iterable, Mapping, Sequence
35
+ from io import TextIOWrapper
36
+
37
+ from typing_extensions import Unpack
38
+
39
+ from ._abc import ReceiveStream, SendStream
40
+
41
+
42
+ # Sphinx cannot parse the stringified version
43
+ StrOrBytesPath: TypeAlias = str | bytes | os.PathLike[str] | os.PathLike[bytes]
44
+
45
+
46
+ # Linux-specific, but has complex lifetime management stuff so we hard-code it
47
+ # here instead of hiding it behind the _subprocess_platform abstraction
48
+ can_try_pidfd_open: bool
49
+ if TYPE_CHECKING:
50
+
51
+ def pidfd_open(fd: int, flags: int) -> int: ...
52
+
53
+ from ._subprocess_platform import ClosableReceiveStream, ClosableSendStream
54
+
55
+ else:
56
+ can_try_pidfd_open = True
57
+ try:
58
+ from os import pidfd_open
59
+ except ImportError:
60
+ if sys.platform == "linux":
61
+ # this workaround is needed on:
62
+ # - CPython <= 3.8
63
+ # - non-CPython (maybe?)
64
+ # - Anaconda's interpreter (as it is built to assume an older
65
+ # than current linux kernel)
66
+ #
67
+ # The last point implies that other custom builds might not work;
68
+ # therefore, no assertion should be here.
69
+ import ctypes
70
+
71
+ _cdll_for_pidfd_open = ctypes.CDLL(None, use_errno=True)
72
+ _cdll_for_pidfd_open.syscall.restype = ctypes.c_long
73
+ # pid and flags are actually int-sized, but the syscall() function
74
+ # always takes longs. (Except on x32 where long is 32-bits and syscall
75
+ # takes 64-bit arguments. But in the unlikely case that anyone is
76
+ # using x32, this will still work, b/c we only need to pass in 32 bits
77
+ # of data, and the C ABI doesn't distinguish between passing 32-bit vs
78
+ # 64-bit integers; our 32-bit values will get loaded into 64-bit
79
+ # registers where syscall() will find them.)
80
+ _cdll_for_pidfd_open.syscall.argtypes = [
81
+ ctypes.c_long, # syscall number
82
+ ctypes.c_long, # pid
83
+ ctypes.c_long, # flags
84
+ ]
85
+ __NR_pidfd_open = 434
86
+
87
+ def pidfd_open(fd: int, flags: int) -> int:
88
+ result = _cdll_for_pidfd_open.syscall(__NR_pidfd_open, fd, flags)
89
+ if result < 0: # pragma: no cover
90
+ err = ctypes.get_errno()
91
+ raise OSError(err, os.strerror(err))
92
+ return result
93
+
94
+ else:
95
+ can_try_pidfd_open = False
96
+
97
+
98
+ class HasFileno(Protocol):
99
+ """Represents any file-like object that has a file descriptor."""
100
+
101
+ def fileno(self) -> int: ...
102
+
103
+
104
+ @final
105
+ class Process(metaclass=NoPublicConstructor):
106
+ r"""A child process. Like :class:`subprocess.Popen`, but async.
107
+
108
+ This class has no public constructor. The most common way to get a
109
+ `Process` object is to combine `Nursery.start` with `run_process`::
110
+
111
+ process_object = await nursery.start(run_process, ...)
112
+
113
+ This way, `run_process` supervises the process and makes sure that it is
114
+ cleaned up properly, while optionally checking the return value, feeding
115
+ it input, and so on.
116
+
117
+ If you need more control – for example, because you want to spawn a child
118
+ process that outlives your program – then another option is to use
119
+ `trio.lowlevel.open_process`::
120
+
121
+ process_object = await trio.lowlevel.open_process(...)
122
+
123
+ Attributes:
124
+ args (str or list): The ``command`` passed at construction time,
125
+ specifying the process to execute and its arguments.
126
+ pid (int): The process ID of the child process managed by this object.
127
+ stdin (trio.abc.SendStream or None): A stream connected to the child's
128
+ standard input stream: when you write bytes here, they become available
129
+ for the child to read. Only available if the :class:`Process`
130
+ was constructed using ``stdin=PIPE``; otherwise this will be None.
131
+ stdout (trio.abc.ReceiveStream or None): A stream connected to
132
+ the child's standard output stream: when the child writes to
133
+ standard output, the written bytes become available for you
134
+ to read here. Only available if the :class:`Process` was
135
+ constructed using ``stdout=PIPE``; otherwise this will be None.
136
+ stderr (trio.abc.ReceiveStream or None): A stream connected to
137
+ the child's standard error stream: when the child writes to
138
+ standard error, the written bytes become available for you
139
+ to read here. Only available if the :class:`Process` was
140
+ constructed using ``stderr=PIPE``; otherwise this will be None.
141
+ stdio (trio.StapledStream or None): A stream that sends data to
142
+ the child's standard input and receives from the child's standard
143
+ output. Only available if both :attr:`stdin` and :attr:`stdout` are
144
+ available; otherwise this will be None.
145
+
146
+ """
147
+
148
+ # We're always in binary mode.
149
+ universal_newlines: Final = False
150
+ encoding: Final = None
151
+ errors: Final = None
152
+
153
+ # Available for the per-platform wait_child_exiting() implementations
154
+ # to stash some state; waitid platforms use this to avoid spawning
155
+ # arbitrarily many threads if wait() keeps getting cancelled.
156
+ _wait_for_exit_data: object = None
157
+
158
+ def __init__(
159
+ self,
160
+ popen: subprocess.Popen[bytes],
161
+ stdin: SendStream | None,
162
+ stdout: ReceiveStream | None,
163
+ stderr: ReceiveStream | None,
164
+ ) -> None:
165
+ self._proc = popen
166
+ self.stdin = stdin
167
+ self.stdout = stdout
168
+ self.stderr = stderr
169
+
170
+ self.stdio: StapledStream[SendStream, ReceiveStream] | None = None
171
+ if self.stdin is not None and self.stdout is not None:
172
+ self.stdio = StapledStream(self.stdin, self.stdout)
173
+
174
+ self._wait_lock: Lock = Lock()
175
+
176
+ self._pidfd: TextIOWrapper | None = None
177
+ if can_try_pidfd_open:
178
+ try:
179
+ fd: int = pidfd_open(self._proc.pid, 0)
180
+ except OSError: # pragma: no cover
181
+ # Well, we tried, but it didn't work (probably because we're
182
+ # running on an older kernel, or in an older sandbox, that
183
+ # hasn't been updated to support pidfd_open). We'll fall back
184
+ # on waitid instead.
185
+ pass
186
+ else:
187
+ # It worked! Wrap the raw fd up in a Python file object to
188
+ # make sure it'll get closed.
189
+ # SIM115: open-file-with-context-handler
190
+ self._pidfd = open(fd) # noqa: SIM115
191
+
192
+ self.args: StrOrBytesPath | Sequence[StrOrBytesPath] = self._proc.args
193
+ self.pid: int = self._proc.pid
194
+
195
+ def __repr__(self) -> str:
196
+ returncode = self.returncode
197
+ if returncode is None:
198
+ status = f"running with PID {self.pid}"
199
+ else:
200
+ if returncode < 0:
201
+ status = f"exited with signal {-returncode}"
202
+ else:
203
+ status = f"exited with status {returncode}"
204
+ return f"<trio.Process {self.args!r}: {status}>"
205
+
206
+ @property
207
+ def returncode(self) -> int | None:
208
+ """The exit status of the process (an integer), or ``None`` if it's
209
+ still running.
210
+
211
+ By convention, a return code of zero indicates success. On
212
+ UNIX, negative values indicate termination due to a signal,
213
+ e.g., -11 if terminated by signal 11 (``SIGSEGV``). On
214
+ Windows, a process that exits due to a call to
215
+ :meth:`Process.terminate` will have an exit status of 1.
216
+
217
+ Unlike the standard library `subprocess.Popen.returncode`, you don't
218
+ have to call `poll` or `wait` to update this attribute; it's
219
+ automatically updated as needed, and will always give you the latest
220
+ information.
221
+
222
+ """
223
+ result = self._proc.poll()
224
+ if result is not None:
225
+ self._close_pidfd()
226
+ return result
227
+
228
+ def _close_pidfd(self) -> None:
229
+ if self._pidfd is not None:
230
+ trio.lowlevel.notify_closing(self._pidfd.fileno())
231
+ self._pidfd.close()
232
+ self._pidfd = None
233
+
234
+ async def wait(self) -> int:
235
+ """Block until the process exits.
236
+
237
+ Returns:
238
+ The exit status of the process; see :attr:`returncode`.
239
+ """
240
+ async with self._wait_lock:
241
+ if self.poll() is None:
242
+ if self._pidfd is not None:
243
+ with contextlib.suppress(
244
+ ClosedResourceError,
245
+ ): # something else (probably a call to poll) already closed the pidfd
246
+ await trio.lowlevel.wait_readable(self._pidfd.fileno())
247
+ else:
248
+ await wait_child_exiting(self)
249
+ # We have to use .wait() here, not .poll(), because on macOS
250
+ # (and maybe other systems, who knows), there's a race
251
+ # condition inside the kernel that creates a tiny window where
252
+ # kqueue reports that the process has exited, but
253
+ # waitpid(WNOHANG) can't yet reap it. So this .wait() may
254
+ # actually block for a tiny fraction of a second.
255
+ self._proc.wait()
256
+ self._close_pidfd()
257
+ assert self._proc.returncode is not None
258
+ return self._proc.returncode
259
+
260
+ def poll(self) -> int | None:
261
+ """Returns the exit status of the process (an integer), or ``None`` if
262
+ it's still running.
263
+
264
+ Note that on Trio (unlike the standard library `subprocess.Popen`),
265
+ ``process.poll()`` and ``process.returncode`` always give the same
266
+ result. See `returncode` for more details. This method is only
267
+ included to make it easier to port code from `subprocess`.
268
+
269
+ """
270
+ return self.returncode
271
+
272
+ def send_signal(self, sig: signal.Signals | int) -> None:
273
+ """Send signal ``sig`` to the process.
274
+
275
+ On UNIX, ``sig`` may be any signal defined in the
276
+ :mod:`signal` module, such as ``signal.SIGINT`` or
277
+ ``signal.SIGTERM``. On Windows, it may be anything accepted by
278
+ the standard library :meth:`subprocess.Popen.send_signal`.
279
+ """
280
+ self._proc.send_signal(sig)
281
+
282
+ def terminate(self) -> None:
283
+ """Terminate the process, politely if possible.
284
+
285
+ On UNIX, this is equivalent to
286
+ ``send_signal(signal.SIGTERM)``; by convention this requests
287
+ graceful termination, but a misbehaving or buggy process might
288
+ ignore it. On Windows, :meth:`terminate` forcibly terminates the
289
+ process in the same manner as :meth:`kill`.
290
+ """
291
+ self._proc.terminate()
292
+
293
+ def kill(self) -> None:
294
+ """Immediately terminate the process.
295
+
296
+ On UNIX, this is equivalent to
297
+ ``send_signal(signal.SIGKILL)``. On Windows, it calls
298
+ ``TerminateProcess``. In both cases, the process cannot
299
+ prevent itself from being killed, but the termination will be
300
+ delivered asynchronously; use :meth:`wait` if you want to
301
+ ensure the process is actually dead before proceeding.
302
+ """
303
+ self._proc.kill()
304
+
305
+
306
+ async def _open_process(
307
+ command: StrOrBytesPath | Sequence[StrOrBytesPath],
308
+ *,
309
+ stdin: int | HasFileno | None = None,
310
+ stdout: int | HasFileno | None = None,
311
+ stderr: int | HasFileno | None = None,
312
+ **options: object,
313
+ ) -> Process:
314
+ r"""Execute a child program in a new process.
315
+
316
+ After construction, you can interact with the child process by writing data to its
317
+ `~trio.Process.stdin` stream (a `~trio.abc.SendStream`), reading data from its
318
+ `~trio.Process.stdout` and/or `~trio.Process.stderr` streams (both
319
+ `~trio.abc.ReceiveStream`\s), sending it signals using `~trio.Process.terminate`,
320
+ `~trio.Process.kill`, or `~trio.Process.send_signal`, and waiting for it to exit
321
+ using `~trio.Process.wait`. See `trio.Process` for details.
322
+
323
+ Each standard stream is only available if you specify that a pipe should be created
324
+ for it. For example, if you pass ``stdin=subprocess.PIPE``, you can write to the
325
+ `~trio.Process.stdin` stream, else `~trio.Process.stdin` will be ``None``.
326
+
327
+ Unlike `trio.run_process`, this function doesn't do any kind of automatic
328
+ management of the child process. It's up to you to implement whatever semantics you
329
+ want.
330
+
331
+ Args:
332
+ command: The command to run. Typically this is a sequence of strings or
333
+ bytes such as ``['ls', '-l', 'directory with spaces']``, where the
334
+ first element names the executable to invoke and the other elements
335
+ specify its arguments. With ``shell=True`` in the ``**options``, or on
336
+ Windows, ``command`` can be a string or bytes, which will be parsed
337
+ following platform-dependent :ref:`quoting rules
338
+ <subprocess-quoting>`. In all cases ``command`` can be a path or a
339
+ sequence of paths.
340
+ stdin: Specifies what the child process's standard input
341
+ stream should connect to: output written by the parent
342
+ (``subprocess.PIPE``), nothing (``subprocess.DEVNULL``),
343
+ or an open file (pass a file descriptor or something whose
344
+ ``fileno`` method returns one). If ``stdin`` is unspecified,
345
+ the child process will have the same standard input stream
346
+ as its parent.
347
+ stdout: Like ``stdin``, but for the child process's standard output
348
+ stream.
349
+ stderr: Like ``stdin``, but for the child process's standard error
350
+ stream. An additional value ``subprocess.STDOUT`` is supported,
351
+ which causes the child's standard output and standard error
352
+ messages to be intermixed on a single standard output stream,
353
+ attached to whatever the ``stdout`` option says to attach it to.
354
+ **options: Other :ref:`general subprocess options <subprocess-options>`
355
+ are also accepted.
356
+
357
+ Returns:
358
+ A new `trio.Process` object.
359
+
360
+ Raises:
361
+ OSError: if the process spawning fails, for example because the
362
+ specified command could not be found.
363
+
364
+ """
365
+ for key in ("universal_newlines", "text", "encoding", "errors", "bufsize"):
366
+ if options.get(key):
367
+ raise TypeError(
368
+ "trio.Process only supports communicating over "
369
+ f"unbuffered byte streams; the '{key}' option is not supported",
370
+ )
371
+
372
+ if os.name == "posix":
373
+ # TODO: how do paths and sequences thereof play with `shell=True`?
374
+ if isinstance(command, (str, bytes)) and not options.get("shell"):
375
+ raise TypeError(
376
+ "command must be a sequence (not a string or bytes) if "
377
+ "shell=False on UNIX systems",
378
+ )
379
+ if not isinstance(command, (str, bytes)) and options.get("shell"):
380
+ raise TypeError(
381
+ "command must be a string or bytes (not a sequence) if "
382
+ "shell=True on UNIX systems",
383
+ )
384
+
385
+ trio_stdin: ClosableSendStream | None = None
386
+ trio_stdout: ClosableReceiveStream | None = None
387
+ trio_stderr: ClosableReceiveStream | None = None
388
+ # Close the parent's handle for each child side of a pipe; we want the child to
389
+ # have the only copy, so that when it exits we can read EOF on our side. The
390
+ # trio ends of pipes will be transferred to the Process object, which will be
391
+ # responsible for their lifetime. If process spawning fails, though, we still
392
+ # want to close them before letting the failure bubble out
393
+ with ExitStack() as always_cleanup, ExitStack() as cleanup_on_fail:
394
+ if stdin == subprocess.PIPE:
395
+ trio_stdin, stdin = create_pipe_to_child_stdin()
396
+ always_cleanup.callback(os.close, stdin)
397
+ cleanup_on_fail.callback(trio_stdin.close)
398
+ if stdout == subprocess.PIPE:
399
+ trio_stdout, stdout = create_pipe_from_child_output()
400
+ always_cleanup.callback(os.close, stdout)
401
+ cleanup_on_fail.callback(trio_stdout.close)
402
+ if stderr == subprocess.STDOUT:
403
+ # If we created a pipe for stdout, pass the same pipe for
404
+ # stderr. If stdout was some non-pipe thing (DEVNULL or a
405
+ # given FD), pass the same thing. If stdout was passed as
406
+ # None, keep stderr as STDOUT to allow subprocess to dup
407
+ # our stdout. Regardless of which of these is applicable,
408
+ # don't create a new Trio stream for stderr -- if stdout
409
+ # is piped, stderr will be intermixed on the stdout stream.
410
+ if stdout is not None:
411
+ stderr = stdout
412
+ elif stderr == subprocess.PIPE:
413
+ trio_stderr, stderr = create_pipe_from_child_output()
414
+ always_cleanup.callback(os.close, stderr)
415
+ cleanup_on_fail.callback(trio_stderr.close)
416
+
417
+ popen = await trio.to_thread.run_sync(
418
+ partial(
419
+ subprocess.Popen,
420
+ command,
421
+ stdin=stdin,
422
+ stdout=stdout,
423
+ stderr=stderr,
424
+ **options,
425
+ ),
426
+ )
427
+ # We did not fail, so dismiss the stack for the trio ends
428
+ cleanup_on_fail.pop_all()
429
+
430
+ return Process._create(popen, trio_stdin, trio_stdout, trio_stderr)
431
+
432
+
433
+ # async function missing await
434
+ async def _windows_deliver_cancel(p: Process) -> None: # noqa: RUF029
435
+ try:
436
+ p.terminate()
437
+ except OSError as exc:
438
+ warnings.warn(
439
+ RuntimeWarning(f"TerminateProcess on {p!r} failed with: {exc!r}"),
440
+ stacklevel=1,
441
+ )
442
+
443
+
444
+ async def _posix_deliver_cancel(p: Process) -> None:
445
+ try:
446
+ p.terminate()
447
+ await trio.sleep(5)
448
+ warnings.warn(
449
+ RuntimeWarning(
450
+ f"process {p!r} ignored SIGTERM for 5 seconds. "
451
+ "(Maybe you should pass a custom deliver_cancel?) "
452
+ "Trying SIGKILL.",
453
+ ),
454
+ stacklevel=1,
455
+ )
456
+ p.kill()
457
+ except OSError as exc:
458
+ warnings.warn(
459
+ RuntimeWarning(f"tried to kill process {p!r}, but failed with: {exc!r}"),
460
+ stacklevel=1,
461
+ )
462
+
463
+
464
+ # Use a private name, so we can declare platform-specific stubs below.
465
+ # This is also the signature read by Sphinx
466
+ async def _run_process(
467
+ command: StrOrBytesPath | Sequence[StrOrBytesPath],
468
+ *,
469
+ stdin: bytes | bytearray | memoryview | int | HasFileno | None = b"",
470
+ capture_stdout: bool = False,
471
+ capture_stderr: bool = False,
472
+ check: bool = True,
473
+ deliver_cancel: Callable[[Process], Awaitable[object]] | None = None,
474
+ task_status: TaskStatus[Process] = trio.TASK_STATUS_IGNORED,
475
+ **options: object,
476
+ ) -> subprocess.CompletedProcess[bytes]:
477
+ """Run ``command`` in a subprocess and wait for it to complete.
478
+
479
+ This function can be called in two different ways.
480
+
481
+ One option is a direct call, like::
482
+
483
+ completed_process_info = await trio.run_process(...)
484
+
485
+ In this case, it returns a :class:`subprocess.CompletedProcess` instance
486
+ describing the results. Use this if you want to treat a process like a
487
+ function call.
488
+
489
+ The other option is to run it as a task using `Nursery.start` – the enhanced version
490
+ of `~Nursery.start_soon` that lets a task pass back a value during startup::
491
+
492
+ process = await nursery.start(trio.run_process, ...)
493
+
494
+ In this case, `~Nursery.start` returns a `Process` object that you can use
495
+ to interact with the process while it's running. Use this if you want to
496
+ treat a process like a background task.
497
+
498
+ Either way, `run_process` makes sure that the process has exited before
499
+ returning, handles cancellation, optionally checks for errors, and
500
+ provides some convenient shorthands for dealing with the child's
501
+ input/output.
502
+
503
+ **Input:** `run_process` supports all the same ``stdin=`` arguments as
504
+ `subprocess.Popen`. In addition, if you simply want to pass in some fixed
505
+ data, you can pass a plain `bytes` object, and `run_process` will take
506
+ care of setting up a pipe, feeding in the data you gave, and then sending
507
+ end-of-file. The default is ``b""``, which means that the child will receive
508
+ an empty stdin. If you want the child to instead read from the parent's
509
+ stdin, use ``stdin=None``.
510
+
511
+ **Output:** By default, any output produced by the subprocess is
512
+ passed through to the standard output and error streams of the
513
+ parent Trio process.
514
+
515
+ When calling `run_process` directly, you can capture the subprocess's output by
516
+ passing ``capture_stdout=True`` to capture the subprocess's standard output, and/or
517
+ ``capture_stderr=True`` to capture its standard error. Captured data is collected up
518
+ by Trio into an in-memory buffer, and then provided as the
519
+ :attr:`~subprocess.CompletedProcess.stdout` and/or
520
+ :attr:`~subprocess.CompletedProcess.stderr` attributes of the returned
521
+ :class:`~subprocess.CompletedProcess` object. The value for any stream that was not
522
+ captured will be ``None``.
523
+
524
+ If you want to capture both stdout and stderr while keeping them
525
+ separate, pass ``capture_stdout=True, capture_stderr=True``.
526
+
527
+ If you want to capture both stdout and stderr but mixed together
528
+ in the order they were printed, use: ``capture_stdout=True, stderr=subprocess.STDOUT``.
529
+ This directs the child's stderr into its stdout, so the combined
530
+ output will be available in the `~subprocess.CompletedProcess.stdout`
531
+ attribute.
532
+
533
+ If you're using ``await nursery.start(trio.run_process, ...)`` and want to capture
534
+ the subprocess's output for further processing, then use ``stdout=subprocess.PIPE``
535
+ and then make sure to read the data out of the `Process.stdout` stream. If you want
536
+ to capture stderr separately, use ``stderr=subprocess.PIPE``. If you want to capture
537
+ both, but mixed together in the correct order, use ``stdout=subprocess.PIPE,
538
+ stderr=subprocess.STDOUT``.
539
+
540
+ **Error checking:** If the subprocess exits with a nonzero status
541
+ code, indicating failure, :func:`run_process` raises a
542
+ :exc:`subprocess.CalledProcessError` exception rather than
543
+ returning normally. The captured outputs are still available as
544
+ the :attr:`~subprocess.CalledProcessError.stdout` and
545
+ :attr:`~subprocess.CalledProcessError.stderr` attributes of that
546
+ exception. To disable this behavior, so that :func:`run_process`
547
+ returns normally even if the subprocess exits abnormally, pass ``check=False``.
548
+
549
+ Note that this can make the ``capture_stdout`` and ``capture_stderr``
550
+ arguments useful even when starting `run_process` as a task: if you only
551
+ care about the output if the process fails, then you can enable capturing
552
+ and then read the output off of the `~subprocess.CalledProcessError`.
553
+
554
+ **Cancellation:** If cancelled, `run_process` sends a termination
555
+ request to the subprocess, then waits for it to fully exit. The
556
+ ``deliver_cancel`` argument lets you control how the process is terminated.
557
+
558
+ .. note:: `run_process` is intentionally similar to the standard library
559
+ `subprocess.run`, but some of the defaults are different. Specifically, we
560
+ default to:
561
+
562
+ - ``check=True``, because `"errors should never pass silently / unless
563
+ explicitly silenced" <https://www.python.org/dev/peps/pep-0020/>`__.
564
+
565
+ - ``stdin=b""``, because it produces less-confusing results if a subprocess
566
+ unexpectedly tries to read from stdin.
567
+
568
+ To get the `subprocess.run` semantics, use ``check=False, stdin=None``.
569
+
570
+ Args:
571
+ command (list or str): The command to run. Typically this is a
572
+ sequence of strings such as ``['ls', '-l', 'directory with spaces']``,
573
+ where the first element names the executable to invoke and the other
574
+ elements specify its arguments. With ``shell=True`` in the
575
+ ``**options``, or on Windows, ``command`` may alternatively
576
+ be a string, which will be parsed following platform-dependent
577
+ :ref:`quoting rules <subprocess-quoting>`.
578
+
579
+ stdin (:obj:`bytes`, subprocess.PIPE, file descriptor, or None): The
580
+ bytes to provide to the subprocess on its standard input stream, or
581
+ ``None`` if the subprocess's standard input should come from the
582
+ same place as the parent Trio process's standard input. As is the
583
+ case with the :mod:`subprocess` module, you can also pass a file
584
+ descriptor or an object with a ``fileno()`` method, in which case
585
+ the subprocess's standard input will come from that file.
586
+
587
+ When starting `run_process` as a background task, you can also use
588
+ ``stdin=subprocess.PIPE``, in which case `Process.stdin` will be a
589
+ `~trio.abc.SendStream` that you can use to send data to the child.
590
+
591
+ capture_stdout (bool): If true, capture the bytes that the subprocess
592
+ writes to its standard output stream and return them in the
593
+ `~subprocess.CompletedProcess.stdout` attribute of the returned
594
+ `subprocess.CompletedProcess` or `subprocess.CalledProcessError`.
595
+
596
+ capture_stderr (bool): If true, capture the bytes that the subprocess
597
+ writes to its standard error stream and return them in the
598
+ `~subprocess.CompletedProcess.stderr` attribute of the returned
599
+ `~subprocess.CompletedProcess` or `subprocess.CalledProcessError`.
600
+
601
+ check (bool): If false, don't validate that the subprocess exits
602
+ successfully. You should be sure to check the
603
+ ``returncode`` attribute of the returned object if you pass
604
+ ``check=False``, so that errors don't pass silently.
605
+
606
+ deliver_cancel (async function or None): If `run_process` is cancelled,
607
+ then it needs to kill the child process. There are multiple ways to
608
+ do this, so we let you customize it.
609
+
610
+ If you pass None (the default), then the behavior depends on the
611
+ platform:
612
+
613
+ - On Windows, Trio calls ``TerminateProcess``, which should kill the
614
+ process immediately.
615
+
616
+ - On Unix-likes, the default behavior is to send a ``SIGTERM``, wait
617
+ 5 seconds, and send a ``SIGKILL``.
618
+
619
+ Alternatively, you can customize this behavior by passing in an
620
+ arbitrary async function, which will be called with the `Process`
621
+ object as an argument. For example, the default Unix behavior could
622
+ be implemented like this::
623
+
624
+ async def my_deliver_cancel(process):
625
+ process.send_signal(signal.SIGTERM)
626
+ await trio.sleep(5)
627
+ process.send_signal(signal.SIGKILL)
628
+
629
+ When the process actually exits, the ``deliver_cancel`` function
630
+ will automatically be cancelled – so if the process exits after
631
+ ``SIGTERM``, then we'll never reach the ``SIGKILL``.
632
+
633
+ In any case, `run_process` will always wait for the child process to
634
+ exit before raising `Cancelled`.
635
+
636
+ **options: :func:`run_process` also accepts any :ref:`general subprocess
637
+ options <subprocess-options>` and passes them on to the
638
+ :class:`~trio.Process` constructor. This includes the
639
+ ``stdout`` and ``stderr`` options, which provide additional
640
+ redirection possibilities such as ``stderr=subprocess.STDOUT``,
641
+ ``stdout=subprocess.DEVNULL``, or file descriptors.
642
+
643
+ Returns:
644
+
645
+ When called normally – a `subprocess.CompletedProcess` instance
646
+ describing the return code and outputs.
647
+
648
+ When called via `Nursery.start` – a `trio.Process` instance.
649
+
650
+ Raises:
651
+ UnicodeError: if ``stdin`` is specified as a Unicode string, rather
652
+ than bytes
653
+ ValueError: if multiple redirections are specified for the same
654
+ stream, e.g., both ``capture_stdout=True`` and
655
+ ``stdout=subprocess.DEVNULL``
656
+ subprocess.CalledProcessError: if ``check=False`` is not passed
657
+ and the process exits with a nonzero exit status
658
+ OSError: if an error is encountered starting or communicating with
659
+ the process
660
+ ExceptionGroup: if exceptions occur in ``deliver_cancel``,
661
+ or when exceptions occur when communicating with the subprocess.
662
+ If strict_exception_groups is set to false in the global context,
663
+ which is deprecated, then single exceptions will be collapsed.
664
+
665
+ .. note:: The child process runs in the same process group as the parent
666
+ Trio process, so a Ctrl+C will be delivered simultaneously to both
667
+ parent and child. If you don't want this behavior, consult your
668
+ platform's documentation for starting child processes in a different
669
+ process group.
670
+
671
+ """
672
+
673
+ if isinstance(stdin, str):
674
+ raise UnicodeError("process stdin must be bytes, not str")
675
+ if task_status is trio.TASK_STATUS_IGNORED:
676
+ if stdin is subprocess.PIPE:
677
+ raise ValueError(
678
+ "stdout=subprocess.PIPE is only valid with nursery.start, "
679
+ "since that's the only way to access the pipe; use nursery.start "
680
+ "or pass the data you want to write directly",
681
+ )
682
+ if options.get("stdout") is subprocess.PIPE:
683
+ raise ValueError(
684
+ "stdout=subprocess.PIPE is only valid with nursery.start, "
685
+ "since that's the only way to access the pipe",
686
+ )
687
+ if options.get("stderr") is subprocess.PIPE:
688
+ raise ValueError(
689
+ "stderr=subprocess.PIPE is only valid with nursery.start, "
690
+ "since that's the only way to access the pipe",
691
+ )
692
+ if isinstance(stdin, (bytes, bytearray, memoryview)):
693
+ input_ = stdin
694
+ options["stdin"] = subprocess.PIPE
695
+ else:
696
+ # stdin should be something acceptable to Process
697
+ # (None, DEVNULL, a file descriptor, etc) and Process
698
+ # will raise if it's not
699
+ input_ = None
700
+ options["stdin"] = stdin
701
+
702
+ if capture_stdout:
703
+ if "stdout" in options:
704
+ raise ValueError("can't specify both stdout and capture_stdout")
705
+ options["stdout"] = subprocess.PIPE
706
+ if capture_stderr:
707
+ if "stderr" in options:
708
+ raise ValueError("can't specify both stderr and capture_stderr")
709
+ options["stderr"] = subprocess.PIPE
710
+
711
+ if deliver_cancel is None:
712
+ if os.name == "nt":
713
+ deliver_cancel = _windows_deliver_cancel
714
+ else:
715
+ assert os.name == "posix"
716
+ deliver_cancel = _posix_deliver_cancel
717
+
718
+ stdout_chunks: list[bytes | bytearray] = []
719
+ stderr_chunks: list[bytes | bytearray] = []
720
+
721
+ async def feed_input(stream: SendStream) -> None:
722
+ async with stream:
723
+ try:
724
+ assert input_ is not None
725
+ await stream.send_all(input_)
726
+ except trio.BrokenResourceError:
727
+ pass
728
+
729
+ async def read_output(
730
+ stream: ReceiveStream,
731
+ chunks: list[bytes | bytearray],
732
+ ) -> None:
733
+ async with stream:
734
+ async for chunk in stream:
735
+ chunks.append(chunk) # noqa: PERF401
736
+
737
+ # Opening the process does not need to be inside the nursery, so we put it outside
738
+ # so any exceptions get directly seen by users.
739
+ proc = await _open_process(command, **options) # type: ignore[arg-type]
740
+ async with trio.open_nursery() as nursery:
741
+ try:
742
+ if input_ is not None:
743
+ assert proc.stdin is not None
744
+ nursery.start_soon(feed_input, proc.stdin)
745
+ proc.stdin = None
746
+ proc.stdio = None
747
+ if capture_stdout:
748
+ assert proc.stdout is not None
749
+ nursery.start_soon(read_output, proc.stdout, stdout_chunks)
750
+ proc.stdout = None
751
+ proc.stdio = None
752
+ if capture_stderr:
753
+ assert proc.stderr is not None
754
+ nursery.start_soon(read_output, proc.stderr, stderr_chunks)
755
+ proc.stderr = None
756
+ task_status.started(proc)
757
+ await proc.wait()
758
+ except BaseException:
759
+ with trio.CancelScope(shield=True):
760
+ killer_cscope = trio.CancelScope(shield=True)
761
+
762
+ async def killer() -> None:
763
+ with killer_cscope:
764
+ await deliver_cancel(proc)
765
+
766
+ nursery.start_soon(killer)
767
+ await proc.wait()
768
+ killer_cscope.cancel(reason="trio internal implementation detail")
769
+ raise
770
+
771
+ stdout = b"".join(stdout_chunks) if capture_stdout else None
772
+ stderr = b"".join(stderr_chunks) if capture_stderr else None
773
+
774
+ if proc.returncode and check:
775
+ raise subprocess.CalledProcessError(
776
+ proc.returncode,
777
+ proc.args,
778
+ output=stdout,
779
+ stderr=stderr,
780
+ )
781
+ else:
782
+ assert proc.returncode is not None
783
+ return subprocess.CompletedProcess(proc.args, proc.returncode, stdout, stderr)
784
+
785
+
786
+ # There's a lot of duplication here because type checkers don't
787
+ # have a good way to represent overloads that differ only
788
+ # slightly. A cheat sheet:
789
+ #
790
+ # - on Windows, command is Union[str, Sequence[str]];
791
+ # on Unix, command is str if shell=True and Sequence[str] otherwise
792
+ #
793
+ # - on Windows, there are startupinfo and creationflags options;
794
+ # on Unix, there are preexec_fn, restore_signals, start_new_session,
795
+ # pass_fds, group (3.9+), extra_groups (3.9+), user (3.9+),
796
+ # umask (3.9+), pipesize (3.10+), process_group (3.11+)
797
+ #
798
+ # - run_process() has the signature of open_process() plus arguments
799
+ # capture_stdout, capture_stderr, check, deliver_cancel, the ability
800
+ # to pass bytes as stdin, and the ability to run in `nursery.start`
801
+
802
+
803
+ class GeneralProcessArgs(TypedDict, total=False):
804
+ """Arguments shared between all runs."""
805
+
806
+ stdout: int | HasFileno | None
807
+ stderr: int | HasFileno | None
808
+ close_fds: bool
809
+ cwd: StrOrBytesPath | None
810
+ env: Mapping[str, str] | None
811
+ executable: StrOrBytesPath | None
812
+
813
+
814
+ if TYPE_CHECKING:
815
+ if sys.platform == "win32":
816
+
817
+ class WindowsProcessArgs(GeneralProcessArgs, total=False):
818
+ """Arguments shared between all Windows runs."""
819
+
820
+ shell: bool
821
+ startupinfo: subprocess.STARTUPINFO | None
822
+ creationflags: int
823
+
824
+ async def open_process(
825
+ command: StrOrBytesPath | Sequence[StrOrBytesPath],
826
+ *,
827
+ stdin: int | HasFileno | None = None,
828
+ **kwargs: Unpack[WindowsProcessArgs],
829
+ ) -> trio.Process:
830
+ r"""Execute a child program in a new process.
831
+
832
+ After construction, you can interact with the child process by writing data to its
833
+ `~trio.Process.stdin` stream (a `~trio.abc.SendStream`), reading data from its
834
+ `~trio.Process.stdout` and/or `~trio.Process.stderr` streams (both
835
+ `~trio.abc.ReceiveStream`\s), sending it signals using `~trio.Process.terminate`,
836
+ `~trio.Process.kill`, or `~trio.Process.send_signal`, and waiting for it to exit
837
+ using `~trio.Process.wait`. See `trio.Process` for details.
838
+
839
+ Each standard stream is only available if you specify that a pipe should be created
840
+ for it. For example, if you pass ``stdin=subprocess.PIPE``, you can write to the
841
+ `~trio.Process.stdin` stream, else `~trio.Process.stdin` will be ``None``.
842
+
843
+ Unlike `trio.run_process`, this function doesn't do any kind of automatic
844
+ management of the child process. It's up to you to implement whatever semantics you
845
+ want.
846
+
847
+ Args:
848
+ command (list or str): The command to run. Typically this is a
849
+ sequence of strings such as ``['ls', '-l', 'directory with spaces']``,
850
+ where the first element names the executable to invoke and the other
851
+ elements specify its arguments. With ``shell=True`` in the
852
+ ``**options``, or on Windows, ``command`` may alternatively
853
+ be a string, which will be parsed following platform-dependent
854
+ :ref:`quoting rules <subprocess-quoting>`.
855
+ stdin: Specifies what the child process's standard input
856
+ stream should connect to: output written by the parent
857
+ (``subprocess.PIPE``), nothing (``subprocess.DEVNULL``),
858
+ or an open file (pass a file descriptor or something whose
859
+ ``fileno`` method returns one). If ``stdin`` is unspecified,
860
+ the child process will have the same standard input stream
861
+ as its parent.
862
+ stdout: Like ``stdin``, but for the child process's standard output
863
+ stream.
864
+ stderr: Like ``stdin``, but for the child process's standard error
865
+ stream. An additional value ``subprocess.STDOUT`` is supported,
866
+ which causes the child's standard output and standard error
867
+ messages to be intermixed on a single standard output stream,
868
+ attached to whatever the ``stdout`` option says to attach it to.
869
+ **options: Other :ref:`general subprocess options <subprocess-options>`
870
+ are also accepted.
871
+
872
+ Returns:
873
+ A new `trio.Process` object.
874
+
875
+ Raises:
876
+ OSError: if the process spawning fails, for example because the
877
+ specified command could not be found.
878
+
879
+ """
880
+ ...
881
+
882
+ async def run_process(
883
+ command: StrOrBytesPath | Sequence[StrOrBytesPath],
884
+ *,
885
+ task_status: TaskStatus[Process] = trio.TASK_STATUS_IGNORED,
886
+ stdin: bytes | bytearray | memoryview | int | HasFileno | None = None,
887
+ capture_stdout: bool = False,
888
+ capture_stderr: bool = False,
889
+ check: bool = True,
890
+ deliver_cancel: Callable[[Process], Awaitable[object]] | None = None,
891
+ **kwargs: Unpack[WindowsProcessArgs],
892
+ ) -> subprocess.CompletedProcess[bytes]:
893
+ """Run ``command`` in a subprocess and wait for it to complete.
894
+
895
+ This function can be called in two different ways.
896
+
897
+ One option is a direct call, like::
898
+
899
+ completed_process_info = await trio.run_process(...)
900
+
901
+ In this case, it returns a :class:`subprocess.CompletedProcess` instance
902
+ describing the results. Use this if you want to treat a process like a
903
+ function call.
904
+
905
+ The other option is to run it as a task using `Nursery.start` – the enhanced version
906
+ of `~Nursery.start_soon` that lets a task pass back a value during startup::
907
+
908
+ process = await nursery.start(trio.run_process, ...)
909
+
910
+ In this case, `~Nursery.start` returns a `Process` object that you can use
911
+ to interact with the process while it's running. Use this if you want to
912
+ treat a process like a background task.
913
+
914
+ Either way, `run_process` makes sure that the process has exited before
915
+ returning, handles cancellation, optionally checks for errors, and
916
+ provides some convenient shorthands for dealing with the child's
917
+ input/output.
918
+
919
+ **Input:** `run_process` supports all the same ``stdin=`` arguments as
920
+ `subprocess.Popen`. In addition, if you simply want to pass in some fixed
921
+ data, you can pass a plain `bytes` object, and `run_process` will take
922
+ care of setting up a pipe, feeding in the data you gave, and then sending
923
+ end-of-file. The default is ``b""``, which means that the child will receive
924
+ an empty stdin. If you want the child to instead read from the parent's
925
+ stdin, use ``stdin=None``.
926
+
927
+ **Output:** By default, any output produced by the subprocess is
928
+ passed through to the standard output and error streams of the
929
+ parent Trio process.
930
+
931
+ When calling `run_process` directly, you can capture the subprocess's output by
932
+ passing ``capture_stdout=True`` to capture the subprocess's standard output, and/or
933
+ ``capture_stderr=True`` to capture its standard error. Captured data is collected up
934
+ by Trio into an in-memory buffer, and then provided as the
935
+ :attr:`~subprocess.CompletedProcess.stdout` and/or
936
+ :attr:`~subprocess.CompletedProcess.stderr` attributes of the returned
937
+ :class:`~subprocess.CompletedProcess` object. The value for any stream that was not
938
+ captured will be ``None``.
939
+
940
+ If you want to capture both stdout and stderr while keeping them
941
+ separate, pass ``capture_stdout=True, capture_stderr=True``.
942
+
943
+ If you want to capture both stdout and stderr but mixed together
944
+ in the order they were printed, use: ``capture_stdout=True, stderr=subprocess.STDOUT``.
945
+ This directs the child's stderr into its stdout, so the combined
946
+ output will be available in the `~subprocess.CompletedProcess.stdout`
947
+ attribute.
948
+
949
+ If you're using ``await nursery.start(trio.run_process, ...)`` and want to capture
950
+ the subprocess's output for further processing, then use ``stdout=subprocess.PIPE``
951
+ and then make sure to read the data out of the `Process.stdout` stream. If you want
952
+ to capture stderr separately, use ``stderr=subprocess.PIPE``. If you want to capture
953
+ both, but mixed together in the correct order, use ``stdout=subprocess.PIPE,
954
+ stderr=subprocess.STDOUT``.
955
+
956
+ **Error checking:** If the subprocess exits with a nonzero status
957
+ code, indicating failure, :func:`run_process` raises a
958
+ :exc:`subprocess.CalledProcessError` exception rather than
959
+ returning normally. The captured outputs are still available as
960
+ the :attr:`~subprocess.CalledProcessError.stdout` and
961
+ :attr:`~subprocess.CalledProcessError.stderr` attributes of that
962
+ exception. To disable this behavior, so that :func:`run_process`
963
+ returns normally even if the subprocess exits abnormally, pass ``check=False``.
964
+
965
+ Note that this can make the ``capture_stdout`` and ``capture_stderr``
966
+ arguments useful even when starting `run_process` as a task: if you only
967
+ care about the output if the process fails, then you can enable capturing
968
+ and then read the output off of the `~subprocess.CalledProcessError`.
969
+
970
+ **Cancellation:** If cancelled, `run_process` sends a termination
971
+ request to the subprocess, then waits for it to fully exit. The
972
+ ``deliver_cancel`` argument lets you control how the process is terminated.
973
+
974
+ .. note:: `run_process` is intentionally similar to the standard library
975
+ `subprocess.run`, but some of the defaults are different. Specifically, we
976
+ default to:
977
+
978
+ - ``check=True``, because `"errors should never pass silently / unless
979
+ explicitly silenced" <https://www.python.org/dev/peps/pep-0020/>`__.
980
+
981
+ - ``stdin=b""``, because it produces less-confusing results if a subprocess
982
+ unexpectedly tries to read from stdin.
983
+
984
+ To get the `subprocess.run` semantics, use ``check=False, stdin=None``.
985
+
986
+ Args:
987
+ command (list or str): The command to run. Typically this is a
988
+ sequence of strings such as ``['ls', '-l', 'directory with spaces']``,
989
+ where the first element names the executable to invoke and the other
990
+ elements specify its arguments. With ``shell=True`` in the
991
+ ``**options``, or on Windows, ``command`` may alternatively
992
+ be a string, which will be parsed following platform-dependent
993
+ :ref:`quoting rules <subprocess-quoting>`.
994
+
995
+ stdin (:obj:`bytes`, subprocess.PIPE, file descriptor, or None): The
996
+ bytes to provide to the subprocess on its standard input stream, or
997
+ ``None`` if the subprocess's standard input should come from the
998
+ same place as the parent Trio process's standard input. As is the
999
+ case with the :mod:`subprocess` module, you can also pass a file
1000
+ descriptor or an object with a ``fileno()`` method, in which case
1001
+ the subprocess's standard input will come from that file.
1002
+
1003
+ When starting `run_process` as a background task, you can also use
1004
+ ``stdin=subprocess.PIPE``, in which case `Process.stdin` will be a
1005
+ `~trio.abc.SendStream` that you can use to send data to the child.
1006
+
1007
+ capture_stdout (bool): If true, capture the bytes that the subprocess
1008
+ writes to its standard output stream and return them in the
1009
+ `~subprocess.CompletedProcess.stdout` attribute of the returned
1010
+ `subprocess.CompletedProcess` or `subprocess.CalledProcessError`.
1011
+
1012
+ capture_stderr (bool): If true, capture the bytes that the subprocess
1013
+ writes to its standard error stream and return them in the
1014
+ `~subprocess.CompletedProcess.stderr` attribute of the returned
1015
+ `~subprocess.CompletedProcess` or `subprocess.CalledProcessError`.
1016
+
1017
+ check (bool): If false, don't validate that the subprocess exits
1018
+ successfully. You should be sure to check the
1019
+ ``returncode`` attribute of the returned object if you pass
1020
+ ``check=False``, so that errors don't pass silently.
1021
+
1022
+ deliver_cancel (async function or None): If `run_process` is cancelled,
1023
+ then it needs to kill the child process. There are multiple ways to
1024
+ do this, so we let you customize it.
1025
+
1026
+ If you pass None (the default), then the behavior depends on the
1027
+ platform:
1028
+
1029
+ - On Windows, Trio calls ``TerminateProcess``, which should kill the
1030
+ process immediately.
1031
+
1032
+ - On Unix-likes, the default behavior is to send a ``SIGTERM``, wait
1033
+ 5 seconds, and send a ``SIGKILL``.
1034
+
1035
+ Alternatively, you can customize this behavior by passing in an
1036
+ arbitrary async function, which will be called with the `Process`
1037
+ object as an argument. For example, the default Unix behavior could
1038
+ be implemented like this::
1039
+
1040
+ async def my_deliver_cancel(process):
1041
+ process.send_signal(signal.SIGTERM)
1042
+ await trio.sleep(5)
1043
+ process.send_signal(signal.SIGKILL)
1044
+
1045
+ When the process actually exits, the ``deliver_cancel`` function
1046
+ will automatically be cancelled – so if the process exits after
1047
+ ``SIGTERM``, then we'll never reach the ``SIGKILL``.
1048
+
1049
+ In any case, `run_process` will always wait for the child process to
1050
+ exit before raising `Cancelled`.
1051
+
1052
+ **options: :func:`run_process` also accepts any :ref:`general subprocess
1053
+ options <subprocess-options>` and passes them on to the
1054
+ :class:`~trio.Process` constructor. This includes the
1055
+ ``stdout`` and ``stderr`` options, which provide additional
1056
+ redirection possibilities such as ``stderr=subprocess.STDOUT``,
1057
+ ``stdout=subprocess.DEVNULL``, or file descriptors.
1058
+
1059
+ Returns:
1060
+
1061
+ When called normally – a `subprocess.CompletedProcess` instance
1062
+ describing the return code and outputs.
1063
+
1064
+ When called via `Nursery.start` – a `trio.Process` instance.
1065
+
1066
+ Raises:
1067
+ UnicodeError: if ``stdin`` is specified as a Unicode string, rather
1068
+ than bytes
1069
+ ValueError: if multiple redirections are specified for the same
1070
+ stream, e.g., both ``capture_stdout=True`` and
1071
+ ``stdout=subprocess.DEVNULL``
1072
+ subprocess.CalledProcessError: if ``check=False`` is not passed
1073
+ and the process exits with a nonzero exit status
1074
+ OSError: if an error is encountered starting or communicating with
1075
+ the process
1076
+
1077
+ .. note:: The child process runs in the same process group as the parent
1078
+ Trio process, so a Ctrl+C will be delivered simultaneously to both
1079
+ parent and child. If you don't want this behavior, consult your
1080
+ platform's documentation for starting child processes in a different
1081
+ process group.
1082
+
1083
+ """
1084
+ ...
1085
+
1086
+ else: # Unix
1087
+ # pyright doesn't give any error about overloads missing docstrings as they're
1088
+ # overloads. But might still be a problem for other static analyzers / docstring
1089
+ # readers (?)
1090
+
1091
+ class UnixProcessArgs3_10(GeneralProcessArgs, total=False):
1092
+ """Arguments shared between all Unix runs."""
1093
+
1094
+ preexec_fn: Callable[[], object] | None
1095
+ restore_signals: bool
1096
+ start_new_session: bool
1097
+ pass_fds: Sequence[int]
1098
+
1099
+ # 3.9+
1100
+ group: str | int | None
1101
+ extra_groups: Iterable[str | int] | None
1102
+ user: str | int | None
1103
+ umask: int
1104
+
1105
+ # 3.10+
1106
+ pipesize: int
1107
+
1108
+ class UnixProcessArgs3_11(UnixProcessArgs3_10, total=False):
1109
+ """Arguments shared between all Unix runs on 3.11+."""
1110
+
1111
+ process_group: int | None
1112
+
1113
+ class UnixRunProcessMixin(TypedDict, total=False):
1114
+ """Arguments unique to run_process on Unix."""
1115
+
1116
+ task_status: TaskStatus[Process]
1117
+ capture_stdout: bool
1118
+ capture_stderr: bool
1119
+ check: bool
1120
+ deliver_cancel: Callable[[Process], Awaitable[None]] | None
1121
+
1122
+ # TODO: once https://github.com/python/mypy/issues/18692 is
1123
+ # fixed, move the `UnixRunProcessArgs` definition down.
1124
+ if sys.version_info >= (3, 11):
1125
+ UnixProcessArgs = UnixProcessArgs3_11
1126
+
1127
+ class UnixRunProcessArgs(UnixProcessArgs3_11, UnixRunProcessMixin):
1128
+ """Arguments for run_process on Unix with 3.11+"""
1129
+
1130
+ else:
1131
+ UnixProcessArgs = UnixProcessArgs3_10
1132
+
1133
+ class UnixRunProcessArgs(UnixProcessArgs3_10, UnixRunProcessMixin):
1134
+ """Arguments for run_process on Unix with 3.10+"""
1135
+
1136
+ @overload # type: ignore[no-overload-impl]
1137
+ async def open_process(
1138
+ command: StrOrBytesPath,
1139
+ *,
1140
+ stdin: int | HasFileno | None = None,
1141
+ shell: Literal[True],
1142
+ **kwargs: Unpack[UnixProcessArgs],
1143
+ ) -> trio.Process: ...
1144
+
1145
+ @overload
1146
+ async def open_process(
1147
+ command: Sequence[StrOrBytesPath],
1148
+ *,
1149
+ stdin: int | HasFileno | None = None,
1150
+ shell: bool = False,
1151
+ **kwargs: Unpack[UnixProcessArgs],
1152
+ ) -> trio.Process: ...
1153
+
1154
+ @overload # type: ignore[no-overload-impl]
1155
+ async def run_process(
1156
+ command: StrOrBytesPath,
1157
+ *,
1158
+ stdin: bytes | bytearray | memoryview | int | HasFileno | None = b"",
1159
+ shell: Literal[True],
1160
+ **kwargs: Unpack[UnixRunProcessArgs],
1161
+ ) -> subprocess.CompletedProcess[bytes]: ...
1162
+
1163
+ @overload
1164
+ async def run_process(
1165
+ command: Sequence[StrOrBytesPath],
1166
+ *,
1167
+ stdin: bytes | bytearray | memoryview | int | HasFileno | None = b"",
1168
+ shell: bool = False,
1169
+ **kwargs: Unpack[UnixRunProcessArgs],
1170
+ ) -> subprocess.CompletedProcess[bytes]: ...
1171
+
1172
+ else:
1173
+ # At runtime, use the actual implementations.
1174
+ open_process = _open_process
1175
+ open_process.__name__ = open_process.__qualname__ = "open_process"
1176
+
1177
+ run_process = _run_process
1178
+ run_process.__name__ = run_process.__qualname__ = "run_process"