@@ -55,9 +55,10 @@ async def child() -> None:
5555
5656
5757async def test_CapacityLimiter () -> None :
58+ assert CapacityLimiter (0 ).total_tokens == 0
5859 with pytest .raises (TypeError ):
5960 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 $" ):
6162 CapacityLimiter (- 1 )
6263 c = CapacityLimiter (2 )
6364 repr (c ) # smoke test
@@ -145,10 +146,10 @@ async def test_CapacityLimiter_change_total_tokens() -> None:
145146 with pytest .raises (TypeError ):
146147 c .total_tokens = 1.0
147148
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
150151
151- with pytest .raises (ValueError , match = r"^total_tokens must be >= 1 $" ):
152+ with pytest .raises (ValueError , match = r"^total_tokens must be >= 0 $" ):
152153 c .total_tokens = - 10
153154
154155 assert c .total_tokens == 2
@@ -190,6 +191,83 @@ async def test_CapacityLimiter_memleak_548() -> None:
190191 assert len (limiter ._pending_borrowers ) == 0
191192
192193
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+
193271async def test_Semaphore () -> None :
194272 with pytest .raises (TypeError ):
195273 Semaphore (1.0 ) # type: ignore[arg-type]
0 commit comments