@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,750 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
import trio
|
|
9
|
+
from trio import EndOfChannel, as_safe_channel, open_memory_channel
|
|
10
|
+
|
|
11
|
+
from ..testing import assert_checkpoints, wait_all_tasks_blocked
|
|
12
|
+
|
|
13
|
+
if sys.version_info < (3, 11):
|
|
14
|
+
from exceptiongroup import ExceptionGroup
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from collections.abc import AsyncGenerator
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def test_channel() -> None:
|
|
21
|
+
with pytest.raises(TypeError):
|
|
22
|
+
open_memory_channel(1.0)
|
|
23
|
+
with pytest.raises(ValueError, match=r"^max_buffer_size must be >= 0$"):
|
|
24
|
+
open_memory_channel(-1)
|
|
25
|
+
|
|
26
|
+
s, r = open_memory_channel[int | str | None](2)
|
|
27
|
+
repr(s) # smoke test
|
|
28
|
+
repr(r) # smoke test
|
|
29
|
+
|
|
30
|
+
s.send_nowait(1)
|
|
31
|
+
with assert_checkpoints():
|
|
32
|
+
await s.send(2)
|
|
33
|
+
with pytest.raises(trio.WouldBlock):
|
|
34
|
+
s.send_nowait(None)
|
|
35
|
+
|
|
36
|
+
with assert_checkpoints():
|
|
37
|
+
assert await r.receive() == 1
|
|
38
|
+
assert r.receive_nowait() == 2
|
|
39
|
+
with pytest.raises(trio.WouldBlock):
|
|
40
|
+
r.receive_nowait()
|
|
41
|
+
|
|
42
|
+
s.send_nowait("last")
|
|
43
|
+
await s.aclose()
|
|
44
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
45
|
+
await s.send("too late")
|
|
46
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
47
|
+
s.send_nowait("too late")
|
|
48
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
49
|
+
s.clone()
|
|
50
|
+
await s.aclose()
|
|
51
|
+
|
|
52
|
+
assert r.receive_nowait() == "last"
|
|
53
|
+
with pytest.raises(EndOfChannel):
|
|
54
|
+
await r.receive()
|
|
55
|
+
await r.aclose()
|
|
56
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
57
|
+
await r.receive()
|
|
58
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
59
|
+
r.receive_nowait()
|
|
60
|
+
await r.aclose()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def test_553(autojump_clock: trio.abc.Clock) -> None:
|
|
64
|
+
s, r = open_memory_channel[str](1)
|
|
65
|
+
with trio.move_on_after(10) as timeout_scope:
|
|
66
|
+
await r.receive()
|
|
67
|
+
assert timeout_scope.cancelled_caught
|
|
68
|
+
await s.send("Test for PR #553")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async def test_channel_multiple_producers() -> None:
|
|
72
|
+
async def producer(send_channel: trio.MemorySendChannel[int], i: int) -> None:
|
|
73
|
+
# We close our handle when we're done with it
|
|
74
|
+
async with send_channel:
|
|
75
|
+
for j in range(3 * i, 3 * (i + 1)):
|
|
76
|
+
await send_channel.send(j)
|
|
77
|
+
|
|
78
|
+
send_channel, receive_channel = open_memory_channel[int](0)
|
|
79
|
+
async with trio.open_nursery() as nursery:
|
|
80
|
+
# We hand out clones to all the new producers, and then close the
|
|
81
|
+
# original.
|
|
82
|
+
async with send_channel:
|
|
83
|
+
for i in range(10):
|
|
84
|
+
nursery.start_soon(producer, send_channel.clone(), i)
|
|
85
|
+
|
|
86
|
+
got = [value async for value in receive_channel]
|
|
87
|
+
|
|
88
|
+
got.sort()
|
|
89
|
+
assert got == list(range(30))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
async def test_channel_multiple_consumers() -> None:
|
|
93
|
+
successful_receivers = set()
|
|
94
|
+
received = []
|
|
95
|
+
|
|
96
|
+
async def consumer(receive_channel: trio.MemoryReceiveChannel[int], i: int) -> None:
|
|
97
|
+
async for value in receive_channel:
|
|
98
|
+
successful_receivers.add(i)
|
|
99
|
+
received.append(value)
|
|
100
|
+
|
|
101
|
+
async with trio.open_nursery() as nursery:
|
|
102
|
+
send_channel, receive_channel = trio.open_memory_channel[int](1)
|
|
103
|
+
async with send_channel:
|
|
104
|
+
for i in range(5):
|
|
105
|
+
nursery.start_soon(consumer, receive_channel, i)
|
|
106
|
+
await wait_all_tasks_blocked()
|
|
107
|
+
for i in range(10):
|
|
108
|
+
await send_channel.send(i)
|
|
109
|
+
|
|
110
|
+
assert successful_receivers == set(range(5))
|
|
111
|
+
assert len(received) == 10
|
|
112
|
+
assert set(received) == set(range(10))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
async def test_close_basics() -> None:
|
|
116
|
+
async def send_block(
|
|
117
|
+
s: trio.MemorySendChannel[None],
|
|
118
|
+
expect: type[BaseException],
|
|
119
|
+
) -> None:
|
|
120
|
+
with pytest.raises(expect):
|
|
121
|
+
await s.send(None)
|
|
122
|
+
|
|
123
|
+
# closing send -> other send gets ClosedResourceError
|
|
124
|
+
s, r = open_memory_channel[None](0)
|
|
125
|
+
async with trio.open_nursery() as nursery:
|
|
126
|
+
nursery.start_soon(send_block, s, trio.ClosedResourceError)
|
|
127
|
+
await wait_all_tasks_blocked()
|
|
128
|
+
await s.aclose()
|
|
129
|
+
|
|
130
|
+
# and it's persistent
|
|
131
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
132
|
+
s.send_nowait(None)
|
|
133
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
134
|
+
await s.send(None)
|
|
135
|
+
|
|
136
|
+
# and receive gets EndOfChannel
|
|
137
|
+
with pytest.raises(EndOfChannel):
|
|
138
|
+
r.receive_nowait()
|
|
139
|
+
with pytest.raises(EndOfChannel):
|
|
140
|
+
await r.receive()
|
|
141
|
+
|
|
142
|
+
# closing receive -> send gets BrokenResourceError
|
|
143
|
+
s, r = open_memory_channel[None](0)
|
|
144
|
+
async with trio.open_nursery() as nursery:
|
|
145
|
+
nursery.start_soon(send_block, s, trio.BrokenResourceError)
|
|
146
|
+
await wait_all_tasks_blocked()
|
|
147
|
+
await r.aclose()
|
|
148
|
+
|
|
149
|
+
# and it's persistent
|
|
150
|
+
with pytest.raises(trio.BrokenResourceError):
|
|
151
|
+
s.send_nowait(None)
|
|
152
|
+
with pytest.raises(trio.BrokenResourceError):
|
|
153
|
+
await s.send(None)
|
|
154
|
+
|
|
155
|
+
# closing receive -> other receive gets ClosedResourceError
|
|
156
|
+
async def receive_block(r: trio.MemoryReceiveChannel[int]) -> None:
|
|
157
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
158
|
+
await r.receive()
|
|
159
|
+
|
|
160
|
+
_s2, r2 = open_memory_channel[int](0)
|
|
161
|
+
async with trio.open_nursery() as nursery:
|
|
162
|
+
nursery.start_soon(receive_block, r2)
|
|
163
|
+
await wait_all_tasks_blocked()
|
|
164
|
+
await r2.aclose()
|
|
165
|
+
|
|
166
|
+
# and it's persistent
|
|
167
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
168
|
+
r2.receive_nowait()
|
|
169
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
170
|
+
await r2.receive()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
async def test_close_sync() -> None:
|
|
174
|
+
async def send_block(
|
|
175
|
+
s: trio.MemorySendChannel[None],
|
|
176
|
+
expect: type[BaseException],
|
|
177
|
+
) -> None:
|
|
178
|
+
with pytest.raises(expect):
|
|
179
|
+
await s.send(None)
|
|
180
|
+
|
|
181
|
+
# closing send -> other send gets ClosedResourceError
|
|
182
|
+
s, r = open_memory_channel[None](0)
|
|
183
|
+
async with trio.open_nursery() as nursery:
|
|
184
|
+
nursery.start_soon(send_block, s, trio.ClosedResourceError)
|
|
185
|
+
await wait_all_tasks_blocked()
|
|
186
|
+
s.close()
|
|
187
|
+
|
|
188
|
+
# and it's persistent
|
|
189
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
190
|
+
s.send_nowait(None)
|
|
191
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
192
|
+
await s.send(None)
|
|
193
|
+
|
|
194
|
+
# and receive gets EndOfChannel
|
|
195
|
+
with pytest.raises(EndOfChannel):
|
|
196
|
+
r.receive_nowait()
|
|
197
|
+
with pytest.raises(EndOfChannel):
|
|
198
|
+
await r.receive()
|
|
199
|
+
|
|
200
|
+
# closing receive -> send gets BrokenResourceError
|
|
201
|
+
s, r = open_memory_channel[None](0)
|
|
202
|
+
async with trio.open_nursery() as nursery:
|
|
203
|
+
nursery.start_soon(send_block, s, trio.BrokenResourceError)
|
|
204
|
+
await wait_all_tasks_blocked()
|
|
205
|
+
r.close()
|
|
206
|
+
|
|
207
|
+
# and it's persistent
|
|
208
|
+
with pytest.raises(trio.BrokenResourceError):
|
|
209
|
+
s.send_nowait(None)
|
|
210
|
+
with pytest.raises(trio.BrokenResourceError):
|
|
211
|
+
await s.send(None)
|
|
212
|
+
|
|
213
|
+
# closing receive -> other receive gets ClosedResourceError
|
|
214
|
+
async def receive_block(r: trio.MemoryReceiveChannel[None]) -> None:
|
|
215
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
216
|
+
await r.receive()
|
|
217
|
+
|
|
218
|
+
s, r = open_memory_channel[None](0)
|
|
219
|
+
async with trio.open_nursery() as nursery:
|
|
220
|
+
nursery.start_soon(receive_block, r)
|
|
221
|
+
await wait_all_tasks_blocked()
|
|
222
|
+
r.close()
|
|
223
|
+
|
|
224
|
+
# and it's persistent
|
|
225
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
226
|
+
r.receive_nowait()
|
|
227
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
228
|
+
await r.receive()
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
async def test_receive_channel_clone_and_close() -> None:
|
|
232
|
+
s, r = open_memory_channel[None](10)
|
|
233
|
+
|
|
234
|
+
r2 = r.clone()
|
|
235
|
+
r3 = r.clone()
|
|
236
|
+
|
|
237
|
+
s.send_nowait(None)
|
|
238
|
+
await r.aclose()
|
|
239
|
+
with r2:
|
|
240
|
+
pass
|
|
241
|
+
|
|
242
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
243
|
+
r.clone()
|
|
244
|
+
|
|
245
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
246
|
+
r2.clone()
|
|
247
|
+
|
|
248
|
+
# Can still send, r3 is still open
|
|
249
|
+
s.send_nowait(None)
|
|
250
|
+
|
|
251
|
+
await r3.aclose()
|
|
252
|
+
|
|
253
|
+
# But now the receiver is really closed
|
|
254
|
+
with pytest.raises(trio.BrokenResourceError):
|
|
255
|
+
s.send_nowait(None)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
async def test_close_multiple_send_handles() -> None:
|
|
259
|
+
# With multiple send handles, closing one handle only wakes senders on
|
|
260
|
+
# that handle, but others can continue just fine
|
|
261
|
+
s1, r = open_memory_channel[str](0)
|
|
262
|
+
s2 = s1.clone()
|
|
263
|
+
|
|
264
|
+
async def send_will_close() -> None:
|
|
265
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
266
|
+
await s1.send("nope")
|
|
267
|
+
|
|
268
|
+
async def send_will_succeed() -> None:
|
|
269
|
+
await s2.send("ok")
|
|
270
|
+
|
|
271
|
+
async with trio.open_nursery() as nursery:
|
|
272
|
+
nursery.start_soon(send_will_close)
|
|
273
|
+
nursery.start_soon(send_will_succeed)
|
|
274
|
+
await wait_all_tasks_blocked()
|
|
275
|
+
await s1.aclose()
|
|
276
|
+
assert await r.receive() == "ok"
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
async def test_close_multiple_receive_handles() -> None:
|
|
280
|
+
# With multiple receive handles, closing one handle only wakes receivers on
|
|
281
|
+
# that handle, but others can continue just fine
|
|
282
|
+
s, r1 = open_memory_channel[str](0)
|
|
283
|
+
r2 = r1.clone()
|
|
284
|
+
|
|
285
|
+
async def receive_will_close() -> None:
|
|
286
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
287
|
+
await r1.receive()
|
|
288
|
+
|
|
289
|
+
async def receive_will_succeed() -> None:
|
|
290
|
+
assert await r2.receive() == "ok"
|
|
291
|
+
|
|
292
|
+
async with trio.open_nursery() as nursery:
|
|
293
|
+
nursery.start_soon(receive_will_close)
|
|
294
|
+
nursery.start_soon(receive_will_succeed)
|
|
295
|
+
await wait_all_tasks_blocked()
|
|
296
|
+
await r1.aclose()
|
|
297
|
+
await s.send("ok")
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
async def test_inf_capacity() -> None:
|
|
301
|
+
send, receive = open_memory_channel[int](float("inf"))
|
|
302
|
+
|
|
303
|
+
# It's accepted, and we can send all day without blocking
|
|
304
|
+
with send:
|
|
305
|
+
for i in range(10):
|
|
306
|
+
send.send_nowait(i)
|
|
307
|
+
|
|
308
|
+
got = [i async for i in receive]
|
|
309
|
+
assert got == list(range(10))
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
async def test_statistics() -> None:
|
|
313
|
+
s, r = open_memory_channel[None](2)
|
|
314
|
+
|
|
315
|
+
assert s.statistics() == r.statistics()
|
|
316
|
+
stats = s.statistics()
|
|
317
|
+
assert stats.current_buffer_used == 0
|
|
318
|
+
assert stats.max_buffer_size == 2
|
|
319
|
+
assert stats.open_send_channels == 1
|
|
320
|
+
assert stats.open_receive_channels == 1
|
|
321
|
+
assert stats.tasks_waiting_send == 0
|
|
322
|
+
assert stats.tasks_waiting_receive == 0
|
|
323
|
+
|
|
324
|
+
s.send_nowait(None)
|
|
325
|
+
assert s.statistics().current_buffer_used == 1
|
|
326
|
+
|
|
327
|
+
s2 = s.clone()
|
|
328
|
+
assert s.statistics().open_send_channels == 2
|
|
329
|
+
await s.aclose()
|
|
330
|
+
assert s2.statistics().open_send_channels == 1
|
|
331
|
+
|
|
332
|
+
r2 = r.clone()
|
|
333
|
+
assert s2.statistics().open_receive_channels == 2
|
|
334
|
+
await r2.aclose()
|
|
335
|
+
assert s2.statistics().open_receive_channels == 1
|
|
336
|
+
|
|
337
|
+
async with trio.open_nursery() as nursery:
|
|
338
|
+
s2.send_nowait(None) # fill up the buffer
|
|
339
|
+
assert s.statistics().current_buffer_used == 2
|
|
340
|
+
nursery.start_soon(s2.send, None)
|
|
341
|
+
nursery.start_soon(s2.send, None)
|
|
342
|
+
await wait_all_tasks_blocked()
|
|
343
|
+
assert s.statistics().tasks_waiting_send == 2
|
|
344
|
+
nursery.cancel_scope.cancel()
|
|
345
|
+
assert s.statistics().tasks_waiting_send == 0
|
|
346
|
+
|
|
347
|
+
# empty out the buffer again
|
|
348
|
+
try:
|
|
349
|
+
while True:
|
|
350
|
+
r.receive_nowait()
|
|
351
|
+
except trio.WouldBlock:
|
|
352
|
+
pass
|
|
353
|
+
|
|
354
|
+
async with trio.open_nursery() as nursery:
|
|
355
|
+
nursery.start_soon(r.receive)
|
|
356
|
+
await wait_all_tasks_blocked()
|
|
357
|
+
assert s.statistics().tasks_waiting_receive == 1
|
|
358
|
+
nursery.cancel_scope.cancel()
|
|
359
|
+
assert s.statistics().tasks_waiting_receive == 0
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
async def test_channel_fairness() -> None:
|
|
363
|
+
# We can remove an item we just sent, and send an item back in after, if
|
|
364
|
+
# no-one else is waiting.
|
|
365
|
+
s, r = open_memory_channel[int | None](1)
|
|
366
|
+
s.send_nowait(1)
|
|
367
|
+
assert r.receive_nowait() == 1
|
|
368
|
+
s.send_nowait(2)
|
|
369
|
+
assert r.receive_nowait() == 2
|
|
370
|
+
|
|
371
|
+
# But if someone else is waiting to receive, then they "own" the item we
|
|
372
|
+
# send, so we can't receive it (even though we run first):
|
|
373
|
+
|
|
374
|
+
result: int | None = None
|
|
375
|
+
|
|
376
|
+
async def do_receive(r: trio.MemoryReceiveChannel[int | None]) -> None:
|
|
377
|
+
nonlocal result
|
|
378
|
+
result = await r.receive()
|
|
379
|
+
|
|
380
|
+
async with trio.open_nursery() as nursery:
|
|
381
|
+
nursery.start_soon(do_receive, r)
|
|
382
|
+
await wait_all_tasks_blocked()
|
|
383
|
+
s.send_nowait(2)
|
|
384
|
+
with pytest.raises(trio.WouldBlock):
|
|
385
|
+
r.receive_nowait()
|
|
386
|
+
assert result == 2
|
|
387
|
+
|
|
388
|
+
# And the analogous situation for send: if we free up a space, we can't
|
|
389
|
+
# immediately send something in it if someone is already waiting to do
|
|
390
|
+
# that
|
|
391
|
+
s, r = open_memory_channel[int | None](1)
|
|
392
|
+
s.send_nowait(1)
|
|
393
|
+
with pytest.raises(trio.WouldBlock):
|
|
394
|
+
s.send_nowait(None)
|
|
395
|
+
async with trio.open_nursery() as nursery:
|
|
396
|
+
nursery.start_soon(s.send, 2)
|
|
397
|
+
await wait_all_tasks_blocked()
|
|
398
|
+
assert r.receive_nowait() == 1
|
|
399
|
+
with pytest.raises(trio.WouldBlock):
|
|
400
|
+
s.send_nowait(3)
|
|
401
|
+
assert (await r.receive()) == 2
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
async def test_unbuffered() -> None:
|
|
405
|
+
s, r = open_memory_channel[int](0)
|
|
406
|
+
with pytest.raises(trio.WouldBlock):
|
|
407
|
+
r.receive_nowait()
|
|
408
|
+
with pytest.raises(trio.WouldBlock):
|
|
409
|
+
s.send_nowait(1)
|
|
410
|
+
|
|
411
|
+
async def do_send(s: trio.MemorySendChannel[int], v: int) -> None:
|
|
412
|
+
with assert_checkpoints():
|
|
413
|
+
await s.send(v)
|
|
414
|
+
|
|
415
|
+
async with trio.open_nursery() as nursery:
|
|
416
|
+
nursery.start_soon(do_send, s, 1)
|
|
417
|
+
with assert_checkpoints():
|
|
418
|
+
assert await r.receive() == 1
|
|
419
|
+
with pytest.raises(trio.WouldBlock):
|
|
420
|
+
r.receive_nowait()
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
async def test_as_safe_channel_exhaust() -> None:
|
|
424
|
+
@as_safe_channel
|
|
425
|
+
async def agen() -> AsyncGenerator[int]:
|
|
426
|
+
yield 1
|
|
427
|
+
|
|
428
|
+
async with agen() as recv_chan:
|
|
429
|
+
async for x in recv_chan:
|
|
430
|
+
assert x == 1
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
async def test_as_safe_channel_broken_resource() -> None:
|
|
434
|
+
@as_safe_channel
|
|
435
|
+
async def agen() -> AsyncGenerator[int]:
|
|
436
|
+
yield 1
|
|
437
|
+
yield 2 # pragma: no cover
|
|
438
|
+
|
|
439
|
+
async with agen() as recv_chan:
|
|
440
|
+
assert await recv_chan.__anext__() == 1
|
|
441
|
+
|
|
442
|
+
# close the receiving channel
|
|
443
|
+
await recv_chan.aclose()
|
|
444
|
+
|
|
445
|
+
# trying to get the next element errors
|
|
446
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
447
|
+
await recv_chan.__anext__()
|
|
448
|
+
|
|
449
|
+
# but we don't get an error on exit of the cm
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
async def test_as_safe_channel_cancelled() -> None:
|
|
453
|
+
with trio.CancelScope() as cs:
|
|
454
|
+
|
|
455
|
+
@as_safe_channel
|
|
456
|
+
async def agen() -> AsyncGenerator[None]: # pragma: no cover
|
|
457
|
+
raise AssertionError(
|
|
458
|
+
"cancel before consumption means generator should not be iterated"
|
|
459
|
+
)
|
|
460
|
+
yield # indicate that we're an iterator
|
|
461
|
+
|
|
462
|
+
async with agen():
|
|
463
|
+
cs.cancel()
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
async def test_as_safe_channel_no_race() -> None:
|
|
467
|
+
# this previously led to a race condition due to
|
|
468
|
+
# https://github.com/python-trio/trio/issues/1559
|
|
469
|
+
@as_safe_channel
|
|
470
|
+
async def agen() -> AsyncGenerator[int]:
|
|
471
|
+
yield 1
|
|
472
|
+
raise ValueError("oae")
|
|
473
|
+
|
|
474
|
+
with pytest.raises(ValueError, match=r"^oae$"):
|
|
475
|
+
async with agen() as recv_chan:
|
|
476
|
+
async for x in recv_chan:
|
|
477
|
+
assert x == 1
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
async def test_as_safe_channel_buffer_size_too_small(
|
|
481
|
+
autojump_clock: trio.testing.MockClock,
|
|
482
|
+
) -> None:
|
|
483
|
+
@as_safe_channel
|
|
484
|
+
async def agen() -> AsyncGenerator[int]:
|
|
485
|
+
yield 1
|
|
486
|
+
raise AssertionError(
|
|
487
|
+
"buffer size 0 means we shouldn't be asked for another value"
|
|
488
|
+
) # pragma: no cover
|
|
489
|
+
|
|
490
|
+
with trio.move_on_after(5):
|
|
491
|
+
async with agen() as recv_chan:
|
|
492
|
+
async for x in recv_chan: # pragma: no branch
|
|
493
|
+
assert x == 1
|
|
494
|
+
await trio.sleep_forever()
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
async def test_as_safe_channel_no_interleave() -> None:
|
|
498
|
+
@as_safe_channel
|
|
499
|
+
async def agen() -> AsyncGenerator[int]:
|
|
500
|
+
yield 1
|
|
501
|
+
raise AssertionError # pragma: no cover
|
|
502
|
+
|
|
503
|
+
async with agen() as recv_chan:
|
|
504
|
+
assert await recv_chan.__anext__() == 1
|
|
505
|
+
await trio.lowlevel.checkpoint()
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
async def test_as_safe_channel_genexit_finally() -> None:
|
|
509
|
+
@as_safe_channel
|
|
510
|
+
async def agen(events: list[str]) -> AsyncGenerator[int]:
|
|
511
|
+
try:
|
|
512
|
+
yield 1
|
|
513
|
+
except BaseException as e:
|
|
514
|
+
events.append(repr(e))
|
|
515
|
+
raise
|
|
516
|
+
finally:
|
|
517
|
+
events.append("finally")
|
|
518
|
+
raise ValueError("agen")
|
|
519
|
+
|
|
520
|
+
events: list[str] = []
|
|
521
|
+
with pytest.RaisesGroup(
|
|
522
|
+
pytest.RaisesExc(ValueError, match="^agen$"),
|
|
523
|
+
pytest.RaisesExc(TypeError, match="^iterator$"),
|
|
524
|
+
) as g:
|
|
525
|
+
async with agen(events) as recv_chan:
|
|
526
|
+
async for i in recv_chan: # pragma: no branch
|
|
527
|
+
assert i == 1
|
|
528
|
+
raise TypeError("iterator")
|
|
529
|
+
|
|
530
|
+
if sys.version_info >= (3, 11):
|
|
531
|
+
assert g.value.__notes__ == [
|
|
532
|
+
"Encountered exception during cleanup of generator object, as "
|
|
533
|
+
"well as exception in the contextmanager body - unable to unwrap."
|
|
534
|
+
]
|
|
535
|
+
|
|
536
|
+
assert events == ["GeneratorExit()", "finally"]
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
async def test_as_safe_channel_nested_loop() -> None:
|
|
540
|
+
@as_safe_channel
|
|
541
|
+
async def agen() -> AsyncGenerator[int]:
|
|
542
|
+
for i in range(2):
|
|
543
|
+
yield i
|
|
544
|
+
|
|
545
|
+
ii = 0
|
|
546
|
+
async with agen() as recv_chan1:
|
|
547
|
+
async for i in recv_chan1:
|
|
548
|
+
async with agen() as recv_chan:
|
|
549
|
+
jj = 0
|
|
550
|
+
async for j in recv_chan:
|
|
551
|
+
assert (i, j) == (ii, jj)
|
|
552
|
+
jj += 1
|
|
553
|
+
ii += 1
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
async def test_as_safe_channel_doesnt_leak_cancellation() -> None:
|
|
557
|
+
@as_safe_channel
|
|
558
|
+
async def agen() -> AsyncGenerator[None]:
|
|
559
|
+
yield
|
|
560
|
+
with trio.CancelScope() as cscope:
|
|
561
|
+
cscope.cancel()
|
|
562
|
+
yield
|
|
563
|
+
|
|
564
|
+
with pytest.raises(AssertionError):
|
|
565
|
+
async with agen() as recv_chan:
|
|
566
|
+
async for _ in recv_chan:
|
|
567
|
+
pass
|
|
568
|
+
raise AssertionError("should be reachable")
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
async def test_as_safe_channel_dont_unwrap_user_exceptiongroup() -> None:
|
|
572
|
+
@as_safe_channel
|
|
573
|
+
async def agen() -> AsyncGenerator[None]:
|
|
574
|
+
raise NotImplementedError("not entered")
|
|
575
|
+
yield # pragma: no cover
|
|
576
|
+
|
|
577
|
+
with pytest.RaisesGroup(pytest.RaisesExc(ValueError, match="bar"), match="foo"):
|
|
578
|
+
async with agen() as _:
|
|
579
|
+
raise ExceptionGroup("foo", [ValueError("bar")])
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
async def test_as_safe_channel_multiple_receiver() -> None:
|
|
583
|
+
event = trio.Event()
|
|
584
|
+
|
|
585
|
+
@as_safe_channel
|
|
586
|
+
async def agen() -> AsyncGenerator[int]:
|
|
587
|
+
await event.wait()
|
|
588
|
+
yield 0
|
|
589
|
+
yield 1
|
|
590
|
+
|
|
591
|
+
async def handle_value(
|
|
592
|
+
recv_chan: trio.abc.ReceiveChannel[int],
|
|
593
|
+
value: int,
|
|
594
|
+
task_status: trio.TaskStatus,
|
|
595
|
+
) -> None:
|
|
596
|
+
task_status.started()
|
|
597
|
+
assert await recv_chan.receive() == value
|
|
598
|
+
|
|
599
|
+
async with agen() as recv_chan:
|
|
600
|
+
async with trio.open_nursery() as nursery:
|
|
601
|
+
await nursery.start(handle_value, recv_chan, 0)
|
|
602
|
+
await nursery.start(handle_value, recv_chan, 1)
|
|
603
|
+
event.set()
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
async def test_as_safe_channel_multi_cancel() -> None:
|
|
607
|
+
@as_safe_channel
|
|
608
|
+
async def agen(events: list[str]) -> AsyncGenerator[None]:
|
|
609
|
+
try:
|
|
610
|
+
yield
|
|
611
|
+
finally:
|
|
612
|
+
# this will give a warning of ASYNC120, although it's not technically a
|
|
613
|
+
# problem of swallowing existing exceptions
|
|
614
|
+
try:
|
|
615
|
+
await trio.lowlevel.checkpoint()
|
|
616
|
+
except trio.Cancelled:
|
|
617
|
+
events.append("agen cancel")
|
|
618
|
+
raise
|
|
619
|
+
|
|
620
|
+
events: list[str] = []
|
|
621
|
+
with trio.CancelScope() as cs:
|
|
622
|
+
with pytest.raises(trio.Cancelled):
|
|
623
|
+
async with agen(events) as recv_chan:
|
|
624
|
+
async for _ in recv_chan: # pragma: no branch
|
|
625
|
+
cs.cancel()
|
|
626
|
+
try:
|
|
627
|
+
await trio.lowlevel.checkpoint()
|
|
628
|
+
except trio.Cancelled:
|
|
629
|
+
events.append("body cancel")
|
|
630
|
+
raise
|
|
631
|
+
assert events == ["body cancel", "agen cancel"]
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
async def test_as_safe_channel_genexit_exception_group() -> None:
|
|
635
|
+
@as_safe_channel
|
|
636
|
+
async def agen() -> AsyncGenerator[None]:
|
|
637
|
+
try:
|
|
638
|
+
async with trio.open_nursery():
|
|
639
|
+
yield
|
|
640
|
+
except BaseException as e:
|
|
641
|
+
assert pytest.RaisesGroup(GeneratorExit).matches(e) # noqa: PT017
|
|
642
|
+
raise
|
|
643
|
+
|
|
644
|
+
async with agen() as g:
|
|
645
|
+
async for _ in g:
|
|
646
|
+
break
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
async def test_as_safe_channel_does_not_suppress_nested_genexit() -> None:
|
|
650
|
+
@as_safe_channel
|
|
651
|
+
async def agen() -> AsyncGenerator[None]:
|
|
652
|
+
yield
|
|
653
|
+
|
|
654
|
+
with pytest.RaisesGroup(GeneratorExit):
|
|
655
|
+
async with agen() as g, trio.open_nursery():
|
|
656
|
+
await g.receive() # this is for coverage reasons
|
|
657
|
+
raise GeneratorExit
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
async def test_as_safe_channel_genexit_filter() -> None:
|
|
661
|
+
async def wait_then_raise() -> None:
|
|
662
|
+
try:
|
|
663
|
+
await trio.sleep_forever()
|
|
664
|
+
except trio.Cancelled:
|
|
665
|
+
raise ValueError from None
|
|
666
|
+
|
|
667
|
+
@as_safe_channel
|
|
668
|
+
async def agen() -> AsyncGenerator[None]:
|
|
669
|
+
async with trio.open_nursery() as nursery:
|
|
670
|
+
nursery.start_soon(wait_then_raise)
|
|
671
|
+
yield
|
|
672
|
+
|
|
673
|
+
with pytest.RaisesGroup(ValueError):
|
|
674
|
+
async with agen() as g:
|
|
675
|
+
async for _ in g:
|
|
676
|
+
break
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
async def test_as_safe_channel_swallowing_extra_exceptions() -> None:
|
|
680
|
+
async def wait_then_raise(ex: type[BaseException]) -> None:
|
|
681
|
+
try:
|
|
682
|
+
await trio.sleep_forever()
|
|
683
|
+
except trio.Cancelled:
|
|
684
|
+
raise ex from None
|
|
685
|
+
|
|
686
|
+
@as_safe_channel
|
|
687
|
+
async def agen(ex: type[BaseException]) -> AsyncGenerator[None]:
|
|
688
|
+
async with trio.open_nursery() as nursery:
|
|
689
|
+
nursery.start_soon(wait_then_raise, ex)
|
|
690
|
+
nursery.start_soon(wait_then_raise, GeneratorExit)
|
|
691
|
+
yield
|
|
692
|
+
|
|
693
|
+
with pytest.RaisesGroup(AssertionError):
|
|
694
|
+
async with agen(GeneratorExit) as g:
|
|
695
|
+
async for _ in g:
|
|
696
|
+
break
|
|
697
|
+
|
|
698
|
+
with pytest.RaisesGroup(ValueError, AssertionError):
|
|
699
|
+
async with agen(ValueError) as g:
|
|
700
|
+
async for _ in g:
|
|
701
|
+
break
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
async def test_as_safe_channel_close_between_iteration() -> None:
|
|
705
|
+
@as_safe_channel
|
|
706
|
+
async def agen() -> AsyncGenerator[None]:
|
|
707
|
+
while True:
|
|
708
|
+
yield
|
|
709
|
+
|
|
710
|
+
async with agen() as chan, trio.open_nursery() as nursery:
|
|
711
|
+
|
|
712
|
+
async def close_channel() -> None:
|
|
713
|
+
await trio.lowlevel.checkpoint()
|
|
714
|
+
await chan.aclose()
|
|
715
|
+
|
|
716
|
+
nursery.start_soon(close_channel)
|
|
717
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
718
|
+
async for _ in chan:
|
|
719
|
+
pass
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
async def test_as_safe_channel_close_before_iteration() -> None:
|
|
723
|
+
@as_safe_channel
|
|
724
|
+
async def agen() -> AsyncGenerator[None]:
|
|
725
|
+
raise AssertionError("should be unreachable") # pragma: no cover
|
|
726
|
+
yield # pragma: no cover
|
|
727
|
+
|
|
728
|
+
async with agen() as chan:
|
|
729
|
+
await chan.aclose()
|
|
730
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
731
|
+
await chan.receive()
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
async def test_as_safe_channel_close_during_iteration() -> None:
|
|
735
|
+
@as_safe_channel
|
|
736
|
+
async def agen() -> AsyncGenerator[None]:
|
|
737
|
+
yield
|
|
738
|
+
await chan.aclose()
|
|
739
|
+
while True:
|
|
740
|
+
yield
|
|
741
|
+
|
|
742
|
+
async with agen() as chan:
|
|
743
|
+
with pytest.raises(trio.ClosedResourceError):
|
|
744
|
+
async for _ in chan:
|
|
745
|
+
pass
|
|
746
|
+
|
|
747
|
+
# This is necessary to ensure that `chan` has been sent
|
|
748
|
+
# to. Otherwise, this test sometimes passes on a broken
|
|
749
|
+
# version of trio.
|
|
750
|
+
await trio.testing.wait_all_tasks_blocked()
|