@@ -55,9 +55,10 @@ async def child() -> None:
55
55
56
56
57
57
async def test_CapacityLimiter () -> None :
58
+ assert CapacityLimiter (0 ).total_tokens == 0
58
59
with pytest .raises (TypeError ):
59
60
CapacityLimiter (1.0 )
60
- with pytest .raises (ValueError , match = r"^total_tokens must be >= 1 $" ):
61
+ with pytest .raises (ValueError , match = r"^total_tokens must be >= 0 $" ):
61
62
CapacityLimiter (- 1 )
62
63
c = CapacityLimiter (2 )
63
64
repr (c ) # smoke test
@@ -145,10 +146,10 @@ async def test_CapacityLimiter_change_total_tokens() -> None:
145
146
with pytest .raises (TypeError ):
146
147
c .total_tokens = 1.0
147
148
148
- with pytest .raises (ValueError , match = r"^total_tokens must be >= 1 $" ):
149
- c .total_tokens = 0
149
+ with pytest .raises (ValueError , match = r"^total_tokens must be >= 0 $" ):
150
+ c .total_tokens = - 1
150
151
151
- with pytest .raises (ValueError , match = r"^total_tokens must be >= 1 $" ):
152
+ with pytest .raises (ValueError , match = r"^total_tokens must be >= 0 $" ):
152
153
c .total_tokens = - 10
153
154
154
155
assert c .total_tokens == 2
@@ -190,6 +191,83 @@ async def test_CapacityLimiter_memleak_548() -> None:
190
191
assert len (limiter ._pending_borrowers ) == 0
191
192
192
193
194
+ async def test_CapacityLimiter_zero_limit_tokens () -> None :
195
+ c = CapacityLimiter (5 )
196
+
197
+ assert c .total_tokens == 5
198
+
199
+ async with _core .open_nursery () as nursery :
200
+ c .total_tokens = 0
201
+
202
+ for i in range (5 ):
203
+ nursery .start_soon (c .acquire_on_behalf_of , i )
204
+ await wait_all_tasks_blocked ()
205
+
206
+ assert set (c .statistics ().borrowers ) == set ()
207
+ assert c .statistics ().tasks_waiting == 5
208
+
209
+ c .total_tokens = 5
210
+
211
+ assert set (c .statistics ().borrowers ) == {0 , 1 , 2 , 3 , 4 }
212
+
213
+ nursery .start_soon (c .acquire_on_behalf_of , 5 )
214
+ await wait_all_tasks_blocked ()
215
+
216
+ assert c .statistics ().tasks_waiting == 1
217
+
218
+ for i in range (5 ):
219
+ c .release_on_behalf_of (i )
220
+
221
+ assert c .statistics ().tasks_waiting == 0
222
+ c .release_on_behalf_of (5 )
223
+
224
+ # making sure that zero limit capacity limiter doesn't let any tasks through
225
+
226
+ c .total_tokens = 0
227
+
228
+ with pytest .raises (_core .WouldBlock ):
229
+ c .acquire_nowait ()
230
+
231
+ nursery .start_soon (c .acquire_on_behalf_of , 6 )
232
+ await wait_all_tasks_blocked ()
233
+
234
+ assert c .statistics ().tasks_waiting == 1
235
+ assert c .statistics ().borrowers == []
236
+
237
+ c .total_tokens = 1
238
+ assert c .statistics ().tasks_waiting == 0
239
+ assert c .statistics ().borrowers == [6 ]
240
+ c .release_on_behalf_of (6 )
241
+
242
+ await c .acquire_on_behalf_of (0 ) # total_tokens is 1
243
+
244
+ nursery .start_soon (c .acquire_on_behalf_of , 1 )
245
+ await wait_all_tasks_blocked ()
246
+ c .total_tokens = 0
247
+
248
+ assert c .statistics ().borrowers == [0 ]
249
+
250
+ c .release_on_behalf_of (0 )
251
+ await wait_all_tasks_blocked ()
252
+ assert c .statistics ().borrowers == []
253
+ assert c .statistics ().tasks_waiting == 1
254
+
255
+ c .total_tokens = 1
256
+ await wait_all_tasks_blocked ()
257
+ assert c .statistics ().borrowers == [1 ]
258
+ assert c .statistics ().tasks_waiting == 0
259
+
260
+ c .release_on_behalf_of (1 )
261
+
262
+ c .total_tokens = 0
263
+
264
+ nursery .cancel_scope .cancel ()
265
+
266
+ assert c .total_tokens == 0
267
+ assert c .statistics ().borrowers == []
268
+ assert c ._pending_borrowers == {}
269
+
270
+
193
271
async def test_Semaphore () -> None :
194
272
with pytest .raises (TypeError ):
195
273
Semaphore (1.0 ) # type: ignore[arg-type]
0 commit comments