@ajksunkang-aios/kgraph-linux-x64 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/bin/kgraph-launcher +15 -3
  2. package/lib/kgraph/scripts/build-bundle.sh +17 -4
  3. package/lib/site-packages/outcome/__init__.py +20 -0
  4. package/lib/site-packages/outcome/_impl.py +239 -0
  5. package/lib/site-packages/outcome/_util.py +33 -0
  6. package/lib/site-packages/outcome/_version.py +7 -0
  7. package/lib/site-packages/outcome/py.typed +0 -0
  8. package/lib/site-packages/outcome-1.3.0.post0.dist-info/INSTALLER +1 -0
  9. package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE +3 -0
  10. package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.APACHE2 +202 -0
  11. package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.MIT +20 -0
  12. package/lib/site-packages/outcome-1.3.0.post0.dist-info/METADATA +63 -0
  13. package/lib/site-packages/outcome-1.3.0.post0.dist-info/RECORD +13 -0
  14. package/lib/site-packages/outcome-1.3.0.post0.dist-info/WHEEL +6 -0
  15. package/lib/site-packages/outcome-1.3.0.post0.dist-info/top_level.txt +1 -0
  16. package/lib/site-packages/sniffio/__init__.py +17 -0
  17. package/lib/site-packages/sniffio/_impl.py +95 -0
  18. package/lib/site-packages/sniffio/_tests/__init__.py +0 -0
  19. package/lib/site-packages/sniffio/_tests/test_sniffio.py +84 -0
  20. package/lib/site-packages/sniffio/_version.py +3 -0
  21. package/lib/site-packages/sniffio/py.typed +0 -0
  22. package/lib/site-packages/sniffio-1.3.1.dist-info/INSTALLER +1 -0
  23. package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE +3 -0
  24. package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 +202 -0
  25. package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT +20 -0
  26. package/lib/site-packages/sniffio-1.3.1.dist-info/METADATA +104 -0
  27. package/lib/site-packages/sniffio-1.3.1.dist-info/RECORD +14 -0
  28. package/lib/site-packages/sniffio-1.3.1.dist-info/WHEEL +5 -0
  29. package/lib/site-packages/sniffio-1.3.1.dist-info/top_level.txt +1 -0
  30. package/lib/site-packages/sortedcontainers/__init__.py +74 -0
  31. package/lib/site-packages/sortedcontainers/sorteddict.py +812 -0
  32. package/lib/site-packages/sortedcontainers/sortedlist.py +2646 -0
  33. package/lib/site-packages/sortedcontainers/sortedset.py +733 -0
  34. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/INSTALLER +1 -0
  35. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/LICENSE +13 -0
  36. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/METADATA +264 -0
  37. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/RECORD +10 -0
  38. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/WHEEL +6 -0
  39. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/top_level.txt +1 -0
  40. package/lib/site-packages/trio/__init__.py +133 -0
  41. package/lib/site-packages/trio/__main__.py +3 -0
  42. package/lib/site-packages/trio/_abc.py +714 -0
  43. package/lib/site-packages/trio/_channel.py +610 -0
  44. package/lib/site-packages/trio/_core/__init__.py +94 -0
  45. package/lib/site-packages/trio/_core/_asyncgens.py +243 -0
  46. package/lib/site-packages/trio/_core/_concat_tb.py +26 -0
  47. package/lib/site-packages/trio/_core/_entry_queue.py +223 -0
  48. package/lib/site-packages/trio/_core/_exceptions.py +169 -0
  49. package/lib/site-packages/trio/_core/_generated_instrumentation.py +50 -0
  50. package/lib/site-packages/trio/_core/_generated_io_epoll.py +98 -0
  51. package/lib/site-packages/trio/_core/_generated_io_kqueue.py +153 -0
  52. package/lib/site-packages/trio/_core/_generated_io_windows.py +204 -0
  53. package/lib/site-packages/trio/_core/_generated_run.py +269 -0
  54. package/lib/site-packages/trio/_core/_generated_windows_ffi.py +10 -0
  55. package/lib/site-packages/trio/_core/_instrumentation.py +117 -0
  56. package/lib/site-packages/trio/_core/_io_common.py +31 -0
  57. package/lib/site-packages/trio/_core/_io_epoll.py +385 -0
  58. package/lib/site-packages/trio/_core/_io_kqueue.py +292 -0
  59. package/lib/site-packages/trio/_core/_io_windows.py +1036 -0
  60. package/lib/site-packages/trio/_core/_ki.py +271 -0
  61. package/lib/site-packages/trio/_core/_local.py +104 -0
  62. package/lib/site-packages/trio/_core/_mock_clock.py +165 -0
  63. package/lib/site-packages/trio/_core/_parking_lot.py +317 -0
  64. package/lib/site-packages/trio/_core/_run.py +3148 -0
  65. package/lib/site-packages/trio/_core/_run_context.py +15 -0
  66. package/lib/site-packages/trio/_core/_tests/__init__.py +0 -0
  67. package/lib/site-packages/trio/_core/_tests/test_asyncgen.py +339 -0
  68. package/lib/site-packages/trio/_core/_tests/test_cancelled.py +222 -0
  69. package/lib/site-packages/trio/_core/_tests/test_exceptiongroup_gc.py +103 -0
  70. package/lib/site-packages/trio/_core/_tests/test_guest_mode.py +755 -0
  71. package/lib/site-packages/trio/_core/_tests/test_instrumentation.py +315 -0
  72. package/lib/site-packages/trio/_core/_tests/test_io.py +522 -0
  73. package/lib/site-packages/trio/_core/_tests/test_ki.py +703 -0
  74. package/lib/site-packages/trio/_core/_tests/test_local.py +118 -0
  75. package/lib/site-packages/trio/_core/_tests/test_mock_clock.py +193 -0
  76. package/lib/site-packages/trio/_core/_tests/test_parking_lot.py +389 -0
  77. package/lib/site-packages/trio/_core/_tests/test_run.py +3024 -0
  78. package/lib/site-packages/trio/_core/_tests/test_thread_cache.py +227 -0
  79. package/lib/site-packages/trio/_core/_tests/test_tutil.py +13 -0
  80. package/lib/site-packages/trio/_core/_tests/test_unbounded_queue.py +154 -0
  81. package/lib/site-packages/trio/_core/_tests/test_windows.py +305 -0
  82. package/lib/site-packages/trio/_core/_tests/tutil.py +117 -0
  83. package/lib/site-packages/trio/_core/_tests/type_tests/nursery_start.py +79 -0
  84. package/lib/site-packages/trio/_core/_tests/type_tests/run.py +51 -0
  85. package/lib/site-packages/trio/_core/_thread_cache.py +317 -0
  86. package/lib/site-packages/trio/_core/_traps.py +318 -0
  87. package/lib/site-packages/trio/_core/_unbounded_queue.py +163 -0
  88. package/lib/site-packages/trio/_core/_wakeup_socketpair.py +75 -0
  89. package/lib/site-packages/trio/_core/_windows_cffi.py +313 -0
  90. package/lib/site-packages/trio/_deprecate.py +171 -0
  91. package/lib/site-packages/trio/_dtls.py +1380 -0
  92. package/lib/site-packages/trio/_file_io.py +513 -0
  93. package/lib/site-packages/trio/_highlevel_generic.py +125 -0
  94. package/lib/site-packages/trio/_highlevel_open_tcp_listeners.py +251 -0
  95. package/lib/site-packages/trio/_highlevel_open_tcp_stream.py +397 -0
  96. package/lib/site-packages/trio/_highlevel_open_unix_stream.py +65 -0
  97. package/lib/site-packages/trio/_highlevel_serve_listeners.py +148 -0
  98. package/lib/site-packages/trio/_highlevel_socket.py +423 -0
  99. package/lib/site-packages/trio/_highlevel_ssl_helpers.py +180 -0
  100. package/lib/site-packages/trio/_path.py +289 -0
  101. package/lib/site-packages/trio/_repl.py +159 -0
  102. package/lib/site-packages/trio/_signals.py +185 -0
  103. package/lib/site-packages/trio/_socket.py +1326 -0
  104. package/lib/site-packages/trio/_ssl.py +964 -0
  105. package/lib/site-packages/trio/_subprocess.py +1178 -0
  106. package/lib/site-packages/trio/_subprocess_platform/__init__.py +123 -0
  107. package/lib/site-packages/trio/_subprocess_platform/kqueue.py +48 -0
  108. package/lib/site-packages/trio/_subprocess_platform/waitid.py +113 -0
  109. package/lib/site-packages/trio/_subprocess_platform/windows.py +11 -0
  110. package/lib/site-packages/trio/_sync.py +908 -0
  111. package/lib/site-packages/trio/_tests/__init__.py +0 -0
  112. package/lib/site-packages/trio/_tests/astrill-codesigning-cert.cer +0 -0
  113. package/lib/site-packages/trio/_tests/check_type_completeness.py +247 -0
  114. package/lib/site-packages/trio/_tests/module_with_deprecations.py +22 -0
  115. package/lib/site-packages/trio/_tests/pytest_plugin.py +54 -0
  116. package/lib/site-packages/trio/_tests/test_abc.py +72 -0
  117. package/lib/site-packages/trio/_tests/test_channel.py +750 -0
  118. package/lib/site-packages/trio/_tests/test_contextvars.py +56 -0
  119. package/lib/site-packages/trio/_tests/test_deprecate.py +277 -0
  120. package/lib/site-packages/trio/_tests/test_deprecate_strict_exception_groups_false.py +64 -0
  121. package/lib/site-packages/trio/_tests/test_dtls.py +950 -0
  122. package/lib/site-packages/trio/_tests/test_exports.py +626 -0
  123. package/lib/site-packages/trio/_tests/test_fakenet.py +317 -0
  124. package/lib/site-packages/trio/_tests/test_file_io.py +269 -0
  125. package/lib/site-packages/trio/_tests/test_highlevel_generic.py +98 -0
  126. package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_listeners.py +419 -0
  127. package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_stream.py +693 -0
  128. package/lib/site-packages/trio/_tests/test_highlevel_open_unix_stream.py +86 -0
  129. package/lib/site-packages/trio/_tests/test_highlevel_serve_listeners.py +186 -0
  130. package/lib/site-packages/trio/_tests/test_highlevel_socket.py +336 -0
  131. package/lib/site-packages/trio/_tests/test_highlevel_ssl_helpers.py +169 -0
  132. package/lib/site-packages/trio/_tests/test_path.py +279 -0
  133. package/lib/site-packages/trio/_tests/test_repl.py +428 -0
  134. package/lib/site-packages/trio/_tests/test_scheduler_determinism.py +47 -0
  135. package/lib/site-packages/trio/_tests/test_signals.py +186 -0
  136. package/lib/site-packages/trio/_tests/test_socket.py +1253 -0
  137. package/lib/site-packages/trio/_tests/test_ssl.py +1371 -0
  138. package/lib/site-packages/trio/_tests/test_subprocess.py +767 -0
  139. package/lib/site-packages/trio/_tests/test_sync.py +735 -0
  140. package/lib/site-packages/trio/_tests/test_testing.py +682 -0
  141. package/lib/site-packages/trio/_tests/test_testing_raisesgroup.py +1128 -0
  142. package/lib/site-packages/trio/_tests/test_threads.py +1173 -0
  143. package/lib/site-packages/trio/_tests/test_timeouts.py +281 -0
  144. package/lib/site-packages/trio/_tests/test_tracing.py +88 -0
  145. package/lib/site-packages/trio/_tests/test_trio.py +8 -0
  146. package/lib/site-packages/trio/_tests/test_unix_pipes.py +288 -0
  147. package/lib/site-packages/trio/_tests/test_util.py +349 -0
  148. package/lib/site-packages/trio/_tests/test_wait_for_object.py +225 -0
  149. package/lib/site-packages/trio/_tests/test_windows_pipes.py +112 -0
  150. package/lib/site-packages/trio/_tests/tools/__init__.py +0 -0
  151. package/lib/site-packages/trio/_tests/tools/test_gen_exports.py +179 -0
  152. package/lib/site-packages/trio/_tests/tools/test_mypy_annotate.py +140 -0
  153. package/lib/site-packages/trio/_tests/tools/test_sync_requirements.py +80 -0
  154. package/lib/site-packages/trio/_tests/type_tests/check_wraps.py +9 -0
  155. package/lib/site-packages/trio/_tests/type_tests/open_memory_channel.py +4 -0
  156. package/lib/site-packages/trio/_tests/type_tests/path.py +140 -0
  157. package/lib/site-packages/trio/_tests/type_tests/subprocesses.py +23 -0
  158. package/lib/site-packages/trio/_tests/type_tests/task_status.py +29 -0
  159. package/lib/site-packages/trio/_threads.py +610 -0
  160. package/lib/site-packages/trio/_timeouts.py +197 -0
  161. package/lib/site-packages/trio/_tools/__init__.py +0 -0
  162. package/lib/site-packages/trio/_tools/gen_exports.py +401 -0
  163. package/lib/site-packages/trio/_tools/mypy_annotate.py +126 -0
  164. package/lib/site-packages/trio/_tools/sync_requirements.py +98 -0
  165. package/lib/site-packages/trio/_tools/windows_ffi_build.py +220 -0
  166. package/lib/site-packages/trio/_unix_pipes.py +197 -0
  167. package/lib/site-packages/trio/_util.py +385 -0
  168. package/lib/site-packages/trio/_version.py +3 -0
  169. package/lib/site-packages/trio/_wait_for_object.py +67 -0
  170. package/lib/site-packages/trio/_windows_pipes.py +144 -0
  171. package/lib/site-packages/trio/abc.py +23 -0
  172. package/lib/site-packages/trio/from_thread.py +13 -0
  173. package/lib/site-packages/trio/lowlevel.py +95 -0
  174. package/lib/site-packages/trio/py.typed +0 -0
  175. package/lib/site-packages/trio/socket.py +602 -0
  176. package/lib/site-packages/trio/testing/__init__.py +58 -0
  177. package/lib/site-packages/trio/testing/_check_streams.py +570 -0
  178. package/lib/site-packages/trio/testing/_checkpoints.py +69 -0
  179. package/lib/site-packages/trio/testing/_fake_net.py +584 -0
  180. package/lib/site-packages/trio/testing/_memory_streams.py +633 -0
  181. package/lib/site-packages/trio/testing/_network.py +36 -0
  182. package/lib/site-packages/trio/testing/_raises_group.py +1015 -0
  183. package/lib/site-packages/trio/testing/_sequencer.py +87 -0
  184. package/lib/site-packages/trio/testing/_trio_test.py +50 -0
  185. package/lib/site-packages/trio/to_thread.py +4 -0
  186. package/lib/site-packages/trio-0.33.0.dist-info/INSTALLER +1 -0
  187. package/lib/site-packages/trio-0.33.0.dist-info/METADATA +186 -0
  188. package/lib/site-packages/trio-0.33.0.dist-info/RECORD +156 -0
  189. package/lib/site-packages/trio-0.33.0.dist-info/REQUESTED +0 -0
  190. package/lib/site-packages/trio-0.33.0.dist-info/WHEEL +5 -0
  191. package/lib/site-packages/trio-0.33.0.dist-info/entry_points.txt +2 -0
  192. package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE +3 -0
  193. package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.APACHE2 +202 -0
  194. package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.MIT +22 -0
  195. package/lib/site-packages/trio-0.33.0.dist-info/top_level.txt +1 -0
  196. package/package.json +1 -1
@@ -0,0 +1,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)