async def test_contextmanager(self): async def acquire(): async with semaphore: assert semaphore.value in (0, 1) semaphore = Semaphore(2) async with create_task_group() as tg: tg.start_soon(acquire, name='task 1') tg.start_soon(acquire, name='task 2') assert semaphore.value == 2
async def test_manual_acquire(self): async def acquire(): await semaphore.acquire() try: assert semaphore.value in (0, 1) finally: semaphore.release() semaphore = Semaphore(2) async with create_task_group() as tg: tg.start_soon(acquire, name='task 1') tg.start_soon(acquire, name='task 2') assert semaphore.value == 2
async def test_asyncio_deadlock(self) -> None: """Regression test for #398.""" semaphore = Semaphore(1) async def acquire() -> None: async with semaphore: await asyncio.sleep(0) loop = asyncio.get_event_loop() task1 = loop.create_task(acquire()) task2 = loop.create_task(acquire()) await asyncio.sleep(0) task1.cancel() await asyncio.wait_for(task2, 1)
async def test_acquire_cancel(self): async def task(): nonlocal local_scope, acquired with CancelScope() as local_scope: async with semaphore: acquired = True local_scope = acquired = None semaphore = Semaphore(1) async with create_task_group() as tg: async with semaphore: tg.start_soon(task) await wait_all_tasks_blocked() local_scope.cancel() assert not acquired
async def test_acquire_race(self): """ Test against a race condition: when a task waiting on acquire() is rescheduled but another task snatches the last available slot, the task should not raise WouldBlock. """ semaphore = Semaphore(1) async with create_task_group() as tg: semaphore.acquire_nowait() tg.start_soon(semaphore.acquire) await wait_all_tasks_blocked() semaphore.release() pytest.raises(WouldBlock, semaphore.acquire_nowait)
async def test_statistics(self): async def waiter(): async with semaphore: pass semaphore = Semaphore(1) async with create_task_group() as tg: assert semaphore.statistics().tasks_waiting == 0 async with semaphore: assert semaphore.statistics().tasks_waiting == 0 for i in range(1, 3): tg.start_soon(waiter) await wait_all_tasks_blocked() assert semaphore.statistics().tasks_waiting == i assert semaphore.statistics().tasks_waiting == 0
async def test_cancel_during_acquire(self, release_first: bool) -> None: acquired = False async def task(*, task_status: TaskStatus) -> None: nonlocal acquired task_status.started() async with semaphore: acquired = True semaphore = Semaphore(1) async with create_task_group() as tg: await semaphore.acquire() await tg.start(task) tg.cancel_scope.cancel() with CancelScope(shield=True): if release_first: semaphore.release() await wait_all_tasks_blocked() else: await wait_all_tasks_blocked() semaphore.release() assert not acquired assert semaphore.value == 1
async def test_max_value_exceeded(self): semaphore = Semaphore(1, max_value=2) semaphore.release() pytest.raises(ValueError, semaphore.release)
async def test_max_value(self, max_value): semaphore = Semaphore(0, max_value=max_value) assert semaphore.max_value == max_value
async def test_acquire_nowait(self): semaphore = Semaphore(1) semaphore.acquire_nowait() assert semaphore.value == 0 pytest.raises(WouldBlock, semaphore.acquire_nowait)
async def test_semaphore_release(self) -> None: semaphore = Semaphore(1) semaphore.acquire_nowait() with pytest.deprecated_call(): await semaphore.release()
async def test_max_value(self, max_value: Optional[int]) -> None: semaphore = Semaphore(0, max_value=max_value) assert semaphore.max_value == max_value