async def test_acquire_nowait_wouldblock(self): async def try_lock(): pytest.raises(WouldBlock, condition.acquire_nowait) condition = Condition() async with condition, create_task_group() as tg: assert condition.locked() tg.start_soon(try_lock)
def _ensure_condition_exists(self) -> None: """Creates the condition object that the backend will use to notify listeners about network change events. This function is used to avoid having to create a condition object in the constructor, which may be executed outside the context of an event loop. """ if self._condition is None: self._condition = Condition()
async def test_contextmanager(self): async def notifier(): async with condition: condition.notify_all() condition = Condition() async with create_task_group() as tg: async with condition: assert condition.locked() tg.start_soon(notifier) await condition.wait()
async def test_manual_acquire(self): async def notifier(): await condition.acquire() try: condition.notify_all() finally: condition.release() condition = Condition() async with create_task_group() as tg: await condition.acquire() try: assert condition.locked() tg.start_soon(notifier) await condition.wait() finally: condition.release()
async def test_wait_cancel(self): async def task(): nonlocal task_started, notified task_started = True async with condition: event.set() await condition.wait() notified = True task_started = notified = False event = Event() condition = Condition() async with create_task_group() as tg: tg.start_soon(task) await event.wait() await wait_all_tasks_blocked() tg.cancel_scope.cancel() assert task_started assert not notified
async def test_statistics(self): async def waiter(): async with condition: await condition.wait() condition = Condition() async with create_task_group() as tg: assert not condition.statistics().lock_statistics.locked assert condition.statistics().tasks_waiting == 0 async with condition: assert condition.statistics().lock_statistics.locked assert condition.statistics().tasks_waiting == 0 for i in range(1, 3): tg.start_soon(waiter) await wait_all_tasks_blocked() assert condition.statistics().tasks_waiting == i for i in range(1, -1, -1): async with condition: condition.notify(1) await wait_all_tasks_blocked() assert condition.statistics().tasks_waiting == i assert not condition.statistics().lock_statistics.locked assert condition.statistics().tasks_waiting == 0
async def test_acquire_nowait(self): condition = Condition() condition.acquire_nowait() assert condition.locked()
class SystemConfigurationBasedNetworkEventDetectorBackend( PortableNetworkEventDetectorBackend): """Specialized network event detector backend for macOS using the SystemConfiguration framework. This backend works similarly to the PortableNetworkEventDetectorBackend_, but it uses an SCDynamicStore instance to detect changes to network interfaces and addresses instead of polling the network configuration. This backend is preferred by the autodetection mechanism on macOS over the default portable backend. """ _condition: Optional[Condition] def __init__(self) -> None: super().__init__() self._condition = None @asynccontextmanager async def use(self) -> AsyncIterator[None]: self._ensure_condition_exists() async with create_task_group() as tg: tg.start_soon( partial(to_thread.run_sync, self._run_worker_thread, cancellable=True)) yield def _ensure_condition_exists(self) -> None: """Creates the condition object that the backend will use to notify listeners about network change events. This function is used to avoid having to create a condition object in the constructor, which may be executed outside the context of an event loop. """ if self._condition is None: self._condition = Condition() def _on_network_changed(self, *args, **kwds): """Callback that is called by the SystemConfiguration framework when the network configuration has changed. This function runs in the context of the worker thread. """ from_thread.run(self._on_network_changed_main) async def _on_network_changed_main(self): """Task that is scheduled on the main event loop when the network configuration has changed. This function runs in the context of the main event loop. """ assert self._condition is not None async with self._condition: self._condition.notify_all() def _run_worker_thread(self): """Runs the worker thread that waits for network events.""" from Foundation import ( CFRunLoopAddSource, CFRunLoopGetCurrent, kCFRunLoopCommonModes, CFRunLoopRun, ) from SystemConfiguration import ( SCDynamicStoreCreate, SCDynamicStoreSetNotificationKeys, SCDynamicStoreCreateRunLoopSource, ) store = SCDynamicStoreCreate(None, "global-network-watcher", self._on_network_changed, None) SCDynamicStoreSetNotificationKeys( store, None, ["State:/Network/Global/IPv4", "State:/Network/Global/IPv6"]) CFRunLoopAddSource( CFRunLoopGetCurrent(), SCDynamicStoreCreateRunLoopSource(None, store, 0), kCFRunLoopCommonModes, ) CFRunLoopRun() async def wait_until_next_scan(self) -> None: assert self._condition is not None self._ensure_condition_exists() async with self._condition: await self._condition.wait()
async def test_condition_release(self) -> None: condition = Condition() condition.acquire_nowait() with pytest.deprecated_call(): await condition.release()