@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,610 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import contextvars
5
+ import inspect
6
+ import queue as stdlib_queue
7
+ import threading
8
+ from itertools import count
9
+ from typing import TYPE_CHECKING, Generic, TypeVar
10
+
11
+ import attrs
12
+ import outcome
13
+ from attrs import define
14
+ from sniffio import current_async_library_cvar
15
+
16
+ import trio
17
+
18
+ from ._core import (
19
+ RunVar,
20
+ TrioToken,
21
+ checkpoint,
22
+ disable_ki_protection,
23
+ enable_ki_protection,
24
+ start_thread_soon,
25
+ )
26
+ from ._sync import CapacityLimiter, Event
27
+ from ._util import coroutine_or_error
28
+
29
+ if TYPE_CHECKING:
30
+ from collections.abc import Awaitable, Callable, Generator
31
+
32
+ from typing_extensions import TypeVarTuple, Unpack
33
+
34
+ from trio._core._traps import RaiseCancelT
35
+
36
+ Ts = TypeVarTuple("Ts")
37
+
38
+ RetT = TypeVar("RetT")
39
+
40
+
41
+ class _ParentTaskData(threading.local):
42
+ """Global due to Threading API, thread local storage for data related to the
43
+ parent task of native Trio threads."""
44
+
45
+ token: TrioToken
46
+ abandon_on_cancel: bool
47
+ cancel_register: list[RaiseCancelT | None]
48
+ task_register: list[trio.lowlevel.Task | None]
49
+
50
+
51
+ PARENT_TASK_DATA = _ParentTaskData()
52
+
53
+ _limiter_local: RunVar[CapacityLimiter] = RunVar("limiter")
54
+ # I pulled this number out of the air; it isn't based on anything. Probably we
55
+ # should make some kind of measurements to pick a good value.
56
+ DEFAULT_LIMIT = 40
57
+ _thread_counter = count()
58
+
59
+
60
+ @define
61
+ class _ActiveThreadCount:
62
+ count: int
63
+ event: Event
64
+
65
+
66
+ _active_threads_local: RunVar[_ActiveThreadCount] = RunVar("active_threads")
67
+
68
+
69
+ @contextlib.contextmanager
70
+ def _track_active_thread() -> Generator[None, None, None]:
71
+ try:
72
+ active_threads_local = _active_threads_local.get()
73
+ except LookupError:
74
+ active_threads_local = _ActiveThreadCount(0, Event())
75
+ _active_threads_local.set(active_threads_local)
76
+
77
+ active_threads_local.count += 1
78
+ try:
79
+ yield
80
+ finally:
81
+ active_threads_local.count -= 1
82
+ if active_threads_local.count == 0:
83
+ active_threads_local.event.set()
84
+ active_threads_local.event = Event()
85
+
86
+
87
+ async def wait_all_threads_completed() -> None:
88
+ """Wait until no threads are still running tasks.
89
+
90
+ This is intended to be used when testing code with trio.to_thread to
91
+ make sure no tasks are still making progress in a thread. See the
92
+ following code for a usage example::
93
+
94
+ async def wait_all_settled():
95
+ while True:
96
+ await trio.testing.wait_all_threads_complete()
97
+ await trio.testing.wait_all_tasks_blocked()
98
+ if trio.testing.active_thread_count() == 0:
99
+ break
100
+ """
101
+
102
+ await checkpoint()
103
+
104
+ try:
105
+ active_threads_local = _active_threads_local.get()
106
+ except LookupError:
107
+ # If there would have been active threads, the
108
+ # _active_threads_local would have been set
109
+ return
110
+
111
+ while active_threads_local.count != 0:
112
+ await active_threads_local.event.wait()
113
+
114
+
115
+ def active_thread_count() -> int:
116
+ """Returns the number of threads that are currently running a task
117
+
118
+ See `trio.testing.wait_all_threads_completed`
119
+ """
120
+ try:
121
+ return _active_threads_local.get().count
122
+ except LookupError:
123
+ return 0
124
+
125
+
126
+ def current_default_thread_limiter() -> CapacityLimiter:
127
+ """Get the default `~trio.CapacityLimiter` used by
128
+ `trio.to_thread.run_sync`.
129
+
130
+ The most common reason to call this would be if you want to modify its
131
+ :attr:`~trio.CapacityLimiter.total_tokens` attribute.
132
+
133
+ """
134
+ try:
135
+ limiter = _limiter_local.get()
136
+ except LookupError:
137
+ limiter = CapacityLimiter(DEFAULT_LIMIT)
138
+ _limiter_local.set(limiter)
139
+ return limiter
140
+
141
+
142
+ # Eventually we might build this into a full-fledged deadlock-detection
143
+ # system; see https://github.com/python-trio/trio/issues/182
144
+ # But for now we just need an object to stand in for the thread, so we can
145
+ # keep track of who's holding the CapacityLimiter's token.
146
+ @attrs.frozen(eq=False, slots=False)
147
+ class ThreadPlaceholder:
148
+ name: str
149
+
150
+
151
+ # Types for the to_thread_run_sync message loop
152
+ @attrs.frozen(eq=False, slots=False)
153
+ class Run(Generic[RetT]): # type: ignore[explicit-any]
154
+ afn: Callable[..., Awaitable[RetT]] # type: ignore[explicit-any]
155
+ args: tuple[object, ...]
156
+ context: contextvars.Context = attrs.field(
157
+ init=False,
158
+ factory=contextvars.copy_context,
159
+ )
160
+ queue: stdlib_queue.SimpleQueue[outcome.Outcome[RetT]] = attrs.field(
161
+ init=False,
162
+ factory=stdlib_queue.SimpleQueue,
163
+ )
164
+
165
+ @disable_ki_protection
166
+ async def unprotected_afn(self) -> RetT:
167
+ coro = coroutine_or_error(self.afn, *self.args)
168
+ return await coro
169
+
170
+ async def run(self) -> None:
171
+ # we use extra checkpoints to pick up and reset any context changes
172
+ task = trio.lowlevel.current_task()
173
+ old_context = task.context
174
+ task.context = self.context.copy()
175
+ await trio.lowlevel.cancel_shielded_checkpoint()
176
+ result = await outcome.acapture(self.unprotected_afn)
177
+ task.context = old_context
178
+ await trio.lowlevel.cancel_shielded_checkpoint()
179
+ self.queue.put_nowait(result)
180
+
181
+ async def run_system(self) -> None:
182
+ result = await outcome.acapture(self.unprotected_afn)
183
+ self.queue.put_nowait(result)
184
+
185
+ def run_in_host_task(self, token: TrioToken) -> None:
186
+ task_register = PARENT_TASK_DATA.task_register
187
+
188
+ def in_trio_thread() -> None:
189
+ task = task_register[0]
190
+ assert task is not None, "guaranteed by abandon_on_cancel semantics"
191
+ trio.lowlevel.reschedule(task, outcome.Value(self))
192
+
193
+ token.run_sync_soon(in_trio_thread)
194
+
195
+ def run_in_system_nursery(self, token: TrioToken) -> None:
196
+ def in_trio_thread() -> None:
197
+ try:
198
+ trio.lowlevel.spawn_system_task(
199
+ self.run_system,
200
+ name=self.afn,
201
+ context=self.context,
202
+ )
203
+ except RuntimeError: # system nursery is closed
204
+ self.queue.put_nowait(
205
+ outcome.Error(trio.RunFinishedError("system nursery is closed")),
206
+ )
207
+
208
+ token.run_sync_soon(in_trio_thread)
209
+
210
+
211
+ @attrs.frozen(eq=False, slots=False)
212
+ class RunSync(Generic[RetT]): # type: ignore[explicit-any]
213
+ fn: Callable[..., RetT] # type: ignore[explicit-any]
214
+ args: tuple[object, ...]
215
+ context: contextvars.Context = attrs.field(
216
+ init=False,
217
+ factory=contextvars.copy_context,
218
+ )
219
+ queue: stdlib_queue.SimpleQueue[outcome.Outcome[RetT]] = attrs.field(
220
+ init=False,
221
+ factory=stdlib_queue.SimpleQueue,
222
+ )
223
+
224
+ @disable_ki_protection
225
+ def unprotected_fn(self) -> RetT:
226
+ ret = self.context.run(self.fn, *self.args)
227
+
228
+ if inspect.iscoroutine(ret):
229
+ # Manually close coroutine to avoid RuntimeWarnings
230
+ ret.close()
231
+ raise TypeError(
232
+ "Trio expected a synchronous function, but {!r} appears to be "
233
+ "asynchronous".format(getattr(self.fn, "__qualname__", self.fn)),
234
+ )
235
+
236
+ return ret
237
+
238
+ def run_sync(self) -> None:
239
+ result = outcome.capture(self.unprotected_fn)
240
+ self.queue.put_nowait(result)
241
+
242
+ def run_in_host_task(self, token: TrioToken) -> None:
243
+ task_register = PARENT_TASK_DATA.task_register
244
+
245
+ def in_trio_thread() -> None:
246
+ task = task_register[0]
247
+ assert task is not None, "guaranteed by abandon_on_cancel semantics"
248
+ trio.lowlevel.reschedule(task, outcome.Value(self))
249
+
250
+ token.run_sync_soon(in_trio_thread)
251
+
252
+ def run_in_system_nursery(self, token: TrioToken) -> None:
253
+ token.run_sync_soon(self.run_sync)
254
+
255
+
256
+ @enable_ki_protection
257
+ async def to_thread_run_sync(
258
+ sync_fn: Callable[[Unpack[Ts]], RetT],
259
+ *args: Unpack[Ts],
260
+ thread_name: str | None = None,
261
+ abandon_on_cancel: bool = False,
262
+ limiter: CapacityLimiter | None = None,
263
+ ) -> RetT:
264
+ """Convert a blocking operation into an async operation using a thread.
265
+
266
+ These two lines are equivalent::
267
+
268
+ sync_fn(*args)
269
+ await trio.to_thread.run_sync(sync_fn, *args)
270
+
271
+ except that if ``sync_fn`` takes a long time, then the first line will
272
+ block the Trio loop while it runs, while the second line allows other Trio
273
+ tasks to continue working while ``sync_fn`` runs. This is accomplished by
274
+ pushing the call to ``sync_fn(*args)`` off into a worker thread.
275
+
276
+ From inside the worker thread, you can get back into Trio using the
277
+ functions in `trio.from_thread`.
278
+
279
+ Args:
280
+ sync_fn: An arbitrary synchronous callable.
281
+ *args: Positional arguments to pass to sync_fn. If you need keyword
282
+ arguments, use :func:`functools.partial`.
283
+ abandon_on_cancel (bool): Whether to abandon this thread upon
284
+ cancellation of this operation. See discussion below.
285
+ thread_name (str): Optional string to set the name of the thread.
286
+ Will always set `threading.Thread.name`, but only set the os name
287
+ if pthread.h is available (i.e. most POSIX installations).
288
+ pthread names are limited to 15 characters, and can be read from
289
+ ``/proc/<PID>/task/<SPID>/comm`` or with ``ps -eT``, among others.
290
+ Defaults to ``{sync_fn.__name__|None} from {trio.lowlevel.current_task().name}``.
291
+ limiter (None, or CapacityLimiter-like object):
292
+ An object used to limit the number of simultaneous threads. Most
293
+ commonly this will be a `~trio.CapacityLimiter`, but it could be
294
+ anything providing compatible
295
+ :meth:`~trio.CapacityLimiter.acquire_on_behalf_of` and
296
+ :meth:`~trio.CapacityLimiter.release_on_behalf_of` methods. This
297
+ function will call ``acquire_on_behalf_of`` before starting the
298
+ thread, and ``release_on_behalf_of`` after the thread has finished.
299
+
300
+ If None (the default), uses the default `~trio.CapacityLimiter`, as
301
+ returned by :func:`current_default_thread_limiter`.
302
+
303
+ **Cancellation handling**: Cancellation is a tricky issue here, because
304
+ neither Python nor the operating systems it runs on provide any general
305
+ mechanism for cancelling an arbitrary synchronous function running in a
306
+ thread. This function will always check for cancellation on entry, before
307
+ starting the thread. But once the thread is running, there are two ways it
308
+ can handle being cancelled:
309
+
310
+ * If ``abandon_on_cancel=False``, the function ignores the cancellation and
311
+ keeps going, just like if we had called ``sync_fn`` synchronously. This
312
+ is the default behavior.
313
+
314
+ * If ``abandon_on_cancel=True``, then this function immediately raises
315
+ `~trio.Cancelled`. In this case **the thread keeps running in
316
+ background** – we just abandon it to do whatever it's going to do, and
317
+ silently discard any return value or errors that it raises. Only use
318
+ this if you know that the operation is safe and side-effect free. (For
319
+ example: :func:`trio.socket.getaddrinfo` uses a thread with
320
+ ``abandon_on_cancel=True``, because it doesn't really affect anything if a
321
+ stray hostname lookup keeps running in the background.)
322
+
323
+ The ``limiter`` is only released after the thread has *actually*
324
+ finished – which in the case of cancellation may be some time after this
325
+ function has returned. If :func:`trio.run` finishes before the thread
326
+ does, then the limiter release method will never be called at all.
327
+
328
+ .. warning::
329
+
330
+ You should not use this function to call long-running CPU-bound
331
+ functions! In addition to the usual GIL-related reasons why using
332
+ threads for CPU-bound work is not very effective in Python, there is an
333
+ additional problem: on CPython, `CPU-bound threads tend to "starve out"
334
+ IO-bound threads <https://bugs.python.org/issue7946>`__, so using
335
+ threads for CPU-bound work is likely to adversely affect the main
336
+ thread running Trio. If you need to do this, you're better off using a
337
+ worker process, or perhaps PyPy (which still has a GIL, but may do a
338
+ better job of fairly allocating CPU time between threads).
339
+
340
+ Returns:
341
+ Whatever ``sync_fn(*args)`` returns.
342
+
343
+ Raises:
344
+ Exception: Whatever ``sync_fn(*args)`` raises.
345
+
346
+ """
347
+ await trio.lowlevel.checkpoint_if_cancelled()
348
+ # raise early if abandon_on_cancel.__bool__ raises
349
+ # and give a new name to ensure mypy knows it's never None
350
+ abandon_bool = bool(abandon_on_cancel)
351
+ if limiter is None:
352
+ limiter = current_default_thread_limiter()
353
+
354
+ # Holds a reference to the task that's blocked in this function waiting
355
+ # for the result – or None if this function was cancelled and we should
356
+ # discard the result.
357
+ task_register: list[trio.lowlevel.Task | None] = [trio.lowlevel.current_task()]
358
+ # Holds a reference to the raise_cancel function provided if a cancellation
359
+ # is attempted against this task - or None if no such delivery has happened.
360
+ cancel_register: list[RaiseCancelT | None] = [None] # type: ignore[assignment]
361
+ name = f"trio.to_thread.run_sync-{next(_thread_counter)}"
362
+ placeholder = ThreadPlaceholder(name)
363
+
364
+ # This function gets scheduled into the Trio run loop to deliver the
365
+ # thread's result.
366
+ def report_back_in_trio_thread_fn(result: outcome.Outcome[RetT]) -> None:
367
+ def do_release_then_return_result() -> RetT:
368
+ # release_on_behalf_of is an arbitrary user-defined method, so it
369
+ # might raise an error. If it does, we want that error to
370
+ # replace the regular return value, and if the regular return was
371
+ # already an exception then we want them to chain.
372
+ try:
373
+ return result.unwrap()
374
+ finally:
375
+ limiter.release_on_behalf_of(placeholder)
376
+
377
+ result = outcome.capture(do_release_then_return_result)
378
+ if task_register[0] is not None:
379
+ trio.lowlevel.reschedule(task_register[0], outcome.Value(result))
380
+
381
+ current_trio_token = trio.lowlevel.current_trio_token()
382
+
383
+ if thread_name is None:
384
+ thread_name = f"{getattr(sync_fn, '__name__', None)} from {trio.lowlevel.current_task().name}"
385
+
386
+ def worker_fn() -> RetT:
387
+ PARENT_TASK_DATA.token = current_trio_token
388
+ PARENT_TASK_DATA.abandon_on_cancel = abandon_bool
389
+ PARENT_TASK_DATA.cancel_register = cancel_register
390
+ PARENT_TASK_DATA.task_register = task_register
391
+ try:
392
+ ret = context.run(sync_fn, *args)
393
+
394
+ if inspect.iscoroutine(ret):
395
+ # Manually close coroutine to avoid RuntimeWarnings
396
+ ret.close()
397
+ raise TypeError(
398
+ "Trio expected a sync function, but {!r} appears to be "
399
+ "asynchronous".format(getattr(sync_fn, "__qualname__", sync_fn)),
400
+ )
401
+
402
+ return ret
403
+ finally:
404
+ del PARENT_TASK_DATA.token
405
+ del PARENT_TASK_DATA.abandon_on_cancel
406
+ del PARENT_TASK_DATA.cancel_register
407
+ del PARENT_TASK_DATA.task_register
408
+
409
+ context = contextvars.copy_context()
410
+ # Trio doesn't use current_async_library_cvar, but if someone
411
+ # else set it, it would now shine through since
412
+ # sniffio.thread_local isn't set in the new thread. Make sure
413
+ # the new thread sees that it's not running in async context.
414
+ context.run(current_async_library_cvar.set, None)
415
+
416
+ def deliver_worker_fn_result(result: outcome.Outcome[RetT]) -> None:
417
+ # If the entire run finished, the task we're trying to contact is
418
+ # certainly long gone -- it must have been cancelled and abandoned
419
+ # us. Just ignore the error in this case.
420
+ with contextlib.suppress(trio.RunFinishedError):
421
+ current_trio_token.run_sync_soon(report_back_in_trio_thread_fn, result)
422
+
423
+ await limiter.acquire_on_behalf_of(placeholder)
424
+ with _track_active_thread():
425
+ try:
426
+ start_thread_soon(worker_fn, deliver_worker_fn_result, thread_name)
427
+ except:
428
+ limiter.release_on_behalf_of(placeholder)
429
+ raise
430
+
431
+ def abort(raise_cancel: RaiseCancelT) -> trio.lowlevel.Abort:
432
+ # fill so from_thread_check_cancelled can raise
433
+ # 'raise_cancel' will immediately delete its reason object, so we make
434
+ # a copy in each thread
435
+ cancel_register[0] = raise_cancel
436
+ if abandon_bool:
437
+ # empty so report_back_in_trio_thread_fn cannot reschedule
438
+ task_register[0] = None
439
+ return trio.lowlevel.Abort.SUCCEEDED
440
+ else:
441
+ return trio.lowlevel.Abort.FAILED
442
+
443
+ while True:
444
+ # wait_task_rescheduled return value cannot be typed
445
+ msg_from_thread: outcome.Outcome[RetT] | Run[object] | RunSync[object] = (
446
+ await trio.lowlevel.wait_task_rescheduled(abort)
447
+ )
448
+ if isinstance(msg_from_thread, outcome.Outcome):
449
+ return msg_from_thread.unwrap()
450
+ elif isinstance(msg_from_thread, Run):
451
+ await msg_from_thread.run()
452
+ elif isinstance(msg_from_thread, RunSync):
453
+ msg_from_thread.run_sync()
454
+ else: # pragma: no cover, internal debugging guard TODO: use assert_never
455
+ raise TypeError(
456
+ f"trio.to_thread.run_sync received unrecognized thread message {msg_from_thread!r}.",
457
+ )
458
+ del msg_from_thread
459
+
460
+
461
+ def from_thread_check_cancelled() -> None:
462
+ """Raise `trio.Cancelled` if the associated Trio task entered a cancelled status.
463
+
464
+ Only applicable to threads spawned by `trio.to_thread.run_sync`. Poll to allow
465
+ ``abandon_on_cancel=False`` threads to raise :exc:`~trio.Cancelled` at a suitable
466
+ place, or to end abandoned ``abandon_on_cancel=True`` threads sooner than they may
467
+ otherwise.
468
+
469
+ Raises:
470
+ Cancelled: If the corresponding call to `trio.to_thread.run_sync` has had a
471
+ delivery of cancellation attempted against it, regardless of the value of
472
+ ``abandon_on_cancel`` supplied as an argument to it.
473
+ RuntimeError: If this thread is not spawned from `trio.to_thread.run_sync`.
474
+
475
+ .. note::
476
+
477
+ To be precise, :func:`~trio.from_thread.check_cancelled` checks whether the task
478
+ running :func:`trio.to_thread.run_sync` has ever been cancelled since the last
479
+ time it was running a :func:`trio.from_thread.run` or :func:`trio.from_thread.run_sync`
480
+ function. It may raise `trio.Cancelled` even if a cancellation occurred that was
481
+ later hidden by a modification to `trio.CancelScope.shield` between the cancelled
482
+ `~trio.CancelScope` and :func:`trio.to_thread.run_sync`. This differs from the
483
+ behavior of normal Trio checkpoints, which raise `~trio.Cancelled` only if the
484
+ cancellation is still active when the checkpoint executes. The distinction here is
485
+ *exceedingly* unlikely to be relevant to your application, but we mention it
486
+ for completeness.
487
+ """
488
+ try:
489
+ raise_cancel = PARENT_TASK_DATA.cancel_register[0]
490
+ except AttributeError:
491
+ raise RuntimeError(
492
+ "this thread wasn't created by Trio, can't check for cancellation",
493
+ ) from None
494
+ if raise_cancel is not None:
495
+ raise_cancel()
496
+
497
+
498
+ def _send_message_to_trio(
499
+ trio_token: TrioToken | None,
500
+ message_to_trio: Run[RetT] | RunSync[RetT],
501
+ ) -> RetT:
502
+ """Shared logic of from_thread functions"""
503
+ token_provided = trio_token is not None
504
+
505
+ if not token_provided:
506
+ try:
507
+ trio_token = PARENT_TASK_DATA.token
508
+ except AttributeError:
509
+ raise RuntimeError(
510
+ "this thread wasn't created by Trio, pass kwarg trio_token=...",
511
+ ) from None
512
+ elif not isinstance(trio_token, TrioToken):
513
+ raise RuntimeError("Passed kwarg trio_token is not of type TrioToken")
514
+
515
+ # Avoid deadlock by making sure we're not called from Trio thread
516
+ try:
517
+ trio.lowlevel.current_task()
518
+ except RuntimeError:
519
+ pass
520
+ else:
521
+ raise RuntimeError("this is a blocking function; call it from a thread")
522
+
523
+ if token_provided or PARENT_TASK_DATA.abandon_on_cancel:
524
+ message_to_trio.run_in_system_nursery(trio_token)
525
+ else:
526
+ message_to_trio.run_in_host_task(trio_token)
527
+
528
+ return message_to_trio.queue.get().unwrap()
529
+
530
+
531
+ def from_thread_run(
532
+ afn: Callable[[Unpack[Ts]], Awaitable[RetT]],
533
+ *args: Unpack[Ts],
534
+ trio_token: TrioToken | None = None,
535
+ ) -> RetT:
536
+ """Run the given async function in the parent Trio thread, blocking until it
537
+ is complete.
538
+
539
+ Returns:
540
+ Whatever ``afn(*args)`` returns.
541
+
542
+ Returns or raises whatever the given function returns or raises. It
543
+ can also raise exceptions of its own:
544
+
545
+ Raises:
546
+ RunFinishedError: if the corresponding call to :func:`trio.run` has
547
+ already completed, or if the run has started its final cleanup phase
548
+ and can no longer spawn new system tasks.
549
+ Cancelled: If the original call to :func:`trio.to_thread.run_sync` is cancelled
550
+ (if *trio_token* is None) or the call to :func:`trio.run` completes
551
+ (if *trio_token* is not None) while ``afn(*args)`` is running,
552
+ then *afn* is likely to raise :exc:`trio.Cancelled`.
553
+ RuntimeError: if you try calling this from inside the Trio thread,
554
+ which would otherwise cause a deadlock, or if no ``trio_token`` was
555
+ provided, and we can't infer one from context.
556
+ TypeError: if ``afn`` is not an asynchronous function.
557
+
558
+ **Locating a TrioToken**: There are two ways to specify which
559
+ `trio.run` loop to reenter:
560
+
561
+ - Spawn this thread from `trio.to_thread.run_sync`. Trio will
562
+ automatically capture the relevant Trio token and use it
563
+ to re-enter the same Trio task.
564
+ - Pass a keyword argument, ``trio_token`` specifying a specific
565
+ `trio.run` loop to re-enter. This is useful in case you have a
566
+ "foreign" thread, spawned using some other framework, and still want
567
+ to enter Trio, or if you want to use a new system task to call ``afn``,
568
+ maybe to avoid the cancellation context of a corresponding
569
+ `trio.to_thread.run_sync` task. You can get this token from
570
+ :func:`trio.lowlevel.current_trio_token`.
571
+ """
572
+ return _send_message_to_trio(trio_token, Run(afn, args))
573
+
574
+
575
+ def from_thread_run_sync(
576
+ fn: Callable[[Unpack[Ts]], RetT],
577
+ *args: Unpack[Ts],
578
+ trio_token: TrioToken | None = None,
579
+ ) -> RetT:
580
+ """Run the given sync function in the parent Trio thread, blocking until it
581
+ is complete.
582
+
583
+ Returns:
584
+ Whatever ``fn(*args)`` returns.
585
+
586
+ Returns or raises whatever the given function returns or raises. It
587
+ can also raise exceptions of its own:
588
+
589
+ Raises:
590
+ RunFinishedError: if the corresponding call to `trio.run` has
591
+ already completed.
592
+ RuntimeError: if you try calling this from inside the Trio thread,
593
+ which would otherwise cause a deadlock or if no ``trio_token`` was
594
+ provided, and we can't infer one from context.
595
+ TypeError: if ``fn`` is an async function.
596
+
597
+ **Locating a TrioToken**: There are two ways to specify which
598
+ `trio.run` loop to reenter:
599
+
600
+ - Spawn this thread from `trio.to_thread.run_sync`. Trio will
601
+ automatically capture the relevant Trio token and use it when you
602
+ want to re-enter Trio.
603
+ - Pass a keyword argument, ``trio_token`` specifying a specific
604
+ `trio.run` loop to re-enter. This is useful in case you have a
605
+ "foreign" thread, spawned using some other framework, and still want
606
+ to enter Trio, or if you want to use a new system task to call ``fn``,
607
+ maybe to avoid the cancellation context of a corresponding
608
+ `trio.to_thread.run_sync` task.
609
+ """
610
+ return _send_message_to_trio(trio_token, RunSync(fn, args))