@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.
- package/bin/kgraph-launcher +15 -3
- package/lib/kgraph/scripts/build-bundle.sh +17 -4
- package/lib/site-packages/outcome/__init__.py +20 -0
- package/lib/site-packages/outcome/_impl.py +239 -0
- package/lib/site-packages/outcome/_util.py +33 -0
- package/lib/site-packages/outcome/_version.py +7 -0
- package/lib/site-packages/outcome/py.typed +0 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/INSTALLER +1 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE +3 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.APACHE2 +202 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.MIT +20 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/METADATA +63 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/RECORD +13 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/WHEEL +6 -0
- package/lib/site-packages/outcome-1.3.0.post0.dist-info/top_level.txt +1 -0
- package/lib/site-packages/sniffio/__init__.py +17 -0
- package/lib/site-packages/sniffio/_impl.py +95 -0
- package/lib/site-packages/sniffio/_tests/__init__.py +0 -0
- package/lib/site-packages/sniffio/_tests/test_sniffio.py +84 -0
- package/lib/site-packages/sniffio/_version.py +3 -0
- package/lib/site-packages/sniffio/py.typed +0 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/INSTALLER +1 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE +3 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 +202 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT +20 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/METADATA +104 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/RECORD +14 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/WHEEL +5 -0
- package/lib/site-packages/sniffio-1.3.1.dist-info/top_level.txt +1 -0
- package/lib/site-packages/sortedcontainers/__init__.py +74 -0
- package/lib/site-packages/sortedcontainers/sorteddict.py +812 -0
- package/lib/site-packages/sortedcontainers/sortedlist.py +2646 -0
- package/lib/site-packages/sortedcontainers/sortedset.py +733 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/INSTALLER +1 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/LICENSE +13 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/METADATA +264 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/RECORD +10 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/WHEEL +6 -0
- package/lib/site-packages/sortedcontainers-2.4.0.dist-info/top_level.txt +1 -0
- package/lib/site-packages/trio/__init__.py +133 -0
- package/lib/site-packages/trio/__main__.py +3 -0
- package/lib/site-packages/trio/_abc.py +714 -0
- package/lib/site-packages/trio/_channel.py +610 -0
- package/lib/site-packages/trio/_core/__init__.py +94 -0
- package/lib/site-packages/trio/_core/_asyncgens.py +243 -0
- package/lib/site-packages/trio/_core/_concat_tb.py +26 -0
- package/lib/site-packages/trio/_core/_entry_queue.py +223 -0
- package/lib/site-packages/trio/_core/_exceptions.py +169 -0
- package/lib/site-packages/trio/_core/_generated_instrumentation.py +50 -0
- package/lib/site-packages/trio/_core/_generated_io_epoll.py +98 -0
- package/lib/site-packages/trio/_core/_generated_io_kqueue.py +153 -0
- package/lib/site-packages/trio/_core/_generated_io_windows.py +204 -0
- package/lib/site-packages/trio/_core/_generated_run.py +269 -0
- package/lib/site-packages/trio/_core/_generated_windows_ffi.py +10 -0
- package/lib/site-packages/trio/_core/_instrumentation.py +117 -0
- package/lib/site-packages/trio/_core/_io_common.py +31 -0
- package/lib/site-packages/trio/_core/_io_epoll.py +385 -0
- package/lib/site-packages/trio/_core/_io_kqueue.py +292 -0
- package/lib/site-packages/trio/_core/_io_windows.py +1036 -0
- package/lib/site-packages/trio/_core/_ki.py +271 -0
- package/lib/site-packages/trio/_core/_local.py +104 -0
- package/lib/site-packages/trio/_core/_mock_clock.py +165 -0
- package/lib/site-packages/trio/_core/_parking_lot.py +317 -0
- package/lib/site-packages/trio/_core/_run.py +3148 -0
- package/lib/site-packages/trio/_core/_run_context.py +15 -0
- package/lib/site-packages/trio/_core/_tests/__init__.py +0 -0
- package/lib/site-packages/trio/_core/_tests/test_asyncgen.py +339 -0
- package/lib/site-packages/trio/_core/_tests/test_cancelled.py +222 -0
- package/lib/site-packages/trio/_core/_tests/test_exceptiongroup_gc.py +103 -0
- package/lib/site-packages/trio/_core/_tests/test_guest_mode.py +755 -0
- package/lib/site-packages/trio/_core/_tests/test_instrumentation.py +315 -0
- package/lib/site-packages/trio/_core/_tests/test_io.py +522 -0
- package/lib/site-packages/trio/_core/_tests/test_ki.py +703 -0
- package/lib/site-packages/trio/_core/_tests/test_local.py +118 -0
- package/lib/site-packages/trio/_core/_tests/test_mock_clock.py +193 -0
- package/lib/site-packages/trio/_core/_tests/test_parking_lot.py +389 -0
- package/lib/site-packages/trio/_core/_tests/test_run.py +3024 -0
- package/lib/site-packages/trio/_core/_tests/test_thread_cache.py +227 -0
- package/lib/site-packages/trio/_core/_tests/test_tutil.py +13 -0
- package/lib/site-packages/trio/_core/_tests/test_unbounded_queue.py +154 -0
- package/lib/site-packages/trio/_core/_tests/test_windows.py +305 -0
- package/lib/site-packages/trio/_core/_tests/tutil.py +117 -0
- package/lib/site-packages/trio/_core/_tests/type_tests/nursery_start.py +79 -0
- package/lib/site-packages/trio/_core/_tests/type_tests/run.py +51 -0
- package/lib/site-packages/trio/_core/_thread_cache.py +317 -0
- package/lib/site-packages/trio/_core/_traps.py +318 -0
- package/lib/site-packages/trio/_core/_unbounded_queue.py +163 -0
- package/lib/site-packages/trio/_core/_wakeup_socketpair.py +75 -0
- package/lib/site-packages/trio/_core/_windows_cffi.py +313 -0
- package/lib/site-packages/trio/_deprecate.py +171 -0
- package/lib/site-packages/trio/_dtls.py +1380 -0
- package/lib/site-packages/trio/_file_io.py +513 -0
- package/lib/site-packages/trio/_highlevel_generic.py +125 -0
- package/lib/site-packages/trio/_highlevel_open_tcp_listeners.py +251 -0
- package/lib/site-packages/trio/_highlevel_open_tcp_stream.py +397 -0
- package/lib/site-packages/trio/_highlevel_open_unix_stream.py +65 -0
- package/lib/site-packages/trio/_highlevel_serve_listeners.py +148 -0
- package/lib/site-packages/trio/_highlevel_socket.py +423 -0
- package/lib/site-packages/trio/_highlevel_ssl_helpers.py +180 -0
- package/lib/site-packages/trio/_path.py +289 -0
- package/lib/site-packages/trio/_repl.py +159 -0
- package/lib/site-packages/trio/_signals.py +185 -0
- package/lib/site-packages/trio/_socket.py +1326 -0
- package/lib/site-packages/trio/_ssl.py +964 -0
- package/lib/site-packages/trio/_subprocess.py +1178 -0
- package/lib/site-packages/trio/_subprocess_platform/__init__.py +123 -0
- package/lib/site-packages/trio/_subprocess_platform/kqueue.py +48 -0
- package/lib/site-packages/trio/_subprocess_platform/waitid.py +113 -0
- package/lib/site-packages/trio/_subprocess_platform/windows.py +11 -0
- package/lib/site-packages/trio/_sync.py +908 -0
- package/lib/site-packages/trio/_tests/__init__.py +0 -0
- package/lib/site-packages/trio/_tests/astrill-codesigning-cert.cer +0 -0
- package/lib/site-packages/trio/_tests/check_type_completeness.py +247 -0
- package/lib/site-packages/trio/_tests/module_with_deprecations.py +22 -0
- package/lib/site-packages/trio/_tests/pytest_plugin.py +54 -0
- package/lib/site-packages/trio/_tests/test_abc.py +72 -0
- package/lib/site-packages/trio/_tests/test_channel.py +750 -0
- package/lib/site-packages/trio/_tests/test_contextvars.py +56 -0
- package/lib/site-packages/trio/_tests/test_deprecate.py +277 -0
- package/lib/site-packages/trio/_tests/test_deprecate_strict_exception_groups_false.py +64 -0
- package/lib/site-packages/trio/_tests/test_dtls.py +950 -0
- package/lib/site-packages/trio/_tests/test_exports.py +626 -0
- package/lib/site-packages/trio/_tests/test_fakenet.py +317 -0
- package/lib/site-packages/trio/_tests/test_file_io.py +269 -0
- package/lib/site-packages/trio/_tests/test_highlevel_generic.py +98 -0
- package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_listeners.py +419 -0
- package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_stream.py +693 -0
- package/lib/site-packages/trio/_tests/test_highlevel_open_unix_stream.py +86 -0
- package/lib/site-packages/trio/_tests/test_highlevel_serve_listeners.py +186 -0
- package/lib/site-packages/trio/_tests/test_highlevel_socket.py +336 -0
- package/lib/site-packages/trio/_tests/test_highlevel_ssl_helpers.py +169 -0
- package/lib/site-packages/trio/_tests/test_path.py +279 -0
- package/lib/site-packages/trio/_tests/test_repl.py +428 -0
- package/lib/site-packages/trio/_tests/test_scheduler_determinism.py +47 -0
- package/lib/site-packages/trio/_tests/test_signals.py +186 -0
- package/lib/site-packages/trio/_tests/test_socket.py +1253 -0
- package/lib/site-packages/trio/_tests/test_ssl.py +1371 -0
- package/lib/site-packages/trio/_tests/test_subprocess.py +767 -0
- package/lib/site-packages/trio/_tests/test_sync.py +735 -0
- package/lib/site-packages/trio/_tests/test_testing.py +682 -0
- package/lib/site-packages/trio/_tests/test_testing_raisesgroup.py +1128 -0
- package/lib/site-packages/trio/_tests/test_threads.py +1173 -0
- package/lib/site-packages/trio/_tests/test_timeouts.py +281 -0
- package/lib/site-packages/trio/_tests/test_tracing.py +88 -0
- package/lib/site-packages/trio/_tests/test_trio.py +8 -0
- package/lib/site-packages/trio/_tests/test_unix_pipes.py +288 -0
- package/lib/site-packages/trio/_tests/test_util.py +349 -0
- package/lib/site-packages/trio/_tests/test_wait_for_object.py +225 -0
- package/lib/site-packages/trio/_tests/test_windows_pipes.py +112 -0
- package/lib/site-packages/trio/_tests/tools/__init__.py +0 -0
- package/lib/site-packages/trio/_tests/tools/test_gen_exports.py +179 -0
- package/lib/site-packages/trio/_tests/tools/test_mypy_annotate.py +140 -0
- package/lib/site-packages/trio/_tests/tools/test_sync_requirements.py +80 -0
- package/lib/site-packages/trio/_tests/type_tests/check_wraps.py +9 -0
- package/lib/site-packages/trio/_tests/type_tests/open_memory_channel.py +4 -0
- package/lib/site-packages/trio/_tests/type_tests/path.py +140 -0
- package/lib/site-packages/trio/_tests/type_tests/subprocesses.py +23 -0
- package/lib/site-packages/trio/_tests/type_tests/task_status.py +29 -0
- package/lib/site-packages/trio/_threads.py +610 -0
- package/lib/site-packages/trio/_timeouts.py +197 -0
- package/lib/site-packages/trio/_tools/__init__.py +0 -0
- package/lib/site-packages/trio/_tools/gen_exports.py +401 -0
- package/lib/site-packages/trio/_tools/mypy_annotate.py +126 -0
- package/lib/site-packages/trio/_tools/sync_requirements.py +98 -0
- package/lib/site-packages/trio/_tools/windows_ffi_build.py +220 -0
- package/lib/site-packages/trio/_unix_pipes.py +197 -0
- package/lib/site-packages/trio/_util.py +385 -0
- package/lib/site-packages/trio/_version.py +3 -0
- package/lib/site-packages/trio/_wait_for_object.py +67 -0
- package/lib/site-packages/trio/_windows_pipes.py +144 -0
- package/lib/site-packages/trio/abc.py +23 -0
- package/lib/site-packages/trio/from_thread.py +13 -0
- package/lib/site-packages/trio/lowlevel.py +95 -0
- package/lib/site-packages/trio/py.typed +0 -0
- package/lib/site-packages/trio/socket.py +602 -0
- package/lib/site-packages/trio/testing/__init__.py +58 -0
- package/lib/site-packages/trio/testing/_check_streams.py +570 -0
- package/lib/site-packages/trio/testing/_checkpoints.py +69 -0
- package/lib/site-packages/trio/testing/_fake_net.py +584 -0
- package/lib/site-packages/trio/testing/_memory_streams.py +633 -0
- package/lib/site-packages/trio/testing/_network.py +36 -0
- package/lib/site-packages/trio/testing/_raises_group.py +1015 -0
- package/lib/site-packages/trio/testing/_sequencer.py +87 -0
- package/lib/site-packages/trio/testing/_trio_test.py +50 -0
- package/lib/site-packages/trio/to_thread.py +4 -0
- package/lib/site-packages/trio-0.33.0.dist-info/INSTALLER +1 -0
- package/lib/site-packages/trio-0.33.0.dist-info/METADATA +186 -0
- package/lib/site-packages/trio-0.33.0.dist-info/RECORD +156 -0
- package/lib/site-packages/trio-0.33.0.dist-info/REQUESTED +0 -0
- package/lib/site-packages/trio-0.33.0.dist-info/WHEEL +5 -0
- package/lib/site-packages/trio-0.33.0.dist-info/entry_points.txt +2 -0
- package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE +3 -0
- package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.APACHE2 +202 -0
- package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.MIT +22 -0
- package/lib/site-packages/trio-0.33.0.dist-info/top_level.txt +1 -0
- 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}
|