@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,735 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ import weakref
5
+ from collections.abc import Callable
6
+ from typing import TypeAlias
7
+
8
+ import pytest
9
+
10
+ from .. import _core
11
+ from .._core._parking_lot import GLOBAL_PARKING_LOT_BREAKER
12
+ from .._sync import *
13
+ from .._timeouts import sleep_forever
14
+ from ..testing import assert_checkpoints, wait_all_tasks_blocked
15
+
16
+
17
+ async def test_Event() -> None:
18
+ e = Event()
19
+ assert not e.is_set()
20
+ assert e.statistics().tasks_waiting == 0
21
+
22
+ with pytest.warns(
23
+ DeprecationWarning,
24
+ match=r"trio\.Event\.__bool__ is deprecated since Trio 0\.31\.0; use trio\.Event\.is_set instead \(https://github.com/python-trio/trio/issues/3238\)",
25
+ ):
26
+ e.__bool__()
27
+
28
+ e.set()
29
+ assert e.is_set()
30
+ with assert_checkpoints():
31
+ await e.wait()
32
+
33
+ e = Event()
34
+
35
+ record = []
36
+
37
+ async def child() -> None:
38
+ record.append("sleeping")
39
+ await e.wait()
40
+ record.append("woken")
41
+
42
+ async with _core.open_nursery() as nursery:
43
+ nursery.start_soon(child)
44
+ nursery.start_soon(child)
45
+ await wait_all_tasks_blocked()
46
+ assert record == ["sleeping", "sleeping"]
47
+ assert e.statistics().tasks_waiting == 2
48
+ e.set()
49
+ await wait_all_tasks_blocked()
50
+ assert record == ["sleeping", "sleeping", "woken", "woken"]
51
+
52
+
53
+ async def test_CapacityLimiter() -> None:
54
+ assert CapacityLimiter(0).total_tokens == 0
55
+ with pytest.raises(TypeError):
56
+ CapacityLimiter(1.0)
57
+ with pytest.raises(ValueError, match=r"^total_tokens must be >= 0$"):
58
+ CapacityLimiter(-1)
59
+ c = CapacityLimiter(2)
60
+ repr(c) # smoke test
61
+ assert c.total_tokens == 2
62
+ assert c.borrowed_tokens == 0
63
+ assert c.available_tokens == 2
64
+ with pytest.raises(RuntimeError):
65
+ c.release()
66
+ assert c.borrowed_tokens == 0
67
+ c.acquire_nowait()
68
+ assert c.borrowed_tokens == 1
69
+ assert c.available_tokens == 1
70
+
71
+ stats = c.statistics()
72
+ assert stats.borrowed_tokens == 1
73
+ assert stats.total_tokens == 2
74
+ assert stats.borrowers == [_core.current_task()]
75
+ assert stats.tasks_waiting == 0
76
+
77
+ # Can't re-acquire when we already have it
78
+ with pytest.raises(RuntimeError):
79
+ c.acquire_nowait()
80
+ assert c.borrowed_tokens == 1
81
+ with pytest.raises(RuntimeError):
82
+ await c.acquire()
83
+ assert c.borrowed_tokens == 1
84
+
85
+ # We can acquire on behalf of someone else though
86
+ with assert_checkpoints():
87
+ await c.acquire_on_behalf_of("someone")
88
+
89
+ # But then we've run out of capacity
90
+ assert c.borrowed_tokens == 2
91
+ with pytest.raises(_core.WouldBlock):
92
+ c.acquire_on_behalf_of_nowait("third party")
93
+
94
+ assert set(c.statistics().borrowers) == {_core.current_task(), "someone"}
95
+
96
+ # Until we release one
97
+ c.release_on_behalf_of(_core.current_task())
98
+ assert c.statistics().borrowers == ["someone"]
99
+
100
+ c.release_on_behalf_of("someone")
101
+ assert c.borrowed_tokens == 0
102
+ with assert_checkpoints():
103
+ async with c:
104
+ assert c.borrowed_tokens == 1
105
+
106
+ async with _core.open_nursery() as nursery:
107
+ await c.acquire_on_behalf_of("value 1")
108
+ await c.acquire_on_behalf_of("value 2")
109
+ nursery.start_soon(c.acquire_on_behalf_of, "value 3")
110
+ await wait_all_tasks_blocked()
111
+ assert c.borrowed_tokens == 2
112
+ assert c.statistics().tasks_waiting == 1
113
+ c.release_on_behalf_of("value 2")
114
+ # Fairness:
115
+ assert c.borrowed_tokens == 2
116
+ with pytest.raises(_core.WouldBlock):
117
+ c.acquire_nowait()
118
+
119
+ c.release_on_behalf_of("value 3")
120
+ c.release_on_behalf_of("value 1")
121
+
122
+
123
+ async def test_CapacityLimiter_inf() -> None:
124
+ from math import inf
125
+
126
+ c = CapacityLimiter(inf)
127
+ repr(c) # smoke test
128
+ assert c.total_tokens == inf
129
+ assert c.borrowed_tokens == 0
130
+ assert c.available_tokens == inf
131
+ with pytest.raises(RuntimeError):
132
+ c.release()
133
+ assert c.borrowed_tokens == 0
134
+ c.acquire_nowait()
135
+ assert c.borrowed_tokens == 1
136
+ assert c.available_tokens == inf
137
+
138
+
139
+ async def test_CapacityLimiter_change_total_tokens() -> None:
140
+ c = CapacityLimiter(2)
141
+
142
+ with pytest.raises(TypeError):
143
+ c.total_tokens = 1.0
144
+
145
+ with pytest.raises(ValueError, match=r"^total_tokens must be >= 0$"):
146
+ c.total_tokens = -1
147
+
148
+ with pytest.raises(ValueError, match=r"^total_tokens must be >= 0$"):
149
+ c.total_tokens = -10
150
+
151
+ assert c.total_tokens == 2
152
+
153
+ async with _core.open_nursery() as nursery:
154
+ for i in range(5):
155
+ nursery.start_soon(c.acquire_on_behalf_of, i)
156
+ await wait_all_tasks_blocked()
157
+ assert set(c.statistics().borrowers) == {0, 1}
158
+ assert c.statistics().tasks_waiting == 3
159
+ c.total_tokens += 2
160
+ assert set(c.statistics().borrowers) == {0, 1, 2, 3}
161
+ assert c.statistics().tasks_waiting == 1
162
+ c.total_tokens -= 3
163
+ assert c.borrowed_tokens == 4
164
+ assert c.total_tokens == 1
165
+ c.release_on_behalf_of(0)
166
+ c.release_on_behalf_of(1)
167
+ c.release_on_behalf_of(2)
168
+ assert set(c.statistics().borrowers) == {3}
169
+ assert c.statistics().tasks_waiting == 1
170
+ c.release_on_behalf_of(3)
171
+ assert set(c.statistics().borrowers) == {4}
172
+ assert c.statistics().tasks_waiting == 0
173
+
174
+
175
+ # regression test for issue #548
176
+ async def test_CapacityLimiter_memleak_548() -> None:
177
+ limiter = CapacityLimiter(total_tokens=1)
178
+ await limiter.acquire()
179
+
180
+ async with _core.open_nursery() as n:
181
+ n.start_soon(limiter.acquire)
182
+ await wait_all_tasks_blocked() # give it a chance to run the task
183
+ n.cancel_scope.cancel()
184
+
185
+ # if this is 1, the acquire call (despite being killed) is still there in the task, and will
186
+ # leak memory all the while the limiter is active
187
+ assert len(limiter._pending_borrowers) == 0
188
+
189
+
190
+ async def test_CapacityLimiter_zero_limit_tokens() -> None:
191
+ c = CapacityLimiter(5)
192
+
193
+ assert c.total_tokens == 5
194
+
195
+ async with _core.open_nursery() as nursery:
196
+ c.total_tokens = 0
197
+
198
+ for i in range(5):
199
+ nursery.start_soon(c.acquire_on_behalf_of, i)
200
+ await wait_all_tasks_blocked()
201
+
202
+ assert set(c.statistics().borrowers) == set()
203
+ assert c.statistics().tasks_waiting == 5
204
+
205
+ c.total_tokens = 5
206
+
207
+ assert set(c.statistics().borrowers) == {0, 1, 2, 3, 4}
208
+
209
+ nursery.start_soon(c.acquire_on_behalf_of, 5)
210
+ await wait_all_tasks_blocked()
211
+
212
+ assert c.statistics().tasks_waiting == 1
213
+
214
+ for i in range(5):
215
+ c.release_on_behalf_of(i)
216
+
217
+ assert c.statistics().tasks_waiting == 0
218
+ c.release_on_behalf_of(5)
219
+
220
+ # making sure that zero limit capacity limiter doesn't let any tasks through
221
+
222
+ c.total_tokens = 0
223
+
224
+ with pytest.raises(_core.WouldBlock):
225
+ c.acquire_nowait()
226
+
227
+ nursery.start_soon(c.acquire_on_behalf_of, 6)
228
+ await wait_all_tasks_blocked()
229
+
230
+ assert c.statistics().tasks_waiting == 1
231
+ assert c.statistics().borrowers == []
232
+
233
+ c.total_tokens = 1
234
+ assert c.statistics().tasks_waiting == 0
235
+ assert c.statistics().borrowers == [6]
236
+ c.release_on_behalf_of(6)
237
+
238
+ await c.acquire_on_behalf_of(0) # total_tokens is 1
239
+
240
+ nursery.start_soon(c.acquire_on_behalf_of, 1)
241
+ await wait_all_tasks_blocked()
242
+ c.total_tokens = 0
243
+
244
+ assert c.statistics().borrowers == [0]
245
+
246
+ c.release_on_behalf_of(0)
247
+ await wait_all_tasks_blocked()
248
+ assert c.statistics().borrowers == []
249
+ assert c.statistics().tasks_waiting == 1
250
+
251
+ c.total_tokens = 1
252
+ await wait_all_tasks_blocked()
253
+ assert c.statistics().borrowers == [1]
254
+ assert c.statistics().tasks_waiting == 0
255
+
256
+ c.release_on_behalf_of(1)
257
+
258
+ c.total_tokens = 0
259
+
260
+ nursery.cancel_scope.cancel()
261
+
262
+ assert c.total_tokens == 0
263
+ assert c.statistics().borrowers == []
264
+ assert c._pending_borrowers == {}
265
+
266
+
267
+ async def test_Semaphore() -> None:
268
+ with pytest.raises(TypeError):
269
+ Semaphore(1.0) # type: ignore[arg-type]
270
+ with pytest.raises(ValueError, match=r"^initial value must be >= 0$"):
271
+ Semaphore(-1)
272
+ s = Semaphore(1)
273
+ repr(s) # smoke test
274
+ assert s.value == 1
275
+ assert s.max_value is None
276
+ s.release()
277
+ assert s.value == 2
278
+ assert s.statistics().tasks_waiting == 0
279
+ s.acquire_nowait()
280
+ assert s.value == 1
281
+ with assert_checkpoints():
282
+ await s.acquire()
283
+ assert s.value == 0
284
+ with pytest.raises(_core.WouldBlock):
285
+ s.acquire_nowait()
286
+
287
+ s.release()
288
+ assert s.value == 1
289
+ with assert_checkpoints():
290
+ async with s:
291
+ assert s.value == 0
292
+ assert s.value == 1
293
+ s.acquire_nowait()
294
+
295
+ record = []
296
+
297
+ async def do_acquire(s: Semaphore) -> None:
298
+ record.append("started")
299
+ await s.acquire()
300
+ record.append("finished")
301
+
302
+ async with _core.open_nursery() as nursery:
303
+ nursery.start_soon(do_acquire, s)
304
+ await wait_all_tasks_blocked()
305
+ assert record == ["started"]
306
+ assert s.value == 0
307
+ s.release()
308
+ # Fairness:
309
+ assert s.value == 0
310
+ with pytest.raises(_core.WouldBlock):
311
+ s.acquire_nowait()
312
+ assert record == ["started", "finished"]
313
+
314
+
315
+ def test_Semaphore_bounded() -> None:
316
+ with pytest.raises(TypeError):
317
+ Semaphore(1, max_value=1.0) # type: ignore[arg-type]
318
+ with pytest.raises(ValueError, match=r"^max_values must be >= initial_value$"):
319
+ Semaphore(2, max_value=1)
320
+ bs = Semaphore(1, max_value=1)
321
+ assert bs.max_value == 1
322
+ repr(bs) # smoke test
323
+ with pytest.raises(ValueError, match=r"^semaphore released too many times$"):
324
+ bs.release()
325
+ assert bs.value == 1
326
+ bs.acquire_nowait()
327
+ assert bs.value == 0
328
+ bs.release()
329
+ assert bs.value == 1
330
+
331
+
332
+ @pytest.mark.parametrize("lockcls", [Lock, StrictFIFOLock], ids=lambda fn: fn.__name__)
333
+ async def test_Lock_and_StrictFIFOLock(
334
+ lockcls: type[Lock | StrictFIFOLock],
335
+ ) -> None:
336
+ l = lockcls() # noqa
337
+ assert not l.locked()
338
+
339
+ # make sure locks can be weakref'ed (gh-331)
340
+ r = weakref.ref(l)
341
+ assert r() is l
342
+
343
+ repr(l) # smoke test
344
+ # make sure repr uses the right name for subclasses
345
+ assert lockcls.__name__ in repr(l)
346
+ with assert_checkpoints():
347
+ async with l:
348
+ assert l.locked()
349
+ repr(l) # smoke test (repr branches on locked/unlocked)
350
+ assert not l.locked()
351
+ l.acquire_nowait()
352
+ assert l.locked()
353
+ l.release()
354
+ assert not l.locked()
355
+ with assert_checkpoints():
356
+ await l.acquire()
357
+ assert l.locked()
358
+ l.release()
359
+ assert not l.locked()
360
+
361
+ l.acquire_nowait()
362
+ with pytest.raises(RuntimeError):
363
+ # Error out if we already own the lock
364
+ l.acquire_nowait()
365
+ l.release()
366
+ with pytest.raises(RuntimeError):
367
+ # Error out if we don't own the lock
368
+ l.release()
369
+
370
+ holder_task = None
371
+
372
+ async def holder() -> None:
373
+ nonlocal holder_task
374
+ holder_task = _core.current_task()
375
+ async with l:
376
+ await sleep_forever()
377
+
378
+ async with _core.open_nursery() as nursery:
379
+ assert not l.locked()
380
+ nursery.start_soon(holder)
381
+ await wait_all_tasks_blocked()
382
+ assert l.locked()
383
+ # WouldBlock if someone else holds the lock
384
+ with pytest.raises(_core.WouldBlock):
385
+ l.acquire_nowait()
386
+ # Can't release a lock someone else holds
387
+ with pytest.raises(RuntimeError):
388
+ l.release()
389
+
390
+ statistics = l.statistics()
391
+ print(statistics)
392
+ assert statistics.locked
393
+ assert statistics.owner is holder_task
394
+ assert statistics.tasks_waiting == 0
395
+
396
+ nursery.start_soon(holder)
397
+ await wait_all_tasks_blocked()
398
+ statistics = l.statistics()
399
+ print(statistics)
400
+ assert statistics.tasks_waiting == 1
401
+
402
+ nursery.cancel_scope.cancel()
403
+
404
+ statistics = l.statistics()
405
+ assert not statistics.locked
406
+ assert statistics.owner is None
407
+ assert statistics.tasks_waiting == 0
408
+
409
+
410
+ async def test_Condition() -> None:
411
+ with pytest.raises(TypeError):
412
+ Condition(Semaphore(1)) # type: ignore[arg-type]
413
+ with pytest.raises(TypeError):
414
+ Condition(StrictFIFOLock) # type: ignore[arg-type]
415
+ l = Lock() # noqa
416
+ c = Condition(l)
417
+ assert not l.locked()
418
+ assert not c.locked()
419
+ with assert_checkpoints():
420
+ await c.acquire()
421
+ assert l.locked()
422
+ assert c.locked()
423
+
424
+ c = Condition()
425
+ assert not c.locked()
426
+ c.acquire_nowait()
427
+ assert c.locked()
428
+ with pytest.raises(RuntimeError):
429
+ c.acquire_nowait()
430
+ c.release()
431
+
432
+ with pytest.raises(RuntimeError):
433
+ # Can't wait without holding the lock
434
+ await c.wait()
435
+ with pytest.raises(RuntimeError):
436
+ # Can't notify without holding the lock
437
+ c.notify()
438
+ with pytest.raises(RuntimeError):
439
+ # Can't notify without holding the lock
440
+ c.notify_all()
441
+
442
+ finished_waiters = set()
443
+
444
+ async def waiter(i: int) -> None:
445
+ async with c:
446
+ await c.wait()
447
+ finished_waiters.add(i)
448
+
449
+ async with _core.open_nursery() as nursery:
450
+ for i in range(3):
451
+ nursery.start_soon(waiter, i)
452
+ await wait_all_tasks_blocked()
453
+ async with c:
454
+ c.notify()
455
+ assert c.locked()
456
+ await wait_all_tasks_blocked()
457
+ assert finished_waiters == {0}
458
+ async with c:
459
+ c.notify_all()
460
+ await wait_all_tasks_blocked()
461
+ assert finished_waiters == {0, 1, 2}
462
+
463
+ finished_waiters = set()
464
+ async with _core.open_nursery() as nursery:
465
+ for i in range(3):
466
+ nursery.start_soon(waiter, i)
467
+ await wait_all_tasks_blocked()
468
+ async with c:
469
+ c.notify(2)
470
+ statistics = c.statistics()
471
+ print(statistics)
472
+ assert statistics.tasks_waiting == 1
473
+ assert statistics.lock_statistics.tasks_waiting == 2
474
+ # exiting the context manager hands off the lock to the first task
475
+ assert c.statistics().lock_statistics.tasks_waiting == 1
476
+
477
+ await wait_all_tasks_blocked()
478
+ assert finished_waiters == {0, 1}
479
+
480
+ async with c:
481
+ c.notify_all()
482
+
483
+ # After being cancelled still hold the lock (!)
484
+ # (Note that c.__aexit__ checks that we hold the lock as well)
485
+ with _core.CancelScope() as scope:
486
+ async with c:
487
+ scope.cancel()
488
+ try:
489
+ await c.wait()
490
+ finally:
491
+ assert c.locked()
492
+
493
+
494
+ from .._channel import open_memory_channel
495
+ from .._sync import AsyncContextManagerMixin
496
+
497
+ # Three ways of implementing a Lock in terms of a channel. Used to let us put
498
+ # the channel through the generic lock tests.
499
+
500
+
501
+ class ChannelLock1(AsyncContextManagerMixin):
502
+ def __init__(self, capacity: int) -> None:
503
+ self.s, self.r = open_memory_channel[None](capacity)
504
+ for _ in range(capacity - 1):
505
+ self.s.send_nowait(None)
506
+
507
+ def acquire_nowait(self) -> None:
508
+ self.s.send_nowait(None)
509
+
510
+ async def acquire(self) -> None:
511
+ await self.s.send(None)
512
+
513
+ def release(self) -> None:
514
+ self.r.receive_nowait()
515
+
516
+
517
+ class ChannelLock2(AsyncContextManagerMixin):
518
+ def __init__(self) -> None:
519
+ self.s, self.r = open_memory_channel[None](10)
520
+ self.s.send_nowait(None)
521
+
522
+ def acquire_nowait(self) -> None:
523
+ self.r.receive_nowait()
524
+
525
+ async def acquire(self) -> None:
526
+ await self.r.receive()
527
+
528
+ def release(self) -> None:
529
+ self.s.send_nowait(None)
530
+
531
+
532
+ class ChannelLock3(AsyncContextManagerMixin):
533
+ def __init__(self) -> None:
534
+ self.s, self.r = open_memory_channel[None](0)
535
+ # self.acquired is true when one task acquires the lock and
536
+ # only becomes false when it's released and no tasks are
537
+ # waiting to acquire.
538
+ self.acquired = False
539
+
540
+ def acquire_nowait(self) -> None:
541
+ assert not self.acquired
542
+ self.acquired = True
543
+
544
+ async def acquire(self) -> None:
545
+ if self.acquired:
546
+ await self.s.send(None)
547
+ else:
548
+ self.acquired = True
549
+ await _core.checkpoint()
550
+
551
+ def release(self) -> None:
552
+ try:
553
+ self.r.receive_nowait()
554
+ except _core.WouldBlock:
555
+ assert self.acquired
556
+ self.acquired = False
557
+
558
+
559
+ lock_factories = [
560
+ lambda: CapacityLimiter(1),
561
+ lambda: Semaphore(1),
562
+ Lock,
563
+ StrictFIFOLock,
564
+ lambda: ChannelLock1(10),
565
+ lambda: ChannelLock1(1),
566
+ ChannelLock2,
567
+ ChannelLock3,
568
+ ]
569
+ lock_factory_names = [
570
+ "CapacityLimiter(1)",
571
+ "Semaphore(1)",
572
+ "Lock",
573
+ "StrictFIFOLock",
574
+ "ChannelLock1(10)",
575
+ "ChannelLock1(1)",
576
+ "ChannelLock2",
577
+ "ChannelLock3",
578
+ ]
579
+
580
+ generic_lock_test = pytest.mark.parametrize(
581
+ "lock_factory",
582
+ lock_factories,
583
+ ids=lock_factory_names,
584
+ )
585
+
586
+ LockLike: TypeAlias = (
587
+ CapacityLimiter
588
+ | Semaphore
589
+ | Lock
590
+ | StrictFIFOLock
591
+ | ChannelLock1
592
+ | ChannelLock2
593
+ | ChannelLock3
594
+ )
595
+ LockFactory: TypeAlias = Callable[[], LockLike]
596
+
597
+
598
+ # Spawn a bunch of workers that take a lock and then yield; make sure that
599
+ # only one worker is ever in the critical section at a time.
600
+ @generic_lock_test
601
+ async def test_generic_lock_exclusion(lock_factory: LockFactory) -> None:
602
+ LOOPS = 10
603
+ WORKERS = 5
604
+ in_critical_section = False
605
+ acquires = 0
606
+
607
+ async def worker(lock_like: LockLike) -> None:
608
+ nonlocal in_critical_section, acquires
609
+ for _ in range(LOOPS):
610
+ async with lock_like:
611
+ acquires += 1
612
+ assert not in_critical_section
613
+ in_critical_section = True
614
+ await _core.checkpoint()
615
+ await _core.checkpoint()
616
+ assert in_critical_section
617
+ in_critical_section = False
618
+
619
+ async with _core.open_nursery() as nursery:
620
+ lock_like = lock_factory()
621
+ for _ in range(WORKERS):
622
+ nursery.start_soon(worker, lock_like)
623
+ assert not in_critical_section
624
+ assert acquires == LOOPS * WORKERS
625
+
626
+
627
+ # Several workers queue on the same lock; make sure they each get it, in
628
+ # order.
629
+ @generic_lock_test
630
+ async def test_generic_lock_fifo_fairness(lock_factory: LockFactory) -> None:
631
+ initial_order = []
632
+ record = []
633
+ LOOPS = 5
634
+
635
+ async def loopy(name: int, lock_like: LockLike) -> None:
636
+ # Record the order each task was initially scheduled in
637
+ initial_order.append(name)
638
+ for _ in range(LOOPS):
639
+ async with lock_like:
640
+ record.append(name)
641
+
642
+ lock_like = lock_factory()
643
+ async with _core.open_nursery() as nursery:
644
+ nursery.start_soon(loopy, 1, lock_like)
645
+ nursery.start_soon(loopy, 2, lock_like)
646
+ nursery.start_soon(loopy, 3, lock_like)
647
+ # The first three could be in any order due to scheduling randomness,
648
+ # but after that they should repeat in the same order
649
+ for i in range(LOOPS):
650
+ assert record[3 * i : 3 * (i + 1)] == initial_order
651
+
652
+
653
+ @generic_lock_test
654
+ async def test_generic_lock_acquire_nowait_blocks_acquire(
655
+ lock_factory: LockFactory,
656
+ ) -> None:
657
+ lock_like = lock_factory()
658
+
659
+ record = []
660
+
661
+ async def lock_taker() -> None:
662
+ record.append("started")
663
+ async with lock_like:
664
+ pass
665
+ record.append("finished")
666
+
667
+ async with _core.open_nursery() as nursery:
668
+ lock_like.acquire_nowait()
669
+ nursery.start_soon(lock_taker)
670
+ await wait_all_tasks_blocked()
671
+ assert record == ["started"]
672
+ lock_like.release()
673
+
674
+
675
+ async def test_lock_acquire_unowned_lock() -> None:
676
+ """Test that trying to acquire a lock whose owner has exited raises an error.
677
+ see https://github.com/python-trio/trio/issues/3035
678
+ """
679
+ assert not GLOBAL_PARKING_LOT_BREAKER
680
+ lock = trio.Lock()
681
+ async with trio.open_nursery() as nursery:
682
+ nursery.start_soon(lock.acquire)
683
+ owner_str = re.escape(str(lock._lot.broken_by[0]))
684
+ with pytest.raises(
685
+ trio.BrokenResourceError,
686
+ match=f"^Owner of this lock exited without releasing: {owner_str}$",
687
+ ):
688
+ await lock.acquire()
689
+ assert not GLOBAL_PARKING_LOT_BREAKER
690
+
691
+
692
+ async def test_lock_multiple_acquire() -> None:
693
+ """Test for error if awaiting on a lock whose owner exits without releasing.
694
+ see https://github.com/python-trio/trio/issues/3035"""
695
+ assert not GLOBAL_PARKING_LOT_BREAKER
696
+ lock = trio.Lock()
697
+ with pytest.RaisesGroup(
698
+ pytest.RaisesExc(
699
+ trio.BrokenResourceError,
700
+ match="^Owner of this lock exited without releasing: ",
701
+ ),
702
+ ):
703
+ async with trio.open_nursery() as nursery:
704
+ nursery.start_soon(lock.acquire)
705
+ nursery.start_soon(lock.acquire)
706
+ assert not GLOBAL_PARKING_LOT_BREAKER
707
+
708
+
709
+ async def test_lock_handover() -> None:
710
+ assert not GLOBAL_PARKING_LOT_BREAKER
711
+ child_task: Task | None = None
712
+ lock = trio.Lock()
713
+
714
+ # this task acquires the lock
715
+ lock.acquire_nowait()
716
+ assert {
717
+ _core.current_task(): [
718
+ lock._lot,
719
+ ],
720
+ } == GLOBAL_PARKING_LOT_BREAKER
721
+
722
+ async with trio.open_nursery() as nursery:
723
+ nursery.start_soon(lock.acquire)
724
+ await wait_all_tasks_blocked()
725
+
726
+ # hand over the lock to the child task
727
+ lock.release()
728
+
729
+ # check values, and get the identifier out of the dict for later check
730
+ assert len(GLOBAL_PARKING_LOT_BREAKER) == 1
731
+ child_task = next(iter(GLOBAL_PARKING_LOT_BREAKER))
732
+ assert GLOBAL_PARKING_LOT_BREAKER[child_task] == [lock._lot]
733
+
734
+ assert lock._lot.broken_by == [child_task]
735
+ assert not GLOBAL_PARKING_LOT_BREAKER