from asyncio import all_tasks, CancelledError, create_task, current_task, gather, get_running_loop, run from loguru import logger from signal import SIGINT, SIGTERM from typing import Callable from .containers import Container from .core.logging import setup_logging async def handle_shutdown_signal(*args) -> None: logger.info("Received shutdown signal.") for arg in args: await arg() logger.info("Executed shutdown tasks.") for task in all_tasks(): if task is not current_task() and not task.done(): task.cancel() logger.trace("Cancelled task {}.", task.get_name()) logger.info("Cancelled all tasks.") def create_shutdown_signal_handler(*args) -> Callable[[], None]: def shutdown_signal_handler() -> None: create_task(handle_shutdown_signal(*args)) return shutdown_signal_handler async def main() -> None: # Setup logging. setup_logging() logger.info("Starting application…") # Set up dependency injection container. container = Container() container.wire(packages=["ctp_slack_bot"]) logger.debug("Created dependency injection container with providers: {}", '; '.join(container.providers)) # Initialize/instantiate services which should be available from the start. await container.content_ingestion_service() await container.question_dispatch_service() http_server = await container.http_server() slack_service = await container.slack_service() task_service = await container.task_service() logger.debug("Initialized services.") # Install the shutdown signal handler. shutdown_signal_handler = create_shutdown_signal_handler(http_server.stop, slack_service.stop, task_service.stop) loop = get_running_loop() loop.add_signal_handler(SIGINT, shutdown_signal_handler) loop.add_signal_handler(SIGTERM, shutdown_signal_handler) # Start the HTTP server and Slack socket mode handler in the background; clean up resources when shut down. try: logger.info("Starting services…") await gather(http_server.start(), slack_service.start(), task_service.start()) except CancelledError: logger.info("Shutting down application…") finally: await container.shutdown_resources() if __name__ == "__main__": run(main())