@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,767 @@
1
+ from __future__ import annotations
2
+
3
+ import gc
4
+ import os
5
+ import random
6
+ import signal
7
+ import subprocess
8
+ import sys
9
+ from collections.abc import AsyncIterator, Callable
10
+ from contextlib import AbstractAsyncContextManager, asynccontextmanager
11
+ from functools import partial
12
+ from pathlib import Path as SyncPath
13
+ from signal import Signals
14
+ from typing import (
15
+ TYPE_CHECKING,
16
+ Any,
17
+ NoReturn,
18
+ TypeAlias,
19
+ )
20
+ from unittest import mock
21
+
22
+ import pytest
23
+
24
+ import trio
25
+
26
+ from .. import (
27
+ Event,
28
+ Process,
29
+ _core,
30
+ fail_after,
31
+ move_on_after,
32
+ run_process,
33
+ sleep,
34
+ sleep_forever,
35
+ )
36
+ from .._core._tests.tutil import skip_if_fbsd_pipes_broken, slow
37
+ from ..lowlevel import open_process
38
+ from ..testing import MockClock, assert_no_checkpoints, wait_all_tasks_blocked
39
+
40
+ if TYPE_CHECKING:
41
+ from types import FrameType
42
+
43
+ from .._abc import ReceiveStream
44
+
45
+ if sys.platform == "win32":
46
+ SignalType: TypeAlias = None
47
+ else:
48
+ SignalType: TypeAlias = Signals
49
+
50
+ SIGKILL: SignalType
51
+ SIGTERM: SignalType
52
+ SIGUSR1: SignalType
53
+
54
+ posix = os.name == "posix"
55
+ if (not TYPE_CHECKING and posix) or sys.platform != "win32":
56
+ from signal import SIGKILL, SIGTERM, SIGUSR1
57
+ else:
58
+ SIGKILL, SIGTERM, SIGUSR1 = None, None, None
59
+
60
+
61
+ # Since Windows has very few command-line utilities generally available,
62
+ # all of our subprocesses are Python processes running short bits of
63
+ # (mostly) cross-platform code.
64
+ def python(code: str) -> list[str]:
65
+ return [sys.executable, "-u", "-c", "import sys; " + code]
66
+
67
+
68
+ EXIT_TRUE = python("sys.exit(0)")
69
+ EXIT_FALSE = python("sys.exit(1)")
70
+ CAT = python("sys.stdout.buffer.write(sys.stdin.buffer.read())")
71
+
72
+ if posix:
73
+
74
+ def SLEEP(seconds: int) -> list[str]:
75
+ return ["sleep", str(seconds)]
76
+
77
+ else:
78
+
79
+ def SLEEP(seconds: int) -> list[str]:
80
+ return python(f"import time; time.sleep({seconds})")
81
+
82
+
83
+ @asynccontextmanager
84
+ async def open_process_then_kill( # type: ignore[misc, explicit-any]
85
+ *args: Any,
86
+ **kwargs: Any,
87
+ ) -> AsyncIterator[Process]:
88
+ proc = await open_process(*args, **kwargs)
89
+ try:
90
+ yield proc
91
+ finally:
92
+ proc.kill()
93
+ await proc.wait()
94
+
95
+
96
+ @asynccontextmanager
97
+ async def run_process_in_nursery( # type: ignore[misc, explicit-any]
98
+ *args: Any,
99
+ **kwargs: Any,
100
+ ) -> AsyncIterator[Process]:
101
+ async with _core.open_nursery() as nursery:
102
+ kwargs.setdefault("check", False)
103
+ value = await nursery.start(partial(run_process, *args, **kwargs))
104
+ assert isinstance(value, Process)
105
+ proc: Process = value
106
+ yield proc
107
+ nursery.cancel_scope.cancel()
108
+
109
+
110
+ background_process_param = pytest.mark.parametrize(
111
+ "background_process",
112
+ [open_process_then_kill, run_process_in_nursery],
113
+ ids=["open_process", "run_process in nursery"],
114
+ )
115
+
116
+ BackgroundProcessType: TypeAlias = Callable[ # type: ignore[explicit-any]
117
+ ...,
118
+ AbstractAsyncContextManager[Process],
119
+ ]
120
+
121
+
122
+ @background_process_param
123
+ async def test_basic(background_process: BackgroundProcessType) -> None:
124
+ async with background_process(EXIT_TRUE) as proc:
125
+ await proc.wait()
126
+ assert isinstance(proc, Process)
127
+ assert proc._pidfd is None
128
+ assert proc.returncode == 0
129
+ assert repr(proc) == f"<trio.Process {EXIT_TRUE}: exited with status 0>"
130
+
131
+ async with background_process(EXIT_FALSE) as proc:
132
+ await proc.wait()
133
+ assert proc.returncode == 1
134
+ assert repr(proc) == "<trio.Process {!r}: {}>".format(
135
+ EXIT_FALSE,
136
+ "exited with status 1",
137
+ )
138
+
139
+
140
+ @background_process_param
141
+ async def test_basic_no_pidfd(background_process: BackgroundProcessType) -> None:
142
+ with mock.patch("trio._subprocess.can_try_pidfd_open", new=False):
143
+ async with background_process(EXIT_TRUE) as proc:
144
+ assert proc._pidfd is None
145
+ await proc.wait()
146
+ assert isinstance(proc, Process)
147
+ assert proc._pidfd is None
148
+ assert proc.returncode == 0
149
+ assert repr(proc) == f"<trio.Process {EXIT_TRUE}: exited with status 0>"
150
+
151
+ async with background_process(EXIT_FALSE) as proc:
152
+ await proc.wait()
153
+ assert proc.returncode == 1
154
+ assert repr(proc) == "<trio.Process {!r}: {}>".format(
155
+ EXIT_FALSE,
156
+ "exited with status 1",
157
+ )
158
+
159
+
160
+ @background_process_param
161
+ async def test_auto_update_returncode(
162
+ background_process: BackgroundProcessType,
163
+ ) -> None:
164
+ async with background_process(SLEEP(9999)) as p:
165
+ assert p.returncode is None
166
+ assert "running" in repr(p)
167
+ p.kill()
168
+ p._proc.wait()
169
+ assert p.returncode is not None
170
+ assert "exited" in repr(p)
171
+ assert p._pidfd is None
172
+ assert p.returncode is not None
173
+
174
+
175
+ @background_process_param
176
+ async def test_multi_wait(background_process: BackgroundProcessType) -> None:
177
+ async with background_process(SLEEP(10)) as proc:
178
+ # Check that wait (including multi-wait) tolerates being cancelled
179
+ async with _core.open_nursery() as nursery:
180
+ nursery.start_soon(proc.wait)
181
+ nursery.start_soon(proc.wait)
182
+ nursery.start_soon(proc.wait)
183
+ await wait_all_tasks_blocked()
184
+ nursery.cancel_scope.cancel()
185
+
186
+ # Now try waiting for real
187
+ async with _core.open_nursery() as nursery:
188
+ nursery.start_soon(proc.wait)
189
+ nursery.start_soon(proc.wait)
190
+ nursery.start_soon(proc.wait)
191
+ await wait_all_tasks_blocked()
192
+ proc.kill()
193
+
194
+
195
+ @background_process_param
196
+ async def test_multi_wait_no_pidfd(background_process: BackgroundProcessType) -> None:
197
+ with mock.patch("trio._subprocess.can_try_pidfd_open", new=False):
198
+ async with background_process(SLEEP(10)) as proc:
199
+ # Check that wait (including multi-wait) tolerates being cancelled
200
+ async with _core.open_nursery() as nursery:
201
+ nursery.start_soon(proc.wait)
202
+ nursery.start_soon(proc.wait)
203
+ nursery.start_soon(proc.wait)
204
+ await wait_all_tasks_blocked()
205
+ nursery.cancel_scope.cancel()
206
+
207
+ # Now try waiting for real
208
+ async with _core.open_nursery() as nursery:
209
+ nursery.start_soon(proc.wait)
210
+ nursery.start_soon(proc.wait)
211
+ nursery.start_soon(proc.wait)
212
+ await wait_all_tasks_blocked()
213
+ proc.kill()
214
+
215
+
216
+ COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR = python(
217
+ "data = sys.stdin.buffer.read(); "
218
+ "sys.stdout.buffer.write(data); "
219
+ "sys.stderr.buffer.write(data[::-1])",
220
+ )
221
+
222
+
223
+ @background_process_param
224
+ async def test_pipes(background_process: BackgroundProcessType) -> None:
225
+ async with background_process(
226
+ COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR,
227
+ stdin=subprocess.PIPE,
228
+ stdout=subprocess.PIPE,
229
+ stderr=subprocess.PIPE,
230
+ ) as proc:
231
+ msg = b"the quick brown fox jumps over the lazy dog"
232
+
233
+ async def feed_input() -> None:
234
+ assert proc.stdin is not None
235
+ await proc.stdin.send_all(msg)
236
+ await proc.stdin.aclose()
237
+
238
+ async def check_output(stream: ReceiveStream, expected: bytes) -> None:
239
+ seen = bytearray()
240
+ async for chunk in stream:
241
+ seen += chunk
242
+ assert seen == expected
243
+
244
+ assert proc.stdout is not None
245
+ assert proc.stderr is not None
246
+
247
+ async with _core.open_nursery() as nursery:
248
+ # fail eventually if something is broken
249
+ nursery.cancel_scope.deadline = _core.current_time() + 30.0
250
+ nursery.start_soon(feed_input)
251
+ nursery.start_soon(check_output, proc.stdout, msg)
252
+ nursery.start_soon(check_output, proc.stderr, msg[::-1])
253
+
254
+ assert not nursery.cancel_scope.cancelled_caught
255
+ assert await proc.wait() == 0
256
+
257
+
258
+ @background_process_param
259
+ async def test_interactive(background_process: BackgroundProcessType) -> None:
260
+ # Test some back-and-forth with a subprocess. This one works like so:
261
+ # in: 32\n
262
+ # out: 0000...0000\n (32 zeroes)
263
+ # err: 1111...1111\n (64 ones)
264
+ # in: 10\n
265
+ # out: 2222222222\n (10 twos)
266
+ # err: 3333....3333\n (20 threes)
267
+ # in: EOF
268
+ # out: EOF
269
+ # err: EOF
270
+
271
+ async with background_process(
272
+ python(
273
+ "idx = 0\n"
274
+ "while True:\n"
275
+ " line = sys.stdin.readline()\n"
276
+ " if line == '': break\n"
277
+ " request = int(line.strip())\n"
278
+ " print(str(idx * 2) * request)\n"
279
+ " print(str(idx * 2 + 1) * request * 2, file=sys.stderr)\n"
280
+ " idx += 1\n",
281
+ ),
282
+ stdin=subprocess.PIPE,
283
+ stdout=subprocess.PIPE,
284
+ stderr=subprocess.PIPE,
285
+ ) as proc:
286
+ newline = b"\n" if posix else b"\r\n"
287
+
288
+ async def expect(idx: int, request: int) -> None:
289
+ async with _core.open_nursery() as nursery:
290
+
291
+ async def drain_one(
292
+ stream: ReceiveStream,
293
+ count: int,
294
+ digit: int,
295
+ ) -> None:
296
+ while count > 0:
297
+ result = await stream.receive_some(count)
298
+ assert result == (f"{digit}".encode() * len(result))
299
+ count -= len(result)
300
+ assert count == 0
301
+ assert await stream.receive_some(len(newline)) == newline
302
+
303
+ assert proc.stdout is not None
304
+ assert proc.stderr is not None
305
+ nursery.start_soon(drain_one, proc.stdout, request, idx * 2)
306
+ nursery.start_soon(drain_one, proc.stderr, request * 2, idx * 2 + 1)
307
+
308
+ assert proc.stdin is not None
309
+ assert proc.stdout is not None
310
+ assert proc.stderr is not None
311
+ with fail_after(5):
312
+ await proc.stdin.send_all(b"12")
313
+ await sleep(0.1)
314
+ await proc.stdin.send_all(b"345" + newline)
315
+ await expect(0, 12345)
316
+ await proc.stdin.send_all(b"100" + newline + b"200" + newline)
317
+ await expect(1, 100)
318
+ await expect(2, 200)
319
+ await proc.stdin.send_all(b"0" + newline)
320
+ await expect(3, 0)
321
+ await proc.stdin.send_all(b"999999")
322
+ with move_on_after(0.1) as scope:
323
+ await expect(4, 0)
324
+ assert scope.cancelled_caught
325
+ await proc.stdin.send_all(newline)
326
+ await expect(4, 999999)
327
+ await proc.stdin.aclose()
328
+ assert await proc.stdout.receive_some(1) == b""
329
+ assert await proc.stderr.receive_some(1) == b""
330
+ await proc.wait()
331
+
332
+ assert proc.returncode == 0
333
+
334
+
335
+ async def test_run() -> None:
336
+ data = bytes(random.randint(0, 255) for _ in range(2**18))
337
+
338
+ result = await run_process(
339
+ CAT,
340
+ stdin=data,
341
+ capture_stdout=True,
342
+ capture_stderr=True,
343
+ )
344
+ assert result.args == CAT
345
+ assert result.returncode == 0
346
+ assert result.stdout == data
347
+ assert result.stderr == b""
348
+
349
+ result = await run_process(CAT, capture_stdout=True)
350
+ assert result.args == CAT
351
+ assert result.returncode == 0
352
+ assert result.stdout == b""
353
+ assert result.stderr is None
354
+
355
+ result = await run_process(
356
+ COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR,
357
+ stdin=data,
358
+ capture_stdout=True,
359
+ capture_stderr=True,
360
+ )
361
+ assert result.args == COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR
362
+ assert result.returncode == 0
363
+ assert result.stdout == data
364
+ assert result.stderr == data[::-1]
365
+
366
+ # invalid combinations
367
+ with pytest.raises(UnicodeError):
368
+ await run_process(CAT, stdin="oh no, it's text")
369
+
370
+ pipe_stdout_error = r"^stdout=subprocess\.PIPE is only valid with nursery\.start, since that's the only way to access the pipe(; use nursery\.start or pass the data you want to write directly)*$"
371
+ with pytest.raises(ValueError, match=pipe_stdout_error):
372
+ await run_process(CAT, stdin=subprocess.PIPE)
373
+ with pytest.raises(ValueError, match=pipe_stdout_error):
374
+ await run_process(CAT, stdout=subprocess.PIPE)
375
+ with pytest.raises(
376
+ ValueError,
377
+ match=pipe_stdout_error.replace("stdout", "stderr", 1),
378
+ ):
379
+ await run_process(CAT, stderr=subprocess.PIPE)
380
+ with pytest.raises(
381
+ ValueError,
382
+ match=r"^can't specify both stdout and capture_stdout$",
383
+ ):
384
+ await run_process(CAT, capture_stdout=True, stdout=subprocess.DEVNULL)
385
+ with pytest.raises(
386
+ ValueError,
387
+ match=r"^can't specify both stderr and capture_stderr$",
388
+ ):
389
+ await run_process(CAT, capture_stderr=True, stderr=None)
390
+
391
+
392
+ async def test_run_check() -> None:
393
+ cmd = python("sys.stderr.buffer.write(b'test\\n'); sys.exit(1)")
394
+ with pytest.raises(subprocess.CalledProcessError) as excinfo:
395
+ await run_process(cmd, stdin=subprocess.DEVNULL, capture_stderr=True)
396
+ assert excinfo.value.cmd == cmd
397
+ assert excinfo.value.returncode == 1
398
+ assert excinfo.value.stderr == b"test\n"
399
+ assert excinfo.value.stdout is None
400
+
401
+ result = await run_process(
402
+ cmd,
403
+ capture_stdout=True,
404
+ capture_stderr=True,
405
+ check=False,
406
+ )
407
+ assert result.args == cmd
408
+ assert result.stdout == b""
409
+ assert result.stderr == b"test\n"
410
+ assert result.returncode == 1
411
+
412
+
413
+ @skip_if_fbsd_pipes_broken
414
+ async def test_run_with_broken_pipe() -> None:
415
+ result = await run_process(
416
+ [sys.executable, "-c", "import sys; sys.stdin.close()"],
417
+ stdin=b"x" * 131072,
418
+ )
419
+ assert result.returncode == 0
420
+ assert result.stdout is result.stderr is None
421
+
422
+
423
+ @background_process_param
424
+ async def test_stderr_stdout(background_process: BackgroundProcessType) -> None:
425
+ async with background_process(
426
+ COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR,
427
+ stdin=subprocess.PIPE,
428
+ stdout=subprocess.PIPE,
429
+ stderr=subprocess.STDOUT,
430
+ ) as proc:
431
+ assert proc.stdio is not None
432
+ assert proc.stdout is not None
433
+ assert proc.stderr is None
434
+ await proc.stdio.send_all(b"1234")
435
+ await proc.stdio.send_eof()
436
+
437
+ output = []
438
+ while True:
439
+ chunk = await proc.stdio.receive_some(16)
440
+ if chunk == b"":
441
+ break
442
+ output.append(chunk)
443
+ assert b"".join(output) == b"12344321"
444
+ assert proc.returncode == 0
445
+
446
+ # equivalent test with run_process()
447
+ result = await run_process(
448
+ COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR,
449
+ stdin=b"1234",
450
+ capture_stdout=True,
451
+ stderr=subprocess.STDOUT,
452
+ )
453
+ assert result.returncode == 0
454
+ assert result.stdout == b"12344321"
455
+ assert result.stderr is None
456
+
457
+ # this one hits the branch where stderr=STDOUT but stdout
458
+ # is not redirected
459
+ async with background_process(
460
+ CAT,
461
+ stdin=subprocess.PIPE,
462
+ stderr=subprocess.STDOUT,
463
+ ) as proc:
464
+ assert proc.stdout is None
465
+ assert proc.stderr is None
466
+ await proc.stdin.aclose()
467
+ await proc.wait()
468
+ assert proc.returncode == 0
469
+
470
+ if posix:
471
+ try:
472
+ r, w = os.pipe()
473
+
474
+ async with background_process(
475
+ COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR,
476
+ stdin=subprocess.PIPE,
477
+ stdout=w,
478
+ stderr=subprocess.STDOUT,
479
+ ) as proc:
480
+ os.close(w)
481
+ assert proc.stdio is None
482
+ assert proc.stdout is None
483
+ assert proc.stderr is None
484
+ await proc.stdin.send_all(b"1234")
485
+ await proc.stdin.aclose()
486
+ assert await proc.wait() == 0
487
+ assert os.read(r, 4096) == b"12344321"
488
+ assert os.read(r, 4096) == b""
489
+ finally:
490
+ os.close(r)
491
+
492
+
493
+ async def test_errors() -> None:
494
+ with pytest.raises(TypeError) as excinfo:
495
+ # call-overload on unix, call-arg on windows
496
+ await open_process(["ls"], encoding="utf-8") # type: ignore
497
+ assert "unbuffered byte streams" in str(excinfo.value)
498
+ assert "the 'encoding' option is not supported" in str(excinfo.value)
499
+
500
+ if posix:
501
+ with pytest.raises(TypeError) as excinfo:
502
+ await open_process(["ls"], shell=True)
503
+ with pytest.raises(TypeError) as excinfo:
504
+ await open_process("ls", shell=False)
505
+
506
+
507
+ @background_process_param
508
+ async def test_signals(background_process: BackgroundProcessType) -> None:
509
+ async def test_one_signal(
510
+ send_it: Callable[[Process], None],
511
+ signum: signal.Signals | None,
512
+ ) -> None:
513
+ with move_on_after(1.0) as scope:
514
+ async with background_process(SLEEP(3600)) as proc:
515
+ send_it(proc)
516
+ await proc.wait()
517
+ assert not scope.cancelled_caught
518
+ if posix:
519
+ assert signum is not None
520
+ assert proc.returncode == -signum
521
+ else:
522
+ assert proc.returncode != 0
523
+
524
+ await test_one_signal(Process.kill, SIGKILL)
525
+ await test_one_signal(Process.terminate, SIGTERM)
526
+ # Test that we can send arbitrary signals.
527
+ #
528
+ # We used to use SIGINT here, but it turns out that the Python interpreter
529
+ # has race conditions that can cause it to explode in weird ways if it
530
+ # tries to handle SIGINT during startup. SIGUSR1's default disposition is
531
+ # to terminate the target process, and Python doesn't try to do anything
532
+ # clever to handle it.
533
+ if (not TYPE_CHECKING and posix) or sys.platform != "win32":
534
+ await test_one_signal(lambda proc: proc.send_signal(SIGUSR1), SIGUSR1)
535
+
536
+
537
+ @pytest.mark.skipif(not posix, reason="POSIX specific")
538
+ @background_process_param
539
+ async def test_wait_reapable_fails(background_process: BackgroundProcessType) -> None:
540
+ if TYPE_CHECKING and sys.platform == "win32":
541
+ return
542
+ old_sigchld = signal.signal(signal.SIGCHLD, signal.SIG_IGN)
543
+ try:
544
+ # With SIGCHLD disabled, the wait() syscall will wait for the
545
+ # process to exit but then fail with ECHILD. Make sure we
546
+ # support this case as the stdlib subprocess module does.
547
+ async with background_process(SLEEP(3600)) as proc:
548
+ async with _core.open_nursery() as nursery:
549
+ nursery.start_soon(proc.wait)
550
+ await wait_all_tasks_blocked()
551
+ proc.kill()
552
+ nursery.cancel_scope.deadline = _core.current_time() + 1.0
553
+ assert not nursery.cancel_scope.cancelled_caught
554
+ assert proc.returncode == 0 # exit status unknowable, so...
555
+ finally:
556
+ signal.signal(signal.SIGCHLD, old_sigchld)
557
+
558
+
559
+ @pytest.mark.skipif(not posix, reason="POSIX specific")
560
+ @background_process_param
561
+ async def test_wait_reapable_fails_no_pidfd(
562
+ background_process: BackgroundProcessType,
563
+ ) -> None:
564
+ if TYPE_CHECKING and sys.platform == "win32":
565
+ return
566
+ with mock.patch("trio._subprocess.can_try_pidfd_open", new=False):
567
+ old_sigchld = signal.signal(signal.SIGCHLD, signal.SIG_IGN)
568
+ try:
569
+ # With SIGCHLD disabled, the wait() syscall will wait for the
570
+ # process to exit but then fail with ECHILD. Make sure we
571
+ # support this case as the stdlib subprocess module does.
572
+ async with background_process(SLEEP(3600)) as proc:
573
+ async with _core.open_nursery() as nursery:
574
+ nursery.start_soon(proc.wait)
575
+ await wait_all_tasks_blocked()
576
+ proc.kill()
577
+ nursery.cancel_scope.deadline = _core.current_time() + 1.0
578
+ assert not nursery.cancel_scope.cancelled_caught
579
+ assert proc.returncode == 0 # exit status unknowable, so...
580
+ finally:
581
+ signal.signal(signal.SIGCHLD, old_sigchld)
582
+
583
+
584
+ @slow
585
+ def test_waitid_eintr() -> None:
586
+ # This only matters on PyPy (where we're coding EINTR handling
587
+ # ourselves) but the test works on all waitid platforms.
588
+ from .._subprocess_platform import wait_child_exiting
589
+
590
+ if TYPE_CHECKING and (sys.platform == "win32" or sys.platform == "darwin"):
591
+ return
592
+
593
+ if not wait_child_exiting.__module__.endswith("waitid"):
594
+ pytest.skip("waitid only")
595
+
596
+ # despite the TYPE_CHECKING early return silencing warnings about signal.SIGALRM etc
597
+ # this import is still checked on win32&darwin and raises [attr-defined].
598
+ # Linux doesn't raise [attr-defined] though, so we need [unused-ignore]
599
+ from .._subprocess_platform.waitid import ( # type: ignore[attr-defined, unused-ignore]
600
+ sync_wait_reapable,
601
+ )
602
+
603
+ got_alarm = False
604
+ sleeper = subprocess.Popen(["sleep", "3600"])
605
+
606
+ def on_alarm(sig: int, frame: FrameType | None) -> None:
607
+ nonlocal got_alarm
608
+ got_alarm = True
609
+ sleeper.kill()
610
+
611
+ old_sigalrm = signal.signal(signal.SIGALRM, on_alarm)
612
+ try:
613
+ signal.alarm(1)
614
+ sync_wait_reapable(sleeper.pid)
615
+ assert sleeper.wait(timeout=1) == -9
616
+ finally:
617
+ if sleeper.returncode is None: # pragma: no cover
618
+ # We only get here if something fails in the above;
619
+ # if the test passes, wait() will reap the process
620
+ sleeper.kill()
621
+ sleeper.wait()
622
+ signal.signal(signal.SIGALRM, old_sigalrm)
623
+
624
+
625
+ async def test_custom_deliver_cancel() -> None:
626
+ custom_deliver_cancel_called = False
627
+
628
+ async def custom_deliver_cancel(proc: Process) -> None:
629
+ nonlocal custom_deliver_cancel_called
630
+ custom_deliver_cancel_called = True
631
+ proc.terminate()
632
+ # Make sure this does get cancelled when the process exits, and that
633
+ # the process really exited.
634
+ try:
635
+ await sleep_forever()
636
+ finally:
637
+ assert proc.returncode is not None
638
+
639
+ async with _core.open_nursery() as nursery:
640
+ nursery.start_soon(
641
+ partial(run_process, SLEEP(9999), deliver_cancel=custom_deliver_cancel),
642
+ )
643
+ await wait_all_tasks_blocked()
644
+ nursery.cancel_scope.cancel()
645
+
646
+ assert custom_deliver_cancel_called
647
+
648
+
649
+ def test_bad_deliver_cancel() -> None:
650
+ async def custom_deliver_cancel(proc: Process) -> None:
651
+ proc.terminate()
652
+ raise ValueError("foo")
653
+
654
+ async def do_stuff() -> None:
655
+ async with _core.open_nursery() as nursery:
656
+ nursery.start_soon(
657
+ partial(run_process, SLEEP(9999), deliver_cancel=custom_deliver_cancel),
658
+ )
659
+ await wait_all_tasks_blocked()
660
+ nursery.cancel_scope.cancel()
661
+
662
+ # double wrap from our nursery + the internal nursery
663
+ with pytest.RaisesGroup(
664
+ pytest.RaisesGroup(pytest.RaisesExc(ValueError, match="^foo$"))
665
+ ):
666
+ _core.run(do_stuff, strict_exception_groups=True)
667
+
668
+
669
+ async def test_warn_on_failed_cancel_terminate(monkeypatch: pytest.MonkeyPatch) -> None:
670
+ original_terminate = Process.terminate
671
+
672
+ def broken_terminate(self: Process) -> NoReturn:
673
+ original_terminate(self)
674
+ raise OSError("whoops")
675
+
676
+ monkeypatch.setattr(Process, "terminate", broken_terminate)
677
+
678
+ with pytest.warns(RuntimeWarning, match=".*whoops.*"): # noqa: PT031
679
+ async with _core.open_nursery() as nursery:
680
+ nursery.start_soon(run_process, SLEEP(9999))
681
+ await wait_all_tasks_blocked()
682
+ nursery.cancel_scope.cancel()
683
+
684
+
685
+ @pytest.mark.skipif(not posix, reason="posix only")
686
+ async def test_warn_on_cancel_SIGKILL_escalation(
687
+ autojump_clock: MockClock,
688
+ monkeypatch: pytest.MonkeyPatch,
689
+ ) -> None:
690
+ monkeypatch.setattr(Process, "terminate", lambda *args: None)
691
+
692
+ with pytest.warns(RuntimeWarning, match=".*ignored SIGTERM.*"): # noqa: PT031
693
+ async with _core.open_nursery() as nursery:
694
+ nursery.start_soon(run_process, SLEEP(9999))
695
+ await wait_all_tasks_blocked()
696
+ nursery.cancel_scope.cancel()
697
+
698
+
699
+ # the background_process_param exercises a lot of run_process cases, but it uses
700
+ # check=False, so lets have a test that uses check=True as well
701
+ async def test_run_process_background_fail() -> None:
702
+ with pytest.RaisesGroup(subprocess.CalledProcessError):
703
+ async with _core.open_nursery() as nursery:
704
+ value = await nursery.start(run_process, EXIT_FALSE)
705
+ assert isinstance(value, Process)
706
+ proc: Process = value
707
+ assert proc.returncode == 1
708
+
709
+
710
+ @pytest.mark.skipif(
711
+ not SyncPath("/dev/fd").exists(),
712
+ reason="requires a way to iterate through open files",
713
+ )
714
+ async def test_for_leaking_fds() -> None:
715
+ gc.collect() # address possible flakiness on PyPy
716
+
717
+ starting_fds = set(SyncPath("/dev/fd").iterdir()) # noqa: ASYNC240
718
+ await run_process(EXIT_TRUE)
719
+ assert set(SyncPath("/dev/fd").iterdir()) == starting_fds # noqa: ASYNC240
720
+
721
+ with pytest.raises(subprocess.CalledProcessError):
722
+ await run_process(EXIT_FALSE)
723
+ assert set(SyncPath("/dev/fd").iterdir()) == starting_fds # noqa: ASYNC240
724
+
725
+ with pytest.raises(PermissionError):
726
+ await run_process(["/dev/fd/0"])
727
+ assert set(SyncPath("/dev/fd").iterdir()) == starting_fds # noqa: ASYNC240
728
+
729
+
730
+ async def test_run_process_internal_error(monkeypatch: pytest.MonkeyPatch) -> None:
731
+ # There's probably less extreme ways of triggering errors inside the nursery
732
+ # in run_process.
733
+ async def very_broken_open(*args: object, **kwargs: object) -> str:
734
+ return "oops"
735
+
736
+ monkeypatch.setattr(trio._subprocess, "_open_process", very_broken_open)
737
+ with pytest.RaisesGroup(AttributeError, AttributeError):
738
+ await run_process(EXIT_TRUE, capture_stdout=True)
739
+
740
+
741
+ # regression test for #2209
742
+ async def test_subprocess_pidfd_unnotified() -> None:
743
+ noticed_exit = None
744
+
745
+ async def wait_and_tell(proc: Process) -> None:
746
+ nonlocal noticed_exit
747
+ noticed_exit = Event()
748
+ await proc.wait()
749
+ noticed_exit.set()
750
+
751
+ proc = await open_process(SLEEP(9999))
752
+ async with _core.open_nursery() as nursery:
753
+ nursery.start_soon(wait_and_tell, proc)
754
+ await wait_all_tasks_blocked()
755
+ assert isinstance(noticed_exit, Event)
756
+ proc.terminate()
757
+ # without giving trio a chance to do so,
758
+ with assert_no_checkpoints():
759
+ # wait until the process has actually exited;
760
+ proc._proc.wait()
761
+ # force a call to poll (that closes the pidfd on linux)
762
+ proc.poll()
763
+ with move_on_after(5):
764
+ # Some platforms use threads to wait for exit, so it might take a bit
765
+ # for everything to notice
766
+ await noticed_exit.wait()
767
+ assert noticed_exit.is_set(), "child task wasn't woken after poll, DEADLOCK"