@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,703 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import inspect
|
|
5
|
+
import signal
|
|
6
|
+
import sys
|
|
7
|
+
import threading
|
|
8
|
+
import weakref
|
|
9
|
+
from collections.abc import AsyncIterator, Callable, Iterator
|
|
10
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
11
|
+
|
|
12
|
+
import outcome
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
from .tutil import gc_collect_harder
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
from async_generator import async_generator, yield_
|
|
19
|
+
except ImportError: # pragma: no cover
|
|
20
|
+
async_generator = yield_ = None
|
|
21
|
+
|
|
22
|
+
from ... import _core
|
|
23
|
+
from ..._abc import Instrument
|
|
24
|
+
from ..._core import _ki
|
|
25
|
+
from ..._timeouts import sleep
|
|
26
|
+
from ...testing import wait_all_tasks_blocked
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from collections.abc import (
|
|
30
|
+
AsyncGenerator,
|
|
31
|
+
AsyncIterator,
|
|
32
|
+
Callable,
|
|
33
|
+
Generator,
|
|
34
|
+
Iterator,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
from ..._core import Abort, RaiseCancelT
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def ki_self() -> None:
|
|
41
|
+
signal.raise_signal(signal.SIGINT)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_ki_self() -> None:
|
|
45
|
+
with pytest.raises(KeyboardInterrupt):
|
|
46
|
+
ki_self()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def test_ki_enabled() -> None:
|
|
50
|
+
# Regular tasks aren't KI-protected
|
|
51
|
+
assert not _core.currently_ki_protected()
|
|
52
|
+
|
|
53
|
+
# Low-level call-soon callbacks are KI-protected
|
|
54
|
+
token = _core.current_trio_token()
|
|
55
|
+
record = []
|
|
56
|
+
|
|
57
|
+
def check() -> None:
|
|
58
|
+
record.append(_core.currently_ki_protected())
|
|
59
|
+
|
|
60
|
+
token.run_sync_soon(check)
|
|
61
|
+
await wait_all_tasks_blocked()
|
|
62
|
+
assert record == [True]
|
|
63
|
+
|
|
64
|
+
@_core.enable_ki_protection
|
|
65
|
+
def protected() -> None:
|
|
66
|
+
assert _core.currently_ki_protected()
|
|
67
|
+
unprotected()
|
|
68
|
+
|
|
69
|
+
@_core.disable_ki_protection
|
|
70
|
+
def unprotected() -> None:
|
|
71
|
+
assert not _core.currently_ki_protected()
|
|
72
|
+
|
|
73
|
+
protected()
|
|
74
|
+
|
|
75
|
+
@_core.enable_ki_protection
|
|
76
|
+
async def aprotected() -> None:
|
|
77
|
+
assert _core.currently_ki_protected()
|
|
78
|
+
await aunprotected()
|
|
79
|
+
|
|
80
|
+
@_core.disable_ki_protection
|
|
81
|
+
async def aunprotected() -> None:
|
|
82
|
+
assert not _core.currently_ki_protected()
|
|
83
|
+
|
|
84
|
+
await aprotected()
|
|
85
|
+
|
|
86
|
+
# make sure that the decorator here overrides the automatic manipulation
|
|
87
|
+
# that start_soon() does:
|
|
88
|
+
async with _core.open_nursery() as nursery:
|
|
89
|
+
nursery.start_soon(aprotected)
|
|
90
|
+
nursery.start_soon(aunprotected)
|
|
91
|
+
|
|
92
|
+
@_core.enable_ki_protection
|
|
93
|
+
def gen_protected() -> Iterator[None]:
|
|
94
|
+
assert _core.currently_ki_protected()
|
|
95
|
+
yield
|
|
96
|
+
|
|
97
|
+
for _ in gen_protected():
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
@_core.disable_ki_protection
|
|
101
|
+
def gen_unprotected() -> Iterator[None]:
|
|
102
|
+
assert not _core.currently_ki_protected()
|
|
103
|
+
yield
|
|
104
|
+
|
|
105
|
+
for _ in gen_unprotected():
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# This used to be broken due to
|
|
110
|
+
#
|
|
111
|
+
# https://bugs.python.org/issue29590
|
|
112
|
+
#
|
|
113
|
+
# Specifically, after a coroutine is resumed with .throw(), then the stack
|
|
114
|
+
# makes it look like the immediate caller is the function that called
|
|
115
|
+
# .throw(), not the actual caller. So child() here would have a caller deep in
|
|
116
|
+
# the guts of the run loop, and always be protected, even when it shouldn't
|
|
117
|
+
# have been. (Solution: we don't use .throw() anymore.)
|
|
118
|
+
async def test_ki_enabled_after_yield_briefly() -> None:
|
|
119
|
+
@_core.enable_ki_protection
|
|
120
|
+
async def protected() -> None:
|
|
121
|
+
await child(True)
|
|
122
|
+
|
|
123
|
+
@_core.disable_ki_protection
|
|
124
|
+
async def unprotected() -> None:
|
|
125
|
+
await child(False)
|
|
126
|
+
|
|
127
|
+
async def child(expected: bool) -> None:
|
|
128
|
+
import traceback
|
|
129
|
+
|
|
130
|
+
traceback.print_stack()
|
|
131
|
+
assert _core.currently_ki_protected() == expected
|
|
132
|
+
await _core.checkpoint()
|
|
133
|
+
traceback.print_stack()
|
|
134
|
+
assert _core.currently_ki_protected() == expected
|
|
135
|
+
|
|
136
|
+
await protected()
|
|
137
|
+
await unprotected()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# This also used to be broken due to
|
|
141
|
+
# https://bugs.python.org/issue29590
|
|
142
|
+
async def test_generator_based_context_manager_throw() -> None:
|
|
143
|
+
@contextlib.contextmanager
|
|
144
|
+
@_core.enable_ki_protection
|
|
145
|
+
def protected_manager() -> Iterator[None]:
|
|
146
|
+
assert _core.currently_ki_protected()
|
|
147
|
+
try:
|
|
148
|
+
yield
|
|
149
|
+
finally:
|
|
150
|
+
assert _core.currently_ki_protected()
|
|
151
|
+
|
|
152
|
+
with protected_manager():
|
|
153
|
+
assert not _core.currently_ki_protected()
|
|
154
|
+
|
|
155
|
+
with pytest.raises(KeyError):
|
|
156
|
+
# This is the one that used to fail
|
|
157
|
+
with protected_manager():
|
|
158
|
+
raise KeyError
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# the async_generator package isn't typed, hence all the type: ignores
|
|
162
|
+
@pytest.mark.skipif(async_generator is None, reason="async_generator not installed")
|
|
163
|
+
async def test_async_generator_agen_protection() -> None:
|
|
164
|
+
@_core.enable_ki_protection
|
|
165
|
+
@async_generator # type: ignore[untyped-decorator]
|
|
166
|
+
async def agen_protected1() -> None: # type: ignore[misc] # untyped generator
|
|
167
|
+
assert _core.currently_ki_protected()
|
|
168
|
+
try:
|
|
169
|
+
await yield_()
|
|
170
|
+
finally:
|
|
171
|
+
assert _core.currently_ki_protected()
|
|
172
|
+
|
|
173
|
+
@_core.disable_ki_protection
|
|
174
|
+
@async_generator # type: ignore[untyped-decorator]
|
|
175
|
+
async def agen_unprotected1() -> None: # type: ignore[misc] # untyped generator
|
|
176
|
+
assert not _core.currently_ki_protected()
|
|
177
|
+
try:
|
|
178
|
+
await yield_()
|
|
179
|
+
finally:
|
|
180
|
+
assert not _core.currently_ki_protected()
|
|
181
|
+
|
|
182
|
+
# Swap the order of the decorators:
|
|
183
|
+
@async_generator # type: ignore[untyped-decorator]
|
|
184
|
+
@_core.enable_ki_protection
|
|
185
|
+
async def agen_protected2() -> None: # type: ignore[misc] # untyped generator
|
|
186
|
+
assert _core.currently_ki_protected()
|
|
187
|
+
try:
|
|
188
|
+
await yield_()
|
|
189
|
+
finally:
|
|
190
|
+
assert _core.currently_ki_protected()
|
|
191
|
+
|
|
192
|
+
@async_generator # type: ignore[untyped-decorator]
|
|
193
|
+
@_core.disable_ki_protection
|
|
194
|
+
async def agen_unprotected2() -> None: # type: ignore[misc] # untyped generator
|
|
195
|
+
assert not _core.currently_ki_protected()
|
|
196
|
+
try:
|
|
197
|
+
await yield_()
|
|
198
|
+
finally:
|
|
199
|
+
assert not _core.currently_ki_protected()
|
|
200
|
+
|
|
201
|
+
await _check_agen(agen_protected1)
|
|
202
|
+
await _check_agen(agen_protected2)
|
|
203
|
+
await _check_agen(agen_unprotected1)
|
|
204
|
+
await _check_agen(agen_unprotected2)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
async def test_native_agen_protection() -> None:
|
|
208
|
+
# Native async generators
|
|
209
|
+
@_core.enable_ki_protection
|
|
210
|
+
async def agen_protected() -> AsyncIterator[None]:
|
|
211
|
+
assert _core.currently_ki_protected()
|
|
212
|
+
try:
|
|
213
|
+
yield
|
|
214
|
+
finally:
|
|
215
|
+
assert _core.currently_ki_protected()
|
|
216
|
+
|
|
217
|
+
@_core.disable_ki_protection
|
|
218
|
+
async def agen_unprotected() -> AsyncIterator[None]:
|
|
219
|
+
assert not _core.currently_ki_protected()
|
|
220
|
+
try:
|
|
221
|
+
yield
|
|
222
|
+
finally:
|
|
223
|
+
assert not _core.currently_ki_protected()
|
|
224
|
+
|
|
225
|
+
await _check_agen(agen_protected)
|
|
226
|
+
await _check_agen(agen_unprotected)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
async def _check_agen(agen_fn: Callable[[], AsyncIterator[None]]) -> None:
|
|
230
|
+
async for _ in agen_fn():
|
|
231
|
+
assert not _core.currently_ki_protected()
|
|
232
|
+
|
|
233
|
+
# asynccontextmanager insists that the function passed must itself be an
|
|
234
|
+
# async gen function, not a wrapper around one
|
|
235
|
+
if inspect.isasyncgenfunction(agen_fn):
|
|
236
|
+
async with contextlib.asynccontextmanager(agen_fn)():
|
|
237
|
+
assert not _core.currently_ki_protected()
|
|
238
|
+
|
|
239
|
+
# Another case that's tricky due to:
|
|
240
|
+
# https://bugs.python.org/issue29590
|
|
241
|
+
with pytest.raises(KeyError):
|
|
242
|
+
async with contextlib.asynccontextmanager(agen_fn)():
|
|
243
|
+
raise KeyError
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
# Test the case where there's no magic local anywhere in the call stack
|
|
247
|
+
def test_ki_disabled_out_of_context() -> None:
|
|
248
|
+
assert _core.currently_ki_protected()
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def test_ki_disabled_in_del() -> None:
|
|
252
|
+
def nestedfunction() -> bool:
|
|
253
|
+
return _core.currently_ki_protected()
|
|
254
|
+
|
|
255
|
+
def __del__() -> None:
|
|
256
|
+
assert _core.currently_ki_protected()
|
|
257
|
+
assert nestedfunction()
|
|
258
|
+
|
|
259
|
+
@_core.disable_ki_protection
|
|
260
|
+
def outerfunction() -> None:
|
|
261
|
+
assert not _core.currently_ki_protected()
|
|
262
|
+
assert not nestedfunction()
|
|
263
|
+
__del__()
|
|
264
|
+
|
|
265
|
+
__del__()
|
|
266
|
+
outerfunction()
|
|
267
|
+
assert nestedfunction()
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def test_ki_protection_works() -> None:
|
|
271
|
+
async def sleeper(name: str, record: set[str]) -> None:
|
|
272
|
+
try:
|
|
273
|
+
while True:
|
|
274
|
+
await _core.checkpoint()
|
|
275
|
+
except _core.Cancelled:
|
|
276
|
+
record.add(name + " ok")
|
|
277
|
+
|
|
278
|
+
async def raiser(name: str, record: set[str]) -> None:
|
|
279
|
+
try:
|
|
280
|
+
# os.kill runs signal handlers before returning, so we don't need
|
|
281
|
+
# to worry that the handler will be delayed
|
|
282
|
+
print("killing, protection =", _core.currently_ki_protected())
|
|
283
|
+
ki_self()
|
|
284
|
+
except KeyboardInterrupt:
|
|
285
|
+
print("raised!")
|
|
286
|
+
# Make sure we aren't getting cancelled as well as siginted
|
|
287
|
+
await _core.checkpoint()
|
|
288
|
+
record.add(name + " raise ok")
|
|
289
|
+
raise
|
|
290
|
+
else:
|
|
291
|
+
print("didn't raise!")
|
|
292
|
+
# If we didn't raise (b/c protected), then we *should* get
|
|
293
|
+
# cancelled at the next opportunity
|
|
294
|
+
try:
|
|
295
|
+
await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)
|
|
296
|
+
except _core.Cancelled:
|
|
297
|
+
record.add(name + " cancel ok")
|
|
298
|
+
|
|
299
|
+
# simulated control-C during raiser, which is *unprotected*
|
|
300
|
+
print("check 1")
|
|
301
|
+
record_set: set[str] = set()
|
|
302
|
+
|
|
303
|
+
async def check_unprotected_kill() -> None:
|
|
304
|
+
async with _core.open_nursery() as nursery:
|
|
305
|
+
nursery.start_soon(sleeper, "s1", record_set)
|
|
306
|
+
nursery.start_soon(sleeper, "s2", record_set)
|
|
307
|
+
nursery.start_soon(raiser, "r1", record_set)
|
|
308
|
+
|
|
309
|
+
# raises inside a nursery, so the KeyboardInterrupt is wrapped in an ExceptionGroup
|
|
310
|
+
with pytest.RaisesGroup(KeyboardInterrupt):
|
|
311
|
+
_core.run(check_unprotected_kill)
|
|
312
|
+
assert record_set == {"s1 ok", "s2 ok", "r1 raise ok"}
|
|
313
|
+
|
|
314
|
+
# simulated control-C during raiser, which is *protected*, so the KI gets
|
|
315
|
+
# delivered to the main task instead
|
|
316
|
+
print("check 2")
|
|
317
|
+
record_set = set()
|
|
318
|
+
|
|
319
|
+
async def check_protected_kill() -> None:
|
|
320
|
+
async with _core.open_nursery() as nursery:
|
|
321
|
+
nursery.start_soon(sleeper, "s1", record_set)
|
|
322
|
+
nursery.start_soon(sleeper, "s2", record_set)
|
|
323
|
+
nursery.start_soon(_core.enable_ki_protection(raiser), "r1", record_set)
|
|
324
|
+
# __aexit__ blocks, and then receives the KI
|
|
325
|
+
|
|
326
|
+
# raises inside a nursery, so the KeyboardInterrupt is wrapped in an ExceptionGroup
|
|
327
|
+
with pytest.RaisesGroup(KeyboardInterrupt):
|
|
328
|
+
_core.run(check_protected_kill)
|
|
329
|
+
assert record_set == {"s1 ok", "s2 ok", "r1 cancel ok"}
|
|
330
|
+
|
|
331
|
+
# kill at last moment still raises (run_sync_soon until it raises an
|
|
332
|
+
# error, then kill)
|
|
333
|
+
print("check 3")
|
|
334
|
+
|
|
335
|
+
async def check_kill_during_shutdown() -> None:
|
|
336
|
+
token = _core.current_trio_token()
|
|
337
|
+
|
|
338
|
+
def kill_during_shutdown() -> None:
|
|
339
|
+
assert _core.currently_ki_protected()
|
|
340
|
+
try:
|
|
341
|
+
token.run_sync_soon(kill_during_shutdown)
|
|
342
|
+
except _core.RunFinishedError:
|
|
343
|
+
# it's too late for regular handling! handle this!
|
|
344
|
+
print("kill! kill!")
|
|
345
|
+
ki_self()
|
|
346
|
+
|
|
347
|
+
token.run_sync_soon(kill_during_shutdown)
|
|
348
|
+
|
|
349
|
+
# no nurseries involved, so the KeyboardInterrupt isn't wrapped
|
|
350
|
+
with pytest.raises(KeyboardInterrupt):
|
|
351
|
+
_core.run(check_kill_during_shutdown)
|
|
352
|
+
|
|
353
|
+
# KI arrives very early, before main is even spawned
|
|
354
|
+
print("check 4")
|
|
355
|
+
|
|
356
|
+
class InstrumentOfDeath(Instrument):
|
|
357
|
+
def before_run(self) -> None:
|
|
358
|
+
ki_self()
|
|
359
|
+
|
|
360
|
+
async def main_1() -> None:
|
|
361
|
+
await _core.checkpoint()
|
|
362
|
+
|
|
363
|
+
# no nurseries involved, so the KeyboardInterrupt isn't wrapped
|
|
364
|
+
with pytest.raises(KeyboardInterrupt):
|
|
365
|
+
_core.run(main_1, instruments=[InstrumentOfDeath()])
|
|
366
|
+
|
|
367
|
+
# checkpoint_if_cancelled notices pending KI
|
|
368
|
+
print("check 5")
|
|
369
|
+
|
|
370
|
+
@_core.enable_ki_protection
|
|
371
|
+
async def main_2() -> None:
|
|
372
|
+
assert _core.currently_ki_protected()
|
|
373
|
+
ki_self()
|
|
374
|
+
with pytest.raises(KeyboardInterrupt):
|
|
375
|
+
await _core.checkpoint_if_cancelled()
|
|
376
|
+
|
|
377
|
+
_core.run(main_2)
|
|
378
|
+
|
|
379
|
+
# KI arrives while main task is not abortable, b/c already scheduled
|
|
380
|
+
print("check 6")
|
|
381
|
+
|
|
382
|
+
@_core.enable_ki_protection
|
|
383
|
+
async def main_3() -> None:
|
|
384
|
+
assert _core.currently_ki_protected()
|
|
385
|
+
ki_self()
|
|
386
|
+
await _core.cancel_shielded_checkpoint()
|
|
387
|
+
await _core.cancel_shielded_checkpoint()
|
|
388
|
+
await _core.cancel_shielded_checkpoint()
|
|
389
|
+
with pytest.raises(KeyboardInterrupt):
|
|
390
|
+
await _core.checkpoint()
|
|
391
|
+
|
|
392
|
+
_core.run(main_3)
|
|
393
|
+
|
|
394
|
+
# KI arrives while main task is not abortable, b/c refuses to be aborted
|
|
395
|
+
print("check 7")
|
|
396
|
+
|
|
397
|
+
@_core.enable_ki_protection
|
|
398
|
+
async def main_4() -> None:
|
|
399
|
+
assert _core.currently_ki_protected()
|
|
400
|
+
ki_self()
|
|
401
|
+
task = _core.current_task()
|
|
402
|
+
|
|
403
|
+
def abort(_: RaiseCancelT) -> Abort:
|
|
404
|
+
_core.reschedule(task, outcome.Value(1))
|
|
405
|
+
return _core.Abort.FAILED
|
|
406
|
+
|
|
407
|
+
assert await _core.wait_task_rescheduled(abort) == 1
|
|
408
|
+
with pytest.raises(KeyboardInterrupt):
|
|
409
|
+
await _core.checkpoint()
|
|
410
|
+
|
|
411
|
+
_core.run(main_4)
|
|
412
|
+
|
|
413
|
+
# KI delivered via slow abort
|
|
414
|
+
print("check 8")
|
|
415
|
+
|
|
416
|
+
@_core.enable_ki_protection
|
|
417
|
+
async def main_5() -> None:
|
|
418
|
+
assert _core.currently_ki_protected()
|
|
419
|
+
ki_self()
|
|
420
|
+
task = _core.current_task()
|
|
421
|
+
|
|
422
|
+
def abort(raise_cancel: RaiseCancelT) -> Abort:
|
|
423
|
+
result = outcome.capture(raise_cancel)
|
|
424
|
+
_core.reschedule(task, result)
|
|
425
|
+
return _core.Abort.FAILED
|
|
426
|
+
|
|
427
|
+
with pytest.raises(KeyboardInterrupt):
|
|
428
|
+
assert await _core.wait_task_rescheduled(abort)
|
|
429
|
+
await _core.checkpoint()
|
|
430
|
+
|
|
431
|
+
_core.run(main_5)
|
|
432
|
+
|
|
433
|
+
# KI arrives just before main task exits, so the run_sync_soon machinery
|
|
434
|
+
# is still functioning and will accept the callback to deliver the KI, but
|
|
435
|
+
# by the time the callback is actually run, main has exited and can't be
|
|
436
|
+
# aborted.
|
|
437
|
+
print("check 9")
|
|
438
|
+
|
|
439
|
+
@_core.enable_ki_protection
|
|
440
|
+
async def main_6() -> None:
|
|
441
|
+
ki_self()
|
|
442
|
+
|
|
443
|
+
with pytest.raises(KeyboardInterrupt):
|
|
444
|
+
_core.run(main_6)
|
|
445
|
+
|
|
446
|
+
print("check 10")
|
|
447
|
+
# KI in unprotected code, with
|
|
448
|
+
# restrict_keyboard_interrupt_to_checkpoints=True
|
|
449
|
+
record_list = []
|
|
450
|
+
|
|
451
|
+
async def main_7() -> None:
|
|
452
|
+
# We're not KI protected...
|
|
453
|
+
assert not _core.currently_ki_protected()
|
|
454
|
+
ki_self()
|
|
455
|
+
# ...but even after the KI, we keep running uninterrupted...
|
|
456
|
+
record_list.append("ok")
|
|
457
|
+
# ...until we hit a checkpoint:
|
|
458
|
+
with pytest.raises(KeyboardInterrupt):
|
|
459
|
+
await sleep(10)
|
|
460
|
+
|
|
461
|
+
_core.run(main_7, restrict_keyboard_interrupt_to_checkpoints=True)
|
|
462
|
+
assert record_list == ["ok"]
|
|
463
|
+
record_list = []
|
|
464
|
+
# Exact same code raises KI early if we leave off the argument, doesn't
|
|
465
|
+
# even reach the record.append call:
|
|
466
|
+
with pytest.raises(KeyboardInterrupt):
|
|
467
|
+
_core.run(main_7)
|
|
468
|
+
assert record_list == []
|
|
469
|
+
|
|
470
|
+
# KI arrives while main task is inside a cancelled cancellation scope
|
|
471
|
+
# the KeyboardInterrupt should take priority
|
|
472
|
+
print("check 11")
|
|
473
|
+
|
|
474
|
+
@_core.enable_ki_protection
|
|
475
|
+
async def main_8() -> None:
|
|
476
|
+
assert _core.currently_ki_protected()
|
|
477
|
+
with _core.CancelScope() as cancel_scope:
|
|
478
|
+
cancel_scope.cancel()
|
|
479
|
+
with pytest.raises(_core.Cancelled):
|
|
480
|
+
await _core.checkpoint()
|
|
481
|
+
ki_self()
|
|
482
|
+
with pytest.raises(KeyboardInterrupt):
|
|
483
|
+
await _core.checkpoint()
|
|
484
|
+
with pytest.raises(_core.Cancelled):
|
|
485
|
+
await _core.checkpoint()
|
|
486
|
+
|
|
487
|
+
_core.run(main_8)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def test_ki_is_good_neighbor() -> None:
|
|
491
|
+
# in the unlikely event someone overwrites our signal handler, we leave
|
|
492
|
+
# the overwritten one be
|
|
493
|
+
try:
|
|
494
|
+
orig = signal.getsignal(signal.SIGINT)
|
|
495
|
+
|
|
496
|
+
def my_handler(signum: object, frame: object) -> None: # pragma: no cover
|
|
497
|
+
pass
|
|
498
|
+
|
|
499
|
+
async def main() -> None:
|
|
500
|
+
signal.signal(signal.SIGINT, my_handler)
|
|
501
|
+
|
|
502
|
+
_core.run(main)
|
|
503
|
+
|
|
504
|
+
assert signal.getsignal(signal.SIGINT) is my_handler
|
|
505
|
+
finally:
|
|
506
|
+
signal.signal(signal.SIGINT, orig)
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
# Regression test for #461
|
|
510
|
+
# don't know if _active not being visible is a problem
|
|
511
|
+
def test_ki_with_broken_threads() -> None:
|
|
512
|
+
thread = threading.main_thread()
|
|
513
|
+
|
|
514
|
+
# scary!
|
|
515
|
+
original = threading._active[thread.ident] # type: ignore[attr-defined]
|
|
516
|
+
|
|
517
|
+
# put this in a try finally so we don't have a chance of cascading a
|
|
518
|
+
# breakage down to everything else
|
|
519
|
+
try:
|
|
520
|
+
del threading._active[thread.ident] # type: ignore[attr-defined]
|
|
521
|
+
|
|
522
|
+
@_core.enable_ki_protection
|
|
523
|
+
async def inner() -> None:
|
|
524
|
+
assert signal.getsignal(signal.SIGINT) != signal.default_int_handler
|
|
525
|
+
|
|
526
|
+
_core.run(inner)
|
|
527
|
+
finally:
|
|
528
|
+
threading._active[thread.ident] = original # type: ignore[attr-defined]
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
_T = TypeVar("_T")
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def _identity(v: _T) -> _T:
|
|
535
|
+
return v
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
@pytest.mark.xfail(
|
|
539
|
+
strict=True,
|
|
540
|
+
raises=AssertionError,
|
|
541
|
+
reason=(
|
|
542
|
+
"it was decided not to protect against this case, see discussion in: "
|
|
543
|
+
"https://github.com/python-trio/trio/pull/3110#discussion_r1802123644"
|
|
544
|
+
),
|
|
545
|
+
)
|
|
546
|
+
async def test_ki_does_not_leak_across_different_calls_to_inner_functions() -> None:
|
|
547
|
+
assert not _core.currently_ki_protected()
|
|
548
|
+
|
|
549
|
+
def factory(enabled: bool) -> Callable[[], bool]:
|
|
550
|
+
@_core.enable_ki_protection if enabled else _identity
|
|
551
|
+
def decorated() -> bool:
|
|
552
|
+
return _core.currently_ki_protected()
|
|
553
|
+
|
|
554
|
+
return decorated
|
|
555
|
+
|
|
556
|
+
decorated_enabled = factory(True)
|
|
557
|
+
decorated_disabled = factory(False)
|
|
558
|
+
assert decorated_enabled()
|
|
559
|
+
assert not decorated_disabled()
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
async def test_ki_protection_check_does_not_freeze_locals() -> None:
|
|
563
|
+
class A:
|
|
564
|
+
pass
|
|
565
|
+
|
|
566
|
+
a = A()
|
|
567
|
+
wr_a = weakref.ref(a)
|
|
568
|
+
assert not _core.currently_ki_protected()
|
|
569
|
+
del a
|
|
570
|
+
if sys.implementation.name == "pypy":
|
|
571
|
+
gc_collect_harder()
|
|
572
|
+
assert wr_a() is None
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def test_identity_weakref_internals() -> None:
|
|
576
|
+
"""To cover the parts WeakKeyIdentityDictionary won't ever reach."""
|
|
577
|
+
|
|
578
|
+
class A:
|
|
579
|
+
def __eq__(self, other: object) -> bool:
|
|
580
|
+
return False
|
|
581
|
+
|
|
582
|
+
a = A()
|
|
583
|
+
assert a != a
|
|
584
|
+
wr = _ki._IdRef(a)
|
|
585
|
+
wr_other_is_self = wr
|
|
586
|
+
|
|
587
|
+
# dict always checks identity before equality so we need to do it here
|
|
588
|
+
# to cover `if self is other`
|
|
589
|
+
assert wr == wr_other_is_self
|
|
590
|
+
|
|
591
|
+
# we want to cover __ne__ and `return NotImplemented`
|
|
592
|
+
assert wr != object()
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def test_weak_key_identity_dict_remove_callback_keyerror() -> None:
|
|
596
|
+
"""We need to cover the KeyError in self._remove."""
|
|
597
|
+
|
|
598
|
+
class A:
|
|
599
|
+
def __eq__(self, other: object) -> bool:
|
|
600
|
+
return False
|
|
601
|
+
|
|
602
|
+
a = A()
|
|
603
|
+
assert a != a
|
|
604
|
+
d: _ki.WeakKeyIdentityDictionary[A, bool] = _ki.WeakKeyIdentityDictionary()
|
|
605
|
+
|
|
606
|
+
d[a] = True
|
|
607
|
+
|
|
608
|
+
data_copy = d._data.copy()
|
|
609
|
+
d._data.clear()
|
|
610
|
+
del a
|
|
611
|
+
|
|
612
|
+
gc_collect_harder() # would call sys.unraisablehook if there's a problem
|
|
613
|
+
assert data_copy
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def test_weak_key_identity_dict_remove_callback_selfref_expired() -> None:
|
|
617
|
+
"""We need to cover the KeyError in self._remove."""
|
|
618
|
+
|
|
619
|
+
class A:
|
|
620
|
+
def __eq__(self, other: object) -> bool:
|
|
621
|
+
return False
|
|
622
|
+
|
|
623
|
+
a = A()
|
|
624
|
+
assert a != a
|
|
625
|
+
d: _ki.WeakKeyIdentityDictionary[A, bool] = _ki.WeakKeyIdentityDictionary()
|
|
626
|
+
|
|
627
|
+
d[a] = True
|
|
628
|
+
|
|
629
|
+
data_copy = d._data.copy()
|
|
630
|
+
wr_d = weakref.ref(d)
|
|
631
|
+
del d
|
|
632
|
+
gc_collect_harder() # would call sys.unraisablehook if there's a problem
|
|
633
|
+
assert wr_d() is None
|
|
634
|
+
del a
|
|
635
|
+
gc_collect_harder()
|
|
636
|
+
assert data_copy
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
@_core.enable_ki_protection
|
|
640
|
+
async def _protected_async_gen_fn() -> AsyncGenerator[None, None]:
|
|
641
|
+
yield
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
@_core.enable_ki_protection
|
|
645
|
+
async def _protected_async_fn() -> None:
|
|
646
|
+
pass
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
@_core.enable_ki_protection
|
|
650
|
+
def _protected_gen_fn() -> Generator[None, None, None]:
|
|
651
|
+
yield
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
@_core.disable_ki_protection
|
|
655
|
+
async def _unprotected_async_gen_fn() -> AsyncGenerator[None, None]:
|
|
656
|
+
yield
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
@_core.disable_ki_protection
|
|
660
|
+
async def _unprotected_async_fn() -> None:
|
|
661
|
+
pass
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
@_core.disable_ki_protection
|
|
665
|
+
def _unprotected_gen_fn() -> Generator[None, None, None]:
|
|
666
|
+
yield
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
async def _consume_async_generator(agen: AsyncGenerator[None, None]) -> None:
|
|
670
|
+
try:
|
|
671
|
+
with pytest.raises(StopAsyncIteration):
|
|
672
|
+
while True:
|
|
673
|
+
await agen.asend(None)
|
|
674
|
+
finally:
|
|
675
|
+
await agen.aclose()
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
def _consume_function_for_coverage(
|
|
679
|
+
fn: Callable[[], object],
|
|
680
|
+
) -> None:
|
|
681
|
+
result = fn()
|
|
682
|
+
if inspect.isasyncgen(result):
|
|
683
|
+
result = _consume_async_generator(result)
|
|
684
|
+
|
|
685
|
+
assert inspect.isgenerator(result) or inspect.iscoroutine(result)
|
|
686
|
+
with pytest.raises(StopIteration):
|
|
687
|
+
while True:
|
|
688
|
+
result.send(None)
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
def test_enable_disable_ki_protection_passes_on_inspect_flags() -> None:
|
|
692
|
+
assert inspect.isasyncgenfunction(_protected_async_gen_fn)
|
|
693
|
+
_consume_function_for_coverage(_protected_async_gen_fn)
|
|
694
|
+
assert inspect.iscoroutinefunction(_protected_async_fn)
|
|
695
|
+
_consume_function_for_coverage(_protected_async_fn)
|
|
696
|
+
assert inspect.isgeneratorfunction(_protected_gen_fn)
|
|
697
|
+
_consume_function_for_coverage(_protected_gen_fn)
|
|
698
|
+
assert inspect.isasyncgenfunction(_unprotected_async_gen_fn)
|
|
699
|
+
_consume_function_for_coverage(_unprotected_async_gen_fn)
|
|
700
|
+
assert inspect.iscoroutinefunction(_unprotected_async_fn)
|
|
701
|
+
_consume_function_for_coverage(_unprotected_async_fn)
|
|
702
|
+
assert inspect.isgeneratorfunction(_unprotected_gen_fn)
|
|
703
|
+
_consume_function_for_coverage(_unprotected_gen_fn)
|