diff --git a/404.html b/404.html index 45adbeb..cf7a1cc 100644 --- a/404.html +++ b/404.html @@ -12,7 +12,7 @@ - + diff --git a/index.html b/index.html index e4e0918..fcd8189 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,7 @@ - + diff --git a/reference/dispatchers/index.html b/reference/dispatchers/index.html index 4254c0f..109ef0b 100644 --- a/reference/dispatchers/index.html +++ b/reference/dispatchers/index.html @@ -18,7 +18,7 @@ - + diff --git a/reference/index.html b/reference/index.html index a4cac1c..32b9e0b 100644 --- a/reference/index.html +++ b/reference/index.html @@ -18,7 +18,7 @@ - + diff --git a/reference/task_broker/index.html b/reference/task_broker/index.html index 01a82db..d926bc2 100644 --- a/reference/task_broker/index.html +++ b/reference/task_broker/index.html @@ -18,7 +18,7 @@ - + diff --git a/reference/task_manager/index.html b/reference/task_manager/index.html index 37e99e5..b58ce0c 100644 --- a/reference/task_manager/index.html +++ b/reference/task_manager/index.html @@ -18,7 +18,7 @@ - + @@ -1035,7 +1035,8 @@

Source code in fluid/scheduler/consumer.py -
56
+                    
55
+56
 57
 58
 59
@@ -1048,22 +1049,21 @@ 

66 67 68 -69 -70

def __init__(self, **kwargs: Any) -> None:
-    self.state: dict[str, Any] = {}
-    self.config: TaskManagerConfig = TaskManagerConfig(**kwargs)
-    self.dispatcher: Annotated[
-        TaskDispatcher,
-        Doc(
-            """
-            A dispatcher of task run events.
-
-            Register handlers to listen for task run events.
-            """
-        ),
-    ] = TaskDispatcher()
-    self.broker = TaskBroker.from_url(self.config.broker_url)
-    self._stack = AsyncExitStack()
+69
def __init__(self, **kwargs: Any) -> None:
+    self.state: dict[str, Any] = {}
+    self.config: TaskManagerConfig = TaskManagerConfig(**kwargs)
+    self.dispatcher: Annotated[
+        TaskDispatcher,
+        Doc(
+            """
+            A dispatcher of task run events.
+
+            Register handlers to listen for task run events.
+            """
+        ),
+    ] = TaskDispatcher()
+    self.broker = TaskBroker.from_url(self.config.broker_url)
+    self._stack = AsyncExitStack()
 
@@ -1224,9 +1224,9 @@

Source code in fluid/scheduler/consumer.py -
async def enter_async_context(self, cm: Any) -> Any:
-    return await self._stack.enter_async_context(cm)
+              
async def enter_async_context(self, cm: Any) -> Any:
+    return await self._stack.enter_async_context(cm)
 
@@ -1254,15 +1254,15 @@

Source code in fluid/scheduler/consumer.py -
92
+              
91
+92
 93
 94
-95
-96
async def execute(self, task: Task | str, **params: Any) -> TaskRun:
-    """Execute a task and wait for it to finish"""
-    task_run = self.create_task_run(task, **params)
-    await task_run.execute()
-    return task_run
+95
async def execute(self, task: Task | str, **params: Any) -> TaskRun:
+    """Execute a task and wait for it to finish"""
+    task_run = self.create_task_run(task, **params)
+    await task_run.execute()
+    return task_run
 
@@ -1288,9 +1288,9 @@

Source code in fluid/scheduler/consumer.py -
async def on_shutdown(self) -> None:
-    await self.broker.close()
+              
async def on_shutdown(self) -> None:
+    await self.broker.close()
 
@@ -1312,9 +1312,9 @@

Source code in fluid/scheduler/consumer.py -
def execute_sync(self, task: Task | str, **params: Any) -> TaskRun:
-    return asyncio.run(self._execute_and_exit(task, **params))
+              
def execute_sync(self, task: Task | str, **params: Any) -> TaskRun:
+    return asyncio.run(self._execute_and_exit(task, **params))
 
@@ -1339,17 +1339,17 @@

Source code in fluid/scheduler/consumer.py -
104
+              
def register_task(self, task: Task) -> None:
-    """Register a task with the task manager
-
-    Only tasks registered can be executed by a task manager
-    """
-    self.broker.register_task(task)
+108
def register_task(self, task: Task) -> None:
+    """Register a task with the task manager
+
+    Only tasks registered can be executed by a task manager
+    """
+    self.broker.register_task(task)
 
@@ -1382,7 +1382,8 @@

Source code in fluid/scheduler/consumer.py -
111
+              
110
+111
 112
 113
 114
@@ -1398,25 +1399,24 @@ 

124 125 126 -127 -128

async def queue(
-    self,
-    task: str | Task,
-    priority: TaskPriority | None = None,
-    **params: Any,
-) -> TaskRun:
-    """Queue a task for execution
-
-    This methods fires two events:
-
-    - queue: when the task is about to be queued
-    - queued: after the task is queued
-    """
-    task_run = self.create_task_run(task, priority=priority, **params)
-    self.dispatcher.dispatch(task_run)
-    task_run.set_state(TaskState.queued)
-    await self.broker.queue_task(task_run)
-    return task_run
+127
async def queue(
+    self,
+    task: str | Task,
+    priority: TaskPriority | None = None,
+    **params: Any,
+) -> TaskRun:
+    """Queue a task for execution
+
+    This methods fires two events:
+
+    - queue: when the task is about to be queued
+    - queued: after the task is queued
+    """
+    task_run = self.create_task_run(task, priority=priority, **params)
+    self.dispatcher.dispatch(task_run)
+    task_run.set_state(TaskState.queued)
+    await self.broker.queue_task(task_run)
+    return task_run
 
@@ -1440,7 +1440,8 @@

Source code in fluid/scheduler/consumer.py -
130
+              
129
+130
 131
 132
 133
@@ -1456,25 +1457,24 @@ 

143 144 145 -146 -147

def create_task_run(
-    self,
-    task: str | Task,
-    run_id: str = "",
-    priority: TaskPriority | None = None,
-    **params: Any,
-) -> TaskRun:
-    """Create a TaskRun in `init` state"""
-    if isinstance(task, str):
-        task = self.broker.task_from_registry(task)
-    run_id = run_id or self.broker.new_uuid()
-    return TaskRun(
-        id=run_id,
-        task=task,
-        priority=priority or task.priority,
-        params=params,
-        task_manager=self,
-    )
+146
def create_task_run(
+    self,
+    task: str | Task,
+    run_id: str = "",
+    priority: TaskPriority | None = None,
+    **params: Any,
+) -> TaskRun:
+    """Create a TaskRun in `init` state"""
+    if isinstance(task, str):
+        task = self.broker.task_from_registry(task)
+    run_id = run_id or self.broker.new_uuid()
+    return TaskRun(
+        id=run_id,
+        task=task,
+        priority=priority or task.priority,
+        params=params,
+        task_manager=self,
+    )
 
@@ -1496,17 +1496,17 @@

Source code in fluid/scheduler/consumer.py -
149
+              
def register_from_module(self, module: Any) -> None:
-    for name in dir(module):
-        if name.startswith("_"):
-            continue
-        if isinstance(obj := getattr(module, name), Task):
-            self.register_task(obj)
+153
def register_from_module(self, module: Any) -> None:
+    for name in dir(module):
+        if name.startswith("_"):
+            continue
+        if isinstance(obj := getattr(module, name), Task):
+            self.register_task(obj)
 
@@ -1531,15 +1531,15 @@

Source code in fluid/scheduler/consumer.py -
156
+              
def register_async_handler(self, event: str, handler: AsyncHandler) -> None:
-    """Register an async handler for a given event
-
-    This method is a no op for a TaskManager that is not a worker
-    """
+159
def register_async_handler(self, event: str, handler: AsyncHandler) -> None:
+    """Register an async handler for a given event
+
+    This method is a no op for a TaskManager that is not a worker
+    """
 
@@ -1564,17 +1564,17 @@

Source code in fluid/scheduler/consumer.py -
162
+              
def unregister_async_handler(self, event: Event | str) -> AsyncHandler | None:
-    """Unregister an async handler for a given event
-
-    This method is a no op for a TaskManager that is not a worker
-    """
-    return None
+166
def unregister_async_handler(self, event: Event | str) -> AsyncHandler | None:
+    """Unregister an async handler for a given event
+
+    This method is a no op for a TaskManager that is not a worker
+    """
+    return None
 
@@ -1598,7 +1598,8 @@

Source code in fluid/scheduler/consumer.py -
169
+              
168
+169
 170
 171
 172
@@ -1606,17 +1607,16 @@ 

174 175 176 -177 -178

def cli(self, **kwargs: Any) -> Any:
-    """Create the task manager command line interface"""
-    try:
-        from fluid.scheduler.cli import TaskManagerCLI
-    except ImportError:
-        raise ImportError(
-            "TaskManagerCLI is not available - "
-            "install with `pip install aio-fluid[cli]`"
-        ) from None
-    return TaskManagerCLI(self, **kwargs)
+177
def cli(self, **kwargs: Any) -> Any:
+    """Create the task manager command line interface"""
+    try:
+        from fluid.scheduler.cli import TaskManagerCLI
+    except ImportError:
+        raise ImportError(
+            "TaskManagerCLI is not available - "
+            "install with `pip install aio-fluid[cli]`"
+        ) from None
+    return TaskManagerCLI(self, **kwargs)
 
diff --git a/reference/task_run/index.html b/reference/task_run/index.html index 8b14e32..fe93591 100644 --- a/reference/task_run/index.html +++ b/reference/task_run/index.html @@ -18,7 +18,7 @@ - + @@ -1557,23 +1557,23 @@

Source code in fluid/scheduler/models.py -
157
+              
async def execute(self) -> None:
-    try:
-        self.set_state(TaskState.running)
-        await self.task.executor(self)
-    except Exception:
-        self.set_state(TaskState.failure)
-        raise
-    else:
-        self.set_state(TaskState.success)
+164
async def execute(self) -> None:
+    try:
+        self.set_state(TaskState.running)
+        await self.task.executor(self)
+    except Exception:
+        self.set_state(TaskState.failure)
+        raise
+    else:
+        self.set_state(TaskState.success)
 
@@ -1595,11 +1595,11 @@

Source code in fluid/scheduler/models.py -
@field_serializer("task")
-def serialize_task(self, task: Task, _info: Any) -> str:
-    return task.name
+              
@field_serializer("task")
+def serialize_task(self, task: Task, _info: Any) -> str:
+    return task.name
 
@@ -1621,9 +1621,9 @@

Source code in fluid/scheduler/models.py -
def params_dump_json(self) -> str:
-    return self.task.params_dump_json(self.params)
+              
def params_dump_json(self) -> str:
+    return self.task.params_dump_json(self.params)
 
@@ -1645,7 +1645,8 @@

Source code in fluid/scheduler/models.py -
219
+              
218
+219
 220
 221
 222
@@ -1684,48 +1685,47 @@ 

255 256 257 -258 -259

def set_state(
-    self,
-    state: TaskState,
-    state_time: datetime | None = None,
-) -> None:
-    if self.state == state:
-        return
-    state_time = as_utc(state_time)
-    match (self.state, state):
-        case (TaskState.init, TaskState.queued):
-            self.queued = state_time
-            self.state = state
-            self._dispatch()
-        case (TaskState.init, _):
-            self.set_state(TaskState.queued, state_time)
-            self.set_state(state, state_time)
-        case (TaskState.queued, TaskState.running):
-            self.start = state_time
-            self.state = state
-            self._dispatch()
-        case (
-            TaskState.queued,
-            TaskState.success
-            | TaskState.aborted
-            | TaskState.rate_limited
-            | TaskState.failure,
-        ):
-            self.set_state(TaskState.running, state_time)
-            self.set_state(state, state_time)
-        case (
-            TaskState.running,
-            TaskState.success
-            | TaskState.aborted
-            | TaskState.rate_limited
-            | TaskState.failure,
-        ):
-            self.end = state_time
-            self.state = state
-            self._dispatch()
-        case _:
-            raise TaskRunError(f"invalid state transition {self.state} -> {state}")
+258
def set_state(
+    self,
+    state: TaskState,
+    state_time: datetime | None = None,
+) -> None:
+    if self.state == state:
+        return
+    state_time = as_utc(state_time)
+    match (self.state, state):
+        case (TaskState.init, TaskState.queued):
+            self.queued = state_time
+            self.state = state
+            self._dispatch()
+        case (TaskState.init, _):
+            self.set_state(TaskState.queued, state_time)
+            self.set_state(state, state_time)
+        case (TaskState.queued, TaskState.running):
+            self.start = state_time
+            self.state = state
+            self._dispatch()
+        case (
+            TaskState.queued,
+            TaskState.success
+            | TaskState.aborted
+            | TaskState.rate_limited
+            | TaskState.failure,
+        ):
+            self.set_state(TaskState.running, state_time)
+            self.set_state(state, state_time)
+        case (
+            TaskState.running,
+            TaskState.success
+            | TaskState.aborted
+            | TaskState.rate_limited
+            | TaskState.failure,
+        ):
+            self.end = state_time
+            self.state = state
+            self._dispatch()
+        case _:
+            raise TaskRunError(f"invalid state transition {self.state} -> {state}")
 
@@ -1747,9 +1747,9 @@

Source code in fluid/scheduler/models.py -
def lock(self, timeout: float | None) -> Lock:
-    return self.task_manager.broker.lock(self.name, timeout=timeout)
+              
def lock(self, timeout: float | None) -> Lock:
+    return self.task_manager.broker.lock(self.name, timeout=timeout)
 
diff --git a/reference/tast_consumer/index.html b/reference/tast_consumer/index.html index aecd9f2..6896682 100644 --- a/reference/tast_consumer/index.html +++ b/reference/tast_consumer/index.html @@ -18,7 +18,7 @@ - + @@ -1452,7 +1452,8 @@

Source code in fluid/scheduler/consumer.py -
190
+                    
189
+190
 191
 192
 193
@@ -1469,26 +1470,25 @@ 

204 205 206 -207 -208

def __init__(self, **config: Any) -> None:
-    super().__init__(**config)
-    Workers.__init__(self)
-    self._async_dispatcher_worker = AsyncConsumer(AsyncTaskDispatcher())
-    self._concurrent_tasks: dict[str, dict[str, TaskRun]] = defaultdict(dict)
-    self._task_to_queue: deque[str | Task] = deque()
-    self._priority_task_run_queue: deque[TaskRun] = deque()
-    self._queue_tasks_worker = WorkerFunction(
-        self._queue_task, name="queue-task-worker"
-    )
-    self.add_workers(self._queue_tasks_worker)
-    self.add_workers(self._async_dispatcher_worker)
-    for i in range(self.config.max_concurrent_tasks):
-        worker_name = f"task-worker-{i+1}"
-        self.add_workers(
-            WorkerFunction(
-                partial(self._consume_tasks, worker_name), name=worker_name
-            )
-        )
+207
def __init__(self, **config: Any) -> None:
+    super().__init__(**config)
+    Workers.__init__(self)
+    self._async_dispatcher_worker = AsyncConsumer(AsyncTaskDispatcher())
+    self._concurrent_tasks: dict[str, dict[str, TaskRun]] = defaultdict(dict)
+    self._task_to_queue: deque[str | Task] = deque()
+    self._priority_task_run_queue: deque[TaskRun] = deque()
+    self._queue_tasks_worker = WorkerFunction(
+        self._queue_task, name="queue-task-worker"
+    )
+    self.add_workers(self._queue_tasks_worker)
+    self.add_workers(self._async_dispatcher_worker)
+    for i in range(self.config.max_concurrent_tasks):
+        worker_name = f"task-worker-{i+1}"
+        self.add_workers(
+            WorkerFunction(
+                partial(self._consume_tasks, worker_name), name=worker_name
+            )
+        )
 
@@ -1737,9 +1737,9 @@

Source code in fluid/utils/worker.py -
async def status(self) -> dict:
-    return await self._workers.status()
+              
async def status(self) -> dict:
+    return await self._workers.status()
 
@@ -1761,9 +1761,9 @@

Source code in fluid/utils/worker.py -
def gracefully_stop(self) -> None:
-    self._workers.gracefully_stop()
+              
def gracefully_stop(self) -> None:
+    self._workers.gracefully_stop()
 
@@ -1785,9 +1785,9 @@

Source code in fluid/utils/worker.py -
def is_running(self) -> bool:
-    return self._running
+              
def is_running(self) -> bool:
+    return self._running
 
@@ -1809,9 +1809,9 @@

Source code in fluid/utils/worker.py -
def is_stopping(self) -> bool:
-    return self._workers.is_stopping()
+              
def is_stopping(self) -> bool:
+    return self._workers.is_stopping()
 
@@ -1839,7 +1839,8 @@

Source code in fluid/utils/worker.py -
424
+              
423
+424
 425
 426
 427
@@ -1848,18 +1849,17 @@ 

430 431 432 -433 -434

async def run(self) -> None:
-    """run the workers"""
-    with self.start_running():
-        async with self.safe_run():
-            workers, _ = self._workers.workers_tasks()
-            self._workers.workers = tuple(workers)
-            self._workers.tasks = tuple(
-                self.create_task(worker) for worker in workers
-            )
-            await asyncio.gather(*self._workers.tasks)
-        await self.shutdown()
+433
async def run(self) -> None:
+    """run the workers"""
+    with self.start_running():
+        async with self.safe_run():
+            workers, _ = self._workers.workers_tasks()
+            self._workers.workers = tuple(workers)
+            self._workers.tasks = tuple(
+                self.create_task(worker) for worker in workers
+            )
+            await asyncio.gather(*self._workers.tasks)
+        await self.shutdown()
 
@@ -1881,7 +1881,8 @@

Source code in fluid/utils/worker.py -
85
+              
84
+85
 86
 87
 88
@@ -1890,18 +1891,17 @@ 

91 92 93 -94 -95

@contextmanager
-def start_running(self) -> Generator:
-    if self._running:
-        raise RuntimeError("Worker is already running")
-    self._running = True
-    try:
-        logger.info("%s started running", self.worker_name)
-        yield
-    finally:
-        self._running = False
-        logger.warning("%s stopped running", self.worker_name)
+94
@contextmanager
+def start_running(self) -> Generator:
+    if self._running:
+        raise RuntimeError("Worker is already running")
+    self._running = True
+    try:
+        logger.info("%s started running", self.worker_name)
+        yield
+    finally:
+        self._running = False
+        logger.warning("%s stopped running", self.worker_name)
 
@@ -1925,17 +1925,17 @@

Source code in fluid/utils/worker.py -
417
+              
def add_workers(self, *workers: Worker) -> None:
-    """add workers to the workers"""
-    workers_, _ = self._workers.workers_tasks()
-    for worker in workers:
-        if worker not in workers_:
-            workers_.append(worker)
+421
def add_workers(self, *workers: Worker) -> None:
+    """add workers to the workers"""
+    workers_, _ = self._workers.workers_tasks()
+    for worker in workers:
+        if worker not in workers_:
+            workers_.append(worker)
 
@@ -1961,11 +1961,11 @@

Source code in fluid/utils/worker.py -
async def wait_for_exit(self) -> None:
-    if self._workers_task is not None:
-        await self._workers_task
+              
async def wait_for_exit(self) -> None:
+    if self._workers_task is not None:
+        await self._workers_task
 
@@ -1987,13 +1987,13 @@

Source code in fluid/utils/worker.py -
301
+              
def create_task(self, worker: Worker) -> asyncio.Task:
-    return asyncio.create_task(
-        self._run_worker(worker), name=f"{self.worker_name}-{worker.worker_name}"
-    )
+303
def create_task(self, worker: Worker) -> asyncio.Task:
+    return asyncio.create_task(
+        self._run_worker(worker), name=f"{self.worker_name}-{worker.worker_name}"
+    )
 
@@ -2019,9 +2019,9 @@

Source code in fluid/scheduler/consumer.py -
async def on_shutdown(self) -> None:
-    await self.broker.close()
+              
async def on_shutdown(self) -> None:
+    await self.broker.close()
 
@@ -2049,7 +2049,8 @@

Source code in fluid/utils/worker.py -
309
+              
308
+309
 310
 311
 312
@@ -2081,41 +2082,40 @@ 

338 339 340 -341 -342

async def shutdown(self) -> None:
-    """shutdown the workers"""
-    if self._has_shutdown:
-        return
-    self._has_shutdown = True
-    logger.warning(
-        "gracefully stopping %d workers: %s",
-        self.num_workers,
-        ", ".join(w.worker_name for w in self._workers.workers),
-    )
-    self.gracefully_stop()
-    try:
-        async with async_timeout.timeout(self._stopping_grace_period):
-            await self.wait_for_exit()
-        await self.on_shutdown()
-        return
-    except asyncio.TimeoutError:
-        logger.warning(
-            "could not stop workers %s gracefully after %s"
-            " seconds - force shutdown",
-            ", ".join(
-                task.get_name() for task in self._workers.tasks if not task.done()
-            ),
-            self._stopping_grace_period,
-        )
-    except asyncio.CancelledError:
-        pass
-    self._force_shutdown = True
-    self._workers.cancel()
-    try:
-        await self.wait_for_exit()
-    except asyncio.CancelledError:
-        pass
-    await self.on_shutdown()
+341
async def shutdown(self) -> None:
+    """shutdown the workers"""
+    if self._has_shutdown:
+        return
+    self._has_shutdown = True
+    logger.warning(
+        "gracefully stopping %d workers: %s",
+        self.num_workers,
+        ", ".join(w.worker_name for w in self._workers.workers),
+    )
+    self.gracefully_stop()
+    try:
+        async with asyncio.timeout(self._stopping_grace_period):
+            await self.wait_for_exit()
+        await self.on_shutdown()
+        return
+    except asyncio.TimeoutError:
+        logger.warning(
+            "could not stop workers %s gracefully after %s"
+            " seconds - force shutdown",
+            ", ".join(
+                task.get_name() for task in self._workers.tasks if not task.done()
+            ),
+            self._stopping_grace_period,
+        )
+    except asyncio.CancelledError:
+        pass
+    self._force_shutdown = True
+    self._workers.cancel()
+    try:
+        await self.wait_for_exit()
+    except asyncio.CancelledError:
+        pass
+    await self.on_shutdown()
 
@@ -2137,9 +2137,9 @@

Source code in fluid/utils/worker.py -
def bail_out(self, reason: str, code: int = 1) -> None:
-    self.gracefully_stop()
+              
def bail_out(self, reason: str, code: int = 1) -> None:
+    self.gracefully_stop()
 
@@ -2167,7 +2167,8 @@

Source code in fluid/utils/worker.py -
347
+              
346
+347
 348
 349
 350
@@ -2183,25 +2184,24 @@ 

360 361 362 -363 -364

@asynccontextmanager
-async def safe_run(self) -> AsyncGenerator:
-    """Context manager to run a worker safely"""
-    try:
-        yield
-    except asyncio.CancelledError:
-        if self._force_shutdown:
-            # we are shutting down, this is expected
-            pass
-        raise
-    except Exception as e:
-        reason = f"unhandled exception while running workers: {e}"
-        logger.exception(reason)
-        asyncio.get_event_loop().call_soon(self.bail_out, reason, 2)
-    else:
-        # worker finished without error
-        # make sure we are shutting down
-        asyncio.get_event_loop().call_soon(self.bail_out, "worker exit", 1)
+363
@asynccontextmanager
+async def safe_run(self) -> AsyncGenerator:
+    """Context manager to run a worker safely"""
+    try:
+        yield
+    except asyncio.CancelledError:
+        if self._force_shutdown:
+            # we are shutting down, this is expected
+            pass
+        raise
+    except Exception as e:
+        reason = f"unhandled exception while running workers: {e}"
+        logger.exception(reason)
+        asyncio.get_event_loop().call_soon(self.bail_out, reason, 2)
+    else:
+        # worker finished without error
+        # make sure we are shutting down
+        asyncio.get_event_loop().call_soon(self.bail_out, "worker exit", 1)
 
@@ -2225,21 +2225,21 @@

Source code in fluid/utils/worker.py -
440
+              
def remove_workers(self, *workers: Worker) -> None:
-    "remove workers from the workers"
-    workers_, _ = self._workers.workers_tasks()
-    for worker in workers:
-        try:
-            workers_.remove(worker)
-        except ValueError:
-            pass
+446
def remove_workers(self, *workers: Worker) -> None:
+    "remove workers from the workers"
+    workers_, _ = self._workers.workers_tasks()
+    for worker in workers:
+        try:
+            workers_.remove(worker)
+        except ValueError:
+            pass
 
@@ -2267,19 +2267,19 @@

Source code in fluid/utils/worker.py -
449
+              
async def startup(self) -> None:
-    """start the workers"""
-    if self._workers_task is None:
-        self._workers_task = asyncio.create_task(self.run(), name=self.worker_name)
-        for args in self._delayed_callbacks:
-            self._delayed_callback(*args)
-        self._delayed_callbacks = []
+454
async def startup(self) -> None:
+    """start the workers"""
+    if self._workers_task is None:
+        self._workers_task = asyncio.create_task(self.run(), name=self.worker_name)
+        for args in self._delayed_callbacks:
+            self._delayed_callback(*args)
+        self._delayed_callbacks = []
 
@@ -2306,7 +2306,8 @@

Source code in fluid/utils/worker.py -
457
+              
456
+457
 458
 459
 460
@@ -2325,28 +2326,27 @@ 

473 474 475 -476 -477

def register_callback(
-    self,
-    callback: Callable[[], None],
-    seconds: float,
-    jitter: float = 0.0,
-    periodic: bool | float = False,
-) -> None:
-    """Register a callback
-
-    The callback can be periodic or not.
-    """
-    if periodic is True:
-        periodic_float = seconds
-    elif periodic is False:
-        periodic_float = 0.0
-    else:
-        periodic_float = periodic
-    if not self.running:
-        self._delayed_callbacks.append((callback, seconds, jitter, periodic_float))
-    else:
-        self._delayed_callback(callback, seconds, jitter, periodic_float)
+476
def register_callback(
+    self,
+    callback: Callable[[], None],
+    seconds: float,
+    jitter: float = 0.0,
+    periodic: bool | float = False,
+) -> None:
+    """Register a callback
+
+    The callback can be periodic or not.
+    """
+    if periodic is True:
+        periodic_float = seconds
+    elif periodic is False:
+        periodic_float = 0.0
+    else:
+        periodic_float = periodic
+    if not self.running:
+        self._delayed_callbacks.append((callback, seconds, jitter, periodic_float))
+    else:
+        self._delayed_callback(callback, seconds, jitter, periodic_float)
 
@@ -2372,9 +2372,9 @@

Source code in fluid/scheduler/consumer.py -
async def enter_async_context(self, cm: Any) -> Any:
-    return await self._stack.enter_async_context(cm)
+              
async def enter_async_context(self, cm: Any) -> Any:
+    return await self._stack.enter_async_context(cm)
 
@@ -2402,15 +2402,15 @@

Source code in fluid/scheduler/consumer.py -
92
+              
91
+92
 93
 94
-95
-96
async def execute(self, task: Task | str, **params: Any) -> TaskRun:
-    """Execute a task and wait for it to finish"""
-    task_run = self.create_task_run(task, **params)
-    await task_run.execute()
-    return task_run
+95
async def execute(self, task: Task | str, **params: Any) -> TaskRun:
+    """Execute a task and wait for it to finish"""
+    task_run = self.create_task_run(task, **params)
+    await task_run.execute()
+    return task_run
 
@@ -2432,9 +2432,9 @@

Source code in fluid/scheduler/consumer.py -
def execute_sync(self, task: Task | str, **params: Any) -> TaskRun:
-    return asyncio.run(self._execute_and_exit(task, **params))
+              
def execute_sync(self, task: Task | str, **params: Any) -> TaskRun:
+    return asyncio.run(self._execute_and_exit(task, **params))
 
@@ -2459,17 +2459,17 @@

Source code in fluid/scheduler/consumer.py -
104
+              
def register_task(self, task: Task) -> None:
-    """Register a task with the task manager
-
-    Only tasks registered can be executed by a task manager
-    """
-    self.broker.register_task(task)
+108
def register_task(self, task: Task) -> None:
+    """Register a task with the task manager
+
+    Only tasks registered can be executed by a task manager
+    """
+    self.broker.register_task(task)
 
@@ -2502,7 +2502,8 @@

Source code in fluid/scheduler/consumer.py -
111
+              
110
+111
 112
 113
 114
@@ -2518,25 +2519,24 @@ 

124 125 126 -127 -128

async def queue(
-    self,
-    task: str | Task,
-    priority: TaskPriority | None = None,
-    **params: Any,
-) -> TaskRun:
-    """Queue a task for execution
-
-    This methods fires two events:
-
-    - queue: when the task is about to be queued
-    - queued: after the task is queued
-    """
-    task_run = self.create_task_run(task, priority=priority, **params)
-    self.dispatcher.dispatch(task_run)
-    task_run.set_state(TaskState.queued)
-    await self.broker.queue_task(task_run)
-    return task_run
+127
async def queue(
+    self,
+    task: str | Task,
+    priority: TaskPriority | None = None,
+    **params: Any,
+) -> TaskRun:
+    """Queue a task for execution
+
+    This methods fires two events:
+
+    - queue: when the task is about to be queued
+    - queued: after the task is queued
+    """
+    task_run = self.create_task_run(task, priority=priority, **params)
+    self.dispatcher.dispatch(task_run)
+    task_run.set_state(TaskState.queued)
+    await self.broker.queue_task(task_run)
+    return task_run
 
@@ -2560,7 +2560,8 @@

Source code in fluid/scheduler/consumer.py -
130
+              
129
+130
 131
 132
 133
@@ -2576,25 +2577,24 @@ 

143 144 145 -146 -147

def create_task_run(
-    self,
-    task: str | Task,
-    run_id: str = "",
-    priority: TaskPriority | None = None,
-    **params: Any,
-) -> TaskRun:
-    """Create a TaskRun in `init` state"""
-    if isinstance(task, str):
-        task = self.broker.task_from_registry(task)
-    run_id = run_id or self.broker.new_uuid()
-    return TaskRun(
-        id=run_id,
-        task=task,
-        priority=priority or task.priority,
-        params=params,
-        task_manager=self,
-    )
+146
def create_task_run(
+    self,
+    task: str | Task,
+    run_id: str = "",
+    priority: TaskPriority | None = None,
+    **params: Any,
+) -> TaskRun:
+    """Create a TaskRun in `init` state"""
+    if isinstance(task, str):
+        task = self.broker.task_from_registry(task)
+    run_id = run_id or self.broker.new_uuid()
+    return TaskRun(
+        id=run_id,
+        task=task,
+        priority=priority or task.priority,
+        params=params,
+        task_manager=self,
+    )
 
@@ -2616,17 +2616,17 @@

Source code in fluid/scheduler/consumer.py -
149
+              
def register_from_module(self, module: Any) -> None:
-    for name in dir(module):
-        if name.startswith("_"):
-            continue
-        if isinstance(obj := getattr(module, name), Task):
-            self.register_task(obj)
+153
def register_from_module(self, module: Any) -> None:
+    for name in dir(module):
+        if name.startswith("_"):
+            continue
+        if isinstance(obj := getattr(module, name), Task):
+            self.register_task(obj)
 
@@ -2650,7 +2650,8 @@

Source code in fluid/scheduler/consumer.py -
169
+              
168
+169
 170
 171
 172
@@ -2658,17 +2659,16 @@ 

174 175 176 -177 -178

def cli(self, **kwargs: Any) -> Any:
-    """Create the task manager command line interface"""
-    try:
-        from fluid.scheduler.cli import TaskManagerCLI
-    except ImportError:
-        raise ImportError(
-            "TaskManagerCLI is not available - "
-            "install with `pip install aio-fluid[cli]`"
-        ) from None
-    return TaskManagerCLI(self, **kwargs)
+177
def cli(self, **kwargs: Any) -> Any:
+    """Create the task manager command line interface"""
+    try:
+        from fluid.scheduler.cli import TaskManagerCLI
+    except ImportError:
+        raise ImportError(
+            "TaskManagerCLI is not available - "
+            "install with `pip install aio-fluid[cli]`"
+        ) from None
+    return TaskManagerCLI(self, **kwargs)
 
@@ -2690,9 +2690,9 @@

Source code in fluid/scheduler/consumer.py -
def sync_queue(self, task: str | Task) -> None:
-    self._task_to_queue.appendleft(task)
+              
def sync_queue(self, task: str | Task) -> None:
+    self._task_to_queue.appendleft(task)
 
@@ -2714,9 +2714,9 @@

Source code in fluid/scheduler/consumer.py -
def sync_priority_queue(self, task: str | Task) -> None:
-    self._priority_task_run_queue.appendleft(self.create_task_run(task))
+              
def sync_priority_queue(self, task: str | Task) -> None:
+    self._priority_task_run_queue.appendleft(self.create_task_run(task))
 
@@ -2740,11 +2740,11 @@

Source code in fluid/scheduler/consumer.py -
def num_concurrent_tasks_for(self, task_name: str) -> int:
-    """The number of concurrent tasks for a given task_name"""
-    return len(self._concurrent_tasks[task_name])
+              
def num_concurrent_tasks_for(self, task_name: str) -> int:
+    """The number of concurrent tasks for a given task_name"""
+    return len(self._concurrent_tasks[task_name])
 
@@ -2772,17 +2772,17 @@

Source code in fluid/scheduler/consumer.py -
225
+              
async def queue_and_wait(
-    self, task: str, *, timeout: int = 2, **params: Any
-) -> TaskRun:
-    """Queue a task and wait for it to finish"""
-    with TaskRunWaiter(self) as waiter:
-        return await waiter.wait(await self.queue(task, **params), timeout=timeout)
+229
async def queue_and_wait(
+    self, task: str, *, timeout: int = 2, **params: Any
+) -> TaskRun:
+    """Queue a task and wait for it to finish"""
+    with TaskRunWaiter(self) as waiter:
+        return await waiter.wait(await self.queue(task, **params), timeout=timeout)
 
@@ -2804,19 +2804,19 @@

Source code in fluid/scheduler/consumer.py -
232
+              
def register_async_handler(self, event: Event | str, handler: AsyncHandler) -> None:
-    event = Event.from_string_or_event(event)
-    self.dispatcher.register_handler(
-        f"{event.type}.async_dispatch",
-        self._async_dispatcher_worker.send,
-    )
-    self._async_dispatcher_worker.dispatcher.register_handler(event, handler)
+237
def register_async_handler(self, event: Event | str, handler: AsyncHandler) -> None:
+    event = Event.from_string_or_event(event)
+    self.dispatcher.register_handler(
+        f"{event.type}.async_dispatch",
+        self._async_dispatcher_worker.send,
+    )
+    self._async_dispatcher_worker.dispatcher.register_handler(event, handler)
 
@@ -2838,9 +2838,9 @@

Source code in fluid/scheduler/consumer.py -
def unregister_async_handler(self, event: Event | str) -> AsyncHandler | None:
-    return self._async_dispatcher_worker.dispatcher.unregister_handler(event)
+              
def unregister_async_handler(self, event: Event | str) -> AsyncHandler | None:
+    return self._async_dispatcher_worker.dispatcher.unregister_handler(event)
 
diff --git a/reference/workers/index.html b/reference/workers/index.html index ede52de..4df64b2 100644 --- a/reference/workers/index.html +++ b/reference/workers/index.html @@ -18,7 +18,7 @@ - + @@ -2790,9 +2790,9 @@

Source code in fluid/utils/worker.py -
def __init__(self, name: str = "") -> None:
-    self._name: str = name or underscore(type(self).__name__)
+                    
def __init__(self, name: str = "") -> None:
+    self._name: str = name or underscore(type(self).__name__)
 
@@ -2876,15 +2876,15 @@

Source code in fluid/utils/worker.py -
52
+              
51
+52
 53
 54
-55
-56
@abstractmethod
-async def status(self) -> dict:
-    """
-    Get the status of the worker.
-    """
+55
@abstractmethod
+async def status(self) -> dict:
+    """
+    Get the status of the worker.
+    """
 
@@ -2912,11 +2912,11 @@

Source code in fluid/utils/worker.py -
58
-59
-60
@abstractmethod
-def gracefully_stop(self) -> None:
-    "gracefully stop the worker"
+              
57
+58
+59
@abstractmethod
+def gracefully_stop(self) -> None:
+    "gracefully stop the worker"
 
@@ -2944,11 +2944,11 @@

Source code in fluid/utils/worker.py -
62
-63
-64
@abstractmethod
-def is_running(self) -> bool:
-    """Is the worker running?"""
+              
61
+62
+63
@abstractmethod
+def is_running(self) -> bool:
+    """Is the worker running?"""
 
@@ -2976,11 +2976,11 @@

Source code in fluid/utils/worker.py -
66
-67
-68
@abstractmethod
-def is_stopping(self) -> bool:
-    """Is the worker stopping?"""
+              
65
+66
+67
@abstractmethod
+def is_stopping(self) -> bool:
+    """Is the worker stopping?"""
 
@@ -3009,11 +3009,11 @@

Source code in fluid/utils/worker.py -
70
-71
-72
@abstractmethod
-async def run(self) -> None:
-    """run the worker"""
+              
69
+70
+71
@abstractmethod
+async def run(self) -> None:
+    """run the worker"""
 
@@ -3054,11 +3054,11 @@

Source code in fluid/utils/worker.py -
78
-79
-80
def __init__(self, name: str = "") -> None:
-    super().__init__(name)
-    self._running: bool = False
+                    
77
+78
+79
def __init__(self, name: str = "") -> None:
+    super().__init__(name)
+    self._running: bool = False
 
@@ -3142,15 +3142,15 @@

Source code in fluid/utils/worker.py -
52
+              
51
+52
 53
 54
-55
-56
@abstractmethod
-async def status(self) -> dict:
-    """
-    Get the status of the worker.
-    """
+55
@abstractmethod
+async def status(self) -> dict:
+    """
+    Get the status of the worker.
+    """
 
@@ -3178,11 +3178,11 @@

Source code in fluid/utils/worker.py -
58
-59
-60
@abstractmethod
-def gracefully_stop(self) -> None:
-    "gracefully stop the worker"
+              
57
+58
+59
@abstractmethod
+def gracefully_stop(self) -> None:
+    "gracefully stop the worker"
 
@@ -3210,11 +3210,11 @@

Source code in fluid/utils/worker.py -
66
-67
-68
@abstractmethod
-def is_stopping(self) -> bool:
-    """Is the worker stopping?"""
+              
65
+66
+67
@abstractmethod
+def is_stopping(self) -> bool:
+    """Is the worker stopping?"""
 
@@ -3243,11 +3243,11 @@

Source code in fluid/utils/worker.py -
70
-71
-72
@abstractmethod
-async def run(self) -> None:
-    """run the worker"""
+              
69
+70
+71
@abstractmethod
+async def run(self) -> None:
+    """run the worker"""
 
@@ -3269,9 +3269,9 @@

Source code in fluid/utils/worker.py -
def is_running(self) -> bool:
-    return self._running
+              
def is_running(self) -> bool:
+    return self._running
 
@@ -3293,7 +3293,8 @@

Source code in fluid/utils/worker.py -
85
+              
84
+85
 86
 87
 88
@@ -3302,18 +3303,17 @@ 

91 92 93 -94 -95

@contextmanager
-def start_running(self) -> Generator:
-    if self._running:
-        raise RuntimeError("Worker is already running")
-    self._running = True
-    try:
-        logger.info("%s started running", self.worker_name)
-        yield
-    finally:
-        self._running = False
-        logger.warning("%s stopped running", self.worker_name)
+94
@contextmanager
+def start_running(self) -> Generator:
+    if self._running:
+        raise RuntimeError("Worker is already running")
+    self._running = True
+    try:
+        logger.info("%s started running", self.worker_name)
+        yield
+    finally:
+        self._running = False
+        logger.warning("%s stopped running", self.worker_name)
 
@@ -3354,11 +3354,11 @@

Source code in fluid/utils/worker.py -
def __init__(self, name: str = "") -> None:
-    super().__init__(name)
-    self._stopping: bool = False
+                    
def __init__(self, name: str = "") -> None:
+    super().__init__(name)
+    self._stopping: bool = False
 
@@ -3435,9 +3435,9 @@

Source code in fluid/utils/worker.py -
def is_running(self) -> bool:
-    return self._running
+              
def is_running(self) -> bool:
+    return self._running
 
@@ -3466,11 +3466,11 @@

Source code in fluid/utils/worker.py -
70
-71
-72
@abstractmethod
-async def run(self) -> None:
-    """run the worker"""
+              
69
+70
+71
@abstractmethod
+async def run(self) -> None:
+    """run the worker"""
 
@@ -3492,7 +3492,8 @@

Source code in fluid/utils/worker.py -
85
+              
84
+85
 86
 87
 88
@@ -3501,18 +3502,17 @@ 

91 92 93 -94 -95

@contextmanager
-def start_running(self) -> Generator:
-    if self._running:
-        raise RuntimeError("Worker is already running")
-    self._running = True
-    try:
-        logger.info("%s started running", self.worker_name)
-        yield
-    finally:
-        self._running = False
-        logger.warning("%s stopped running", self.worker_name)
+94
@contextmanager
+def start_running(self) -> Generator:
+    if self._running:
+        raise RuntimeError("Worker is already running")
+    self._running = True
+    try:
+        logger.info("%s started running", self.worker_name)
+        yield
+    finally:
+        self._running = False
+        logger.warning("%s stopped running", self.worker_name)
 
@@ -3534,9 +3534,9 @@

Source code in fluid/utils/worker.py -
def is_stopping(self) -> bool:
-    return self._stopping
+              
def is_stopping(self) -> bool:
+    return self._stopping
 
@@ -3558,9 +3558,9 @@

Source code in fluid/utils/worker.py -
def gracefully_stop(self) -> None:
-    self._stopping = True
+              
def gracefully_stop(self) -> None:
+    self._stopping = True
 
@@ -3586,9 +3586,9 @@

Source code in fluid/utils/worker.py -
async def status(self) -> dict:
-    return {"stopping": self.is_stopping(), "running": self.is_running()}
+              
async def status(self) -> dict:
+    return {"stopping": self.is_stopping(), "running": self.is_running()}
 
@@ -3629,23 +3629,23 @@

Source code in fluid/utils/worker.py -
118
+                    
def __init__(
-    self,
-    run_function: Callable[[], Awaitable[None]],
-    heartbeat: float | int = 0,
-    name: str = "",
-) -> None:
-    super().__init__(name=name)
-    self._run_function = run_function
-    self._heartbeat = heartbeat
+125
def __init__(
+    self,
+    run_function: Callable[[], Awaitable[None]],
+    heartbeat: float | int = 0,
+    name: str = "",
+) -> None:
+    super().__init__(name=name)
+    self._run_function = run_function
+    self._heartbeat = heartbeat
 
@@ -3726,9 +3726,9 @@

Source code in fluid/utils/worker.py -
async def status(self) -> dict:
-    return {"stopping": self.is_stopping(), "running": self.is_running()}
+              
async def status(self) -> dict:
+    return {"stopping": self.is_stopping(), "running": self.is_running()}
 
@@ -3750,9 +3750,9 @@

Source code in fluid/utils/worker.py -
def gracefully_stop(self) -> None:
-    self._stopping = True
+              
def gracefully_stop(self) -> None:
+    self._stopping = True
 
@@ -3774,9 +3774,9 @@

Source code in fluid/utils/worker.py -
def is_running(self) -> bool:
-    return self._running
+              
def is_running(self) -> bool:
+    return self._running
 
@@ -3798,9 +3798,9 @@

Source code in fluid/utils/worker.py -
def is_stopping(self) -> bool:
-    return self._stopping
+              
def is_stopping(self) -> bool:
+    return self._stopping
 
@@ -3822,7 +3822,8 @@

Source code in fluid/utils/worker.py -
85
+              
84
+85
 86
 87
 88
@@ -3831,18 +3832,17 @@ 

91 92 93 -94 -95

@contextmanager
-def start_running(self) -> Generator:
-    if self._running:
-        raise RuntimeError("Worker is already running")
-    self._running = True
-    try:
-        logger.info("%s started running", self.worker_name)
-        yield
-    finally:
-        self._running = False
-        logger.warning("%s stopped running", self.worker_name)
+94
@contextmanager
+def start_running(self) -> Generator:
+    if self._running:
+        raise RuntimeError("Worker is already running")
+    self._running = True
+    try:
+        logger.info("%s started running", self.worker_name)
+        yield
+    finally:
+        self._running = False
+        logger.warning("%s stopped running", self.worker_name)
 
@@ -3868,15 +3868,15 @@

Source code in fluid/utils/worker.py -
128
+              
async def run(self) -> None:
-    with self.start_running():
-        while not self.is_stopping():
-            await self._run_function()
-            await asyncio.sleep(self._heartbeat)
+131
async def run(self) -> None:
+    with self.start_running():
+        while not self.is_stopping():
+            await self._run_function()
+            await asyncio.sleep(self._heartbeat)
 
@@ -3918,11 +3918,11 @@

Source code in fluid/utils/worker.py -
def __init__(self, name: str = "") -> None:
-    super().__init__(name=name)
-    self._queue: asyncio.Queue[MessageType | None] = asyncio.Queue()
+                    
def __init__(self, name: str = "") -> None:
+    super().__init__(name=name)
+    self._queue: asyncio.Queue[MessageType | None] = asyncio.Queue()
 
@@ -3999,9 +3999,9 @@

Source code in fluid/utils/worker.py -
def gracefully_stop(self) -> None:
-    self._stopping = True
+              
def gracefully_stop(self) -> None:
+    self._stopping = True
 
@@ -4023,9 +4023,9 @@

Source code in fluid/utils/worker.py -
def is_running(self) -> bool:
-    return self._running
+              
def is_running(self) -> bool:
+    return self._running
 
@@ -4047,9 +4047,9 @@

Source code in fluid/utils/worker.py -
def is_stopping(self) -> bool:
-    return self._stopping
+              
def is_stopping(self) -> bool:
+    return self._stopping
 
@@ -4078,11 +4078,11 @@

Source code in fluid/utils/worker.py -
70
-71
-72
@abstractmethod
-async def run(self) -> None:
-    """run the worker"""
+              
69
+70
+71
@abstractmethod
+async def run(self) -> None:
+    """run the worker"""
 
@@ -4104,7 +4104,8 @@

Source code in fluid/utils/worker.py -
85
+              
84
+85
 86
 87
 88
@@ -4113,18 +4114,17 @@ 

91 92 93 -94 -95

@contextmanager
-def start_running(self) -> Generator:
-    if self._running:
-        raise RuntimeError("Worker is already running")
-    self._running = True
-    try:
-        logger.info("%s started running", self.worker_name)
-        yield
-    finally:
-        self._running = False
-        logger.warning("%s stopped running", self.worker_name)
+94
@contextmanager
+def start_running(self) -> Generator:
+    if self._running:
+        raise RuntimeError("Worker is already running")
+    self._running = True
+    try:
+        logger.info("%s started running", self.worker_name)
+        yield
+    finally:
+        self._running = False
+        logger.warning("%s stopped running", self.worker_name)
 
@@ -4150,7 +4150,8 @@

Source code in fluid/utils/worker.py -
152
+              
151
+152
 153
 154
 155
@@ -4158,17 +4159,16 @@ 

157 158 159 -160 -161

async def get_message(self, timeout: float = 0.5) -> MessageType | None:
-    try:
-        async with asyncio.timeout(timeout):
-            return await self._queue.get()
-    except asyncio.TimeoutError:
-        return None
-    except (asyncio.CancelledError, RuntimeError):
-        if not self.is_stopping():
-            raise
-    return None
+160
async def get_message(self, timeout: float = 0.5) -> MessageType | None:
+    try:
+        async with asyncio.timeout(timeout):
+            return await self._queue.get()
+    except asyncio.TimeoutError:
+        return None
+    except (asyncio.CancelledError, RuntimeError):
+        if not self.is_stopping():
+            raise
+    return None
 
@@ -4190,9 +4190,9 @@

Source code in fluid/utils/worker.py -
def queue_size(self) -> int:
-    return self._queue.qsize()
+              
def queue_size(self) -> int:
+    return self._queue.qsize()
 
@@ -4218,13 +4218,13 @@

Source code in fluid/utils/worker.py -
166
+              
async def status(self) -> dict:
-    status = await super().status()
-    status.update(queue_size=self.queue_size())
-    return status
+168
async def status(self) -> dict:
+    status = await super().status()
+    status.update(queue_size=self.queue_size())
+    return status
 
@@ -4248,11 +4248,11 @@

Source code in fluid/utils/worker.py -
def send(self, message: MessageType | None) -> None:
-    """Send a message into the worker"""
-    self._queue.put_nowait(message)
+              
def send(self, message: MessageType | None) -> None:
+    """Send a message into the worker"""
+    self._queue.put_nowait(message)
 
@@ -4293,19 +4293,19 @@

Source code in fluid/utils/worker.py -
179
+                    
def __init__(
-    self,
-    on_message: Callable[[MessageType], Awaitable[None]],
-    name: str = "",
-) -> None:
-    super().__init__(name=name)
-    self.on_message = on_message
+184
def __init__(
+    self,
+    on_message: Callable[[MessageType], Awaitable[None]],
+    name: str = "",
+) -> None:
+    super().__init__(name=name)
+    self.on_message = on_message
 
@@ -4405,11 +4405,11 @@

Source code in fluid/utils/worker.py -
def send(self, message: MessageType | None) -> None:
-    """Send a message into the worker"""
-    self._queue.put_nowait(message)
+              
def send(self, message: MessageType | None) -> None:
+    """Send a message into the worker"""
+    self._queue.put_nowait(message)
 
@@ -4435,13 +4435,13 @@

Source code in fluid/utils/worker.py -
166
+              
async def status(self) -> dict:
-    status = await super().status()
-    status.update(queue_size=self.queue_size())
-    return status
+168
async def status(self) -> dict:
+    status = await super().status()
+    status.update(queue_size=self.queue_size())
+    return status
 
@@ -4463,9 +4463,9 @@

Source code in fluid/utils/worker.py -
def gracefully_stop(self) -> None:
-    self._stopping = True
+              
def gracefully_stop(self) -> None:
+    self._stopping = True
 
@@ -4487,9 +4487,9 @@

Source code in fluid/utils/worker.py -
def is_running(self) -> bool:
-    return self._running
+              
def is_running(self) -> bool:
+    return self._running
 
@@ -4511,9 +4511,9 @@

Source code in fluid/utils/worker.py -
def is_stopping(self) -> bool:
-    return self._stopping
+              
def is_stopping(self) -> bool:
+    return self._stopping
 
@@ -4535,7 +4535,8 @@

Source code in fluid/utils/worker.py -
85
+              
84
+85
 86
 87
 88
@@ -4544,18 +4545,17 @@ 

91 92 93 -94 -95

@contextmanager
-def start_running(self) -> Generator:
-    if self._running:
-        raise RuntimeError("Worker is already running")
-    self._running = True
-    try:
-        logger.info("%s started running", self.worker_name)
-        yield
-    finally:
-        self._running = False
-        logger.warning("%s stopped running", self.worker_name)
+94
@contextmanager
+def start_running(self) -> Generator:
+    if self._running:
+        raise RuntimeError("Worker is already running")
+    self._running = True
+    try:
+        logger.info("%s started running", self.worker_name)
+        yield
+    finally:
+        self._running = False
+        logger.warning("%s stopped running", self.worker_name)
 
@@ -4581,7 +4581,8 @@

Source code in fluid/utils/worker.py -
152
+              
151
+152
 153
 154
 155
@@ -4589,17 +4590,16 @@ 

157 158 159 -160 -161

async def get_message(self, timeout: float = 0.5) -> MessageType | None:
-    try:
-        async with asyncio.timeout(timeout):
-            return await self._queue.get()
-    except asyncio.TimeoutError:
-        return None
-    except (asyncio.CancelledError, RuntimeError):
-        if not self.is_stopping():
-            raise
-    return None
+160
async def get_message(self, timeout: float = 0.5) -> MessageType | None:
+    try:
+        async with asyncio.timeout(timeout):
+            return await self._queue.get()
+    except asyncio.TimeoutError:
+        return None
+    except (asyncio.CancelledError, RuntimeError):
+        if not self.is_stopping():
+            raise
+    return None
 
@@ -4621,9 +4621,9 @@

Source code in fluid/utils/worker.py -
def queue_size(self) -> int:
-    return self._queue.qsize()
+              
def queue_size(self) -> int:
+    return self._queue.qsize()
 
@@ -4649,17 +4649,17 @@

Source code in fluid/utils/worker.py -
187
+              
async def run(self) -> None:
-    with self.start_running():
-        while not self.is_stopping():
-            message = await self.get_message()
-            if message is not None:
-                await self.on_message(message)
+191
async def run(self) -> None:
+    with self.start_running():
+        while not self.is_stopping():
+            message = await self.get_message()
+            if message is not None:
+                await self.on_message(message)
 
@@ -4700,15 +4700,15 @@

Source code in fluid/utils/worker.py -
200
+                    
def __init__(
-    self, dispatcher: AsyncDispatcher[MessageType], name: str = ""
-) -> None:
-    super().__init__(name)
-    self.dispatcher: AsyncDispatcher[MessageType] = dispatcher
+203
def __init__(
+    self, dispatcher: AsyncDispatcher[MessageType], name: str = ""
+) -> None:
+    super().__init__(name)
+    self.dispatcher: AsyncDispatcher[MessageType] = dispatcher
 
@@ -4829,11 +4829,11 @@

Source code in fluid/utils/worker.py -
def send(self, message: MessageType | None) -> None:
-    """Send a message into the worker"""
-    self._queue.put_nowait(message)
+              
def send(self, message: MessageType | None) -> None:
+    """Send a message into the worker"""
+    self._queue.put_nowait(message)
 
@@ -4859,13 +4859,13 @@

Source code in fluid/utils/worker.py -
166
+              
async def status(self) -> dict:
-    status = await super().status()
-    status.update(queue_size=self.queue_size())
-    return status
+168
async def status(self) -> dict:
+    status = await super().status()
+    status.update(queue_size=self.queue_size())
+    return status
 
@@ -4887,9 +4887,9 @@

Source code in fluid/utils/worker.py -
def gracefully_stop(self) -> None:
-    self._stopping = True
+              
def gracefully_stop(self) -> None:
+    self._stopping = True
 
@@ -4911,9 +4911,9 @@

Source code in fluid/utils/worker.py -
def is_running(self) -> bool:
-    return self._running
+              
def is_running(self) -> bool:
+    return self._running
 
@@ -4935,9 +4935,9 @@

Source code in fluid/utils/worker.py -
def is_stopping(self) -> bool:
-    return self._stopping
+              
def is_stopping(self) -> bool:
+    return self._stopping
 
@@ -4959,7 +4959,8 @@

Source code in fluid/utils/worker.py -
85
+              
84
+85
 86
 87
 88
@@ -4968,18 +4969,17 @@ 

91 92 93 -94 -95

@contextmanager
-def start_running(self) -> Generator:
-    if self._running:
-        raise RuntimeError("Worker is already running")
-    self._running = True
-    try:
-        logger.info("%s started running", self.worker_name)
-        yield
-    finally:
-        self._running = False
-        logger.warning("%s stopped running", self.worker_name)
+94
@contextmanager
+def start_running(self) -> Generator:
+    if self._running:
+        raise RuntimeError("Worker is already running")
+    self._running = True
+    try:
+        logger.info("%s started running", self.worker_name)
+        yield
+    finally:
+        self._running = False
+        logger.warning("%s stopped running", self.worker_name)
 
@@ -5005,7 +5005,8 @@

Source code in fluid/utils/worker.py -
152
+              
151
+152
 153
 154
 155
@@ -5013,17 +5014,16 @@ 

157 158 159 -160 -161

async def get_message(self, timeout: float = 0.5) -> MessageType | None:
-    try:
-        async with asyncio.timeout(timeout):
-            return await self._queue.get()
-    except asyncio.TimeoutError:
-        return None
-    except (asyncio.CancelledError, RuntimeError):
-        if not self.is_stopping():
-            raise
-    return None
+160
async def get_message(self, timeout: float = 0.5) -> MessageType | None:
+    try:
+        async with asyncio.timeout(timeout):
+            return await self._queue.get()
+    except asyncio.TimeoutError:
+        return None
+    except (asyncio.CancelledError, RuntimeError):
+        if not self.is_stopping():
+            raise
+    return None
 
@@ -5045,9 +5045,9 @@

Source code in fluid/utils/worker.py -
def queue_size(self) -> int:
-    return self._queue.qsize()
+              
def queue_size(self) -> int:
+    return self._queue.qsize()
 
@@ -5073,17 +5073,17 @@

Source code in fluid/utils/worker.py -
206
+              
async def run(self) -> None:
-    with self.start_running():
-        while not self.is_stopping():
-            message = await self.get_message()
-            if message is not None:
-                await self.dispatcher.dispatch(message)
+210
async def run(self) -> None:
+    with self.start_running():
+        while not self.is_stopping():
+            message = await self.get_message()
+            if message is not None:
+                await self.dispatcher.dispatch(message)
 
@@ -5128,7 +5128,8 @@

Source code in fluid/utils/worker.py -
398
+                    
397
+398
 399
 400
 401
@@ -5139,20 +5140,19 @@ 

406 407 408 -409 -410

def __init__(
-    self,
-    *workers: Worker,
-    name: str = "",
-    stopping_grace_period: int = settings.STOPPING_GRACE_PERIOD,
-) -> None:
-    super().__init__(
-        *workers, name=name, stopping_grace_period=stopping_grace_period
-    )
-    self._workers_task: asyncio.Task | None = None
-    self._delayed_callbacks: list[
-        tuple[Callable[[], None], float, float, float]
-    ] = []
+409
def __init__(
+    self,
+    *workers: Worker,
+    name: str = "",
+    stopping_grace_period: int = settings.STOPPING_GRACE_PERIOD,
+) -> None:
+    super().__init__(
+        *workers, name=name, stopping_grace_period=stopping_grace_period
+    )
+    self._workers_task: asyncio.Task | None = None
+    self._delayed_callbacks: list[
+        tuple[Callable[[], None], float, float, float]
+    ] = []
 
@@ -5252,9 +5252,9 @@

Source code in fluid/utils/worker.py -
async def status(self) -> dict:
-    return await self._workers.status()
+              
async def status(self) -> dict:
+    return await self._workers.status()
 
@@ -5276,9 +5276,9 @@

Source code in fluid/utils/worker.py -
def gracefully_stop(self) -> None:
-    self._workers.gracefully_stop()
+              
def gracefully_stop(self) -> None:
+    self._workers.gracefully_stop()
 
@@ -5300,9 +5300,9 @@

Source code in fluid/utils/worker.py -
def is_running(self) -> bool:
-    return self._running
+              
def is_running(self) -> bool:
+    return self._running
 
@@ -5324,9 +5324,9 @@

Source code in fluid/utils/worker.py -
def is_stopping(self) -> bool:
-    return self._workers.is_stopping()
+              
def is_stopping(self) -> bool:
+    return self._workers.is_stopping()
 
@@ -5348,7 +5348,8 @@

Source code in fluid/utils/worker.py -
85
+              
84
+85
 86
 87
 88
@@ -5357,18 +5358,17 @@ 

91 92 93 -94 -95

@contextmanager
-def start_running(self) -> Generator:
-    if self._running:
-        raise RuntimeError("Worker is already running")
-    self._running = True
-    try:
-        logger.info("%s started running", self.worker_name)
-        yield
-    finally:
-        self._running = False
-        logger.warning("%s stopped running", self.worker_name)
+94
@contextmanager
+def start_running(self) -> Generator:
+    if self._running:
+        raise RuntimeError("Worker is already running")
+    self._running = True
+    try:
+        logger.info("%s started running", self.worker_name)
+        yield
+    finally:
+        self._running = False
+        logger.warning("%s stopped running", self.worker_name)
 
@@ -5390,13 +5390,13 @@

Source code in fluid/utils/worker.py -
301
+              
def create_task(self, worker: Worker) -> asyncio.Task:
-    return asyncio.create_task(
-        self._run_worker(worker), name=f"{self.worker_name}-{worker.worker_name}"
-    )
+303
def create_task(self, worker: Worker) -> asyncio.Task:
+    return asyncio.create_task(
+        self._run_worker(worker), name=f"{self.worker_name}-{worker.worker_name}"
+    )
 
@@ -5424,9 +5424,9 @@

Source code in fluid/utils/worker.py -
async def on_shutdown(self) -> None:
-    """called after the workers are stopped"""
+              
async def on_shutdown(self) -> None:
+    """called after the workers are stopped"""
 
@@ -5454,7 +5454,8 @@

Source code in fluid/utils/worker.py -
309
+              
308
+309
 310
 311
 312
@@ -5486,41 +5487,40 @@ 

338 339 340 -341 -342

async def shutdown(self) -> None:
-    """shutdown the workers"""
-    if self._has_shutdown:
-        return
-    self._has_shutdown = True
-    logger.warning(
-        "gracefully stopping %d workers: %s",
-        self.num_workers,
-        ", ".join(w.worker_name for w in self._workers.workers),
-    )
-    self.gracefully_stop()
-    try:
-        async with async_timeout.timeout(self._stopping_grace_period):
-            await self.wait_for_exit()
-        await self.on_shutdown()
-        return
-    except asyncio.TimeoutError:
-        logger.warning(
-            "could not stop workers %s gracefully after %s"
-            " seconds - force shutdown",
-            ", ".join(
-                task.get_name() for task in self._workers.tasks if not task.done()
-            ),
-            self._stopping_grace_period,
-        )
-    except asyncio.CancelledError:
-        pass
-    self._force_shutdown = True
-    self._workers.cancel()
-    try:
-        await self.wait_for_exit()
-    except asyncio.CancelledError:
-        pass
-    await self.on_shutdown()
+341
async def shutdown(self) -> None:
+    """shutdown the workers"""
+    if self._has_shutdown:
+        return
+    self._has_shutdown = True
+    logger.warning(
+        "gracefully stopping %d workers: %s",
+        self.num_workers,
+        ", ".join(w.worker_name for w in self._workers.workers),
+    )
+    self.gracefully_stop()
+    try:
+        async with asyncio.timeout(self._stopping_grace_period):
+            await self.wait_for_exit()
+        await self.on_shutdown()
+        return
+    except asyncio.TimeoutError:
+        logger.warning(
+            "could not stop workers %s gracefully after %s"
+            " seconds - force shutdown",
+            ", ".join(
+                task.get_name() for task in self._workers.tasks if not task.done()
+            ),
+            self._stopping_grace_period,
+        )
+    except asyncio.CancelledError:
+        pass
+    self._force_shutdown = True
+    self._workers.cancel()
+    try:
+        await self.wait_for_exit()
+    except asyncio.CancelledError:
+        pass
+    await self.on_shutdown()
 
@@ -5542,9 +5542,9 @@

Source code in fluid/utils/worker.py -
def bail_out(self, reason: str, code: int = 1) -> None:
-    self.gracefully_stop()
+              
def bail_out(self, reason: str, code: int = 1) -> None:
+    self.gracefully_stop()
 
@@ -5572,7 +5572,8 @@

Source code in fluid/utils/worker.py -
347
+              
346
+347
 348
 349
 350
@@ -5588,25 +5589,24 @@ 

360 361 362 -363 -364

@asynccontextmanager
-async def safe_run(self) -> AsyncGenerator:
-    """Context manager to run a worker safely"""
-    try:
-        yield
-    except asyncio.CancelledError:
-        if self._force_shutdown:
-            # we are shutting down, this is expected
-            pass
-        raise
-    except Exception as e:
-        reason = f"unhandled exception while running workers: {e}"
-        logger.exception(reason)
-        asyncio.get_event_loop().call_soon(self.bail_out, reason, 2)
-    else:
-        # worker finished without error
-        # make sure we are shutting down
-        asyncio.get_event_loop().call_soon(self.bail_out, "worker exit", 1)
+363
@asynccontextmanager
+async def safe_run(self) -> AsyncGenerator:
+    """Context manager to run a worker safely"""
+    try:
+        yield
+    except asyncio.CancelledError:
+        if self._force_shutdown:
+            # we are shutting down, this is expected
+            pass
+        raise
+    except Exception as e:
+        reason = f"unhandled exception while running workers: {e}"
+        logger.exception(reason)
+        asyncio.get_event_loop().call_soon(self.bail_out, reason, 2)
+    else:
+        # worker finished without error
+        # make sure we are shutting down
+        asyncio.get_event_loop().call_soon(self.bail_out, "worker exit", 1)
 
@@ -5630,17 +5630,17 @@

Source code in fluid/utils/worker.py -
417
+              
def add_workers(self, *workers: Worker) -> None:
-    """add workers to the workers"""
-    workers_, _ = self._workers.workers_tasks()
-    for worker in workers:
-        if worker not in workers_:
-            workers_.append(worker)
+421
def add_workers(self, *workers: Worker) -> None:
+    """add workers to the workers"""
+    workers_, _ = self._workers.workers_tasks()
+    for worker in workers:
+        if worker not in workers_:
+            workers_.append(worker)
 
@@ -5668,7 +5668,8 @@

Source code in fluid/utils/worker.py -
424
+              
423
+424
 425
 426
 427
@@ -5677,18 +5678,17 @@ 

430 431 432 -433 -434

async def run(self) -> None:
-    """run the workers"""
-    with self.start_running():
-        async with self.safe_run():
-            workers, _ = self._workers.workers_tasks()
-            self._workers.workers = tuple(workers)
-            self._workers.tasks = tuple(
-                self.create_task(worker) for worker in workers
-            )
-            await asyncio.gather(*self._workers.tasks)
-        await self.shutdown()
+433
async def run(self) -> None:
+    """run the workers"""
+    with self.start_running():
+        async with self.safe_run():
+            workers, _ = self._workers.workers_tasks()
+            self._workers.workers = tuple(workers)
+            self._workers.tasks = tuple(
+                self.create_task(worker) for worker in workers
+            )
+            await asyncio.gather(*self._workers.tasks)
+        await self.shutdown()
 
@@ -5714,11 +5714,11 @@

Source code in fluid/utils/worker.py -
async def wait_for_exit(self) -> None:
-    if self._workers_task is not None:
-        await self._workers_task
+              
async def wait_for_exit(self) -> None:
+    if self._workers_task is not None:
+        await self._workers_task
 
@@ -5742,21 +5742,21 @@

Source code in fluid/utils/worker.py -
440
+              
def remove_workers(self, *workers: Worker) -> None:
-    "remove workers from the workers"
-    workers_, _ = self._workers.workers_tasks()
-    for worker in workers:
-        try:
-            workers_.remove(worker)
-        except ValueError:
-            pass
+446
def remove_workers(self, *workers: Worker) -> None:
+    "remove workers from the workers"
+    workers_, _ = self._workers.workers_tasks()
+    for worker in workers:
+        try:
+            workers_.remove(worker)
+        except ValueError:
+            pass
 
@@ -5784,19 +5784,19 @@

Source code in fluid/utils/worker.py -
449
+              
async def startup(self) -> None:
-    """start the workers"""
-    if self._workers_task is None:
-        self._workers_task = asyncio.create_task(self.run(), name=self.worker_name)
-        for args in self._delayed_callbacks:
-            self._delayed_callback(*args)
-        self._delayed_callbacks = []
+454
async def startup(self) -> None:
+    """start the workers"""
+    if self._workers_task is None:
+        self._workers_task = asyncio.create_task(self.run(), name=self.worker_name)
+        for args in self._delayed_callbacks:
+            self._delayed_callback(*args)
+        self._delayed_callbacks = []
 
@@ -5823,7 +5823,8 @@

Source code in fluid/utils/worker.py -
457
+              
456
+457
 458
 459
 460
@@ -5842,28 +5843,27 @@ 

473 474 475 -476 -477

def register_callback(
-    self,
-    callback: Callable[[], None],
-    seconds: float,
-    jitter: float = 0.0,
-    periodic: bool | float = False,
-) -> None:
-    """Register a callback
-
-    The callback can be periodic or not.
-    """
-    if periodic is True:
-        periodic_float = seconds
-    elif periodic is False:
-        periodic_float = 0.0
-    else:
-        periodic_float = periodic
-    if not self.running:
-        self._delayed_callbacks.append((callback, seconds, jitter, periodic_float))
-    else:
-        self._delayed_callback(callback, seconds, jitter, periodic_float)
+476
def register_callback(
+    self,
+    callback: Callable[[], None],
+    seconds: float,
+    jitter: float = 0.0,
+    periodic: bool | float = False,
+) -> None:
+    """Register a callback
+
+    The callback can be periodic or not.
+    """
+    if periodic is True:
+        periodic_float = seconds
+    elif periodic is False:
+        periodic_float = 0.0
+    else:
+        periodic_float = periodic
+    if not self.running:
+        self._delayed_callbacks.append((callback, seconds, jitter, periodic_float))
+    else:
+        self._delayed_callback(callback, seconds, jitter, periodic_float)
 
@@ -5907,7 +5907,8 @@

Source code in fluid/utils/worker.py -
264
+                    
263
+264
 265
 266
 267
@@ -5919,21 +5920,20 @@ 

273 274 275 -276 -277

def __init__(
-    self,
-    *workers: Worker,
-    name: str = "",
-    heartbeat: float | int = 0.1,
-    stopping_grace_period: int = settings.STOPPING_GRACE_PERIOD,
-) -> None:
-    super().__init__(name)
-    self._heartbeat = heartbeat
-    self._workers = WorkerTasks()
-    self._has_shutdown = False
-    self._force_shutdown = False
-    self._stopping_grace_period = stopping_grace_period
-    self.add_workers(*workers)
+276
def __init__(
+    self,
+    *workers: Worker,
+    name: str = "",
+    heartbeat: float | int = 0.1,
+    stopping_grace_period: int = settings.STOPPING_GRACE_PERIOD,
+) -> None:
+    super().__init__(name)
+    self._heartbeat = heartbeat
+    self._workers = WorkerTasks()
+    self._has_shutdown = False
+    self._force_shutdown = False
+    self._stopping_grace_period = stopping_grace_period
+    self.add_workers(*workers)
 
@@ -6012,9 +6012,9 @@

Source code in fluid/utils/worker.py -
async def status(self) -> dict:
-    return await self._workers.status()
+              
async def status(self) -> dict:
+    return await self._workers.status()
 
@@ -6036,9 +6036,9 @@

Source code in fluid/utils/worker.py -
def gracefully_stop(self) -> None:
-    self._workers.gracefully_stop()
+              
def gracefully_stop(self) -> None:
+    self._workers.gracefully_stop()
 
@@ -6060,9 +6060,9 @@

Source code in fluid/utils/worker.py -
def is_running(self) -> bool:
-    return self._running
+              
def is_running(self) -> bool:
+    return self._running
 
@@ -6084,9 +6084,9 @@

Source code in fluid/utils/worker.py -
def is_stopping(self) -> bool:
-    return self._workers.is_stopping()
+              
def is_stopping(self) -> bool:
+    return self._workers.is_stopping()
 
@@ -6108,7 +6108,8 @@

Source code in fluid/utils/worker.py -
85
+              
84
+85
 86
 87
 88
@@ -6117,18 +6118,17 @@ 

91 92 93 -94 -95

@contextmanager
-def start_running(self) -> Generator:
-    if self._running:
-        raise RuntimeError("Worker is already running")
-    self._running = True
-    try:
-        logger.info("%s started running", self.worker_name)
-        yield
-    finally:
-        self._running = False
-        logger.warning("%s stopped running", self.worker_name)
+94
@contextmanager
+def start_running(self) -> Generator:
+    if self._running:
+        raise RuntimeError("Worker is already running")
+    self._running = True
+    try:
+        logger.info("%s started running", self.worker_name)
+        yield
+    finally:
+        self._running = False
+        logger.warning("%s stopped running", self.worker_name)
 
@@ -6150,13 +6150,13 @@

Source code in fluid/utils/worker.py -
301
+              
def create_task(self, worker: Worker) -> asyncio.Task:
-    return asyncio.create_task(
-        self._run_worker(worker), name=f"{self.worker_name}-{worker.worker_name}"
-    )
+303
def create_task(self, worker: Worker) -> asyncio.Task:
+    return asyncio.create_task(
+        self._run_worker(worker), name=f"{self.worker_name}-{worker.worker_name}"
+    )
 
@@ -6184,9 +6184,9 @@

Source code in fluid/utils/worker.py -
async def on_shutdown(self) -> None:
-    """called after the workers are stopped"""
+              
async def on_shutdown(self) -> None:
+    """called after the workers are stopped"""
 
@@ -6214,7 +6214,8 @@

Source code in fluid/utils/worker.py -
309
+              
308
+309
 310
 311
 312
@@ -6246,41 +6247,40 @@ 

338 339 340 -341 -342

async def shutdown(self) -> None:
-    """shutdown the workers"""
-    if self._has_shutdown:
-        return
-    self._has_shutdown = True
-    logger.warning(
-        "gracefully stopping %d workers: %s",
-        self.num_workers,
-        ", ".join(w.worker_name for w in self._workers.workers),
-    )
-    self.gracefully_stop()
-    try:
-        async with async_timeout.timeout(self._stopping_grace_period):
-            await self.wait_for_exit()
-        await self.on_shutdown()
-        return
-    except asyncio.TimeoutError:
-        logger.warning(
-            "could not stop workers %s gracefully after %s"
-            " seconds - force shutdown",
-            ", ".join(
-                task.get_name() for task in self._workers.tasks if not task.done()
-            ),
-            self._stopping_grace_period,
-        )
-    except asyncio.CancelledError:
-        pass
-    self._force_shutdown = True
-    self._workers.cancel()
-    try:
-        await self.wait_for_exit()
-    except asyncio.CancelledError:
-        pass
-    await self.on_shutdown()
+341
async def shutdown(self) -> None:
+    """shutdown the workers"""
+    if self._has_shutdown:
+        return
+    self._has_shutdown = True
+    logger.warning(
+        "gracefully stopping %d workers: %s",
+        self.num_workers,
+        ", ".join(w.worker_name for w in self._workers.workers),
+    )
+    self.gracefully_stop()
+    try:
+        async with asyncio.timeout(self._stopping_grace_period):
+            await self.wait_for_exit()
+        await self.on_shutdown()
+        return
+    except asyncio.TimeoutError:
+        logger.warning(
+            "could not stop workers %s gracefully after %s"
+            " seconds - force shutdown",
+            ", ".join(
+                task.get_name() for task in self._workers.tasks if not task.done()
+            ),
+            self._stopping_grace_period,
+        )
+    except asyncio.CancelledError:
+        pass
+    self._force_shutdown = True
+    self._workers.cancel()
+    try:
+        await self.wait_for_exit()
+    except asyncio.CancelledError:
+        pass
+    await self.on_shutdown()
 
@@ -6302,9 +6302,9 @@

Source code in fluid/utils/worker.py -
def bail_out(self, reason: str, code: int = 1) -> None:
-    self.gracefully_stop()
+              
def bail_out(self, reason: str, code: int = 1) -> None:
+    self.gracefully_stop()
 
@@ -6332,7 +6332,8 @@

Source code in fluid/utils/worker.py -
347
+              
346
+347
 348
 349
 350
@@ -6348,25 +6349,24 @@ 

360 361 362 -363 -364

@asynccontextmanager
-async def safe_run(self) -> AsyncGenerator:
-    """Context manager to run a worker safely"""
-    try:
-        yield
-    except asyncio.CancelledError:
-        if self._force_shutdown:
-            # we are shutting down, this is expected
-            pass
-        raise
-    except Exception as e:
-        reason = f"unhandled exception while running workers: {e}"
-        logger.exception(reason)
-        asyncio.get_event_loop().call_soon(self.bail_out, reason, 2)
-    else:
-        # worker finished without error
-        # make sure we are shutting down
-        asyncio.get_event_loop().call_soon(self.bail_out, "worker exit", 1)
+363
@asynccontextmanager
+async def safe_run(self) -> AsyncGenerator:
+    """Context manager to run a worker safely"""
+    try:
+        yield
+    except asyncio.CancelledError:
+        if self._force_shutdown:
+            # we are shutting down, this is expected
+            pass
+        raise
+    except Exception as e:
+        reason = f"unhandled exception while running workers: {e}"
+        logger.exception(reason)
+        asyncio.get_event_loop().call_soon(self.bail_out, reason, 2)
+    else:
+        # worker finished without error
+        # make sure we are shutting down
+        asyncio.get_event_loop().call_soon(self.bail_out, "worker exit", 1)
 
@@ -6391,23 +6391,23 @@

Source code in fluid/utils/worker.py -
372
+              
def add_workers(self, *workers: Worker) -> None:
-    """add workers to the workers
-
-    They can be added while the workers are running.
-    """
-    workers_, tasks_ = self._workers.workers_tasks()
-    for worker in workers:
-        workers_.append(worker)
-        tasks_.append(self.create_task(worker))
+379
def add_workers(self, *workers: Worker) -> None:
+    """add workers to the workers
+
+    They can be added while the workers are running.
+    """
+    workers_, tasks_ = self._workers.workers_tasks()
+    for worker in workers:
+        workers_.append(worker)
+        tasks_.append(self.create_task(worker))
 
@@ -6433,21 +6433,21 @@

Source code in fluid/utils/worker.py -
382
+              
async def run(self) -> None:
-    with self.start_running():
-        while not self.is_stopping():
-            for worker, task in zip(self._workers.workers, self._workers.tasks):
-                if worker.is_stopping() or task.done():
-                    break
-            await asyncio.sleep(self._heartbeat)
-        await self.shutdown()
+388
async def run(self) -> None:
+    with self.start_running():
+        while not self.is_stopping():
+            for worker, task in zip(self._workers.workers, self._workers.tasks):
+                if worker.is_stopping() or task.done():
+                    break
+            await asyncio.sleep(self._heartbeat)
+        await self.shutdown()
 
@@ -6473,9 +6473,9 @@

Source code in fluid/utils/worker.py -
async def wait_for_exit(self) -> None:
-    await asyncio.gather(*self._workers.tasks)
+              
async def wait_for_exit(self) -> None:
+    await asyncio.gather(*self._workers.tasks)
 
diff --git a/search/search_index.json b/search/search_index.json index 5f30676..5de5d46 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":"

Aio Fluid

Async utilities for backend python services developed by Quantmind.

Documentation: https://quantmind.github.io/aio-fluid

Source Code: https://github.com/quantmind/aio-fluid

"},{"location":"#installation","title":"Installation","text":"

This is a simple python package you can install via pip:

pip install aio-fluid\n

To install all the dependencies:

pip install aio-fluid[cli, db, http, log]\n
this includes the following extra dependencies:

  • cli for the command line interface using click and rich
  • db for database support with asyncpg and sqlalchemy
  • http for http client support with httpx and aiohttp
  • log for JSON logging support with python-json-logger
"},{"location":"#development","title":"Development","text":"

You can run the examples via

poetry run python -m examples.main\n
"},{"location":"reference/","title":"Introduction","text":"

Here's the reference or code API, the classes, functions, parameters, attributes, and all the aio-fluid parts you can use in your applications.

"},{"location":"reference/dispatchers/","title":"Event Dispatchers","text":"

A set of classes for dispatching events, they can be imported from fluid.utils.dispatcher:

from fluid.utils.dispatcher import Dispatcher\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.BaseDispatcher","title":"fluid.utils.dispatcher.BaseDispatcher","text":"
BaseDispatcher()\n

Bases: Generic[MessageType, MessageHandlerType], ABC

Source code in fluid/utils/dispatcher.py
def __init__(self) -> None:\n    self._msg_handlers: defaultdict[str, dict[str, MessageHandlerType]] = (\n        defaultdict(\n            dict,\n        )\n    )\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.BaseDispatcher.register_handler","title":"register_handler","text":"
register_handler(event, handler)\n
Source code in fluid/utils/dispatcher.py
def register_handler(\n    self,\n    event: Event | str,\n    handler: MessageHandlerType,\n) -> MessageHandlerType | None:\n    event = Event.from_string_or_event(event)\n    previous = self._msg_handlers[event.type].get(event.tag)\n    self._msg_handlers[event.type][event.tag] = handler\n    return previous\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.BaseDispatcher.unregister_handler","title":"unregister_handler","text":"
unregister_handler(event)\n
Source code in fluid/utils/dispatcher.py
def unregister_handler(self, event: Event | str) -> MessageHandlerType | None:\n    event = Event.from_string_or_event(event)\n    return self._msg_handlers[event.type].pop(event.tag, None)\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.BaseDispatcher.get_handlers","title":"get_handlers","text":"
get_handlers(message)\n
Source code in fluid/utils/dispatcher.py
def get_handlers(\n    self,\n    message: MessageType,\n) -> dict[str, MessageHandlerType] | None:\n    message_type = str(self.message_type(message))\n    return self._msg_handlers.get(message_type)\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.BaseDispatcher.message_type","title":"message_type abstractmethod","text":"
message_type(message)\n

return the message type

Source code in fluid/utils/dispatcher.py
@abstractmethod\ndef message_type(self, message: MessageType) -> str:\n    \"\"\"return the message type\"\"\"\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.Dispatcher","title":"fluid.utils.dispatcher.Dispatcher","text":"
Dispatcher()\n

Bases: BaseDispatcher[MessageType, Callable[[MessageType], None]]

Dispatcher for sync handlers

Source code in fluid/utils/dispatcher.py
def __init__(self) -> None:\n    self._msg_handlers: defaultdict[str, dict[str, MessageHandlerType]] = (\n        defaultdict(\n            dict,\n        )\n    )\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.Dispatcher.register_handler","title":"register_handler","text":"
register_handler(event, handler)\n
Source code in fluid/utils/dispatcher.py
def register_handler(\n    self,\n    event: Event | str,\n    handler: MessageHandlerType,\n) -> MessageHandlerType | None:\n    event = Event.from_string_or_event(event)\n    previous = self._msg_handlers[event.type].get(event.tag)\n    self._msg_handlers[event.type][event.tag] = handler\n    return previous\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.Dispatcher.unregister_handler","title":"unregister_handler","text":"
unregister_handler(event)\n
Source code in fluid/utils/dispatcher.py
def unregister_handler(self, event: Event | str) -> MessageHandlerType | None:\n    event = Event.from_string_or_event(event)\n    return self._msg_handlers[event.type].pop(event.tag, None)\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.Dispatcher.get_handlers","title":"get_handlers","text":"
get_handlers(message)\n
Source code in fluid/utils/dispatcher.py
def get_handlers(\n    self,\n    message: MessageType,\n) -> dict[str, MessageHandlerType] | None:\n    message_type = str(self.message_type(message))\n    return self._msg_handlers.get(message_type)\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.Dispatcher.message_type","title":"message_type abstractmethod","text":"
message_type(message)\n

return the message type

Source code in fluid/utils/dispatcher.py
@abstractmethod\ndef message_type(self, message: MessageType) -> str:\n    \"\"\"return the message type\"\"\"\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.Dispatcher.dispatch","title":"dispatch","text":"
dispatch(message)\n

dispatch the message

Source code in fluid/utils/dispatcher.py
def dispatch(self, message: MessageType) -> int:\n    \"\"\"dispatch the message\"\"\"\n    handlers = self.get_handlers(message)\n    if handlers:\n        for handler in handlers.values():\n            handler(message)\n    return len(handlers or ())\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.AsyncDispatcher","title":"fluid.utils.dispatcher.AsyncDispatcher","text":"
AsyncDispatcher()\n

Bases: BaseDispatcher[MessageType, Callable[[MessageType], Awaitable[None]]]

Dispatcher for async handlers

Source code in fluid/utils/dispatcher.py
def __init__(self) -> None:\n    self._msg_handlers: defaultdict[str, dict[str, MessageHandlerType]] = (\n        defaultdict(\n            dict,\n        )\n    )\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.AsyncDispatcher.register_handler","title":"register_handler","text":"
register_handler(event, handler)\n
Source code in fluid/utils/dispatcher.py
def register_handler(\n    self,\n    event: Event | str,\n    handler: MessageHandlerType,\n) -> MessageHandlerType | None:\n    event = Event.from_string_or_event(event)\n    previous = self._msg_handlers[event.type].get(event.tag)\n    self._msg_handlers[event.type][event.tag] = handler\n    return previous\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.AsyncDispatcher.unregister_handler","title":"unregister_handler","text":"
unregister_handler(event)\n
Source code in fluid/utils/dispatcher.py
def unregister_handler(self, event: Event | str) -> MessageHandlerType | None:\n    event = Event.from_string_or_event(event)\n    return self._msg_handlers[event.type].pop(event.tag, None)\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.AsyncDispatcher.get_handlers","title":"get_handlers","text":"
get_handlers(message)\n
Source code in fluid/utils/dispatcher.py
def get_handlers(\n    self,\n    message: MessageType,\n) -> dict[str, MessageHandlerType] | None:\n    message_type = str(self.message_type(message))\n    return self._msg_handlers.get(message_type)\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.AsyncDispatcher.message_type","title":"message_type abstractmethod","text":"
message_type(message)\n

return the message type

Source code in fluid/utils/dispatcher.py
@abstractmethod\ndef message_type(self, message: MessageType) -> str:\n    \"\"\"return the message type\"\"\"\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.AsyncDispatcher.dispatch","title":"dispatch async","text":"
dispatch(message)\n

Dispatch the message and wait for all handlers to complete

Source code in fluid/utils/dispatcher.py
async def dispatch(self, message: MessageType) -> int:\n    \"\"\"Dispatch the message and wait for all handlers to complete\"\"\"\n    handlers = self.get_handlers(message)\n    if handlers:\n        await asyncio.gather(*[handler(message) for handler in handlers.values()])\n    return len(handlers or ())\n
"},{"location":"reference/task_broker/","title":"Task Broker","text":"

It can be imported from fluid.scheduler:

from fastapi.scheduler import TaskBroker\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker","title":"fluid.scheduler.TaskBroker","text":"
TaskBroker(url)\n

Bases: ABC

Source code in fluid/scheduler/broker.py
def __init__(self, url: URL) -> None:\n    self.url: URL = url\n    self.registry: TaskRegistry = TaskRegistry()\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.url","title":"url instance-attribute","text":"
url = url\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.registry","title":"registry instance-attribute","text":"
registry = TaskRegistry()\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.task_queue_names","title":"task_queue_names abstractmethod property","text":"
task_queue_names\n

Names of the task queues

"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.queue_task","title":"queue_task abstractmethod async","text":"
queue_task(task_run)\n

Queue a task

Source code in fluid/scheduler/broker.py
@abstractmethod\nasync def queue_task(self, task_run: TaskRun) -> None:\n    \"\"\"Queue a task\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.get_task_run","title":"get_task_run abstractmethod async","text":"
get_task_run(task_manager)\n

Get a Task run from the task queue

Source code in fluid/scheduler/broker.py
@abstractmethod\nasync def get_task_run(self, task_manager: TaskManager) -> TaskRun | None:\n    \"\"\"Get a Task run from the task queue\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.queue_length","title":"queue_length abstractmethod async","text":"
queue_length()\n

Length of task queues

Source code in fluid/scheduler/broker.py
@abstractmethod\nasync def queue_length(self) -> dict[str, int]:\n    \"\"\"Length of task queues\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.get_tasks_info","title":"get_tasks_info abstractmethod async","text":"
get_tasks_info(*task_names)\n

List of TaskInfo objects

Source code in fluid/scheduler/broker.py
@abstractmethod\nasync def get_tasks_info(self, *task_names: str) -> list[TaskInfo]:\n    \"\"\"List of TaskInfo objects\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.update_task","title":"update_task abstractmethod async","text":"
update_task(task, params)\n

Update a task dynamic parameters

Source code in fluid/scheduler/broker.py
@abstractmethod\nasync def update_task(self, task: Task, params: dict[str, Any]) -> TaskInfo:\n    \"\"\"Update a task dynamic parameters\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.close","title":"close abstractmethod async","text":"
close()\n

Close the broker on shutdown

Source code in fluid/scheduler/broker.py
@abstractmethod\nasync def close(self) -> None:\n    \"\"\"Close the broker on shutdown\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.lock","title":"lock abstractmethod","text":"
lock(name, timeout=None)\n

Create a lock

Source code in fluid/scheduler/broker.py
@abstractmethod\ndef lock(self, name: str, timeout: float | None = None) -> Lock:\n    \"\"\"Create a lock\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.new_uuid","title":"new_uuid","text":"
new_uuid()\n
Source code in fluid/scheduler/broker.py
def new_uuid(self) -> str:\n    return uuid4().hex\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.filter_tasks","title":"filter_tasks async","text":"
filter_tasks(scheduled=None, enabled=None)\n
Source code in fluid/scheduler/broker.py
async def filter_tasks(\n    self,\n    scheduled: bool | None = None,\n    enabled: bool | None = None,\n) -> list[Task]:\n    task_info = await self.get_tasks_info()\n    task_map = {info.name: info for info in task_info}\n    tasks = []\n    for task in self.registry.values():\n        if scheduled is not None and bool(task.schedule) is not scheduled:\n            continue\n        if enabled is not None and task_map[task.name].enabled is not enabled:\n            continue\n        tasks.append(task)\n    return tasks\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.task_from_registry","title":"task_from_registry","text":"
task_from_registry(task)\n
Source code in fluid/scheduler/broker.py
def task_from_registry(self, task: str | Task) -> Task:\n    if isinstance(task, Task):\n        self.register_task(task)\n        return task\n    else:\n        if task_ := self.registry.get(task):\n            return task_\n        raise UnknownTaskError(task)\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.register_task","title":"register_task","text":"
register_task(task)\n
Source code in fluid/scheduler/broker.py
def register_task(self, task: Task) -> None:\n    self.registry[task.name] = task\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.enable_task","title":"enable_task async","text":"
enable_task(task_name, enable=True)\n

Enable or disable a registered task

Source code in fluid/scheduler/broker.py
async def enable_task(self, task_name: str, enable: bool = True) -> TaskInfo:\n    \"\"\"Enable or disable a registered task\"\"\"\n    task = self.registry.get(task_name)\n    if not task:\n        raise UnknownTaskError(task_name)\n    return await self.update_task(task, dict(enabled=enable))\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.from_url","title":"from_url classmethod","text":"
from_url(url='')\n
Source code in fluid/scheduler/broker.py
@classmethod\ndef from_url(cls, url: str = \"\") -> TaskBroker:\n    p = URL(url or broker_url_from_env())\n    if factory := _brokers.get(p.scheme):\n        return factory(p)\n    raise RuntimeError(f\"Invalid broker {p}\")\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.register_broker","title":"register_broker classmethod","text":"
register_broker(name, factory)\n
Source code in fluid/scheduler/broker.py
@classmethod\ndef register_broker(cls, name: str, factory: type[TaskBroker]) -> None:\n    _brokers[name] = factory\n
"},{"location":"reference/task_manager/","title":"Task Manager","text":"

It can be imported from fluid.scheduler:

from fastapi.scheduler import TaskManager\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager","title":"fluid.scheduler.TaskManager","text":"
TaskManager(**kwargs)\n

The task manager is the main entry point for managing tasks

Source code in fluid/scheduler/consumer.py
def __init__(self, **kwargs: Any) -> None:\n    self.state: dict[str, Any] = {}\n    self.config: TaskManagerConfig = TaskManagerConfig(**kwargs)\n    self.dispatcher: Annotated[\n        TaskDispatcher,\n        Doc(\n            \"\"\"\n            A dispatcher of task run events.\n\n            Register handlers to listen for task run events.\n            \"\"\"\n        ),\n    ] = TaskDispatcher()\n    self.broker = TaskBroker.from_url(self.config.broker_url)\n    self._stack = AsyncExitStack()\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.state","title":"state instance-attribute","text":"
state = {}\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.config","title":"config instance-attribute","text":"
config = TaskManagerConfig(**kwargs)\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.dispatcher","title":"dispatcher instance-attribute","text":"
dispatcher = TaskDispatcher()\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.broker","title":"broker instance-attribute","text":"
broker = from_url(broker_url)\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.registry","title":"registry property","text":"
registry\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.type","title":"type property","text":"
type\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.enter_async_context","title":"enter_async_context async","text":"
enter_async_context(cm)\n
Source code in fluid/scheduler/consumer.py
async def enter_async_context(self, cm: Any) -> Any:\n    return await self._stack.enter_async_context(cm)\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.execute","title":"execute async","text":"
execute(task, **params)\n

Execute a task and wait for it to finish

Source code in fluid/scheduler/consumer.py
async def execute(self, task: Task | str, **params: Any) -> TaskRun:\n    \"\"\"Execute a task and wait for it to finish\"\"\"\n    task_run = self.create_task_run(task, **params)\n    await task_run.execute()\n    return task_run\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.on_shutdown","title":"on_shutdown async","text":"
on_shutdown()\n
Source code in fluid/scheduler/consumer.py
async def on_shutdown(self) -> None:\n    await self.broker.close()\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.execute_sync","title":"execute_sync","text":"
execute_sync(task, **params)\n
Source code in fluid/scheduler/consumer.py
def execute_sync(self, task: Task | str, **params: Any) -> TaskRun:\n    return asyncio.run(self._execute_and_exit(task, **params))\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.register_task","title":"register_task","text":"
register_task(task)\n

Register a task with the task manager

Only tasks registered can be executed by a task manager

Source code in fluid/scheduler/consumer.py
def register_task(self, task: Task) -> None:\n    \"\"\"Register a task with the task manager\n\n    Only tasks registered can be executed by a task manager\n    \"\"\"\n    self.broker.register_task(task)\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.queue","title":"queue async","text":"
queue(task, priority=None, **params)\n

Queue a task for execution

This methods fires two events:

  • queue: when the task is about to be queued
  • queued: after the task is queued
Source code in fluid/scheduler/consumer.py
async def queue(\n    self,\n    task: str | Task,\n    priority: TaskPriority | None = None,\n    **params: Any,\n) -> TaskRun:\n    \"\"\"Queue a task for execution\n\n    This methods fires two events:\n\n    - queue: when the task is about to be queued\n    - queued: after the task is queued\n    \"\"\"\n    task_run = self.create_task_run(task, priority=priority, **params)\n    self.dispatcher.dispatch(task_run)\n    task_run.set_state(TaskState.queued)\n    await self.broker.queue_task(task_run)\n    return task_run\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.create_task_run","title":"create_task_run","text":"
create_task_run(task, run_id='', priority=None, **params)\n

Create a TaskRun in init state

Source code in fluid/scheduler/consumer.py
def create_task_run(\n    self,\n    task: str | Task,\n    run_id: str = \"\",\n    priority: TaskPriority | None = None,\n    **params: Any,\n) -> TaskRun:\n    \"\"\"Create a TaskRun in `init` state\"\"\"\n    if isinstance(task, str):\n        task = self.broker.task_from_registry(task)\n    run_id = run_id or self.broker.new_uuid()\n    return TaskRun(\n        id=run_id,\n        task=task,\n        priority=priority or task.priority,\n        params=params,\n        task_manager=self,\n    )\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.register_from_module","title":"register_from_module","text":"
register_from_module(module)\n
Source code in fluid/scheduler/consumer.py
def register_from_module(self, module: Any) -> None:\n    for name in dir(module):\n        if name.startswith(\"_\"):\n            continue\n        if isinstance(obj := getattr(module, name), Task):\n            self.register_task(obj)\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.register_async_handler","title":"register_async_handler","text":"
register_async_handler(event, handler)\n

Register an async handler for a given event

This method is a no op for a TaskManager that is not a worker

Source code in fluid/scheduler/consumer.py
def register_async_handler(self, event: str, handler: AsyncHandler) -> None:\n    \"\"\"Register an async handler for a given event\n\n    This method is a no op for a TaskManager that is not a worker\n    \"\"\"\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.unregister_async_handler","title":"unregister_async_handler","text":"
unregister_async_handler(event)\n

Unregister an async handler for a given event

This method is a no op for a TaskManager that is not a worker

Source code in fluid/scheduler/consumer.py
def unregister_async_handler(self, event: Event | str) -> AsyncHandler | None:\n    \"\"\"Unregister an async handler for a given event\n\n    This method is a no op for a TaskManager that is not a worker\n    \"\"\"\n    return None\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.cli","title":"cli","text":"
cli(**kwargs)\n

Create the task manager command line interface

Source code in fluid/scheduler/consumer.py
def cli(self, **kwargs: Any) -> Any:\n    \"\"\"Create the task manager command line interface\"\"\"\n    try:\n        from fluid.scheduler.cli import TaskManagerCLI\n    except ImportError:\n        raise ImportError(\n            \"TaskManagerCLI is not available - \"\n            \"install with `pip install aio-fluid[cli]`\"\n        ) from None\n    return TaskManagerCLI(self, **kwargs)\n
"},{"location":"reference/task_run/","title":"Task Run","text":"

It can be imported from fluid.scheduler:

from fastapi.scheduler import TaskRun\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun","title":"fluid.scheduler.TaskRun","text":"

Bases: BaseModel

A TaskRun contains all the data generated by a Task run

"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.id","title":"id instance-attribute","text":"
id\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.task","title":"task instance-attribute","text":"
task\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.priority","title":"priority instance-attribute","text":"
priority\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.params","title":"params instance-attribute","text":"
params\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.state","title":"state class-attribute instance-attribute","text":"
state = init\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.task_manager","title":"task_manager class-attribute instance-attribute","text":"
task_manager = Field(exclude=True, repr=False)\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.queued","title":"queued class-attribute instance-attribute","text":"
queued = None\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.start","title":"start class-attribute instance-attribute","text":"
start = None\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.end","title":"end class-attribute instance-attribute","text":"
end = None\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.logger","title":"logger property","text":"
logger\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.in_queue","title":"in_queue property","text":"
in_queue\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.duration","title":"duration property","text":"
duration\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.duration_ms","title":"duration_ms property","text":"
duration_ms\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.total","title":"total property","text":"
total\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.name","title":"name property","text":"
name\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.name_id","title":"name_id property","text":"
name_id\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.is_done","title":"is_done property","text":"
is_done\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.is_failure","title":"is_failure property","text":"
is_failure\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.execute","title":"execute async","text":"
execute()\n
Source code in fluid/scheduler/models.py
async def execute(self) -> None:\n    try:\n        self.set_state(TaskState.running)\n        await self.task.executor(self)\n    except Exception:\n        self.set_state(TaskState.failure)\n        raise\n    else:\n        self.set_state(TaskState.success)\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.serialize_task","title":"serialize_task","text":"
serialize_task(task, _info)\n
Source code in fluid/scheduler/models.py
@field_serializer(\"task\")\ndef serialize_task(self, task: Task, _info: Any) -> str:\n    return task.name\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.params_dump_json","title":"params_dump_json","text":"
params_dump_json()\n
Source code in fluid/scheduler/models.py
def params_dump_json(self) -> str:\n    return self.task.params_dump_json(self.params)\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.set_state","title":"set_state","text":"
set_state(state, state_time=None)\n
Source code in fluid/scheduler/models.py
def set_state(\n    self,\n    state: TaskState,\n    state_time: datetime | None = None,\n) -> None:\n    if self.state == state:\n        return\n    state_time = as_utc(state_time)\n    match (self.state, state):\n        case (TaskState.init, TaskState.queued):\n            self.queued = state_time\n            self.state = state\n            self._dispatch()\n        case (TaskState.init, _):\n            self.set_state(TaskState.queued, state_time)\n            self.set_state(state, state_time)\n        case (TaskState.queued, TaskState.running):\n            self.start = state_time\n            self.state = state\n            self._dispatch()\n        case (\n            TaskState.queued,\n            TaskState.success\n            | TaskState.aborted\n            | TaskState.rate_limited\n            | TaskState.failure,\n        ):\n            self.set_state(TaskState.running, state_time)\n            self.set_state(state, state_time)\n        case (\n            TaskState.running,\n            TaskState.success\n            | TaskState.aborted\n            | TaskState.rate_limited\n            | TaskState.failure,\n        ):\n            self.end = state_time\n            self.state = state\n            self._dispatch()\n        case _:\n            raise TaskRunError(f\"invalid state transition {self.state} -> {state}\")\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.lock","title":"lock","text":"
lock(timeout)\n
Source code in fluid/scheduler/models.py
def lock(self, timeout: float | None) -> Lock:\n    return self.task_manager.broker.lock(self.name, timeout=timeout)\n
"},{"location":"reference/tast_consumer/","title":"Task Consumer","text":"

The task consumer is a TaskManager which is also a Workers that consumes tasks from the task queue and executes them. It can be imported from fluid.scheduler:

from fastapi.scheduler import TaskConsumer\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer","title":"fluid.scheduler.TaskConsumer","text":"
TaskConsumer(**config)\n

Bases: TaskManager, Workers

The Task Consumer is a Task Manager responsible for consuming tasks from a task queue

Source code in fluid/scheduler/consumer.py
def __init__(self, **config: Any) -> None:\n    super().__init__(**config)\n    Workers.__init__(self)\n    self._async_dispatcher_worker = AsyncConsumer(AsyncTaskDispatcher())\n    self._concurrent_tasks: dict[str, dict[str, TaskRun]] = defaultdict(dict)\n    self._task_to_queue: deque[str | Task] = deque()\n    self._priority_task_run_queue: deque[TaskRun] = deque()\n    self._queue_tasks_worker = WorkerFunction(\n        self._queue_task, name=\"queue-task-worker\"\n    )\n    self.add_workers(self._queue_tasks_worker)\n    self.add_workers(self._async_dispatcher_worker)\n    for i in range(self.config.max_concurrent_tasks):\n        worker_name = f\"task-worker-{i+1}\"\n        self.add_workers(\n            WorkerFunction(\n                partial(self._consume_tasks, worker_name), name=worker_name\n            )\n        )\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.num_workers","title":"num_workers property","text":"
num_workers\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.running","title":"running property","text":"
running\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.state","title":"state instance-attribute","text":"
state = {}\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.config","title":"config instance-attribute","text":"
config = TaskManagerConfig(**kwargs)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.dispatcher","title":"dispatcher instance-attribute","text":"
dispatcher = TaskDispatcher()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.broker","title":"broker instance-attribute","text":"
broker = from_url(broker_url)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.registry","title":"registry property","text":"
registry\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.type","title":"type property","text":"
type\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.num_concurrent_tasks","title":"num_concurrent_tasks property","text":"
num_concurrent_tasks\n

The number of concurrent_tasks running in the consumer

"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    return await self._workers.status()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._workers.gracefully_stop()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._workers.is_stopping()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.run","title":"run async","text":"
run()\n

run the workers

Source code in fluid/utils/worker.py
async def run(self) -> None:\n    \"\"\"run the workers\"\"\"\n    with self.start_running():\n        async with self.safe_run():\n            workers, _ = self._workers.workers_tasks()\n            self._workers.workers = tuple(workers)\n            self._workers.tasks = tuple(\n                self.create_task(worker) for worker in workers\n            )\n            await asyncio.gather(*self._workers.tasks)\n        await self.shutdown()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.add_workers","title":"add_workers","text":"
add_workers(*workers)\n

add workers to the workers

Source code in fluid/utils/worker.py
def add_workers(self, *workers: Worker) -> None:\n    \"\"\"add workers to the workers\"\"\"\n    workers_, _ = self._workers.workers_tasks()\n    for worker in workers:\n        if worker not in workers_:\n            workers_.append(worker)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.wait_for_exit","title":"wait_for_exit async","text":"
wait_for_exit()\n
Source code in fluid/utils/worker.py
async def wait_for_exit(self) -> None:\n    if self._workers_task is not None:\n        await self._workers_task\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.create_task","title":"create_task","text":"
create_task(worker)\n
Source code in fluid/utils/worker.py
def create_task(self, worker: Worker) -> asyncio.Task:\n    return asyncio.create_task(\n        self._run_worker(worker), name=f\"{self.worker_name}-{worker.worker_name}\"\n    )\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.on_shutdown","title":"on_shutdown async","text":"
on_shutdown()\n
Source code in fluid/scheduler/consumer.py
async def on_shutdown(self) -> None:\n    await self.broker.close()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.shutdown","title":"shutdown async","text":"
shutdown()\n

shutdown the workers

Source code in fluid/utils/worker.py
async def shutdown(self) -> None:\n    \"\"\"shutdown the workers\"\"\"\n    if self._has_shutdown:\n        return\n    self._has_shutdown = True\n    logger.warning(\n        \"gracefully stopping %d workers: %s\",\n        self.num_workers,\n        \", \".join(w.worker_name for w in self._workers.workers),\n    )\n    self.gracefully_stop()\n    try:\n        async with async_timeout.timeout(self._stopping_grace_period):\n            await self.wait_for_exit()\n        await self.on_shutdown()\n        return\n    except asyncio.TimeoutError:\n        logger.warning(\n            \"could not stop workers %s gracefully after %s\"\n            \" seconds - force shutdown\",\n            \", \".join(\n                task.get_name() for task in self._workers.tasks if not task.done()\n            ),\n            self._stopping_grace_period,\n        )\n    except asyncio.CancelledError:\n        pass\n    self._force_shutdown = True\n    self._workers.cancel()\n    try:\n        await self.wait_for_exit()\n    except asyncio.CancelledError:\n        pass\n    await self.on_shutdown()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.bail_out","title":"bail_out","text":"
bail_out(reason, code=1)\n
Source code in fluid/utils/worker.py
def bail_out(self, reason: str, code: int = 1) -> None:\n    self.gracefully_stop()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.safe_run","title":"safe_run async","text":"
safe_run()\n

Context manager to run a worker safely

Source code in fluid/utils/worker.py
@asynccontextmanager\nasync def safe_run(self) -> AsyncGenerator:\n    \"\"\"Context manager to run a worker safely\"\"\"\n    try:\n        yield\n    except asyncio.CancelledError:\n        if self._force_shutdown:\n            # we are shutting down, this is expected\n            pass\n        raise\n    except Exception as e:\n        reason = f\"unhandled exception while running workers: {e}\"\n        logger.exception(reason)\n        asyncio.get_event_loop().call_soon(self.bail_out, reason, 2)\n    else:\n        # worker finished without error\n        # make sure we are shutting down\n        asyncio.get_event_loop().call_soon(self.bail_out, \"worker exit\", 1)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.remove_workers","title":"remove_workers","text":"
remove_workers(*workers)\n

remove workers from the workers

Source code in fluid/utils/worker.py
def remove_workers(self, *workers: Worker) -> None:\n    \"remove workers from the workers\"\n    workers_, _ = self._workers.workers_tasks()\n    for worker in workers:\n        try:\n            workers_.remove(worker)\n        except ValueError:\n            pass\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.startup","title":"startup async","text":"
startup()\n

start the workers

Source code in fluid/utils/worker.py
async def startup(self) -> None:\n    \"\"\"start the workers\"\"\"\n    if self._workers_task is None:\n        self._workers_task = asyncio.create_task(self.run(), name=self.worker_name)\n        for args in self._delayed_callbacks:\n            self._delayed_callback(*args)\n        self._delayed_callbacks = []\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.register_callback","title":"register_callback","text":"
register_callback(\n    callback, seconds, jitter=0.0, periodic=False\n)\n

Register a callback

The callback can be periodic or not.

Source code in fluid/utils/worker.py
def register_callback(\n    self,\n    callback: Callable[[], None],\n    seconds: float,\n    jitter: float = 0.0,\n    periodic: bool | float = False,\n) -> None:\n    \"\"\"Register a callback\n\n    The callback can be periodic or not.\n    \"\"\"\n    if periodic is True:\n        periodic_float = seconds\n    elif periodic is False:\n        periodic_float = 0.0\n    else:\n        periodic_float = periodic\n    if not self.running:\n        self._delayed_callbacks.append((callback, seconds, jitter, periodic_float))\n    else:\n        self._delayed_callback(callback, seconds, jitter, periodic_float)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.enter_async_context","title":"enter_async_context async","text":"
enter_async_context(cm)\n
Source code in fluid/scheduler/consumer.py
async def enter_async_context(self, cm: Any) -> Any:\n    return await self._stack.enter_async_context(cm)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.execute","title":"execute async","text":"
execute(task, **params)\n

Execute a task and wait for it to finish

Source code in fluid/scheduler/consumer.py
async def execute(self, task: Task | str, **params: Any) -> TaskRun:\n    \"\"\"Execute a task and wait for it to finish\"\"\"\n    task_run = self.create_task_run(task, **params)\n    await task_run.execute()\n    return task_run\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.execute_sync","title":"execute_sync","text":"
execute_sync(task, **params)\n
Source code in fluid/scheduler/consumer.py
def execute_sync(self, task: Task | str, **params: Any) -> TaskRun:\n    return asyncio.run(self._execute_and_exit(task, **params))\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.register_task","title":"register_task","text":"
register_task(task)\n

Register a task with the task manager

Only tasks registered can be executed by a task manager

Source code in fluid/scheduler/consumer.py
def register_task(self, task: Task) -> None:\n    \"\"\"Register a task with the task manager\n\n    Only tasks registered can be executed by a task manager\n    \"\"\"\n    self.broker.register_task(task)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.queue","title":"queue async","text":"
queue(task, priority=None, **params)\n

Queue a task for execution

This methods fires two events:

  • queue: when the task is about to be queued
  • queued: after the task is queued
Source code in fluid/scheduler/consumer.py
async def queue(\n    self,\n    task: str | Task,\n    priority: TaskPriority | None = None,\n    **params: Any,\n) -> TaskRun:\n    \"\"\"Queue a task for execution\n\n    This methods fires two events:\n\n    - queue: when the task is about to be queued\n    - queued: after the task is queued\n    \"\"\"\n    task_run = self.create_task_run(task, priority=priority, **params)\n    self.dispatcher.dispatch(task_run)\n    task_run.set_state(TaskState.queued)\n    await self.broker.queue_task(task_run)\n    return task_run\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.create_task_run","title":"create_task_run","text":"
create_task_run(task, run_id='', priority=None, **params)\n

Create a TaskRun in init state

Source code in fluid/scheduler/consumer.py
def create_task_run(\n    self,\n    task: str | Task,\n    run_id: str = \"\",\n    priority: TaskPriority | None = None,\n    **params: Any,\n) -> TaskRun:\n    \"\"\"Create a TaskRun in `init` state\"\"\"\n    if isinstance(task, str):\n        task = self.broker.task_from_registry(task)\n    run_id = run_id or self.broker.new_uuid()\n    return TaskRun(\n        id=run_id,\n        task=task,\n        priority=priority or task.priority,\n        params=params,\n        task_manager=self,\n    )\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.register_from_module","title":"register_from_module","text":"
register_from_module(module)\n
Source code in fluid/scheduler/consumer.py
def register_from_module(self, module: Any) -> None:\n    for name in dir(module):\n        if name.startswith(\"_\"):\n            continue\n        if isinstance(obj := getattr(module, name), Task):\n            self.register_task(obj)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.cli","title":"cli","text":"
cli(**kwargs)\n

Create the task manager command line interface

Source code in fluid/scheduler/consumer.py
def cli(self, **kwargs: Any) -> Any:\n    \"\"\"Create the task manager command line interface\"\"\"\n    try:\n        from fluid.scheduler.cli import TaskManagerCLI\n    except ImportError:\n        raise ImportError(\n            \"TaskManagerCLI is not available - \"\n            \"install with `pip install aio-fluid[cli]`\"\n        ) from None\n    return TaskManagerCLI(self, **kwargs)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.sync_queue","title":"sync_queue","text":"
sync_queue(task)\n
Source code in fluid/scheduler/consumer.py
def sync_queue(self, task: str | Task) -> None:\n    self._task_to_queue.appendleft(task)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.sync_priority_queue","title":"sync_priority_queue","text":"
sync_priority_queue(task)\n
Source code in fluid/scheduler/consumer.py
def sync_priority_queue(self, task: str | Task) -> None:\n    self._priority_task_run_queue.appendleft(self.create_task_run(task))\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.num_concurrent_tasks_for","title":"num_concurrent_tasks_for","text":"
num_concurrent_tasks_for(task_name)\n

The number of concurrent tasks for a given task_name

Source code in fluid/scheduler/consumer.py
def num_concurrent_tasks_for(self, task_name: str) -> int:\n    \"\"\"The number of concurrent tasks for a given task_name\"\"\"\n    return len(self._concurrent_tasks[task_name])\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.queue_and_wait","title":"queue_and_wait async","text":"
queue_and_wait(task, *, timeout=2, **params)\n

Queue a task and wait for it to finish

Source code in fluid/scheduler/consumer.py
async def queue_and_wait(\n    self, task: str, *, timeout: int = 2, **params: Any\n) -> TaskRun:\n    \"\"\"Queue a task and wait for it to finish\"\"\"\n    with TaskRunWaiter(self) as waiter:\n        return await waiter.wait(await self.queue(task, **params), timeout=timeout)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.register_async_handler","title":"register_async_handler","text":"
register_async_handler(event, handler)\n
Source code in fluid/scheduler/consumer.py
def register_async_handler(self, event: Event | str, handler: AsyncHandler) -> None:\n    event = Event.from_string_or_event(event)\n    self.dispatcher.register_handler(\n        f\"{event.type}.async_dispatch\",\n        self._async_dispatcher_worker.send,\n    )\n    self._async_dispatcher_worker.dispatcher.register_handler(event, handler)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.unregister_async_handler","title":"unregister_async_handler","text":"
unregister_async_handler(event)\n
Source code in fluid/scheduler/consumer.py
def unregister_async_handler(self, event: Event | str) -> AsyncHandler | None:\n    return self._async_dispatcher_worker.dispatcher.unregister_handler(event)\n
"},{"location":"reference/workers/","title":"Workers","text":"

Workers are the main building block for asynchronous programming with aio-fluid. They are responsible for running tasks and managing their lifecycle. There are several worker classes which can be imported from fluid.utils.worker:

from fastapi.utils.worker import StoppingWorker\n
"},{"location":"reference/workers/#fluid.utils.worker.Worker","title":"fluid.utils.worker.Worker","text":"
Worker(name='')\n

Bases: ABC

The base class of a worker that can be run

Source code in fluid/utils/worker.py
def __init__(self, name: str = \"\") -> None:\n    self._name: str = name or underscore(type(self).__name__)\n
"},{"location":"reference/workers/#fluid.utils.worker.Worker.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.Worker.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.Worker.status","title":"status abstractmethod async","text":"
status()\n

Get the status of the worker.

Source code in fluid/utils/worker.py
@abstractmethod\nasync def status(self) -> dict:\n    \"\"\"\n    Get the status of the worker.\n    \"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.Worker.gracefully_stop","title":"gracefully_stop abstractmethod","text":"
gracefully_stop()\n

gracefully stop the worker

Source code in fluid/utils/worker.py
@abstractmethod\ndef gracefully_stop(self) -> None:\n    \"gracefully stop the worker\"\n
"},{"location":"reference/workers/#fluid.utils.worker.Worker.is_running","title":"is_running abstractmethod","text":"
is_running()\n

Is the worker running?

Source code in fluid/utils/worker.py
@abstractmethod\ndef is_running(self) -> bool:\n    \"\"\"Is the worker running?\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.Worker.is_stopping","title":"is_stopping abstractmethod","text":"
is_stopping()\n

Is the worker stopping?

Source code in fluid/utils/worker.py
@abstractmethod\ndef is_stopping(self) -> bool:\n    \"\"\"Is the worker stopping?\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.Worker.run","title":"run abstractmethod async","text":"
run()\n

run the worker

Source code in fluid/utils/worker.py
@abstractmethod\nasync def run(self) -> None:\n    \"\"\"run the worker\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker","title":"fluid.utils.worker.RunningWorker","text":"
RunningWorker(name='')\n

Bases: Worker

A Worker that can be started

Source code in fluid/utils/worker.py
def __init__(self, name: str = \"\") -> None:\n    super().__init__(name)\n    self._running: bool = False\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.status","title":"status abstractmethod async","text":"
status()\n

Get the status of the worker.

Source code in fluid/utils/worker.py
@abstractmethod\nasync def status(self) -> dict:\n    \"\"\"\n    Get the status of the worker.\n    \"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.gracefully_stop","title":"gracefully_stop abstractmethod","text":"
gracefully_stop()\n

gracefully stop the worker

Source code in fluid/utils/worker.py
@abstractmethod\ndef gracefully_stop(self) -> None:\n    \"gracefully stop the worker\"\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.is_stopping","title":"is_stopping abstractmethod","text":"
is_stopping()\n

Is the worker stopping?

Source code in fluid/utils/worker.py
@abstractmethod\ndef is_stopping(self) -> bool:\n    \"\"\"Is the worker stopping?\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.run","title":"run abstractmethod async","text":"
run()\n

run the worker

Source code in fluid/utils/worker.py
@abstractmethod\nasync def run(self) -> None:\n    \"\"\"run the worker\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker","title":"fluid.utils.worker.StoppingWorker","text":"
StoppingWorker(name='')\n

Bases: RunningWorker

A Worker that can be stopped

Source code in fluid/utils/worker.py
def __init__(self, name: str = \"\") -> None:\n    super().__init__(name)\n    self._stopping: bool = False\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.run","title":"run abstractmethod async","text":"
run()\n

run the worker

Source code in fluid/utils/worker.py
@abstractmethod\nasync def run(self) -> None:\n    \"\"\"run the worker\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._stopping\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._stopping = True\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    return {\"stopping\": self.is_stopping(), \"running\": self.is_running()}\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction","title":"fluid.utils.worker.WorkerFunction","text":"
WorkerFunction(run_function, heartbeat=0, name='')\n

Bases: StoppingWorker

A Worker that runs a coroutine function

Source code in fluid/utils/worker.py
def __init__(\n    self,\n    run_function: Callable[[], Awaitable[None]],\n    heartbeat: float | int = 0,\n    name: str = \"\",\n) -> None:\n    super().__init__(name=name)\n    self._run_function = run_function\n    self._heartbeat = heartbeat\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    return {\"stopping\": self.is_stopping(), \"running\": self.is_running()}\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._stopping = True\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._stopping\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.run","title":"run async","text":"
run()\n
Source code in fluid/utils/worker.py
async def run(self) -> None:\n    with self.start_running():\n        while not self.is_stopping():\n            await self._run_function()\n            await asyncio.sleep(self._heartbeat)\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer","title":"fluid.utils.worker.QueueConsumer","text":"
QueueConsumer(name='')\n

Bases: StoppingWorker, MessageProducer[MessageType]

A Worker that can receive messages

This worker can receive messages but not consume them.

Source code in fluid/utils/worker.py
def __init__(self, name: str = \"\") -> None:\n    super().__init__(name=name)\n    self._queue: asyncio.Queue[MessageType | None] = asyncio.Queue()\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._stopping = True\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._stopping\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.run","title":"run abstractmethod async","text":"
run()\n

run the worker

Source code in fluid/utils/worker.py
@abstractmethod\nasync def run(self) -> None:\n    \"\"\"run the worker\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.get_message","title":"get_message async","text":"
get_message(timeout=0.5)\n
Source code in fluid/utils/worker.py
async def get_message(self, timeout: float = 0.5) -> MessageType | None:\n    try:\n        async with asyncio.timeout(timeout):\n            return await self._queue.get()\n    except asyncio.TimeoutError:\n        return None\n    except (asyncio.CancelledError, RuntimeError):\n        if not self.is_stopping():\n            raise\n    return None\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.queue_size","title":"queue_size","text":"
queue_size()\n
Source code in fluid/utils/worker.py
def queue_size(self) -> int:\n    return self._queue.qsize()\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    status = await super().status()\n    status.update(queue_size=self.queue_size())\n    return status\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.send","title":"send","text":"
send(message)\n

Send a message into the worker

Source code in fluid/utils/worker.py
def send(self, message: MessageType | None) -> None:\n    \"\"\"Send a message into the worker\"\"\"\n    self._queue.put_nowait(message)\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker","title":"fluid.utils.worker.QueueConsumerWorker","text":"
QueueConsumerWorker(on_message, name='')\n

Bases: QueueConsumer[MessageType]

A Worker that can receive and consume messages

Source code in fluid/utils/worker.py
def __init__(\n    self,\n    on_message: Callable[[MessageType], Awaitable[None]],\n    name: str = \"\",\n) -> None:\n    super().__init__(name=name)\n    self.on_message = on_message\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.on_message","title":"on_message instance-attribute","text":"
on_message = on_message\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.send","title":"send","text":"
send(message)\n

Send a message into the worker

Source code in fluid/utils/worker.py
def send(self, message: MessageType | None) -> None:\n    \"\"\"Send a message into the worker\"\"\"\n    self._queue.put_nowait(message)\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    status = await super().status()\n    status.update(queue_size=self.queue_size())\n    return status\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._stopping = True\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._stopping\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.get_message","title":"get_message async","text":"
get_message(timeout=0.5)\n
Source code in fluid/utils/worker.py
async def get_message(self, timeout: float = 0.5) -> MessageType | None:\n    try:\n        async with asyncio.timeout(timeout):\n            return await self._queue.get()\n    except asyncio.TimeoutError:\n        return None\n    except (asyncio.CancelledError, RuntimeError):\n        if not self.is_stopping():\n            raise\n    return None\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.queue_size","title":"queue_size","text":"
queue_size()\n
Source code in fluid/utils/worker.py
def queue_size(self) -> int:\n    return self._queue.qsize()\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.run","title":"run async","text":"
run()\n
Source code in fluid/utils/worker.py
async def run(self) -> None:\n    with self.start_running():\n        while not self.is_stopping():\n            message = await self.get_message()\n            if message is not None:\n                await self.on_message(message)\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer","title":"fluid.utils.worker.AsyncConsumer","text":"
AsyncConsumer(dispatcher, name='')\n

Bases: QueueConsumer[MessageType]

A Worker that can dispatch async callbacks

Source code in fluid/utils/worker.py
def __init__(\n    self, dispatcher: AsyncDispatcher[MessageType], name: str = \"\"\n) -> None:\n    super().__init__(name)\n    self.dispatcher: AsyncDispatcher[MessageType] = dispatcher\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.AsyncCallback","title":"AsyncCallback instance-attribute","text":"
AsyncCallback\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.dispatcher","title":"dispatcher instance-attribute","text":"
dispatcher = dispatcher\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.send","title":"send","text":"
send(message)\n

Send a message into the worker

Source code in fluid/utils/worker.py
def send(self, message: MessageType | None) -> None:\n    \"\"\"Send a message into the worker\"\"\"\n    self._queue.put_nowait(message)\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    status = await super().status()\n    status.update(queue_size=self.queue_size())\n    return status\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._stopping = True\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._stopping\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.get_message","title":"get_message async","text":"
get_message(timeout=0.5)\n
Source code in fluid/utils/worker.py
async def get_message(self, timeout: float = 0.5) -> MessageType | None:\n    try:\n        async with asyncio.timeout(timeout):\n            return await self._queue.get()\n    except asyncio.TimeoutError:\n        return None\n    except (asyncio.CancelledError, RuntimeError):\n        if not self.is_stopping():\n            raise\n    return None\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.queue_size","title":"queue_size","text":"
queue_size()\n
Source code in fluid/utils/worker.py
def queue_size(self) -> int:\n    return self._queue.qsize()\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.run","title":"run async","text":"
run()\n
Source code in fluid/utils/worker.py
async def run(self) -> None:\n    with self.start_running():\n        while not self.is_stopping():\n            message = await self.get_message()\n            if message is not None:\n                await self.dispatcher.dispatch(message)\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers","title":"fluid.utils.worker.Workers","text":"
Workers(\n    *workers,\n    name=\"\",\n    stopping_grace_period=STOPPING_GRACE_PERIOD\n)\n

Bases: MultipleWorkers

A worker managing several workers

Source code in fluid/utils/worker.py
def __init__(\n    self,\n    *workers: Worker,\n    name: str = \"\",\n    stopping_grace_period: int = settings.STOPPING_GRACE_PERIOD,\n) -> None:\n    super().__init__(\n        *workers, name=name, stopping_grace_period=stopping_grace_period\n    )\n    self._workers_task: asyncio.Task | None = None\n    self._delayed_callbacks: list[\n        tuple[Callable[[], None], float, float, float]\n    ] = []\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.Workers.num_workers","title":"num_workers property","text":"
num_workers\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.running","title":"running property","text":"
running\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    return await self._workers.status()\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._workers.gracefully_stop()\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._workers.is_stopping()\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.create_task","title":"create_task","text":"
create_task(worker)\n
Source code in fluid/utils/worker.py
def create_task(self, worker: Worker) -> asyncio.Task:\n    return asyncio.create_task(\n        self._run_worker(worker), name=f\"{self.worker_name}-{worker.worker_name}\"\n    )\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.on_shutdown","title":"on_shutdown async","text":"
on_shutdown()\n

called after the workers are stopped

Source code in fluid/utils/worker.py
async def on_shutdown(self) -> None:\n    \"\"\"called after the workers are stopped\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.shutdown","title":"shutdown async","text":"
shutdown()\n

shutdown the workers

Source code in fluid/utils/worker.py
async def shutdown(self) -> None:\n    \"\"\"shutdown the workers\"\"\"\n    if self._has_shutdown:\n        return\n    self._has_shutdown = True\n    logger.warning(\n        \"gracefully stopping %d workers: %s\",\n        self.num_workers,\n        \", \".join(w.worker_name for w in self._workers.workers),\n    )\n    self.gracefully_stop()\n    try:\n        async with async_timeout.timeout(self._stopping_grace_period):\n            await self.wait_for_exit()\n        await self.on_shutdown()\n        return\n    except asyncio.TimeoutError:\n        logger.warning(\n            \"could not stop workers %s gracefully after %s\"\n            \" seconds - force shutdown\",\n            \", \".join(\n                task.get_name() for task in self._workers.tasks if not task.done()\n            ),\n            self._stopping_grace_period,\n        )\n    except asyncio.CancelledError:\n        pass\n    self._force_shutdown = True\n    self._workers.cancel()\n    try:\n        await self.wait_for_exit()\n    except asyncio.CancelledError:\n        pass\n    await self.on_shutdown()\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.bail_out","title":"bail_out","text":"
bail_out(reason, code=1)\n
Source code in fluid/utils/worker.py
def bail_out(self, reason: str, code: int = 1) -> None:\n    self.gracefully_stop()\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.safe_run","title":"safe_run async","text":"
safe_run()\n

Context manager to run a worker safely

Source code in fluid/utils/worker.py
@asynccontextmanager\nasync def safe_run(self) -> AsyncGenerator:\n    \"\"\"Context manager to run a worker safely\"\"\"\n    try:\n        yield\n    except asyncio.CancelledError:\n        if self._force_shutdown:\n            # we are shutting down, this is expected\n            pass\n        raise\n    except Exception as e:\n        reason = f\"unhandled exception while running workers: {e}\"\n        logger.exception(reason)\n        asyncio.get_event_loop().call_soon(self.bail_out, reason, 2)\n    else:\n        # worker finished without error\n        # make sure we are shutting down\n        asyncio.get_event_loop().call_soon(self.bail_out, \"worker exit\", 1)\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.add_workers","title":"add_workers","text":"
add_workers(*workers)\n

add workers to the workers

Source code in fluid/utils/worker.py
def add_workers(self, *workers: Worker) -> None:\n    \"\"\"add workers to the workers\"\"\"\n    workers_, _ = self._workers.workers_tasks()\n    for worker in workers:\n        if worker not in workers_:\n            workers_.append(worker)\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.run","title":"run async","text":"
run()\n

run the workers

Source code in fluid/utils/worker.py
async def run(self) -> None:\n    \"\"\"run the workers\"\"\"\n    with self.start_running():\n        async with self.safe_run():\n            workers, _ = self._workers.workers_tasks()\n            self._workers.workers = tuple(workers)\n            self._workers.tasks = tuple(\n                self.create_task(worker) for worker in workers\n            )\n            await asyncio.gather(*self._workers.tasks)\n        await self.shutdown()\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.wait_for_exit","title":"wait_for_exit async","text":"
wait_for_exit()\n
Source code in fluid/utils/worker.py
async def wait_for_exit(self) -> None:\n    if self._workers_task is not None:\n        await self._workers_task\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.remove_workers","title":"remove_workers","text":"
remove_workers(*workers)\n

remove workers from the workers

Source code in fluid/utils/worker.py
def remove_workers(self, *workers: Worker) -> None:\n    \"remove workers from the workers\"\n    workers_, _ = self._workers.workers_tasks()\n    for worker in workers:\n        try:\n            workers_.remove(worker)\n        except ValueError:\n            pass\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.startup","title":"startup async","text":"
startup()\n

start the workers

Source code in fluid/utils/worker.py
async def startup(self) -> None:\n    \"\"\"start the workers\"\"\"\n    if self._workers_task is None:\n        self._workers_task = asyncio.create_task(self.run(), name=self.worker_name)\n        for args in self._delayed_callbacks:\n            self._delayed_callback(*args)\n        self._delayed_callbacks = []\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.register_callback","title":"register_callback","text":"
register_callback(\n    callback, seconds, jitter=0.0, periodic=False\n)\n

Register a callback

The callback can be periodic or not.

Source code in fluid/utils/worker.py
def register_callback(\n    self,\n    callback: Callable[[], None],\n    seconds: float,\n    jitter: float = 0.0,\n    periodic: bool | float = False,\n) -> None:\n    \"\"\"Register a callback\n\n    The callback can be periodic or not.\n    \"\"\"\n    if periodic is True:\n        periodic_float = seconds\n    elif periodic is False:\n        periodic_float = 0.0\n    else:\n        periodic_float = periodic\n    if not self.running:\n        self._delayed_callbacks.append((callback, seconds, jitter, periodic_float))\n    else:\n        self._delayed_callback(callback, seconds, jitter, periodic_float)\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers","title":"fluid.utils.worker.DynamicWorkers","text":"
DynamicWorkers(\n    *workers,\n    name=\"\",\n    heartbeat=0.1,\n    stopping_grace_period=STOPPING_GRACE_PERIOD\n)\n

Bases: MultipleWorkers

Source code in fluid/utils/worker.py
def __init__(\n    self,\n    *workers: Worker,\n    name: str = \"\",\n    heartbeat: float | int = 0.1,\n    stopping_grace_period: int = settings.STOPPING_GRACE_PERIOD,\n) -> None:\n    super().__init__(name)\n    self._heartbeat = heartbeat\n    self._workers = WorkerTasks()\n    self._has_shutdown = False\n    self._force_shutdown = False\n    self._stopping_grace_period = stopping_grace_period\n    self.add_workers(*workers)\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.num_workers","title":"num_workers property","text":"
num_workers\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    return await self._workers.status()\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._workers.gracefully_stop()\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._workers.is_stopping()\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.create_task","title":"create_task","text":"
create_task(worker)\n
Source code in fluid/utils/worker.py
def create_task(self, worker: Worker) -> asyncio.Task:\n    return asyncio.create_task(\n        self._run_worker(worker), name=f\"{self.worker_name}-{worker.worker_name}\"\n    )\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.on_shutdown","title":"on_shutdown async","text":"
on_shutdown()\n

called after the workers are stopped

Source code in fluid/utils/worker.py
async def on_shutdown(self) -> None:\n    \"\"\"called after the workers are stopped\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.shutdown","title":"shutdown async","text":"
shutdown()\n

shutdown the workers

Source code in fluid/utils/worker.py
async def shutdown(self) -> None:\n    \"\"\"shutdown the workers\"\"\"\n    if self._has_shutdown:\n        return\n    self._has_shutdown = True\n    logger.warning(\n        \"gracefully stopping %d workers: %s\",\n        self.num_workers,\n        \", \".join(w.worker_name for w in self._workers.workers),\n    )\n    self.gracefully_stop()\n    try:\n        async with async_timeout.timeout(self._stopping_grace_period):\n            await self.wait_for_exit()\n        await self.on_shutdown()\n        return\n    except asyncio.TimeoutError:\n        logger.warning(\n            \"could not stop workers %s gracefully after %s\"\n            \" seconds - force shutdown\",\n            \", \".join(\n                task.get_name() for task in self._workers.tasks if not task.done()\n            ),\n            self._stopping_grace_period,\n        )\n    except asyncio.CancelledError:\n        pass\n    self._force_shutdown = True\n    self._workers.cancel()\n    try:\n        await self.wait_for_exit()\n    except asyncio.CancelledError:\n        pass\n    await self.on_shutdown()\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.bail_out","title":"bail_out","text":"
bail_out(reason, code=1)\n
Source code in fluid/utils/worker.py
def bail_out(self, reason: str, code: int = 1) -> None:\n    self.gracefully_stop()\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.safe_run","title":"safe_run async","text":"
safe_run()\n

Context manager to run a worker safely

Source code in fluid/utils/worker.py
@asynccontextmanager\nasync def safe_run(self) -> AsyncGenerator:\n    \"\"\"Context manager to run a worker safely\"\"\"\n    try:\n        yield\n    except asyncio.CancelledError:\n        if self._force_shutdown:\n            # we are shutting down, this is expected\n            pass\n        raise\n    except Exception as e:\n        reason = f\"unhandled exception while running workers: {e}\"\n        logger.exception(reason)\n        asyncio.get_event_loop().call_soon(self.bail_out, reason, 2)\n    else:\n        # worker finished without error\n        # make sure we are shutting down\n        asyncio.get_event_loop().call_soon(self.bail_out, \"worker exit\", 1)\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.add_workers","title":"add_workers","text":"
add_workers(*workers)\n

add workers to the workers

They can be added while the workers are running.

Source code in fluid/utils/worker.py
def add_workers(self, *workers: Worker) -> None:\n    \"\"\"add workers to the workers\n\n    They can be added while the workers are running.\n    \"\"\"\n    workers_, tasks_ = self._workers.workers_tasks()\n    for worker in workers:\n        workers_.append(worker)\n        tasks_.append(self.create_task(worker))\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.run","title":"run async","text":"
run()\n
Source code in fluid/utils/worker.py
async def run(self) -> None:\n    with self.start_running():\n        while not self.is_stopping():\n            for worker, task in zip(self._workers.workers, self._workers.tasks):\n                if worker.is_stopping() or task.done():\n                    break\n            await asyncio.sleep(self._heartbeat)\n        await self.shutdown()\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.wait_for_exit","title":"wait_for_exit async","text":"
wait_for_exit()\n
Source code in fluid/utils/worker.py
async def wait_for_exit(self) -> None:\n    await asyncio.gather(*self._workers.tasks)\n
"},{"location":"tutorials/","title":"Tutorials","text":"

The step-by-step guides, the how-to's, the recipes, and all the Aio Fluid parts you can use in your applications.

"},{"location":"tutorials/db/","title":"Async Database","text":"

The fluid.db module provides a simple asynchronous interface to interact with postgres databases. It is built on top of the sqlalchemy and asyncpg libraries.

"},{"location":"tutorials/dispatchers/","title":"Event Dispatchers","text":"

Event dispatchers are a way to decouple the event source from the event handler. This is useful when you want to have multiple handlers for the same event, or when you want to have a single handler for multiple events.

from fluid.utils.dispatcher import SimpleDispatcher\n\nsimple = SimpleDispatcher[Any]()\n\nsimple.dispatch(\"you can dispatch anything to this generic dispatcher\")\n
"},{"location":"tutorials/scheduler/","title":"Task Queue","text":"

This module has a lightweight implementation of a distributed task producer (TaskScheduler) and consumer (TaskConsumer). The middleware for distributing tasks can be configured via the Broker interface. A redis broker is provided for convenience.

"},{"location":"tutorials/scheduler/#tasks","title":"Tasks","text":"

Tasks are standard python async functions decorated with the task decorator.

from fluid.scheduler import task, TaskRun\n\n@task\nasync def say_hi(ctx: TaskRun):\n    return \"Hi!\"\n

There are two types of tasks implemented

  • Simple concurrent tasks - they run concurrently with the task consumer - thy must be IO type tasks (no heavy CPU bound operations)
  from fluid.scheduler import task, TaskRun\n\n  @task\n  async def fecth_data(ctx: TaskRun):\n      # fetch data\n      data = await http_cli.get(\"https://...\")\n      data_id = await datastore_cli.stote(data)\n      # trigger another task\n      ctx.task_manager.queue(\"heavy_calculation\", data_id=data_id)\n
  • CPU bound tasks - they run on a subprocess
from fluid.scheduler import task, TaskRun\n\n@task(cpu_bound=True)\nasync def heavy_calculation(ctx: TaskRun):\n    data = await datastore_cli.get(ctx.params[\"data_id\"])\n    # perform some heavy calculation\n    ...\n    # trigger another task\n    ctx.task_manager.queue(\"fetch_data\")\n

Both tasks can be periodically scheduled via the schedule keyword argument:

from datetime import timedelta\nfrom fluid.scheduler import task, TaskContext, every\n\n@task(schedule=every(timedelta(seconds=1)))\nasync def scheduled(context: TaskContext) -> str:\n    await asyncio.sleep(0.1)\n    return \"OK\"\n
"},{"location":"tutorials/scheduler/#broker","title":"Broker","text":"

A Task broker needs to implement three abstract methods

  @abstractmethod\n  async def queue_task(self, queued_task: QueuedTask) -> TaskRun:\n      \"\"\"Queue a task\"\"\"\n\n  @abstractmethod\n  async def get_task_run(self) -> Optional[TaskRun]:\n      \"\"\"Get a Task run from the task queue\"\"\"\n\n  @abstractmethod\n  async def queue_length(self) -> Dict[str, int]:\n      \"\"\"Length of task queues\"\"\"\n

The library ships a Redis broker for convenience.

from fluid.scheduler import Broker\n\nredis_broker = Broker.from_url(\"redis://localhost:6349\")\n
"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":"

Aio Fluid

Async utilities for backend python services developed by Quantmind.

Documentation: https://quantmind.github.io/aio-fluid

Source Code: https://github.com/quantmind/aio-fluid

"},{"location":"#installation","title":"Installation","text":"

This is a simple python package you can install via pip:

pip install aio-fluid\n

To install all the dependencies:

pip install aio-fluid[cli, db, http, log]\n
this includes the following extra dependencies:

  • cli for the command line interface using click and rich
  • db for database support with asyncpg and sqlalchemy
  • http for http client support with httpx and aiohttp
  • log for JSON logging support with python-json-logger
"},{"location":"#development","title":"Development","text":"

You can run the examples via

poetry run python -m examples.main\n
"},{"location":"reference/","title":"Introduction","text":"

Here's the reference or code API, the classes, functions, parameters, attributes, and all the aio-fluid parts you can use in your applications.

"},{"location":"reference/dispatchers/","title":"Event Dispatchers","text":"

A set of classes for dispatching events, they can be imported from fluid.utils.dispatcher:

from fluid.utils.dispatcher import Dispatcher\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.BaseDispatcher","title":"fluid.utils.dispatcher.BaseDispatcher","text":"
BaseDispatcher()\n

Bases: Generic[MessageType, MessageHandlerType], ABC

Source code in fluid/utils/dispatcher.py
def __init__(self) -> None:\n    self._msg_handlers: defaultdict[str, dict[str, MessageHandlerType]] = (\n        defaultdict(\n            dict,\n        )\n    )\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.BaseDispatcher.register_handler","title":"register_handler","text":"
register_handler(event, handler)\n
Source code in fluid/utils/dispatcher.py
def register_handler(\n    self,\n    event: Event | str,\n    handler: MessageHandlerType,\n) -> MessageHandlerType | None:\n    event = Event.from_string_or_event(event)\n    previous = self._msg_handlers[event.type].get(event.tag)\n    self._msg_handlers[event.type][event.tag] = handler\n    return previous\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.BaseDispatcher.unregister_handler","title":"unregister_handler","text":"
unregister_handler(event)\n
Source code in fluid/utils/dispatcher.py
def unregister_handler(self, event: Event | str) -> MessageHandlerType | None:\n    event = Event.from_string_or_event(event)\n    return self._msg_handlers[event.type].pop(event.tag, None)\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.BaseDispatcher.get_handlers","title":"get_handlers","text":"
get_handlers(message)\n
Source code in fluid/utils/dispatcher.py
def get_handlers(\n    self,\n    message: MessageType,\n) -> dict[str, MessageHandlerType] | None:\n    message_type = str(self.message_type(message))\n    return self._msg_handlers.get(message_type)\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.BaseDispatcher.message_type","title":"message_type abstractmethod","text":"
message_type(message)\n

return the message type

Source code in fluid/utils/dispatcher.py
@abstractmethod\ndef message_type(self, message: MessageType) -> str:\n    \"\"\"return the message type\"\"\"\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.Dispatcher","title":"fluid.utils.dispatcher.Dispatcher","text":"
Dispatcher()\n

Bases: BaseDispatcher[MessageType, Callable[[MessageType], None]]

Dispatcher for sync handlers

Source code in fluid/utils/dispatcher.py
def __init__(self) -> None:\n    self._msg_handlers: defaultdict[str, dict[str, MessageHandlerType]] = (\n        defaultdict(\n            dict,\n        )\n    )\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.Dispatcher.register_handler","title":"register_handler","text":"
register_handler(event, handler)\n
Source code in fluid/utils/dispatcher.py
def register_handler(\n    self,\n    event: Event | str,\n    handler: MessageHandlerType,\n) -> MessageHandlerType | None:\n    event = Event.from_string_or_event(event)\n    previous = self._msg_handlers[event.type].get(event.tag)\n    self._msg_handlers[event.type][event.tag] = handler\n    return previous\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.Dispatcher.unregister_handler","title":"unregister_handler","text":"
unregister_handler(event)\n
Source code in fluid/utils/dispatcher.py
def unregister_handler(self, event: Event | str) -> MessageHandlerType | None:\n    event = Event.from_string_or_event(event)\n    return self._msg_handlers[event.type].pop(event.tag, None)\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.Dispatcher.get_handlers","title":"get_handlers","text":"
get_handlers(message)\n
Source code in fluid/utils/dispatcher.py
def get_handlers(\n    self,\n    message: MessageType,\n) -> dict[str, MessageHandlerType] | None:\n    message_type = str(self.message_type(message))\n    return self._msg_handlers.get(message_type)\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.Dispatcher.message_type","title":"message_type abstractmethod","text":"
message_type(message)\n

return the message type

Source code in fluid/utils/dispatcher.py
@abstractmethod\ndef message_type(self, message: MessageType) -> str:\n    \"\"\"return the message type\"\"\"\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.Dispatcher.dispatch","title":"dispatch","text":"
dispatch(message)\n

dispatch the message

Source code in fluid/utils/dispatcher.py
def dispatch(self, message: MessageType) -> int:\n    \"\"\"dispatch the message\"\"\"\n    handlers = self.get_handlers(message)\n    if handlers:\n        for handler in handlers.values():\n            handler(message)\n    return len(handlers or ())\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.AsyncDispatcher","title":"fluid.utils.dispatcher.AsyncDispatcher","text":"
AsyncDispatcher()\n

Bases: BaseDispatcher[MessageType, Callable[[MessageType], Awaitable[None]]]

Dispatcher for async handlers

Source code in fluid/utils/dispatcher.py
def __init__(self) -> None:\n    self._msg_handlers: defaultdict[str, dict[str, MessageHandlerType]] = (\n        defaultdict(\n            dict,\n        )\n    )\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.AsyncDispatcher.register_handler","title":"register_handler","text":"
register_handler(event, handler)\n
Source code in fluid/utils/dispatcher.py
def register_handler(\n    self,\n    event: Event | str,\n    handler: MessageHandlerType,\n) -> MessageHandlerType | None:\n    event = Event.from_string_or_event(event)\n    previous = self._msg_handlers[event.type].get(event.tag)\n    self._msg_handlers[event.type][event.tag] = handler\n    return previous\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.AsyncDispatcher.unregister_handler","title":"unregister_handler","text":"
unregister_handler(event)\n
Source code in fluid/utils/dispatcher.py
def unregister_handler(self, event: Event | str) -> MessageHandlerType | None:\n    event = Event.from_string_or_event(event)\n    return self._msg_handlers[event.type].pop(event.tag, None)\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.AsyncDispatcher.get_handlers","title":"get_handlers","text":"
get_handlers(message)\n
Source code in fluid/utils/dispatcher.py
def get_handlers(\n    self,\n    message: MessageType,\n) -> dict[str, MessageHandlerType] | None:\n    message_type = str(self.message_type(message))\n    return self._msg_handlers.get(message_type)\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.AsyncDispatcher.message_type","title":"message_type abstractmethod","text":"
message_type(message)\n

return the message type

Source code in fluid/utils/dispatcher.py
@abstractmethod\ndef message_type(self, message: MessageType) -> str:\n    \"\"\"return the message type\"\"\"\n
"},{"location":"reference/dispatchers/#fluid.utils.dispatcher.AsyncDispatcher.dispatch","title":"dispatch async","text":"
dispatch(message)\n

Dispatch the message and wait for all handlers to complete

Source code in fluid/utils/dispatcher.py
async def dispatch(self, message: MessageType) -> int:\n    \"\"\"Dispatch the message and wait for all handlers to complete\"\"\"\n    handlers = self.get_handlers(message)\n    if handlers:\n        await asyncio.gather(*[handler(message) for handler in handlers.values()])\n    return len(handlers or ())\n
"},{"location":"reference/task_broker/","title":"Task Broker","text":"

It can be imported from fluid.scheduler:

from fastapi.scheduler import TaskBroker\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker","title":"fluid.scheduler.TaskBroker","text":"
TaskBroker(url)\n

Bases: ABC

Source code in fluid/scheduler/broker.py
def __init__(self, url: URL) -> None:\n    self.url: URL = url\n    self.registry: TaskRegistry = TaskRegistry()\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.url","title":"url instance-attribute","text":"
url = url\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.registry","title":"registry instance-attribute","text":"
registry = TaskRegistry()\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.task_queue_names","title":"task_queue_names abstractmethod property","text":"
task_queue_names\n

Names of the task queues

"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.queue_task","title":"queue_task abstractmethod async","text":"
queue_task(task_run)\n

Queue a task

Source code in fluid/scheduler/broker.py
@abstractmethod\nasync def queue_task(self, task_run: TaskRun) -> None:\n    \"\"\"Queue a task\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.get_task_run","title":"get_task_run abstractmethod async","text":"
get_task_run(task_manager)\n

Get a Task run from the task queue

Source code in fluid/scheduler/broker.py
@abstractmethod\nasync def get_task_run(self, task_manager: TaskManager) -> TaskRun | None:\n    \"\"\"Get a Task run from the task queue\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.queue_length","title":"queue_length abstractmethod async","text":"
queue_length()\n

Length of task queues

Source code in fluid/scheduler/broker.py
@abstractmethod\nasync def queue_length(self) -> dict[str, int]:\n    \"\"\"Length of task queues\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.get_tasks_info","title":"get_tasks_info abstractmethod async","text":"
get_tasks_info(*task_names)\n

List of TaskInfo objects

Source code in fluid/scheduler/broker.py
@abstractmethod\nasync def get_tasks_info(self, *task_names: str) -> list[TaskInfo]:\n    \"\"\"List of TaskInfo objects\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.update_task","title":"update_task abstractmethod async","text":"
update_task(task, params)\n

Update a task dynamic parameters

Source code in fluid/scheduler/broker.py
@abstractmethod\nasync def update_task(self, task: Task, params: dict[str, Any]) -> TaskInfo:\n    \"\"\"Update a task dynamic parameters\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.close","title":"close abstractmethod async","text":"
close()\n

Close the broker on shutdown

Source code in fluid/scheduler/broker.py
@abstractmethod\nasync def close(self) -> None:\n    \"\"\"Close the broker on shutdown\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.lock","title":"lock abstractmethod","text":"
lock(name, timeout=None)\n

Create a lock

Source code in fluid/scheduler/broker.py
@abstractmethod\ndef lock(self, name: str, timeout: float | None = None) -> Lock:\n    \"\"\"Create a lock\"\"\"\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.new_uuid","title":"new_uuid","text":"
new_uuid()\n
Source code in fluid/scheduler/broker.py
def new_uuid(self) -> str:\n    return uuid4().hex\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.filter_tasks","title":"filter_tasks async","text":"
filter_tasks(scheduled=None, enabled=None)\n
Source code in fluid/scheduler/broker.py
async def filter_tasks(\n    self,\n    scheduled: bool | None = None,\n    enabled: bool | None = None,\n) -> list[Task]:\n    task_info = await self.get_tasks_info()\n    task_map = {info.name: info for info in task_info}\n    tasks = []\n    for task in self.registry.values():\n        if scheduled is not None and bool(task.schedule) is not scheduled:\n            continue\n        if enabled is not None and task_map[task.name].enabled is not enabled:\n            continue\n        tasks.append(task)\n    return tasks\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.task_from_registry","title":"task_from_registry","text":"
task_from_registry(task)\n
Source code in fluid/scheduler/broker.py
def task_from_registry(self, task: str | Task) -> Task:\n    if isinstance(task, Task):\n        self.register_task(task)\n        return task\n    else:\n        if task_ := self.registry.get(task):\n            return task_\n        raise UnknownTaskError(task)\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.register_task","title":"register_task","text":"
register_task(task)\n
Source code in fluid/scheduler/broker.py
def register_task(self, task: Task) -> None:\n    self.registry[task.name] = task\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.enable_task","title":"enable_task async","text":"
enable_task(task_name, enable=True)\n

Enable or disable a registered task

Source code in fluid/scheduler/broker.py
async def enable_task(self, task_name: str, enable: bool = True) -> TaskInfo:\n    \"\"\"Enable or disable a registered task\"\"\"\n    task = self.registry.get(task_name)\n    if not task:\n        raise UnknownTaskError(task_name)\n    return await self.update_task(task, dict(enabled=enable))\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.from_url","title":"from_url classmethod","text":"
from_url(url='')\n
Source code in fluid/scheduler/broker.py
@classmethod\ndef from_url(cls, url: str = \"\") -> TaskBroker:\n    p = URL(url or broker_url_from_env())\n    if factory := _brokers.get(p.scheme):\n        return factory(p)\n    raise RuntimeError(f\"Invalid broker {p}\")\n
"},{"location":"reference/task_broker/#fluid.scheduler.TaskBroker.register_broker","title":"register_broker classmethod","text":"
register_broker(name, factory)\n
Source code in fluid/scheduler/broker.py
@classmethod\ndef register_broker(cls, name: str, factory: type[TaskBroker]) -> None:\n    _brokers[name] = factory\n
"},{"location":"reference/task_manager/","title":"Task Manager","text":"

It can be imported from fluid.scheduler:

from fastapi.scheduler import TaskManager\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager","title":"fluid.scheduler.TaskManager","text":"
TaskManager(**kwargs)\n

The task manager is the main entry point for managing tasks

Source code in fluid/scheduler/consumer.py
def __init__(self, **kwargs: Any) -> None:\n    self.state: dict[str, Any] = {}\n    self.config: TaskManagerConfig = TaskManagerConfig(**kwargs)\n    self.dispatcher: Annotated[\n        TaskDispatcher,\n        Doc(\n            \"\"\"\n            A dispatcher of task run events.\n\n            Register handlers to listen for task run events.\n            \"\"\"\n        ),\n    ] = TaskDispatcher()\n    self.broker = TaskBroker.from_url(self.config.broker_url)\n    self._stack = AsyncExitStack()\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.state","title":"state instance-attribute","text":"
state = {}\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.config","title":"config instance-attribute","text":"
config = TaskManagerConfig(**kwargs)\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.dispatcher","title":"dispatcher instance-attribute","text":"
dispatcher = TaskDispatcher()\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.broker","title":"broker instance-attribute","text":"
broker = from_url(broker_url)\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.registry","title":"registry property","text":"
registry\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.type","title":"type property","text":"
type\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.enter_async_context","title":"enter_async_context async","text":"
enter_async_context(cm)\n
Source code in fluid/scheduler/consumer.py
async def enter_async_context(self, cm: Any) -> Any:\n    return await self._stack.enter_async_context(cm)\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.execute","title":"execute async","text":"
execute(task, **params)\n

Execute a task and wait for it to finish

Source code in fluid/scheduler/consumer.py
async def execute(self, task: Task | str, **params: Any) -> TaskRun:\n    \"\"\"Execute a task and wait for it to finish\"\"\"\n    task_run = self.create_task_run(task, **params)\n    await task_run.execute()\n    return task_run\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.on_shutdown","title":"on_shutdown async","text":"
on_shutdown()\n
Source code in fluid/scheduler/consumer.py
async def on_shutdown(self) -> None:\n    await self.broker.close()\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.execute_sync","title":"execute_sync","text":"
execute_sync(task, **params)\n
Source code in fluid/scheduler/consumer.py
def execute_sync(self, task: Task | str, **params: Any) -> TaskRun:\n    return asyncio.run(self._execute_and_exit(task, **params))\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.register_task","title":"register_task","text":"
register_task(task)\n

Register a task with the task manager

Only tasks registered can be executed by a task manager

Source code in fluid/scheduler/consumer.py
def register_task(self, task: Task) -> None:\n    \"\"\"Register a task with the task manager\n\n    Only tasks registered can be executed by a task manager\n    \"\"\"\n    self.broker.register_task(task)\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.queue","title":"queue async","text":"
queue(task, priority=None, **params)\n

Queue a task for execution

This methods fires two events:

  • queue: when the task is about to be queued
  • queued: after the task is queued
Source code in fluid/scheduler/consumer.py
async def queue(\n    self,\n    task: str | Task,\n    priority: TaskPriority | None = None,\n    **params: Any,\n) -> TaskRun:\n    \"\"\"Queue a task for execution\n\n    This methods fires two events:\n\n    - queue: when the task is about to be queued\n    - queued: after the task is queued\n    \"\"\"\n    task_run = self.create_task_run(task, priority=priority, **params)\n    self.dispatcher.dispatch(task_run)\n    task_run.set_state(TaskState.queued)\n    await self.broker.queue_task(task_run)\n    return task_run\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.create_task_run","title":"create_task_run","text":"
create_task_run(task, run_id='', priority=None, **params)\n

Create a TaskRun in init state

Source code in fluid/scheduler/consumer.py
def create_task_run(\n    self,\n    task: str | Task,\n    run_id: str = \"\",\n    priority: TaskPriority | None = None,\n    **params: Any,\n) -> TaskRun:\n    \"\"\"Create a TaskRun in `init` state\"\"\"\n    if isinstance(task, str):\n        task = self.broker.task_from_registry(task)\n    run_id = run_id or self.broker.new_uuid()\n    return TaskRun(\n        id=run_id,\n        task=task,\n        priority=priority or task.priority,\n        params=params,\n        task_manager=self,\n    )\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.register_from_module","title":"register_from_module","text":"
register_from_module(module)\n
Source code in fluid/scheduler/consumer.py
def register_from_module(self, module: Any) -> None:\n    for name in dir(module):\n        if name.startswith(\"_\"):\n            continue\n        if isinstance(obj := getattr(module, name), Task):\n            self.register_task(obj)\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.register_async_handler","title":"register_async_handler","text":"
register_async_handler(event, handler)\n

Register an async handler for a given event

This method is a no op for a TaskManager that is not a worker

Source code in fluid/scheduler/consumer.py
def register_async_handler(self, event: str, handler: AsyncHandler) -> None:\n    \"\"\"Register an async handler for a given event\n\n    This method is a no op for a TaskManager that is not a worker\n    \"\"\"\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.unregister_async_handler","title":"unregister_async_handler","text":"
unregister_async_handler(event)\n

Unregister an async handler for a given event

This method is a no op for a TaskManager that is not a worker

Source code in fluid/scheduler/consumer.py
def unregister_async_handler(self, event: Event | str) -> AsyncHandler | None:\n    \"\"\"Unregister an async handler for a given event\n\n    This method is a no op for a TaskManager that is not a worker\n    \"\"\"\n    return None\n
"},{"location":"reference/task_manager/#fluid.scheduler.TaskManager.cli","title":"cli","text":"
cli(**kwargs)\n

Create the task manager command line interface

Source code in fluid/scheduler/consumer.py
def cli(self, **kwargs: Any) -> Any:\n    \"\"\"Create the task manager command line interface\"\"\"\n    try:\n        from fluid.scheduler.cli import TaskManagerCLI\n    except ImportError:\n        raise ImportError(\n            \"TaskManagerCLI is not available - \"\n            \"install with `pip install aio-fluid[cli]`\"\n        ) from None\n    return TaskManagerCLI(self, **kwargs)\n
"},{"location":"reference/task_run/","title":"Task Run","text":"

It can be imported from fluid.scheduler:

from fastapi.scheduler import TaskRun\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun","title":"fluid.scheduler.TaskRun","text":"

Bases: BaseModel

A TaskRun contains all the data generated by a Task run

"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.id","title":"id instance-attribute","text":"
id\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.task","title":"task instance-attribute","text":"
task\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.priority","title":"priority instance-attribute","text":"
priority\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.params","title":"params instance-attribute","text":"
params\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.state","title":"state class-attribute instance-attribute","text":"
state = init\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.task_manager","title":"task_manager class-attribute instance-attribute","text":"
task_manager = Field(exclude=True, repr=False)\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.queued","title":"queued class-attribute instance-attribute","text":"
queued = None\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.start","title":"start class-attribute instance-attribute","text":"
start = None\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.end","title":"end class-attribute instance-attribute","text":"
end = None\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.logger","title":"logger property","text":"
logger\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.in_queue","title":"in_queue property","text":"
in_queue\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.duration","title":"duration property","text":"
duration\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.duration_ms","title":"duration_ms property","text":"
duration_ms\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.total","title":"total property","text":"
total\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.name","title":"name property","text":"
name\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.name_id","title":"name_id property","text":"
name_id\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.is_done","title":"is_done property","text":"
is_done\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.is_failure","title":"is_failure property","text":"
is_failure\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.execute","title":"execute async","text":"
execute()\n
Source code in fluid/scheduler/models.py
async def execute(self) -> None:\n    try:\n        self.set_state(TaskState.running)\n        await self.task.executor(self)\n    except Exception:\n        self.set_state(TaskState.failure)\n        raise\n    else:\n        self.set_state(TaskState.success)\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.serialize_task","title":"serialize_task","text":"
serialize_task(task, _info)\n
Source code in fluid/scheduler/models.py
@field_serializer(\"task\")\ndef serialize_task(self, task: Task, _info: Any) -> str:\n    return task.name\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.params_dump_json","title":"params_dump_json","text":"
params_dump_json()\n
Source code in fluid/scheduler/models.py
def params_dump_json(self) -> str:\n    return self.task.params_dump_json(self.params)\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.set_state","title":"set_state","text":"
set_state(state, state_time=None)\n
Source code in fluid/scheduler/models.py
def set_state(\n    self,\n    state: TaskState,\n    state_time: datetime | None = None,\n) -> None:\n    if self.state == state:\n        return\n    state_time = as_utc(state_time)\n    match (self.state, state):\n        case (TaskState.init, TaskState.queued):\n            self.queued = state_time\n            self.state = state\n            self._dispatch()\n        case (TaskState.init, _):\n            self.set_state(TaskState.queued, state_time)\n            self.set_state(state, state_time)\n        case (TaskState.queued, TaskState.running):\n            self.start = state_time\n            self.state = state\n            self._dispatch()\n        case (\n            TaskState.queued,\n            TaskState.success\n            | TaskState.aborted\n            | TaskState.rate_limited\n            | TaskState.failure,\n        ):\n            self.set_state(TaskState.running, state_time)\n            self.set_state(state, state_time)\n        case (\n            TaskState.running,\n            TaskState.success\n            | TaskState.aborted\n            | TaskState.rate_limited\n            | TaskState.failure,\n        ):\n            self.end = state_time\n            self.state = state\n            self._dispatch()\n        case _:\n            raise TaskRunError(f\"invalid state transition {self.state} -> {state}\")\n
"},{"location":"reference/task_run/#fluid.scheduler.TaskRun.lock","title":"lock","text":"
lock(timeout)\n
Source code in fluid/scheduler/models.py
def lock(self, timeout: float | None) -> Lock:\n    return self.task_manager.broker.lock(self.name, timeout=timeout)\n
"},{"location":"reference/tast_consumer/","title":"Task Consumer","text":"

The task consumer is a TaskManager which is also a Workers that consumes tasks from the task queue and executes them. It can be imported from fluid.scheduler:

from fastapi.scheduler import TaskConsumer\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer","title":"fluid.scheduler.TaskConsumer","text":"
TaskConsumer(**config)\n

Bases: TaskManager, Workers

The Task Consumer is a Task Manager responsible for consuming tasks from a task queue

Source code in fluid/scheduler/consumer.py
def __init__(self, **config: Any) -> None:\n    super().__init__(**config)\n    Workers.__init__(self)\n    self._async_dispatcher_worker = AsyncConsumer(AsyncTaskDispatcher())\n    self._concurrent_tasks: dict[str, dict[str, TaskRun]] = defaultdict(dict)\n    self._task_to_queue: deque[str | Task] = deque()\n    self._priority_task_run_queue: deque[TaskRun] = deque()\n    self._queue_tasks_worker = WorkerFunction(\n        self._queue_task, name=\"queue-task-worker\"\n    )\n    self.add_workers(self._queue_tasks_worker)\n    self.add_workers(self._async_dispatcher_worker)\n    for i in range(self.config.max_concurrent_tasks):\n        worker_name = f\"task-worker-{i+1}\"\n        self.add_workers(\n            WorkerFunction(\n                partial(self._consume_tasks, worker_name), name=worker_name\n            )\n        )\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.num_workers","title":"num_workers property","text":"
num_workers\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.running","title":"running property","text":"
running\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.state","title":"state instance-attribute","text":"
state = {}\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.config","title":"config instance-attribute","text":"
config = TaskManagerConfig(**kwargs)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.dispatcher","title":"dispatcher instance-attribute","text":"
dispatcher = TaskDispatcher()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.broker","title":"broker instance-attribute","text":"
broker = from_url(broker_url)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.registry","title":"registry property","text":"
registry\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.type","title":"type property","text":"
type\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.num_concurrent_tasks","title":"num_concurrent_tasks property","text":"
num_concurrent_tasks\n

The number of concurrent_tasks running in the consumer

"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    return await self._workers.status()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._workers.gracefully_stop()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._workers.is_stopping()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.run","title":"run async","text":"
run()\n

run the workers

Source code in fluid/utils/worker.py
async def run(self) -> None:\n    \"\"\"run the workers\"\"\"\n    with self.start_running():\n        async with self.safe_run():\n            workers, _ = self._workers.workers_tasks()\n            self._workers.workers = tuple(workers)\n            self._workers.tasks = tuple(\n                self.create_task(worker) for worker in workers\n            )\n            await asyncio.gather(*self._workers.tasks)\n        await self.shutdown()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.add_workers","title":"add_workers","text":"
add_workers(*workers)\n

add workers to the workers

Source code in fluid/utils/worker.py
def add_workers(self, *workers: Worker) -> None:\n    \"\"\"add workers to the workers\"\"\"\n    workers_, _ = self._workers.workers_tasks()\n    for worker in workers:\n        if worker not in workers_:\n            workers_.append(worker)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.wait_for_exit","title":"wait_for_exit async","text":"
wait_for_exit()\n
Source code in fluid/utils/worker.py
async def wait_for_exit(self) -> None:\n    if self._workers_task is not None:\n        await self._workers_task\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.create_task","title":"create_task","text":"
create_task(worker)\n
Source code in fluid/utils/worker.py
def create_task(self, worker: Worker) -> asyncio.Task:\n    return asyncio.create_task(\n        self._run_worker(worker), name=f\"{self.worker_name}-{worker.worker_name}\"\n    )\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.on_shutdown","title":"on_shutdown async","text":"
on_shutdown()\n
Source code in fluid/scheduler/consumer.py
async def on_shutdown(self) -> None:\n    await self.broker.close()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.shutdown","title":"shutdown async","text":"
shutdown()\n

shutdown the workers

Source code in fluid/utils/worker.py
async def shutdown(self) -> None:\n    \"\"\"shutdown the workers\"\"\"\n    if self._has_shutdown:\n        return\n    self._has_shutdown = True\n    logger.warning(\n        \"gracefully stopping %d workers: %s\",\n        self.num_workers,\n        \", \".join(w.worker_name for w in self._workers.workers),\n    )\n    self.gracefully_stop()\n    try:\n        async with asyncio.timeout(self._stopping_grace_period):\n            await self.wait_for_exit()\n        await self.on_shutdown()\n        return\n    except asyncio.TimeoutError:\n        logger.warning(\n            \"could not stop workers %s gracefully after %s\"\n            \" seconds - force shutdown\",\n            \", \".join(\n                task.get_name() for task in self._workers.tasks if not task.done()\n            ),\n            self._stopping_grace_period,\n        )\n    except asyncio.CancelledError:\n        pass\n    self._force_shutdown = True\n    self._workers.cancel()\n    try:\n        await self.wait_for_exit()\n    except asyncio.CancelledError:\n        pass\n    await self.on_shutdown()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.bail_out","title":"bail_out","text":"
bail_out(reason, code=1)\n
Source code in fluid/utils/worker.py
def bail_out(self, reason: str, code: int = 1) -> None:\n    self.gracefully_stop()\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.safe_run","title":"safe_run async","text":"
safe_run()\n

Context manager to run a worker safely

Source code in fluid/utils/worker.py
@asynccontextmanager\nasync def safe_run(self) -> AsyncGenerator:\n    \"\"\"Context manager to run a worker safely\"\"\"\n    try:\n        yield\n    except asyncio.CancelledError:\n        if self._force_shutdown:\n            # we are shutting down, this is expected\n            pass\n        raise\n    except Exception as e:\n        reason = f\"unhandled exception while running workers: {e}\"\n        logger.exception(reason)\n        asyncio.get_event_loop().call_soon(self.bail_out, reason, 2)\n    else:\n        # worker finished without error\n        # make sure we are shutting down\n        asyncio.get_event_loop().call_soon(self.bail_out, \"worker exit\", 1)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.remove_workers","title":"remove_workers","text":"
remove_workers(*workers)\n

remove workers from the workers

Source code in fluid/utils/worker.py
def remove_workers(self, *workers: Worker) -> None:\n    \"remove workers from the workers\"\n    workers_, _ = self._workers.workers_tasks()\n    for worker in workers:\n        try:\n            workers_.remove(worker)\n        except ValueError:\n            pass\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.startup","title":"startup async","text":"
startup()\n

start the workers

Source code in fluid/utils/worker.py
async def startup(self) -> None:\n    \"\"\"start the workers\"\"\"\n    if self._workers_task is None:\n        self._workers_task = asyncio.create_task(self.run(), name=self.worker_name)\n        for args in self._delayed_callbacks:\n            self._delayed_callback(*args)\n        self._delayed_callbacks = []\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.register_callback","title":"register_callback","text":"
register_callback(\n    callback, seconds, jitter=0.0, periodic=False\n)\n

Register a callback

The callback can be periodic or not.

Source code in fluid/utils/worker.py
def register_callback(\n    self,\n    callback: Callable[[], None],\n    seconds: float,\n    jitter: float = 0.0,\n    periodic: bool | float = False,\n) -> None:\n    \"\"\"Register a callback\n\n    The callback can be periodic or not.\n    \"\"\"\n    if periodic is True:\n        periodic_float = seconds\n    elif periodic is False:\n        periodic_float = 0.0\n    else:\n        periodic_float = periodic\n    if not self.running:\n        self._delayed_callbacks.append((callback, seconds, jitter, periodic_float))\n    else:\n        self._delayed_callback(callback, seconds, jitter, periodic_float)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.enter_async_context","title":"enter_async_context async","text":"
enter_async_context(cm)\n
Source code in fluid/scheduler/consumer.py
async def enter_async_context(self, cm: Any) -> Any:\n    return await self._stack.enter_async_context(cm)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.execute","title":"execute async","text":"
execute(task, **params)\n

Execute a task and wait for it to finish

Source code in fluid/scheduler/consumer.py
async def execute(self, task: Task | str, **params: Any) -> TaskRun:\n    \"\"\"Execute a task and wait for it to finish\"\"\"\n    task_run = self.create_task_run(task, **params)\n    await task_run.execute()\n    return task_run\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.execute_sync","title":"execute_sync","text":"
execute_sync(task, **params)\n
Source code in fluid/scheduler/consumer.py
def execute_sync(self, task: Task | str, **params: Any) -> TaskRun:\n    return asyncio.run(self._execute_and_exit(task, **params))\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.register_task","title":"register_task","text":"
register_task(task)\n

Register a task with the task manager

Only tasks registered can be executed by a task manager

Source code in fluid/scheduler/consumer.py
def register_task(self, task: Task) -> None:\n    \"\"\"Register a task with the task manager\n\n    Only tasks registered can be executed by a task manager\n    \"\"\"\n    self.broker.register_task(task)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.queue","title":"queue async","text":"
queue(task, priority=None, **params)\n

Queue a task for execution

This methods fires two events:

  • queue: when the task is about to be queued
  • queued: after the task is queued
Source code in fluid/scheduler/consumer.py
async def queue(\n    self,\n    task: str | Task,\n    priority: TaskPriority | None = None,\n    **params: Any,\n) -> TaskRun:\n    \"\"\"Queue a task for execution\n\n    This methods fires two events:\n\n    - queue: when the task is about to be queued\n    - queued: after the task is queued\n    \"\"\"\n    task_run = self.create_task_run(task, priority=priority, **params)\n    self.dispatcher.dispatch(task_run)\n    task_run.set_state(TaskState.queued)\n    await self.broker.queue_task(task_run)\n    return task_run\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.create_task_run","title":"create_task_run","text":"
create_task_run(task, run_id='', priority=None, **params)\n

Create a TaskRun in init state

Source code in fluid/scheduler/consumer.py
def create_task_run(\n    self,\n    task: str | Task,\n    run_id: str = \"\",\n    priority: TaskPriority | None = None,\n    **params: Any,\n) -> TaskRun:\n    \"\"\"Create a TaskRun in `init` state\"\"\"\n    if isinstance(task, str):\n        task = self.broker.task_from_registry(task)\n    run_id = run_id or self.broker.new_uuid()\n    return TaskRun(\n        id=run_id,\n        task=task,\n        priority=priority or task.priority,\n        params=params,\n        task_manager=self,\n    )\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.register_from_module","title":"register_from_module","text":"
register_from_module(module)\n
Source code in fluid/scheduler/consumer.py
def register_from_module(self, module: Any) -> None:\n    for name in dir(module):\n        if name.startswith(\"_\"):\n            continue\n        if isinstance(obj := getattr(module, name), Task):\n            self.register_task(obj)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.cli","title":"cli","text":"
cli(**kwargs)\n

Create the task manager command line interface

Source code in fluid/scheduler/consumer.py
def cli(self, **kwargs: Any) -> Any:\n    \"\"\"Create the task manager command line interface\"\"\"\n    try:\n        from fluid.scheduler.cli import TaskManagerCLI\n    except ImportError:\n        raise ImportError(\n            \"TaskManagerCLI is not available - \"\n            \"install with `pip install aio-fluid[cli]`\"\n        ) from None\n    return TaskManagerCLI(self, **kwargs)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.sync_queue","title":"sync_queue","text":"
sync_queue(task)\n
Source code in fluid/scheduler/consumer.py
def sync_queue(self, task: str | Task) -> None:\n    self._task_to_queue.appendleft(task)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.sync_priority_queue","title":"sync_priority_queue","text":"
sync_priority_queue(task)\n
Source code in fluid/scheduler/consumer.py
def sync_priority_queue(self, task: str | Task) -> None:\n    self._priority_task_run_queue.appendleft(self.create_task_run(task))\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.num_concurrent_tasks_for","title":"num_concurrent_tasks_for","text":"
num_concurrent_tasks_for(task_name)\n

The number of concurrent tasks for a given task_name

Source code in fluid/scheduler/consumer.py
def num_concurrent_tasks_for(self, task_name: str) -> int:\n    \"\"\"The number of concurrent tasks for a given task_name\"\"\"\n    return len(self._concurrent_tasks[task_name])\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.queue_and_wait","title":"queue_and_wait async","text":"
queue_and_wait(task, *, timeout=2, **params)\n

Queue a task and wait for it to finish

Source code in fluid/scheduler/consumer.py
async def queue_and_wait(\n    self, task: str, *, timeout: int = 2, **params: Any\n) -> TaskRun:\n    \"\"\"Queue a task and wait for it to finish\"\"\"\n    with TaskRunWaiter(self) as waiter:\n        return await waiter.wait(await self.queue(task, **params), timeout=timeout)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.register_async_handler","title":"register_async_handler","text":"
register_async_handler(event, handler)\n
Source code in fluid/scheduler/consumer.py
def register_async_handler(self, event: Event | str, handler: AsyncHandler) -> None:\n    event = Event.from_string_or_event(event)\n    self.dispatcher.register_handler(\n        f\"{event.type}.async_dispatch\",\n        self._async_dispatcher_worker.send,\n    )\n    self._async_dispatcher_worker.dispatcher.register_handler(event, handler)\n
"},{"location":"reference/tast_consumer/#fluid.scheduler.TaskConsumer.unregister_async_handler","title":"unregister_async_handler","text":"
unregister_async_handler(event)\n
Source code in fluid/scheduler/consumer.py
def unregister_async_handler(self, event: Event | str) -> AsyncHandler | None:\n    return self._async_dispatcher_worker.dispatcher.unregister_handler(event)\n
"},{"location":"reference/workers/","title":"Workers","text":"

Workers are the main building block for asynchronous programming with aio-fluid. They are responsible for running tasks and managing their lifecycle. There are several worker classes which can be imported from fluid.utils.worker:

from fastapi.utils.worker import StoppingWorker\n
"},{"location":"reference/workers/#fluid.utils.worker.Worker","title":"fluid.utils.worker.Worker","text":"
Worker(name='')\n

Bases: ABC

The base class of a worker that can be run

Source code in fluid/utils/worker.py
def __init__(self, name: str = \"\") -> None:\n    self._name: str = name or underscore(type(self).__name__)\n
"},{"location":"reference/workers/#fluid.utils.worker.Worker.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.Worker.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.Worker.status","title":"status abstractmethod async","text":"
status()\n

Get the status of the worker.

Source code in fluid/utils/worker.py
@abstractmethod\nasync def status(self) -> dict:\n    \"\"\"\n    Get the status of the worker.\n    \"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.Worker.gracefully_stop","title":"gracefully_stop abstractmethod","text":"
gracefully_stop()\n

gracefully stop the worker

Source code in fluid/utils/worker.py
@abstractmethod\ndef gracefully_stop(self) -> None:\n    \"gracefully stop the worker\"\n
"},{"location":"reference/workers/#fluid.utils.worker.Worker.is_running","title":"is_running abstractmethod","text":"
is_running()\n

Is the worker running?

Source code in fluid/utils/worker.py
@abstractmethod\ndef is_running(self) -> bool:\n    \"\"\"Is the worker running?\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.Worker.is_stopping","title":"is_stopping abstractmethod","text":"
is_stopping()\n

Is the worker stopping?

Source code in fluid/utils/worker.py
@abstractmethod\ndef is_stopping(self) -> bool:\n    \"\"\"Is the worker stopping?\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.Worker.run","title":"run abstractmethod async","text":"
run()\n

run the worker

Source code in fluid/utils/worker.py
@abstractmethod\nasync def run(self) -> None:\n    \"\"\"run the worker\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker","title":"fluid.utils.worker.RunningWorker","text":"
RunningWorker(name='')\n

Bases: Worker

A Worker that can be started

Source code in fluid/utils/worker.py
def __init__(self, name: str = \"\") -> None:\n    super().__init__(name)\n    self._running: bool = False\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.status","title":"status abstractmethod async","text":"
status()\n

Get the status of the worker.

Source code in fluid/utils/worker.py
@abstractmethod\nasync def status(self) -> dict:\n    \"\"\"\n    Get the status of the worker.\n    \"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.gracefully_stop","title":"gracefully_stop abstractmethod","text":"
gracefully_stop()\n

gracefully stop the worker

Source code in fluid/utils/worker.py
@abstractmethod\ndef gracefully_stop(self) -> None:\n    \"gracefully stop the worker\"\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.is_stopping","title":"is_stopping abstractmethod","text":"
is_stopping()\n

Is the worker stopping?

Source code in fluid/utils/worker.py
@abstractmethod\ndef is_stopping(self) -> bool:\n    \"\"\"Is the worker stopping?\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.run","title":"run abstractmethod async","text":"
run()\n

run the worker

Source code in fluid/utils/worker.py
@abstractmethod\nasync def run(self) -> None:\n    \"\"\"run the worker\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.RunningWorker.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker","title":"fluid.utils.worker.StoppingWorker","text":"
StoppingWorker(name='')\n

Bases: RunningWorker

A Worker that can be stopped

Source code in fluid/utils/worker.py
def __init__(self, name: str = \"\") -> None:\n    super().__init__(name)\n    self._stopping: bool = False\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.run","title":"run abstractmethod async","text":"
run()\n

run the worker

Source code in fluid/utils/worker.py
@abstractmethod\nasync def run(self) -> None:\n    \"\"\"run the worker\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._stopping\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._stopping = True\n
"},{"location":"reference/workers/#fluid.utils.worker.StoppingWorker.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    return {\"stopping\": self.is_stopping(), \"running\": self.is_running()}\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction","title":"fluid.utils.worker.WorkerFunction","text":"
WorkerFunction(run_function, heartbeat=0, name='')\n

Bases: StoppingWorker

A Worker that runs a coroutine function

Source code in fluid/utils/worker.py
def __init__(\n    self,\n    run_function: Callable[[], Awaitable[None]],\n    heartbeat: float | int = 0,\n    name: str = \"\",\n) -> None:\n    super().__init__(name=name)\n    self._run_function = run_function\n    self._heartbeat = heartbeat\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    return {\"stopping\": self.is_stopping(), \"running\": self.is_running()}\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._stopping = True\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._stopping\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.WorkerFunction.run","title":"run async","text":"
run()\n
Source code in fluid/utils/worker.py
async def run(self) -> None:\n    with self.start_running():\n        while not self.is_stopping():\n            await self._run_function()\n            await asyncio.sleep(self._heartbeat)\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer","title":"fluid.utils.worker.QueueConsumer","text":"
QueueConsumer(name='')\n

Bases: StoppingWorker, MessageProducer[MessageType]

A Worker that can receive messages

This worker can receive messages but not consume them.

Source code in fluid/utils/worker.py
def __init__(self, name: str = \"\") -> None:\n    super().__init__(name=name)\n    self._queue: asyncio.Queue[MessageType | None] = asyncio.Queue()\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._stopping = True\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._stopping\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.run","title":"run abstractmethod async","text":"
run()\n

run the worker

Source code in fluid/utils/worker.py
@abstractmethod\nasync def run(self) -> None:\n    \"\"\"run the worker\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.get_message","title":"get_message async","text":"
get_message(timeout=0.5)\n
Source code in fluid/utils/worker.py
async def get_message(self, timeout: float = 0.5) -> MessageType | None:\n    try:\n        async with asyncio.timeout(timeout):\n            return await self._queue.get()\n    except asyncio.TimeoutError:\n        return None\n    except (asyncio.CancelledError, RuntimeError):\n        if not self.is_stopping():\n            raise\n    return None\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.queue_size","title":"queue_size","text":"
queue_size()\n
Source code in fluid/utils/worker.py
def queue_size(self) -> int:\n    return self._queue.qsize()\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    status = await super().status()\n    status.update(queue_size=self.queue_size())\n    return status\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumer.send","title":"send","text":"
send(message)\n

Send a message into the worker

Source code in fluid/utils/worker.py
def send(self, message: MessageType | None) -> None:\n    \"\"\"Send a message into the worker\"\"\"\n    self._queue.put_nowait(message)\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker","title":"fluid.utils.worker.QueueConsumerWorker","text":"
QueueConsumerWorker(on_message, name='')\n

Bases: QueueConsumer[MessageType]

A Worker that can receive and consume messages

Source code in fluid/utils/worker.py
def __init__(\n    self,\n    on_message: Callable[[MessageType], Awaitable[None]],\n    name: str = \"\",\n) -> None:\n    super().__init__(name=name)\n    self.on_message = on_message\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.on_message","title":"on_message instance-attribute","text":"
on_message = on_message\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.send","title":"send","text":"
send(message)\n

Send a message into the worker

Source code in fluid/utils/worker.py
def send(self, message: MessageType | None) -> None:\n    \"\"\"Send a message into the worker\"\"\"\n    self._queue.put_nowait(message)\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    status = await super().status()\n    status.update(queue_size=self.queue_size())\n    return status\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._stopping = True\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._stopping\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.get_message","title":"get_message async","text":"
get_message(timeout=0.5)\n
Source code in fluid/utils/worker.py
async def get_message(self, timeout: float = 0.5) -> MessageType | None:\n    try:\n        async with asyncio.timeout(timeout):\n            return await self._queue.get()\n    except asyncio.TimeoutError:\n        return None\n    except (asyncio.CancelledError, RuntimeError):\n        if not self.is_stopping():\n            raise\n    return None\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.queue_size","title":"queue_size","text":"
queue_size()\n
Source code in fluid/utils/worker.py
def queue_size(self) -> int:\n    return self._queue.qsize()\n
"},{"location":"reference/workers/#fluid.utils.worker.QueueConsumerWorker.run","title":"run async","text":"
run()\n
Source code in fluid/utils/worker.py
async def run(self) -> None:\n    with self.start_running():\n        while not self.is_stopping():\n            message = await self.get_message()\n            if message is not None:\n                await self.on_message(message)\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer","title":"fluid.utils.worker.AsyncConsumer","text":"
AsyncConsumer(dispatcher, name='')\n

Bases: QueueConsumer[MessageType]

A Worker that can dispatch async callbacks

Source code in fluid/utils/worker.py
def __init__(\n    self, dispatcher: AsyncDispatcher[MessageType], name: str = \"\"\n) -> None:\n    super().__init__(name)\n    self.dispatcher: AsyncDispatcher[MessageType] = dispatcher\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.num_workers","title":"num_workers property","text":"
num_workers\n

The number of workers in this worker

"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.AsyncCallback","title":"AsyncCallback instance-attribute","text":"
AsyncCallback\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.dispatcher","title":"dispatcher instance-attribute","text":"
dispatcher = dispatcher\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.send","title":"send","text":"
send(message)\n

Send a message into the worker

Source code in fluid/utils/worker.py
def send(self, message: MessageType | None) -> None:\n    \"\"\"Send a message into the worker\"\"\"\n    self._queue.put_nowait(message)\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    status = await super().status()\n    status.update(queue_size=self.queue_size())\n    return status\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._stopping = True\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._stopping\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.get_message","title":"get_message async","text":"
get_message(timeout=0.5)\n
Source code in fluid/utils/worker.py
async def get_message(self, timeout: float = 0.5) -> MessageType | None:\n    try:\n        async with asyncio.timeout(timeout):\n            return await self._queue.get()\n    except asyncio.TimeoutError:\n        return None\n    except (asyncio.CancelledError, RuntimeError):\n        if not self.is_stopping():\n            raise\n    return None\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.queue_size","title":"queue_size","text":"
queue_size()\n
Source code in fluid/utils/worker.py
def queue_size(self) -> int:\n    return self._queue.qsize()\n
"},{"location":"reference/workers/#fluid.utils.worker.AsyncConsumer.run","title":"run async","text":"
run()\n
Source code in fluid/utils/worker.py
async def run(self) -> None:\n    with self.start_running():\n        while not self.is_stopping():\n            message = await self.get_message()\n            if message is not None:\n                await self.dispatcher.dispatch(message)\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers","title":"fluid.utils.worker.Workers","text":"
Workers(\n    *workers,\n    name=\"\",\n    stopping_grace_period=STOPPING_GRACE_PERIOD\n)\n

Bases: MultipleWorkers

A worker managing several workers

Source code in fluid/utils/worker.py
def __init__(\n    self,\n    *workers: Worker,\n    name: str = \"\",\n    stopping_grace_period: int = settings.STOPPING_GRACE_PERIOD,\n) -> None:\n    super().__init__(\n        *workers, name=name, stopping_grace_period=stopping_grace_period\n    )\n    self._workers_task: asyncio.Task | None = None\n    self._delayed_callbacks: list[\n        tuple[Callable[[], None], float, float, float]\n    ] = []\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.Workers.num_workers","title":"num_workers property","text":"
num_workers\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.running","title":"running property","text":"
running\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    return await self._workers.status()\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._workers.gracefully_stop()\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._workers.is_stopping()\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.create_task","title":"create_task","text":"
create_task(worker)\n
Source code in fluid/utils/worker.py
def create_task(self, worker: Worker) -> asyncio.Task:\n    return asyncio.create_task(\n        self._run_worker(worker), name=f\"{self.worker_name}-{worker.worker_name}\"\n    )\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.on_shutdown","title":"on_shutdown async","text":"
on_shutdown()\n

called after the workers are stopped

Source code in fluid/utils/worker.py
async def on_shutdown(self) -> None:\n    \"\"\"called after the workers are stopped\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.shutdown","title":"shutdown async","text":"
shutdown()\n

shutdown the workers

Source code in fluid/utils/worker.py
async def shutdown(self) -> None:\n    \"\"\"shutdown the workers\"\"\"\n    if self._has_shutdown:\n        return\n    self._has_shutdown = True\n    logger.warning(\n        \"gracefully stopping %d workers: %s\",\n        self.num_workers,\n        \", \".join(w.worker_name for w in self._workers.workers),\n    )\n    self.gracefully_stop()\n    try:\n        async with asyncio.timeout(self._stopping_grace_period):\n            await self.wait_for_exit()\n        await self.on_shutdown()\n        return\n    except asyncio.TimeoutError:\n        logger.warning(\n            \"could not stop workers %s gracefully after %s\"\n            \" seconds - force shutdown\",\n            \", \".join(\n                task.get_name() for task in self._workers.tasks if not task.done()\n            ),\n            self._stopping_grace_period,\n        )\n    except asyncio.CancelledError:\n        pass\n    self._force_shutdown = True\n    self._workers.cancel()\n    try:\n        await self.wait_for_exit()\n    except asyncio.CancelledError:\n        pass\n    await self.on_shutdown()\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.bail_out","title":"bail_out","text":"
bail_out(reason, code=1)\n
Source code in fluid/utils/worker.py
def bail_out(self, reason: str, code: int = 1) -> None:\n    self.gracefully_stop()\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.safe_run","title":"safe_run async","text":"
safe_run()\n

Context manager to run a worker safely

Source code in fluid/utils/worker.py
@asynccontextmanager\nasync def safe_run(self) -> AsyncGenerator:\n    \"\"\"Context manager to run a worker safely\"\"\"\n    try:\n        yield\n    except asyncio.CancelledError:\n        if self._force_shutdown:\n            # we are shutting down, this is expected\n            pass\n        raise\n    except Exception as e:\n        reason = f\"unhandled exception while running workers: {e}\"\n        logger.exception(reason)\n        asyncio.get_event_loop().call_soon(self.bail_out, reason, 2)\n    else:\n        # worker finished without error\n        # make sure we are shutting down\n        asyncio.get_event_loop().call_soon(self.bail_out, \"worker exit\", 1)\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.add_workers","title":"add_workers","text":"
add_workers(*workers)\n

add workers to the workers

Source code in fluid/utils/worker.py
def add_workers(self, *workers: Worker) -> None:\n    \"\"\"add workers to the workers\"\"\"\n    workers_, _ = self._workers.workers_tasks()\n    for worker in workers:\n        if worker not in workers_:\n            workers_.append(worker)\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.run","title":"run async","text":"
run()\n

run the workers

Source code in fluid/utils/worker.py
async def run(self) -> None:\n    \"\"\"run the workers\"\"\"\n    with self.start_running():\n        async with self.safe_run():\n            workers, _ = self._workers.workers_tasks()\n            self._workers.workers = tuple(workers)\n            self._workers.tasks = tuple(\n                self.create_task(worker) for worker in workers\n            )\n            await asyncio.gather(*self._workers.tasks)\n        await self.shutdown()\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.wait_for_exit","title":"wait_for_exit async","text":"
wait_for_exit()\n
Source code in fluid/utils/worker.py
async def wait_for_exit(self) -> None:\n    if self._workers_task is not None:\n        await self._workers_task\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.remove_workers","title":"remove_workers","text":"
remove_workers(*workers)\n

remove workers from the workers

Source code in fluid/utils/worker.py
def remove_workers(self, *workers: Worker) -> None:\n    \"remove workers from the workers\"\n    workers_, _ = self._workers.workers_tasks()\n    for worker in workers:\n        try:\n            workers_.remove(worker)\n        except ValueError:\n            pass\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.startup","title":"startup async","text":"
startup()\n

start the workers

Source code in fluid/utils/worker.py
async def startup(self) -> None:\n    \"\"\"start the workers\"\"\"\n    if self._workers_task is None:\n        self._workers_task = asyncio.create_task(self.run(), name=self.worker_name)\n        for args in self._delayed_callbacks:\n            self._delayed_callback(*args)\n        self._delayed_callbacks = []\n
"},{"location":"reference/workers/#fluid.utils.worker.Workers.register_callback","title":"register_callback","text":"
register_callback(\n    callback, seconds, jitter=0.0, periodic=False\n)\n

Register a callback

The callback can be periodic or not.

Source code in fluid/utils/worker.py
def register_callback(\n    self,\n    callback: Callable[[], None],\n    seconds: float,\n    jitter: float = 0.0,\n    periodic: bool | float = False,\n) -> None:\n    \"\"\"Register a callback\n\n    The callback can be periodic or not.\n    \"\"\"\n    if periodic is True:\n        periodic_float = seconds\n    elif periodic is False:\n        periodic_float = 0.0\n    else:\n        periodic_float = periodic\n    if not self.running:\n        self._delayed_callbacks.append((callback, seconds, jitter, periodic_float))\n    else:\n        self._delayed_callback(callback, seconds, jitter, periodic_float)\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers","title":"fluid.utils.worker.DynamicWorkers","text":"
DynamicWorkers(\n    *workers,\n    name=\"\",\n    heartbeat=0.1,\n    stopping_grace_period=STOPPING_GRACE_PERIOD\n)\n

Bases: MultipleWorkers

Source code in fluid/utils/worker.py
def __init__(\n    self,\n    *workers: Worker,\n    name: str = \"\",\n    heartbeat: float | int = 0.1,\n    stopping_grace_period: int = settings.STOPPING_GRACE_PERIOD,\n) -> None:\n    super().__init__(name)\n    self._heartbeat = heartbeat\n    self._workers = WorkerTasks()\n    self._has_shutdown = False\n    self._force_shutdown = False\n    self._stopping_grace_period = stopping_grace_period\n    self.add_workers(*workers)\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.worker_name","title":"worker_name property","text":"
worker_name\n

The name of the worker

"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.num_workers","title":"num_workers property","text":"
num_workers\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.status","title":"status async","text":"
status()\n
Source code in fluid/utils/worker.py
async def status(self) -> dict:\n    return await self._workers.status()\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.gracefully_stop","title":"gracefully_stop","text":"
gracefully_stop()\n
Source code in fluid/utils/worker.py
def gracefully_stop(self) -> None:\n    self._workers.gracefully_stop()\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.is_running","title":"is_running","text":"
is_running()\n
Source code in fluid/utils/worker.py
def is_running(self) -> bool:\n    return self._running\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.is_stopping","title":"is_stopping","text":"
is_stopping()\n
Source code in fluid/utils/worker.py
def is_stopping(self) -> bool:\n    return self._workers.is_stopping()\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.start_running","title":"start_running","text":"
start_running()\n
Source code in fluid/utils/worker.py
@contextmanager\ndef start_running(self) -> Generator:\n    if self._running:\n        raise RuntimeError(\"Worker is already running\")\n    self._running = True\n    try:\n        logger.info(\"%s started running\", self.worker_name)\n        yield\n    finally:\n        self._running = False\n        logger.warning(\"%s stopped running\", self.worker_name)\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.create_task","title":"create_task","text":"
create_task(worker)\n
Source code in fluid/utils/worker.py
def create_task(self, worker: Worker) -> asyncio.Task:\n    return asyncio.create_task(\n        self._run_worker(worker), name=f\"{self.worker_name}-{worker.worker_name}\"\n    )\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.on_shutdown","title":"on_shutdown async","text":"
on_shutdown()\n

called after the workers are stopped

Source code in fluid/utils/worker.py
async def on_shutdown(self) -> None:\n    \"\"\"called after the workers are stopped\"\"\"\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.shutdown","title":"shutdown async","text":"
shutdown()\n

shutdown the workers

Source code in fluid/utils/worker.py
async def shutdown(self) -> None:\n    \"\"\"shutdown the workers\"\"\"\n    if self._has_shutdown:\n        return\n    self._has_shutdown = True\n    logger.warning(\n        \"gracefully stopping %d workers: %s\",\n        self.num_workers,\n        \", \".join(w.worker_name for w in self._workers.workers),\n    )\n    self.gracefully_stop()\n    try:\n        async with asyncio.timeout(self._stopping_grace_period):\n            await self.wait_for_exit()\n        await self.on_shutdown()\n        return\n    except asyncio.TimeoutError:\n        logger.warning(\n            \"could not stop workers %s gracefully after %s\"\n            \" seconds - force shutdown\",\n            \", \".join(\n                task.get_name() for task in self._workers.tasks if not task.done()\n            ),\n            self._stopping_grace_period,\n        )\n    except asyncio.CancelledError:\n        pass\n    self._force_shutdown = True\n    self._workers.cancel()\n    try:\n        await self.wait_for_exit()\n    except asyncio.CancelledError:\n        pass\n    await self.on_shutdown()\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.bail_out","title":"bail_out","text":"
bail_out(reason, code=1)\n
Source code in fluid/utils/worker.py
def bail_out(self, reason: str, code: int = 1) -> None:\n    self.gracefully_stop()\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.safe_run","title":"safe_run async","text":"
safe_run()\n

Context manager to run a worker safely

Source code in fluid/utils/worker.py
@asynccontextmanager\nasync def safe_run(self) -> AsyncGenerator:\n    \"\"\"Context manager to run a worker safely\"\"\"\n    try:\n        yield\n    except asyncio.CancelledError:\n        if self._force_shutdown:\n            # we are shutting down, this is expected\n            pass\n        raise\n    except Exception as e:\n        reason = f\"unhandled exception while running workers: {e}\"\n        logger.exception(reason)\n        asyncio.get_event_loop().call_soon(self.bail_out, reason, 2)\n    else:\n        # worker finished without error\n        # make sure we are shutting down\n        asyncio.get_event_loop().call_soon(self.bail_out, \"worker exit\", 1)\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.add_workers","title":"add_workers","text":"
add_workers(*workers)\n

add workers to the workers

They can be added while the workers are running.

Source code in fluid/utils/worker.py
def add_workers(self, *workers: Worker) -> None:\n    \"\"\"add workers to the workers\n\n    They can be added while the workers are running.\n    \"\"\"\n    workers_, tasks_ = self._workers.workers_tasks()\n    for worker in workers:\n        workers_.append(worker)\n        tasks_.append(self.create_task(worker))\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.run","title":"run async","text":"
run()\n
Source code in fluid/utils/worker.py
async def run(self) -> None:\n    with self.start_running():\n        while not self.is_stopping():\n            for worker, task in zip(self._workers.workers, self._workers.tasks):\n                if worker.is_stopping() or task.done():\n                    break\n            await asyncio.sleep(self._heartbeat)\n        await self.shutdown()\n
"},{"location":"reference/workers/#fluid.utils.worker.DynamicWorkers.wait_for_exit","title":"wait_for_exit async","text":"
wait_for_exit()\n
Source code in fluid/utils/worker.py
async def wait_for_exit(self) -> None:\n    await asyncio.gather(*self._workers.tasks)\n
"},{"location":"tutorials/","title":"Tutorials","text":"

The step-by-step guides, the how-to's, the recipes, and all the Aio Fluid parts you can use in your applications.

"},{"location":"tutorials/db/","title":"Async Database","text":"

The fluid.db module provides a simple asynchronous interface to interact with postgres databases. It is built on top of the sqlalchemy and asyncpg libraries.

"},{"location":"tutorials/dispatchers/","title":"Event Dispatchers","text":"

Event dispatchers are a way to decouple the event source from the event handler. This is useful when you want to have multiple handlers for the same event, or when you want to have a single handler for multiple events.

from fluid.utils.dispatcher import SimpleDispatcher\n\nsimple = SimpleDispatcher[Any]()\n\nsimple.dispatch(\"you can dispatch anything to this generic dispatcher\")\n
"},{"location":"tutorials/scheduler/","title":"Task Queue","text":"

This module has a lightweight implementation of a distributed task producer (TaskScheduler) and consumer (TaskConsumer). The middleware for distributing tasks can be configured via the Broker interface. A redis broker is provided for convenience.

"},{"location":"tutorials/scheduler/#tasks","title":"Tasks","text":"

Tasks are standard python async functions decorated with the task decorator.

from fluid.scheduler import task, TaskRun\n\n@task\nasync def say_hi(ctx: TaskRun):\n    return \"Hi!\"\n

There are two types of tasks implemented

  • Simple concurrent tasks - they run concurrently with the task consumer - thy must be IO type tasks (no heavy CPU bound operations)
  from fluid.scheduler import task, TaskRun\n\n  @task\n  async def fecth_data(ctx: TaskRun):\n      # fetch data\n      data = await http_cli.get(\"https://...\")\n      data_id = await datastore_cli.stote(data)\n      # trigger another task\n      ctx.task_manager.queue(\"heavy_calculation\", data_id=data_id)\n
  • CPU bound tasks - they run on a subprocess
from fluid.scheduler import task, TaskRun\n\n@task(cpu_bound=True)\nasync def heavy_calculation(ctx: TaskRun):\n    data = await datastore_cli.get(ctx.params[\"data_id\"])\n    # perform some heavy calculation\n    ...\n    # trigger another task\n    ctx.task_manager.queue(\"fetch_data\")\n

Both tasks can be periodically scheduled via the schedule keyword argument:

from datetime import timedelta\nfrom fluid.scheduler import task, TaskContext, every\n\n@task(schedule=every(timedelta(seconds=1)))\nasync def scheduled(context: TaskContext) -> str:\n    await asyncio.sleep(0.1)\n    return \"OK\"\n
"},{"location":"tutorials/scheduler/#broker","title":"Broker","text":"

A Task broker needs to implement three abstract methods

  @abstractmethod\n  async def queue_task(self, queued_task: QueuedTask) -> TaskRun:\n      \"\"\"Queue a task\"\"\"\n\n  @abstractmethod\n  async def get_task_run(self) -> Optional[TaskRun]:\n      \"\"\"Get a Task run from the task queue\"\"\"\n\n  @abstractmethod\n  async def queue_length(self) -> Dict[str, int]:\n      \"\"\"Length of task queues\"\"\"\n

The library ships a Redis broker for convenience.

from fluid.scheduler import Broker\n\nredis_broker = Broker.from_url(\"redis://localhost:6349\")\n
"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index e5fff02..c5853cd 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,50 +2,50 @@ https://aio-fluid.com/ - 2024-10-20 + 2024-11-21 https://aio-fluid.com/reference/ - 2024-10-20 + 2024-11-21 https://aio-fluid.com/reference/dispatchers/ - 2024-10-20 + 2024-11-21 https://aio-fluid.com/reference/task_broker/ - 2024-10-20 + 2024-11-21 https://aio-fluid.com/reference/task_manager/ - 2024-10-20 + 2024-11-21 https://aio-fluid.com/reference/task_run/ - 2024-10-20 + 2024-11-21 https://aio-fluid.com/reference/tast_consumer/ - 2024-10-20 + 2024-11-21 https://aio-fluid.com/reference/workers/ - 2024-10-20 + 2024-11-21 https://aio-fluid.com/tutorials/ - 2024-10-20 + 2024-11-21 https://aio-fluid.com/tutorials/db/ - 2024-10-20 + 2024-11-21 https://aio-fluid.com/tutorials/dispatchers/ - 2024-10-20 + 2024-11-21 https://aio-fluid.com/tutorials/scheduler/ - 2024-10-20 + 2024-11-21 \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index b693a9a..cac15c4 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ diff --git a/tutorials/db/index.html b/tutorials/db/index.html index a7f64af..0ceb153 100644 --- a/tutorials/db/index.html +++ b/tutorials/db/index.html @@ -18,7 +18,7 @@ - + diff --git a/tutorials/dispatchers/index.html b/tutorials/dispatchers/index.html index 1a87610..9271d93 100644 --- a/tutorials/dispatchers/index.html +++ b/tutorials/dispatchers/index.html @@ -18,7 +18,7 @@ - + diff --git a/tutorials/index.html b/tutorials/index.html index 1ed315d..e919a56 100644 --- a/tutorials/index.html +++ b/tutorials/index.html @@ -18,7 +18,7 @@ - + diff --git a/tutorials/scheduler/index.html b/tutorials/scheduler/index.html index 789a6e3..3c0b4ac 100644 --- a/tutorials/scheduler/index.html +++ b/tutorials/scheduler/index.html @@ -16,7 +16,7 @@ - +