@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,118 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from trio import run
|
|
4
|
+
from trio.lowlevel import RunVar, RunVarToken
|
|
5
|
+
|
|
6
|
+
from ... import _core
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# scary runvar tests
|
|
10
|
+
def test_runvar_smoketest() -> None:
|
|
11
|
+
t1 = RunVar[str]("test1")
|
|
12
|
+
t2 = RunVar[str]("test2", default="catfish")
|
|
13
|
+
|
|
14
|
+
assert repr(t1) == "<RunVar name='test1'>"
|
|
15
|
+
|
|
16
|
+
async def first_check() -> None:
|
|
17
|
+
with pytest.raises(LookupError):
|
|
18
|
+
t1.get()
|
|
19
|
+
|
|
20
|
+
t1.set("swordfish")
|
|
21
|
+
assert t1.get() == "swordfish"
|
|
22
|
+
assert t2.get() == "catfish"
|
|
23
|
+
assert t2.get(default="eel") == "eel"
|
|
24
|
+
|
|
25
|
+
t2.set("goldfish")
|
|
26
|
+
assert t2.get() == "goldfish"
|
|
27
|
+
assert t2.get(default="tuna") == "goldfish"
|
|
28
|
+
|
|
29
|
+
async def second_check() -> None:
|
|
30
|
+
with pytest.raises(LookupError):
|
|
31
|
+
t1.get()
|
|
32
|
+
|
|
33
|
+
assert t2.get() == "catfish"
|
|
34
|
+
|
|
35
|
+
run(first_check)
|
|
36
|
+
run(second_check)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_runvar_resetting() -> None:
|
|
40
|
+
t1 = RunVar[str]("test1")
|
|
41
|
+
t2 = RunVar[str]("test2", default="dogfish")
|
|
42
|
+
t3 = RunVar[str]("test3")
|
|
43
|
+
|
|
44
|
+
async def reset_check() -> None:
|
|
45
|
+
token = t1.set("moonfish")
|
|
46
|
+
assert t1.get() == "moonfish"
|
|
47
|
+
t1.reset(token)
|
|
48
|
+
|
|
49
|
+
with pytest.raises(TypeError):
|
|
50
|
+
t1.reset(None) # type: ignore[arg-type]
|
|
51
|
+
|
|
52
|
+
with pytest.raises(LookupError):
|
|
53
|
+
t1.get()
|
|
54
|
+
|
|
55
|
+
token2 = t2.set("catdogfish")
|
|
56
|
+
assert t2.get() == "catdogfish"
|
|
57
|
+
t2.reset(token2)
|
|
58
|
+
assert t2.get() == "dogfish"
|
|
59
|
+
|
|
60
|
+
with pytest.raises(ValueError, match=r"^token has already been used$"):
|
|
61
|
+
t2.reset(token2)
|
|
62
|
+
|
|
63
|
+
token3 = t3.set("basculin")
|
|
64
|
+
assert t3.get() == "basculin"
|
|
65
|
+
|
|
66
|
+
with pytest.raises(ValueError, match=r"^token is not for us$"):
|
|
67
|
+
t1.reset(token3)
|
|
68
|
+
|
|
69
|
+
run(reset_check)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_runvar_sync() -> None:
|
|
73
|
+
t1 = RunVar[str]("test1")
|
|
74
|
+
|
|
75
|
+
async def sync_check() -> None:
|
|
76
|
+
async def task1() -> None:
|
|
77
|
+
t1.set("plaice")
|
|
78
|
+
assert t1.get() == "plaice"
|
|
79
|
+
|
|
80
|
+
async def task2(tok: RunVarToken[str]) -> None:
|
|
81
|
+
t1.reset(tok)
|
|
82
|
+
|
|
83
|
+
with pytest.raises(LookupError):
|
|
84
|
+
t1.get()
|
|
85
|
+
|
|
86
|
+
t1.set("haddock")
|
|
87
|
+
|
|
88
|
+
async with _core.open_nursery() as n:
|
|
89
|
+
token = t1.set("cod")
|
|
90
|
+
assert t1.get() == "cod"
|
|
91
|
+
|
|
92
|
+
n.start_soon(task1)
|
|
93
|
+
await _core.wait_all_tasks_blocked()
|
|
94
|
+
assert t1.get() == "plaice"
|
|
95
|
+
|
|
96
|
+
n.start_soon(task2, token)
|
|
97
|
+
await _core.wait_all_tasks_blocked()
|
|
98
|
+
assert t1.get() == "haddock"
|
|
99
|
+
|
|
100
|
+
run(sync_check)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_accessing_runvar_outside_run_call_fails() -> None:
|
|
104
|
+
t1 = RunVar[str]("test1")
|
|
105
|
+
|
|
106
|
+
with pytest.raises(RuntimeError):
|
|
107
|
+
t1.set("asdf")
|
|
108
|
+
|
|
109
|
+
with pytest.raises(RuntimeError):
|
|
110
|
+
t1.get()
|
|
111
|
+
|
|
112
|
+
async def get_token() -> RunVarToken[str]:
|
|
113
|
+
return t1.set("ok")
|
|
114
|
+
|
|
115
|
+
token = run(get_token)
|
|
116
|
+
|
|
117
|
+
with pytest.raises(RuntimeError):
|
|
118
|
+
t1.reset(token)
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from math import inf
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from trio import sleep
|
|
7
|
+
|
|
8
|
+
from ... import _core
|
|
9
|
+
from .. import wait_all_tasks_blocked
|
|
10
|
+
from .._mock_clock import MockClock
|
|
11
|
+
from .._run import GLOBAL_RUN_CONTEXT
|
|
12
|
+
from .tutil import slow
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_mock_clock() -> None:
|
|
16
|
+
REAL_NOW = 123.0
|
|
17
|
+
c = MockClock()
|
|
18
|
+
c._real_clock = lambda: REAL_NOW
|
|
19
|
+
repr(c) # smoke test
|
|
20
|
+
assert c.rate == 0
|
|
21
|
+
assert c.current_time() == 0
|
|
22
|
+
c.jump(1.2)
|
|
23
|
+
assert c.current_time() == 1.2
|
|
24
|
+
with pytest.raises(ValueError, match=r"^time can't go backwards$"):
|
|
25
|
+
c.jump(-1)
|
|
26
|
+
assert c.current_time() == 1.2
|
|
27
|
+
assert c.deadline_to_sleep_time(1.1) == 0
|
|
28
|
+
assert c.deadline_to_sleep_time(1.2) == 0
|
|
29
|
+
assert c.deadline_to_sleep_time(1.3) > 999999
|
|
30
|
+
|
|
31
|
+
with pytest.raises(ValueError, match=r"^rate must be >= 0$"):
|
|
32
|
+
c.rate = -1
|
|
33
|
+
assert c.rate == 0
|
|
34
|
+
|
|
35
|
+
c.rate = 2
|
|
36
|
+
assert c.current_time() == 1.2
|
|
37
|
+
REAL_NOW += 1
|
|
38
|
+
assert c.current_time() == 3.2
|
|
39
|
+
assert c.deadline_to_sleep_time(3.1) == 0
|
|
40
|
+
assert c.deadline_to_sleep_time(3.2) == 0
|
|
41
|
+
assert c.deadline_to_sleep_time(4.2) == 0.5
|
|
42
|
+
|
|
43
|
+
c.rate = 0.5
|
|
44
|
+
assert c.current_time() == 3.2
|
|
45
|
+
assert c.deadline_to_sleep_time(3.1) == 0
|
|
46
|
+
assert c.deadline_to_sleep_time(3.2) == 0
|
|
47
|
+
assert c.deadline_to_sleep_time(4.2) == 2.0
|
|
48
|
+
|
|
49
|
+
c.jump(0.8)
|
|
50
|
+
assert c.current_time() == 4.0
|
|
51
|
+
REAL_NOW += 1
|
|
52
|
+
assert c.current_time() == 4.5
|
|
53
|
+
|
|
54
|
+
c2 = MockClock(rate=3)
|
|
55
|
+
assert c2.rate == 3
|
|
56
|
+
assert c2.current_time() < 10
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
async def test_mock_clock_autojump(mock_clock: MockClock) -> None:
|
|
60
|
+
assert mock_clock.autojump_threshold == inf
|
|
61
|
+
|
|
62
|
+
mock_clock.autojump_threshold = 0
|
|
63
|
+
assert mock_clock.autojump_threshold == 0
|
|
64
|
+
|
|
65
|
+
real_start = time.perf_counter()
|
|
66
|
+
|
|
67
|
+
virtual_start = _core.current_time()
|
|
68
|
+
for i in range(10):
|
|
69
|
+
print(f"sleeping {10 * i} seconds")
|
|
70
|
+
await sleep(10 * i)
|
|
71
|
+
print("woke up!")
|
|
72
|
+
assert virtual_start + 10 * i == _core.current_time()
|
|
73
|
+
virtual_start = _core.current_time()
|
|
74
|
+
|
|
75
|
+
real_duration = time.perf_counter() - real_start
|
|
76
|
+
print(f"Slept {10 * sum(range(10))} seconds in {real_duration} seconds")
|
|
77
|
+
assert real_duration < 1
|
|
78
|
+
|
|
79
|
+
mock_clock.autojump_threshold = 0.02
|
|
80
|
+
t = _core.current_time()
|
|
81
|
+
# this should wake up before the autojump threshold triggers, so time
|
|
82
|
+
# shouldn't change
|
|
83
|
+
await wait_all_tasks_blocked()
|
|
84
|
+
assert t == _core.current_time()
|
|
85
|
+
# this should too
|
|
86
|
+
await wait_all_tasks_blocked(0.01)
|
|
87
|
+
assert t == _core.current_time()
|
|
88
|
+
|
|
89
|
+
# set up a situation where the autojump task is blocked for a long long
|
|
90
|
+
# time, to make sure that cancel-and-adjust-threshold logic is working
|
|
91
|
+
mock_clock.autojump_threshold = 10000
|
|
92
|
+
await wait_all_tasks_blocked()
|
|
93
|
+
mock_clock.autojump_threshold = 0
|
|
94
|
+
# if the above line didn't take affect immediately, then this would be
|
|
95
|
+
# bad:
|
|
96
|
+
# ignore ASYNC116, not sleep_forever, trying to test a large but finite sleep
|
|
97
|
+
await sleep(100000) # noqa: ASYNC116
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
async def test_mock_clock_autojump_interference(mock_clock: MockClock) -> None:
|
|
101
|
+
mock_clock.autojump_threshold = 0.02
|
|
102
|
+
|
|
103
|
+
mock_clock2 = MockClock()
|
|
104
|
+
# messing with the autojump threshold of a clock that isn't actually
|
|
105
|
+
# installed in the run loop shouldn't do anything.
|
|
106
|
+
mock_clock2.autojump_threshold = 0.01
|
|
107
|
+
|
|
108
|
+
# if the autojump_threshold of 0.01 were in effect, then the next line
|
|
109
|
+
# would block forever, as the autojump task kept waking up to try to
|
|
110
|
+
# jump the clock.
|
|
111
|
+
await wait_all_tasks_blocked(0.015)
|
|
112
|
+
|
|
113
|
+
# but the 0.02 limit does apply
|
|
114
|
+
# ignore ASYNC116, not sleep_forever, trying to test a large but finite sleep
|
|
115
|
+
await sleep(100000) # noqa: ASYNC116
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_mock_clock_autojump_preset() -> None:
|
|
119
|
+
# Check that we can set the autojump_threshold before the clock is
|
|
120
|
+
# actually in use, and it gets picked up
|
|
121
|
+
mock_clock = MockClock(autojump_threshold=0.1)
|
|
122
|
+
mock_clock.autojump_threshold = 0.01
|
|
123
|
+
real_start = time.perf_counter()
|
|
124
|
+
_core.run(sleep, 10000, clock=mock_clock)
|
|
125
|
+
assert time.perf_counter() - real_start < 1
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
async def test_mock_clock_autojump_0_and_wait_all_tasks_blocked_0(
|
|
129
|
+
mock_clock: MockClock,
|
|
130
|
+
) -> None:
|
|
131
|
+
# Checks that autojump_threshold=0 doesn't interfere with
|
|
132
|
+
# calling wait_all_tasks_blocked with the default cushion=0.
|
|
133
|
+
|
|
134
|
+
mock_clock.autojump_threshold = 0
|
|
135
|
+
|
|
136
|
+
record = []
|
|
137
|
+
|
|
138
|
+
async def sleeper() -> None:
|
|
139
|
+
await sleep(100)
|
|
140
|
+
record.append("yawn")
|
|
141
|
+
|
|
142
|
+
async def waiter() -> None:
|
|
143
|
+
await wait_all_tasks_blocked()
|
|
144
|
+
record.append("waiter woke")
|
|
145
|
+
await sleep(1000)
|
|
146
|
+
record.append("waiter done")
|
|
147
|
+
|
|
148
|
+
async with _core.open_nursery() as nursery:
|
|
149
|
+
nursery.start_soon(sleeper)
|
|
150
|
+
nursery.start_soon(waiter)
|
|
151
|
+
|
|
152
|
+
assert record == ["waiter woke", "yawn", "waiter done"]
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@slow
|
|
156
|
+
async def test_mock_clock_autojump_0_and_wait_all_tasks_blocked_nonzero(
|
|
157
|
+
mock_clock: MockClock,
|
|
158
|
+
) -> None:
|
|
159
|
+
# Checks that autojump_threshold=0 doesn't interfere with
|
|
160
|
+
# calling wait_all_tasks_blocked with a non-zero cushion.
|
|
161
|
+
|
|
162
|
+
mock_clock.autojump_threshold = 0
|
|
163
|
+
|
|
164
|
+
record = []
|
|
165
|
+
|
|
166
|
+
async def sleeper() -> None:
|
|
167
|
+
await sleep(100)
|
|
168
|
+
record.append("yawn")
|
|
169
|
+
|
|
170
|
+
async def waiter() -> None:
|
|
171
|
+
await wait_all_tasks_blocked(1)
|
|
172
|
+
record.append("waiter done")
|
|
173
|
+
|
|
174
|
+
async with _core.open_nursery() as nursery:
|
|
175
|
+
nursery.start_soon(sleeper)
|
|
176
|
+
nursery.start_soon(waiter)
|
|
177
|
+
|
|
178
|
+
assert record == ["waiter done", "yawn"]
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
async def test_initialization_doesnt_mutate_runner() -> None:
|
|
182
|
+
before = (
|
|
183
|
+
GLOBAL_RUN_CONTEXT.runner.clock,
|
|
184
|
+
GLOBAL_RUN_CONTEXT.runner.clock_autojump_threshold,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
MockClock(autojump_threshold=2, rate=3)
|
|
188
|
+
|
|
189
|
+
after = (
|
|
190
|
+
GLOBAL_RUN_CONTEXT.runner.clock,
|
|
191
|
+
GLOBAL_RUN_CONTEXT.runner.clock_autojump_threshold,
|
|
192
|
+
)
|
|
193
|
+
assert before == after
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import TypeVar
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
import trio
|
|
9
|
+
from trio.lowlevel import (
|
|
10
|
+
add_parking_lot_breaker,
|
|
11
|
+
current_task,
|
|
12
|
+
remove_parking_lot_breaker,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from ... import _core
|
|
16
|
+
from ...testing import wait_all_tasks_blocked
|
|
17
|
+
from .._parking_lot import ParkingLot
|
|
18
|
+
from .tutil import check_sequence_matches
|
|
19
|
+
|
|
20
|
+
T = TypeVar("T")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def test_parking_lot_basic() -> None:
|
|
24
|
+
record = []
|
|
25
|
+
|
|
26
|
+
async def waiter(i: int, lot: ParkingLot) -> None:
|
|
27
|
+
record.append(f"sleep {i}")
|
|
28
|
+
await lot.park()
|
|
29
|
+
record.append(f"wake {i}")
|
|
30
|
+
|
|
31
|
+
async with _core.open_nursery() as nursery:
|
|
32
|
+
lot = ParkingLot()
|
|
33
|
+
assert not lot
|
|
34
|
+
assert len(lot) == 0
|
|
35
|
+
assert lot.statistics().tasks_waiting == 0
|
|
36
|
+
for i in range(3):
|
|
37
|
+
nursery.start_soon(waiter, i, lot)
|
|
38
|
+
await wait_all_tasks_blocked()
|
|
39
|
+
assert len(record) == 3
|
|
40
|
+
assert bool(lot)
|
|
41
|
+
assert len(lot) == 3
|
|
42
|
+
assert lot.statistics().tasks_waiting == 3
|
|
43
|
+
lot.unpark_all()
|
|
44
|
+
assert lot.statistics().tasks_waiting == 0
|
|
45
|
+
await wait_all_tasks_blocked()
|
|
46
|
+
assert len(record) == 6
|
|
47
|
+
|
|
48
|
+
check_sequence_matches(
|
|
49
|
+
record,
|
|
50
|
+
[{"sleep 0", "sleep 1", "sleep 2"}, {"wake 0", "wake 1", "wake 2"}],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
async with _core.open_nursery() as nursery:
|
|
54
|
+
record = []
|
|
55
|
+
for i in range(3):
|
|
56
|
+
nursery.start_soon(waiter, i, lot)
|
|
57
|
+
await wait_all_tasks_blocked()
|
|
58
|
+
assert len(record) == 3
|
|
59
|
+
for _ in range(3):
|
|
60
|
+
lot.unpark()
|
|
61
|
+
await wait_all_tasks_blocked()
|
|
62
|
+
# 1-by-1 wakeups are strict FIFO
|
|
63
|
+
assert record == [
|
|
64
|
+
"sleep 0",
|
|
65
|
+
"sleep 1",
|
|
66
|
+
"sleep 2",
|
|
67
|
+
"wake 0",
|
|
68
|
+
"wake 1",
|
|
69
|
+
"wake 2",
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
# It's legal (but a no-op) to try and unpark while there's nothing parked
|
|
73
|
+
lot.unpark()
|
|
74
|
+
lot.unpark(count=1)
|
|
75
|
+
lot.unpark(count=100)
|
|
76
|
+
|
|
77
|
+
# Check unpark with count
|
|
78
|
+
async with _core.open_nursery() as nursery:
|
|
79
|
+
record = []
|
|
80
|
+
for i in range(3):
|
|
81
|
+
nursery.start_soon(waiter, i, lot)
|
|
82
|
+
await wait_all_tasks_blocked()
|
|
83
|
+
lot.unpark(count=2)
|
|
84
|
+
await wait_all_tasks_blocked()
|
|
85
|
+
check_sequence_matches(
|
|
86
|
+
record,
|
|
87
|
+
["sleep 0", "sleep 1", "sleep 2", {"wake 0", "wake 1"}],
|
|
88
|
+
)
|
|
89
|
+
lot.unpark_all()
|
|
90
|
+
|
|
91
|
+
with pytest.raises(
|
|
92
|
+
ValueError,
|
|
93
|
+
match=r"^Cannot pop a non-integer number of tasks\.$",
|
|
94
|
+
):
|
|
95
|
+
lot.unpark(count=1.5)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def cancellable_waiter(
|
|
99
|
+
name: T,
|
|
100
|
+
lot: ParkingLot,
|
|
101
|
+
scopes: dict[T, _core.CancelScope],
|
|
102
|
+
record: list[str],
|
|
103
|
+
) -> None:
|
|
104
|
+
with _core.CancelScope() as scope:
|
|
105
|
+
scopes[name] = scope
|
|
106
|
+
record.append(f"sleep {name}")
|
|
107
|
+
try:
|
|
108
|
+
await lot.park()
|
|
109
|
+
except _core.Cancelled:
|
|
110
|
+
record.append(f"cancelled {name}")
|
|
111
|
+
else:
|
|
112
|
+
record.append(f"wake {name}")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
async def test_parking_lot_cancel() -> None:
|
|
116
|
+
record: list[str] = []
|
|
117
|
+
scopes: dict[int, _core.CancelScope] = {}
|
|
118
|
+
|
|
119
|
+
async with _core.open_nursery() as nursery:
|
|
120
|
+
lot = ParkingLot()
|
|
121
|
+
nursery.start_soon(cancellable_waiter, 1, lot, scopes, record)
|
|
122
|
+
await wait_all_tasks_blocked()
|
|
123
|
+
nursery.start_soon(cancellable_waiter, 2, lot, scopes, record)
|
|
124
|
+
await wait_all_tasks_blocked()
|
|
125
|
+
nursery.start_soon(cancellable_waiter, 3, lot, scopes, record)
|
|
126
|
+
await wait_all_tasks_blocked()
|
|
127
|
+
assert len(record) == 3
|
|
128
|
+
|
|
129
|
+
scopes[2].cancel()
|
|
130
|
+
await wait_all_tasks_blocked()
|
|
131
|
+
assert len(record) == 4
|
|
132
|
+
lot.unpark_all()
|
|
133
|
+
await wait_all_tasks_blocked()
|
|
134
|
+
assert len(record) == 6
|
|
135
|
+
|
|
136
|
+
check_sequence_matches(
|
|
137
|
+
record,
|
|
138
|
+
["sleep 1", "sleep 2", "sleep 3", "cancelled 2", {"wake 1", "wake 3"}],
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
async def test_parking_lot_repark() -> None:
|
|
143
|
+
record: list[str] = []
|
|
144
|
+
scopes: dict[int, _core.CancelScope] = {}
|
|
145
|
+
lot1 = ParkingLot()
|
|
146
|
+
lot2 = ParkingLot()
|
|
147
|
+
|
|
148
|
+
with pytest.raises(TypeError):
|
|
149
|
+
lot1.repark([]) # type: ignore[arg-type]
|
|
150
|
+
|
|
151
|
+
async with _core.open_nursery() as nursery:
|
|
152
|
+
nursery.start_soon(cancellable_waiter, 1, lot1, scopes, record)
|
|
153
|
+
await wait_all_tasks_blocked()
|
|
154
|
+
nursery.start_soon(cancellable_waiter, 2, lot1, scopes, record)
|
|
155
|
+
await wait_all_tasks_blocked()
|
|
156
|
+
nursery.start_soon(cancellable_waiter, 3, lot1, scopes, record)
|
|
157
|
+
await wait_all_tasks_blocked()
|
|
158
|
+
assert len(record) == 3
|
|
159
|
+
|
|
160
|
+
assert len(lot1) == 3
|
|
161
|
+
lot1.repark(lot2)
|
|
162
|
+
assert len(lot1) == 2
|
|
163
|
+
assert len(lot2) == 1
|
|
164
|
+
lot2.unpark_all()
|
|
165
|
+
await wait_all_tasks_blocked()
|
|
166
|
+
assert len(record) == 4
|
|
167
|
+
assert record == ["sleep 1", "sleep 2", "sleep 3", "wake 1"]
|
|
168
|
+
|
|
169
|
+
lot1.repark_all(lot2)
|
|
170
|
+
assert len(lot1) == 0
|
|
171
|
+
assert len(lot2) == 2
|
|
172
|
+
|
|
173
|
+
scopes[2].cancel()
|
|
174
|
+
await wait_all_tasks_blocked()
|
|
175
|
+
assert len(lot2) == 1
|
|
176
|
+
assert record == [
|
|
177
|
+
"sleep 1",
|
|
178
|
+
"sleep 2",
|
|
179
|
+
"sleep 3",
|
|
180
|
+
"wake 1",
|
|
181
|
+
"cancelled 2",
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
lot2.unpark_all()
|
|
185
|
+
await wait_all_tasks_blocked()
|
|
186
|
+
assert record == [
|
|
187
|
+
"sleep 1",
|
|
188
|
+
"sleep 2",
|
|
189
|
+
"sleep 3",
|
|
190
|
+
"wake 1",
|
|
191
|
+
"cancelled 2",
|
|
192
|
+
"wake 3",
|
|
193
|
+
]
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
async def test_parking_lot_repark_with_count() -> None:
|
|
197
|
+
record: list[str] = []
|
|
198
|
+
scopes: dict[int, _core.CancelScope] = {}
|
|
199
|
+
lot1 = ParkingLot()
|
|
200
|
+
lot2 = ParkingLot()
|
|
201
|
+
async with _core.open_nursery() as nursery:
|
|
202
|
+
nursery.start_soon(cancellable_waiter, 1, lot1, scopes, record)
|
|
203
|
+
await wait_all_tasks_blocked()
|
|
204
|
+
nursery.start_soon(cancellable_waiter, 2, lot1, scopes, record)
|
|
205
|
+
await wait_all_tasks_blocked()
|
|
206
|
+
nursery.start_soon(cancellable_waiter, 3, lot1, scopes, record)
|
|
207
|
+
await wait_all_tasks_blocked()
|
|
208
|
+
assert len(record) == 3
|
|
209
|
+
|
|
210
|
+
assert len(lot1) == 3
|
|
211
|
+
assert len(lot2) == 0
|
|
212
|
+
lot1.repark(lot2, count=2)
|
|
213
|
+
assert len(lot1) == 1
|
|
214
|
+
assert len(lot2) == 2
|
|
215
|
+
while lot2:
|
|
216
|
+
lot2.unpark()
|
|
217
|
+
await wait_all_tasks_blocked()
|
|
218
|
+
assert record == [
|
|
219
|
+
"sleep 1",
|
|
220
|
+
"sleep 2",
|
|
221
|
+
"sleep 3",
|
|
222
|
+
"wake 1",
|
|
223
|
+
"wake 2",
|
|
224
|
+
]
|
|
225
|
+
lot1.unpark_all()
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
async def dummy_task(
|
|
229
|
+
task_status: _core.TaskStatus[_core.Task] = trio.TASK_STATUS_IGNORED,
|
|
230
|
+
) -> None:
|
|
231
|
+
task_status.started(_core.current_task())
|
|
232
|
+
await trio.sleep_forever()
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
async def test_parking_lot_breaker_basic() -> None:
|
|
236
|
+
"""Test basic functionality for breaking lots."""
|
|
237
|
+
lot = ParkingLot()
|
|
238
|
+
task = current_task()
|
|
239
|
+
|
|
240
|
+
# defaults to current task
|
|
241
|
+
lot.break_lot()
|
|
242
|
+
assert lot.broken_by == [task]
|
|
243
|
+
|
|
244
|
+
# breaking the lot again with the same task appends another copy in `broken_by`
|
|
245
|
+
lot.break_lot()
|
|
246
|
+
assert lot.broken_by == [task, task]
|
|
247
|
+
|
|
248
|
+
# trying to park in broken lot errors
|
|
249
|
+
broken_by_str = re.escape(str([task, task]))
|
|
250
|
+
with pytest.raises(
|
|
251
|
+
_core.BrokenResourceError,
|
|
252
|
+
match=f"^Attempted to park in parking lot broken by {broken_by_str}$",
|
|
253
|
+
):
|
|
254
|
+
await lot.park()
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
async def test_parking_lot_break_parking_tasks() -> None:
|
|
258
|
+
"""Checks that tasks currently waiting to park raise an error when the breaker exits."""
|
|
259
|
+
|
|
260
|
+
async def bad_parker(lot: ParkingLot, scope: _core.CancelScope) -> None:
|
|
261
|
+
add_parking_lot_breaker(current_task(), lot)
|
|
262
|
+
with scope:
|
|
263
|
+
await trio.sleep_forever()
|
|
264
|
+
|
|
265
|
+
lot = ParkingLot()
|
|
266
|
+
cs = _core.CancelScope()
|
|
267
|
+
|
|
268
|
+
# check that parked task errors
|
|
269
|
+
with pytest.RaisesGroup(
|
|
270
|
+
pytest.RaisesExc(_core.BrokenResourceError, match="^Parking lot broken by"),
|
|
271
|
+
):
|
|
272
|
+
async with _core.open_nursery() as nursery:
|
|
273
|
+
nursery.start_soon(bad_parker, lot, cs)
|
|
274
|
+
await wait_all_tasks_blocked()
|
|
275
|
+
|
|
276
|
+
nursery.start_soon(lot.park)
|
|
277
|
+
await wait_all_tasks_blocked()
|
|
278
|
+
|
|
279
|
+
cs.cancel()
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
async def test_parking_lot_breaker_registration() -> None:
|
|
283
|
+
lot = ParkingLot()
|
|
284
|
+
task = current_task()
|
|
285
|
+
|
|
286
|
+
with pytest.raises(
|
|
287
|
+
RuntimeError,
|
|
288
|
+
match="Attempted to remove task as breaker for a lot it is not registered for",
|
|
289
|
+
):
|
|
290
|
+
remove_parking_lot_breaker(task, lot)
|
|
291
|
+
|
|
292
|
+
# check that a task can be registered as breaker for the same lot multiple times
|
|
293
|
+
add_parking_lot_breaker(task, lot)
|
|
294
|
+
add_parking_lot_breaker(task, lot)
|
|
295
|
+
remove_parking_lot_breaker(task, lot)
|
|
296
|
+
remove_parking_lot_breaker(task, lot)
|
|
297
|
+
|
|
298
|
+
with pytest.raises(
|
|
299
|
+
RuntimeError,
|
|
300
|
+
match="Attempted to remove task as breaker for a lot it is not registered for",
|
|
301
|
+
):
|
|
302
|
+
remove_parking_lot_breaker(task, lot)
|
|
303
|
+
|
|
304
|
+
# registering a task as breaker on an already broken lot is fine
|
|
305
|
+
lot.break_lot()
|
|
306
|
+
child_task: _core.Task | None = None
|
|
307
|
+
async with trio.open_nursery() as nursery:
|
|
308
|
+
child_task = await nursery.start(dummy_task)
|
|
309
|
+
assert isinstance(child_task, _core.Task)
|
|
310
|
+
add_parking_lot_breaker(child_task, lot)
|
|
311
|
+
nursery.cancel_scope.cancel()
|
|
312
|
+
assert lot.broken_by == [task, child_task]
|
|
313
|
+
|
|
314
|
+
# manually breaking a lot with an already exited task is fine
|
|
315
|
+
lot = ParkingLot()
|
|
316
|
+
lot.break_lot(child_task)
|
|
317
|
+
assert lot.broken_by == [child_task]
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
async def test_parking_lot_breaker_rebreak() -> None:
|
|
321
|
+
lot = ParkingLot()
|
|
322
|
+
task = current_task()
|
|
323
|
+
lot.break_lot()
|
|
324
|
+
|
|
325
|
+
# breaking an already broken lot with a different task is allowed
|
|
326
|
+
# The nursery is only to create a task we can pass to lot.break_lot
|
|
327
|
+
async with trio.open_nursery() as nursery:
|
|
328
|
+
child_task = await nursery.start(dummy_task)
|
|
329
|
+
lot.break_lot(child_task)
|
|
330
|
+
nursery.cancel_scope.cancel()
|
|
331
|
+
|
|
332
|
+
assert lot.broken_by == [task, child_task]
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
async def test_parking_lot_multiple_breakers_exit() -> None:
|
|
336
|
+
# register multiple tasks as lot breakers, then have them all exit
|
|
337
|
+
lot = ParkingLot()
|
|
338
|
+
async with trio.open_nursery() as nursery:
|
|
339
|
+
child_task1 = await nursery.start(dummy_task)
|
|
340
|
+
child_task2 = await nursery.start(dummy_task)
|
|
341
|
+
child_task3 = await nursery.start(dummy_task)
|
|
342
|
+
assert isinstance(child_task1, _core.Task)
|
|
343
|
+
assert isinstance(child_task2, _core.Task)
|
|
344
|
+
assert isinstance(child_task3, _core.Task)
|
|
345
|
+
add_parking_lot_breaker(child_task1, lot)
|
|
346
|
+
add_parking_lot_breaker(child_task2, lot)
|
|
347
|
+
add_parking_lot_breaker(child_task3, lot)
|
|
348
|
+
nursery.cancel_scope.cancel()
|
|
349
|
+
|
|
350
|
+
# I think the order is guaranteed currently, but doesn't hurt to be safe.
|
|
351
|
+
assert set(lot.broken_by) == {child_task1, child_task2, child_task3}
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
async def test_parking_lot_breaker_register_exited_task() -> None:
|
|
355
|
+
lot = ParkingLot()
|
|
356
|
+
child_task: _core.Task | None = None
|
|
357
|
+
async with trio.open_nursery() as nursery:
|
|
358
|
+
value = await nursery.start(dummy_task)
|
|
359
|
+
assert isinstance(value, _core.Task)
|
|
360
|
+
child_task = value
|
|
361
|
+
nursery.cancel_scope.cancel()
|
|
362
|
+
# trying to register an exited task as lot breaker errors
|
|
363
|
+
with pytest.raises(
|
|
364
|
+
trio.BrokenResourceError,
|
|
365
|
+
match=r"^Attempted to add already exited task as lot breaker.$",
|
|
366
|
+
):
|
|
367
|
+
add_parking_lot_breaker(child_task, lot)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
async def test_parking_lot_break_itself() -> None:
|
|
371
|
+
"""Break a parking lot, where the breakee is parked.
|
|
372
|
+
Doing this is weird, but should probably be supported.
|
|
373
|
+
"""
|
|
374
|
+
|
|
375
|
+
async def return_me_and_park(
|
|
376
|
+
lot: ParkingLot,
|
|
377
|
+
*,
|
|
378
|
+
task_status: _core.TaskStatus[_core.Task] = trio.TASK_STATUS_IGNORED,
|
|
379
|
+
) -> None:
|
|
380
|
+
task_status.started(_core.current_task())
|
|
381
|
+
await lot.park()
|
|
382
|
+
|
|
383
|
+
lot = ParkingLot()
|
|
384
|
+
with pytest.RaisesGroup(
|
|
385
|
+
pytest.RaisesExc(_core.BrokenResourceError, match="^Parking lot broken by"),
|
|
386
|
+
):
|
|
387
|
+
async with _core.open_nursery() as nursery:
|
|
388
|
+
child_task = await nursery.start(return_me_and_park, lot)
|
|
389
|
+
lot.break_lot(child_task)
|