@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,428 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import pathlib
5
+ import signal
6
+ import subprocess
7
+ import sys
8
+ from functools import partial
9
+ from typing import Protocol
10
+
11
+ import pytest
12
+
13
+ import trio._repl
14
+
15
+
16
+ class RawInput(Protocol):
17
+ def __call__(self, prompt: str = "") -> str: ...
18
+
19
+
20
+ def build_raw_input(cmds: list[str]) -> RawInput:
21
+ """
22
+ Pass in a list of strings.
23
+ Returns a callable that returns each string, each time its called
24
+ When there are not more strings to return, raise EOFError
25
+ """
26
+ cmds_iter = iter(cmds)
27
+ prompts = []
28
+
29
+ def _raw_helper(prompt: str = "") -> str:
30
+ prompts.append(prompt)
31
+ try:
32
+ return next(cmds_iter)
33
+ except StopIteration:
34
+ raise EOFError from None
35
+
36
+ return _raw_helper
37
+
38
+
39
+ def test_build_raw_input() -> None:
40
+ """Quick test of our helper function."""
41
+ raw_input = build_raw_input(["cmd1"])
42
+ assert raw_input() == "cmd1"
43
+ with pytest.raises(EOFError):
44
+ raw_input()
45
+
46
+
47
+ async def test_basic_interaction(
48
+ capsys: pytest.CaptureFixture[str],
49
+ monkeypatch: pytest.MonkeyPatch,
50
+ ) -> None:
51
+ """
52
+ Run some basic commands through the interpreter while capturing stdout.
53
+ Ensure that the interpreted prints the expected results.
54
+ """
55
+ console = trio._repl.TrioInteractiveConsole()
56
+ raw_input = build_raw_input(
57
+ [
58
+ # evaluate simple expression and recall the value
59
+ "x = 1",
60
+ "print(f'{x=}')",
61
+ # Literal gets printed
62
+ "'hello'",
63
+ # define and call sync function
64
+ "def func():",
65
+ " print(x + 1)",
66
+ "",
67
+ "func()",
68
+ # define and call async function
69
+ "async def afunc():",
70
+ " return 4",
71
+ "",
72
+ "await afunc()",
73
+ # import works
74
+ "import sys",
75
+ "sys.stdout.write('hello stdout\\n')",
76
+ ],
77
+ )
78
+ monkeypatch.setattr(console, "raw_input", raw_input)
79
+ await trio._repl.run_repl(console)
80
+ out, _err = capsys.readouterr()
81
+ assert out.splitlines() == ["x=1", "'hello'", "2", "4", "hello stdout", "13"]
82
+
83
+
84
+ async def test_system_exits_quit_interpreter(monkeypatch: pytest.MonkeyPatch) -> None:
85
+ console = trio._repl.TrioInteractiveConsole()
86
+ raw_input = build_raw_input(
87
+ [
88
+ "raise SystemExit",
89
+ ],
90
+ )
91
+ monkeypatch.setattr(console, "raw_input", raw_input)
92
+ with pytest.raises(SystemExit):
93
+ await trio._repl.run_repl(console)
94
+
95
+
96
+ async def test_KI_interrupts(
97
+ capsys: pytest.CaptureFixture[str],
98
+ monkeypatch: pytest.MonkeyPatch,
99
+ ) -> None:
100
+ console = trio._repl.TrioInteractiveConsole()
101
+ raw_input = build_raw_input(
102
+ [
103
+ "import signal, trio, trio.lowlevel",
104
+ "async def f():",
105
+ " trio.lowlevel.spawn_system_task("
106
+ " trio.to_thread.run_sync,"
107
+ " signal.raise_signal, signal.SIGINT,"
108
+ " )", # just awaiting this kills the test runner?!
109
+ " await trio.sleep_forever()",
110
+ " print('should not see this')",
111
+ "",
112
+ "await f()",
113
+ "print('AFTER KeyboardInterrupt')",
114
+ ],
115
+ )
116
+ monkeypatch.setattr(console, "raw_input", raw_input)
117
+ await trio._repl.run_repl(console)
118
+ out, err = capsys.readouterr()
119
+ assert "KeyboardInterrupt" in err
120
+ assert "should" not in out
121
+ assert "AFTER KeyboardInterrupt" in out
122
+
123
+
124
+ async def test_system_exits_in_exc_group(
125
+ capsys: pytest.CaptureFixture[str],
126
+ monkeypatch: pytest.MonkeyPatch,
127
+ ) -> None:
128
+ console = trio._repl.TrioInteractiveConsole()
129
+ raw_input = build_raw_input(
130
+ [
131
+ "import sys",
132
+ "if sys.version_info < (3, 11):",
133
+ " from exceptiongroup import BaseExceptionGroup",
134
+ "",
135
+ "raise BaseExceptionGroup('', [RuntimeError(), SystemExit()])",
136
+ "print('AFTER BaseExceptionGroup')",
137
+ ],
138
+ )
139
+ monkeypatch.setattr(console, "raw_input", raw_input)
140
+ await trio._repl.run_repl(console)
141
+ out, _err = capsys.readouterr()
142
+ # assert that raise SystemExit in an exception group
143
+ # doesn't quit
144
+ assert "AFTER BaseExceptionGroup" in out
145
+
146
+
147
+ async def test_system_exits_in_nested_exc_group(
148
+ capsys: pytest.CaptureFixture[str],
149
+ monkeypatch: pytest.MonkeyPatch,
150
+ ) -> None:
151
+ console = trio._repl.TrioInteractiveConsole()
152
+ raw_input = build_raw_input(
153
+ [
154
+ "import sys",
155
+ "if sys.version_info < (3, 11):",
156
+ " from exceptiongroup import BaseExceptionGroup",
157
+ "",
158
+ "raise BaseExceptionGroup(",
159
+ " '', [BaseExceptionGroup('', [RuntimeError(), SystemExit()])])",
160
+ "print('AFTER BaseExceptionGroup')",
161
+ ],
162
+ )
163
+ monkeypatch.setattr(console, "raw_input", raw_input)
164
+ await trio._repl.run_repl(console)
165
+ out, _err = capsys.readouterr()
166
+ # assert that raise SystemExit in an exception group
167
+ # doesn't quit
168
+ assert "AFTER BaseExceptionGroup" in out
169
+
170
+
171
+ async def test_base_exception_captured(
172
+ capsys: pytest.CaptureFixture[str],
173
+ monkeypatch: pytest.MonkeyPatch,
174
+ ) -> None:
175
+ console = trio._repl.TrioInteractiveConsole()
176
+ raw_input = build_raw_input(
177
+ [
178
+ # The statement after raise should still get executed
179
+ "raise BaseException",
180
+ "print('AFTER BaseException')",
181
+ ],
182
+ )
183
+ monkeypatch.setattr(console, "raw_input", raw_input)
184
+ await trio._repl.run_repl(console)
185
+ out, err = capsys.readouterr()
186
+ assert "_threads.py" not in err
187
+ assert "_repl.py" not in err
188
+ assert "AFTER BaseException" in out
189
+
190
+
191
+ async def test_exc_group_captured(
192
+ capsys: pytest.CaptureFixture[str],
193
+ monkeypatch: pytest.MonkeyPatch,
194
+ ) -> None:
195
+ console = trio._repl.TrioInteractiveConsole()
196
+ raw_input = build_raw_input(
197
+ [
198
+ # The statement after raise should still get executed
199
+ "raise ExceptionGroup('', [KeyError()])",
200
+ "print('AFTER ExceptionGroup')",
201
+ ],
202
+ )
203
+ monkeypatch.setattr(console, "raw_input", raw_input)
204
+ await trio._repl.run_repl(console)
205
+ out, _err = capsys.readouterr()
206
+ assert "AFTER ExceptionGroup" in out
207
+
208
+
209
+ async def test_base_exception_capture_from_coroutine(
210
+ capsys: pytest.CaptureFixture[str],
211
+ monkeypatch: pytest.MonkeyPatch,
212
+ ) -> None:
213
+ console = trio._repl.TrioInteractiveConsole()
214
+ raw_input = build_raw_input(
215
+ [
216
+ "async def async_func_raises_base_exception():",
217
+ " raise BaseException",
218
+ "",
219
+ # This will raise, but the statement after should still
220
+ # be executed
221
+ "await async_func_raises_base_exception()",
222
+ "print('AFTER BaseException')",
223
+ ],
224
+ )
225
+ monkeypatch.setattr(console, "raw_input", raw_input)
226
+ await trio._repl.run_repl(console)
227
+ out, err = capsys.readouterr()
228
+ assert "_threads.py" not in err
229
+ assert "_repl.py" not in err
230
+ assert "AFTER BaseException" in out
231
+
232
+
233
+ def test_main_entrypoint() -> None:
234
+ """
235
+ Basic smoke test when running via the package __main__ entrypoint.
236
+ """
237
+ repl = subprocess.run([sys.executable, "-m", "trio"], input=b"exit()")
238
+ assert repl.returncode == 0
239
+
240
+
241
+ def should_try_newline_injection() -> bool:
242
+ if sys.platform != "linux":
243
+ return False
244
+
245
+ sysctl = pathlib.Path("/proc/sys/dev/tty/legacy_tiocsti")
246
+ if not sysctl.exists(): # pragma: no cover
247
+ return True
248
+
249
+ else:
250
+ return sysctl.read_text() == "1"
251
+
252
+
253
+ @pytest.mark.skipif(
254
+ not should_try_newline_injection(),
255
+ reason="the ioctl we use is disabled in CI",
256
+ )
257
+ def test_ki_newline_injection() -> None: # TODO: test this line
258
+ # TODO: we want to remove this functionality, eg by using vendored
259
+ # pyrepls.
260
+ assert sys.platform != "win32"
261
+
262
+ import pty
263
+
264
+ # NOTE: this cannot be subprocess.Popen because pty.fork
265
+ # does some magic to set the controlling terminal.
266
+ # (which I don't know how to replicate... so I copied this
267
+ # structure from pty.spawn...)
268
+ pid, pty_fd = pty.fork() # type: ignore[attr-defined,unused-ignore]
269
+ if pid == 0:
270
+ os.execlp(sys.executable, *[sys.executable, "-u", "-m", "trio"])
271
+
272
+ # setup:
273
+ buffer = b""
274
+ while not buffer.endswith(b"import trio\r\n>>> "):
275
+ buffer += os.read(pty_fd, 4096)
276
+
277
+ # sanity check:
278
+ print(buffer.decode())
279
+ buffer = b""
280
+ os.write(pty_fd, b'print("hello!")\n')
281
+ while not buffer.endswith(b">>> "):
282
+ buffer += os.read(pty_fd, 4096)
283
+
284
+ assert buffer.count(b"hello!") == 2
285
+
286
+ # press ctrl+c
287
+ print(buffer.decode())
288
+ buffer = b""
289
+ os.kill(pid, signal.SIGINT)
290
+ while not buffer.endswith(b">>> "):
291
+ buffer += os.read(pty_fd, 4096)
292
+
293
+ assert b"KeyboardInterrupt" in buffer
294
+
295
+ # press ctrl+c later
296
+ print(buffer.decode())
297
+ buffer = b""
298
+ os.write(pty_fd, b'print("hello!")')
299
+ os.kill(pid, signal.SIGINT)
300
+ while not buffer.endswith(b">>> "):
301
+ buffer += os.read(pty_fd, 4096)
302
+
303
+ assert b"KeyboardInterrupt" in buffer
304
+ print(buffer.decode())
305
+ os.close(pty_fd)
306
+ os.waitpid(pid, 0)[1]
307
+
308
+
309
+ async def test_ki_in_repl() -> None:
310
+ async with trio.open_nursery() as nursery:
311
+ proc = await nursery.start(
312
+ partial(
313
+ trio.run_process,
314
+ [sys.executable, "-u", "-m", "trio"],
315
+ stdout=subprocess.PIPE,
316
+ stderr=subprocess.STDOUT,
317
+ stdin=subprocess.PIPE,
318
+ creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if sys.platform == "win32" else 0, # type: ignore[attr-defined,unused-ignore]
319
+ )
320
+ )
321
+
322
+ async with proc.stdout:
323
+ # setup
324
+ buffer = b""
325
+ async for part in proc.stdout: # pragma: no branch
326
+ buffer += part
327
+ # TODO: consider making run_process stdout have some universal newlines thing
328
+ if buffer.replace(b"\r\n", b"\n").endswith(b"import trio\n>>> "):
329
+ break
330
+
331
+ # ensure things work
332
+ print(buffer.decode())
333
+ buffer = b""
334
+ await proc.stdin.send_all(b'print("hello!")\n')
335
+ async for part in proc.stdout: # pragma: no branch
336
+ buffer += part
337
+ if buffer.endswith(b">>> "):
338
+ break
339
+
340
+ assert b"hello!" in buffer
341
+ print(buffer.decode())
342
+
343
+ # this seems to be necessary on Windows for reasons
344
+ # (the parents of process groups ignore ctrl+c by default...)
345
+ if sys.platform == "win32":
346
+ buffer = b""
347
+ await proc.stdin.send_all(
348
+ b"import ctypes; ctypes.windll.kernel32.SetConsoleCtrlHandler(None, False)\n"
349
+ )
350
+ async for part in proc.stdout: # pragma: no branch
351
+ buffer += part
352
+ if buffer.endswith(b">>> "):
353
+ break
354
+
355
+ print(buffer.decode())
356
+
357
+ # try to decrease flakiness...
358
+ buffer = b""
359
+ await proc.stdin.send_all(
360
+ b"import coverage; trio.lowlevel.enable_ki_protection(coverage.pytracer.PyTracer._trace)\n"
361
+ )
362
+ async for part in proc.stdout: # pragma: no branch
363
+ buffer += part
364
+ if buffer.endswith(b">>> "):
365
+ break
366
+
367
+ print(buffer.decode())
368
+
369
+ # ensure that ctrl+c on a prompt works
370
+ # NOTE: for some reason, signal.SIGINT doesn't work for this test.
371
+ # Using CTRL_C_EVENT is also why we need subprocess.CREATE_NEW_PROCESS_GROUP
372
+ signal_sent = signal.CTRL_C_EVENT if sys.platform == "win32" else signal.SIGINT # type: ignore[attr-defined,unused-ignore]
373
+ os.kill(proc.pid, signal_sent)
374
+ if sys.platform == "win32":
375
+ # we rely on EOFError which... doesn't happen with pipes.
376
+ # I'm not sure how to fix it...
377
+ await proc.stdin.send_all(b"\n")
378
+ else:
379
+ # we test injection separately
380
+ await proc.stdin.send_all(b"\n")
381
+
382
+ buffer = b""
383
+ async for part in proc.stdout: # pragma: no branch
384
+ buffer += part
385
+ if buffer.endswith(b">>> "):
386
+ break
387
+
388
+ assert b"KeyboardInterrupt" in buffer
389
+
390
+ # ensure ctrl+c while a command runs works
391
+ print(buffer.decode())
392
+ await proc.stdin.send_all(b'print("READY"); await trio.sleep_forever()\n')
393
+ killed = False
394
+ buffer = b""
395
+ async for part in proc.stdout: # pragma: no branch
396
+ buffer += part
397
+ if buffer.replace(b"\r\n", b"\n").endswith(b"READY\n") and not killed:
398
+ os.kill(proc.pid, signal_sent)
399
+ killed = True
400
+ if buffer.endswith(b">>> "):
401
+ break
402
+
403
+ assert b"trio" in buffer
404
+ assert b"KeyboardInterrupt" in buffer
405
+
406
+ # make sure it works for sync commands too
407
+ # (though this would be hard to break)
408
+ print(buffer.decode())
409
+ await proc.stdin.send_all(
410
+ b'import time; print("READY"); time.sleep(99999)\n'
411
+ )
412
+ killed = False
413
+ buffer = b""
414
+ async for part in proc.stdout: # pragma: no branch
415
+ buffer += part
416
+ if buffer.replace(b"\r\n", b"\n").endswith(b"READY\n") and not killed:
417
+ os.kill(proc.pid, signal_sent)
418
+ killed = True
419
+ if buffer.endswith(b">>> "):
420
+ break
421
+
422
+ assert b"Traceback" in buffer
423
+ assert b"KeyboardInterrupt" in buffer
424
+
425
+ print(buffer.decode())
426
+
427
+ # kill the process
428
+ nursery.cancel_scope.cancel()
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import trio
6
+
7
+ if TYPE_CHECKING:
8
+ import pytest
9
+
10
+
11
+ async def scheduler_trace() -> tuple[tuple[str, int], ...]:
12
+ """Returns a scheduler-dependent value we can use to check determinism."""
13
+ trace = []
14
+
15
+ async def tracer(name: str) -> None:
16
+ for i in range(50):
17
+ trace.append((name, i))
18
+ await trio.lowlevel.checkpoint()
19
+
20
+ async with trio.open_nursery() as nursery:
21
+ for i in range(5):
22
+ nursery.start_soon(tracer, str(i))
23
+
24
+ return tuple(trace)
25
+
26
+
27
+ def test_the_trio_scheduler_is_not_deterministic() -> None:
28
+ # At least, not yet. See https://github.com/python-trio/trio/issues/32
29
+ traces = [trio.run(scheduler_trace) for _ in range(10)]
30
+ assert len(set(traces)) == len(traces)
31
+
32
+
33
+ def test_the_trio_scheduler_is_deterministic_if_seeded(
34
+ monkeypatch: pytest.MonkeyPatch,
35
+ ) -> None:
36
+ monkeypatch.setattr(trio._core._run, "_ALLOW_DETERMINISTIC_SCHEDULING", True)
37
+ traces = []
38
+ for _ in range(10):
39
+ state = trio._core._run._r.getstate()
40
+ try:
41
+ trio._core._run._r.seed(0)
42
+ traces.append(trio.run(scheduler_trace))
43
+ finally:
44
+ trio._core._run._r.setstate(state)
45
+
46
+ assert len(traces) == 10
47
+ assert len(set(traces)) == 1
@@ -0,0 +1,186 @@
1
+ from __future__ import annotations
2
+
3
+ import signal
4
+ from typing import TYPE_CHECKING, NoReturn
5
+
6
+ import pytest
7
+
8
+ import trio
9
+
10
+ from .. import _core
11
+ from .._signals import _signal_handler, get_pending_signal_count, open_signal_receiver
12
+
13
+ if TYPE_CHECKING:
14
+ from types import FrameType
15
+
16
+
17
+ async def test_open_signal_receiver() -> None:
18
+ orig = signal.getsignal(signal.SIGILL)
19
+ with open_signal_receiver(signal.SIGILL) as receiver:
20
+ # Raise it a few times, to exercise signal coalescing, both at the
21
+ # call_soon level and at the SignalQueue level
22
+ signal.raise_signal(signal.SIGILL)
23
+ signal.raise_signal(signal.SIGILL)
24
+ await _core.wait_all_tasks_blocked()
25
+ signal.raise_signal(signal.SIGILL)
26
+ await _core.wait_all_tasks_blocked()
27
+ async for signum in receiver: # pragma: no branch
28
+ assert signum == signal.SIGILL
29
+ break
30
+ assert get_pending_signal_count(receiver) == 0
31
+ signal.raise_signal(signal.SIGILL)
32
+ async for signum in receiver: # pragma: no branch
33
+ assert signum == signal.SIGILL
34
+ break
35
+ assert get_pending_signal_count(receiver) == 0
36
+ with pytest.raises(RuntimeError):
37
+ await receiver.__anext__()
38
+ assert signal.getsignal(signal.SIGILL) is orig
39
+
40
+
41
+ async def test_open_signal_receiver_restore_handler_after_one_bad_signal() -> None:
42
+ orig = signal.getsignal(signal.SIGILL)
43
+ with pytest.raises(
44
+ ValueError,
45
+ match=r"(signal number out of range|invalid signal value)$",
46
+ ):
47
+ with open_signal_receiver(signal.SIGILL, 1234567):
48
+ pass # pragma: no cover
49
+ # Still restored even if we errored out
50
+ assert signal.getsignal(signal.SIGILL) is orig
51
+
52
+
53
+ def test_open_signal_receiver_empty_fail() -> None:
54
+ with pytest.raises(TypeError, match="No signals were provided"):
55
+ with open_signal_receiver():
56
+ pass
57
+
58
+
59
+ async def test_open_signal_receiver_restore_handler_after_duplicate_signal() -> None:
60
+ orig = signal.getsignal(signal.SIGILL)
61
+ with open_signal_receiver(signal.SIGILL, signal.SIGILL):
62
+ pass
63
+ # Still restored correctly
64
+ assert signal.getsignal(signal.SIGILL) is orig
65
+
66
+
67
+ async def test_catch_signals_wrong_thread() -> None:
68
+ async def naughty() -> None:
69
+ with open_signal_receiver(signal.SIGINT):
70
+ pass # pragma: no cover
71
+
72
+ with pytest.raises(RuntimeError):
73
+ await trio.to_thread.run_sync(trio.run, naughty)
74
+
75
+
76
+ async def test_open_signal_receiver_conflict() -> None:
77
+ with pytest.RaisesGroup(trio.BusyResourceError):
78
+ with open_signal_receiver(signal.SIGILL) as receiver:
79
+ async with trio.open_nursery() as nursery:
80
+ nursery.start_soon(receiver.__anext__)
81
+ nursery.start_soon(receiver.__anext__)
82
+
83
+
84
+ # Blocks until all previous calls to run_sync_soon(idempotent=True) have been
85
+ # processed.
86
+ async def wait_run_sync_soon_idempotent_queue_barrier() -> None:
87
+ ev = trio.Event()
88
+ token = _core.current_trio_token()
89
+ token.run_sync_soon(ev.set, idempotent=True)
90
+ await ev.wait()
91
+
92
+
93
+ async def test_open_signal_receiver_no_starvation() -> None:
94
+ # Set up a situation where there are always 2 pending signals available to
95
+ # report, and make sure that instead of getting the same signal reported
96
+ # over and over, it alternates between reporting both of them.
97
+ with open_signal_receiver(signal.SIGILL, signal.SIGFPE) as receiver:
98
+ try:
99
+ print(signal.getsignal(signal.SIGILL))
100
+ previous = None
101
+ for _ in range(10):
102
+ signal.raise_signal(signal.SIGILL)
103
+ signal.raise_signal(signal.SIGFPE)
104
+ await wait_run_sync_soon_idempotent_queue_barrier()
105
+ if previous is None:
106
+ previous = await receiver.__anext__()
107
+ else:
108
+ got = await receiver.__anext__()
109
+ assert got in [signal.SIGILL, signal.SIGFPE]
110
+ assert got != previous
111
+ previous = got
112
+ # Clear out the last signal so that it doesn't get redelivered
113
+ while get_pending_signal_count(receiver) != 0:
114
+ await receiver.__anext__()
115
+ except BaseException: # pragma: no cover
116
+ # If there's an unhandled exception above, then exiting the
117
+ # open_signal_receiver block might cause the signal to be
118
+ # redelivered and give us a core dump instead of a traceback...
119
+ import traceback
120
+
121
+ traceback.print_exc()
122
+
123
+
124
+ async def test_catch_signals_race_condition_on_exit() -> None:
125
+ delivered_directly: set[int] = set()
126
+
127
+ def direct_handler(signo: int, frame: FrameType | None) -> None:
128
+ delivered_directly.add(signo)
129
+
130
+ print(1)
131
+ # Test the version where the call_soon *doesn't* have a chance to run
132
+ # before we exit the with block:
133
+ with _signal_handler({signal.SIGILL, signal.SIGFPE}, direct_handler):
134
+ with open_signal_receiver(signal.SIGILL, signal.SIGFPE) as receiver:
135
+ signal.raise_signal(signal.SIGILL)
136
+ signal.raise_signal(signal.SIGFPE)
137
+ await wait_run_sync_soon_idempotent_queue_barrier()
138
+ assert delivered_directly == {signal.SIGILL, signal.SIGFPE}
139
+ delivered_directly.clear()
140
+
141
+ print(2)
142
+ # Test the version where the call_soon *does* have a chance to run before
143
+ # we exit the with block:
144
+ with _signal_handler({signal.SIGILL, signal.SIGFPE}, direct_handler):
145
+ with open_signal_receiver(signal.SIGILL, signal.SIGFPE) as receiver:
146
+ signal.raise_signal(signal.SIGILL)
147
+ signal.raise_signal(signal.SIGFPE)
148
+ await wait_run_sync_soon_idempotent_queue_barrier()
149
+ assert get_pending_signal_count(receiver) == 2
150
+ assert delivered_directly == {signal.SIGILL, signal.SIGFPE}
151
+ delivered_directly.clear()
152
+
153
+ # Again, but with a SIG_IGN signal:
154
+
155
+ print(3)
156
+ with _signal_handler({signal.SIGILL}, signal.SIG_IGN):
157
+ with open_signal_receiver(signal.SIGILL) as receiver:
158
+ signal.raise_signal(signal.SIGILL)
159
+ await wait_run_sync_soon_idempotent_queue_barrier()
160
+ # test passes if the process reaches this point without dying
161
+
162
+ print(4)
163
+ with _signal_handler({signal.SIGILL}, signal.SIG_IGN):
164
+ with open_signal_receiver(signal.SIGILL) as receiver:
165
+ signal.raise_signal(signal.SIGILL)
166
+ await wait_run_sync_soon_idempotent_queue_barrier()
167
+ assert get_pending_signal_count(receiver) == 1
168
+ # test passes if the process reaches this point without dying
169
+
170
+ # Check exception chaining if there are multiple exception-raising
171
+ # handlers
172
+ def raise_handler(signum: int, frame: FrameType | None) -> NoReturn:
173
+ raise RuntimeError(signum)
174
+
175
+ with _signal_handler({signal.SIGILL, signal.SIGFPE}, raise_handler):
176
+ with pytest.raises(RuntimeError) as excinfo:
177
+ with open_signal_receiver(signal.SIGILL, signal.SIGFPE) as receiver:
178
+ signal.raise_signal(signal.SIGILL)
179
+ signal.raise_signal(signal.SIGFPE)
180
+ await wait_run_sync_soon_idempotent_queue_barrier()
181
+ assert get_pending_signal_count(receiver) == 2
182
+ exc = excinfo.value
183
+ signums = {exc.args[0]}
184
+ assert isinstance(exc.__context__, RuntimeError)
185
+ signums.add(exc.__context__.args[0])
186
+ assert signums == {signal.SIGILL, signal.SIGFPE}