Skip to content

🔃 Колбек-функции

Класс BaseModule предоставляет несколько методов, которые действуют как callback, позволяя вам выполнять пользовательский код в определенные моменты существования модуля. Вы можете переопределить эти методы в своем классе модуля.

  • def on_init(self):
    • Вызывается после завершения инициализации Stage 2 (self.stage2()), что означает, что все стандартные обработчики были зарегистрированы, а все ModuleExtensions были загружены и инициализированы.
    • Это хорошее место для настройки внутреннего состояния, загрузки данных из источников, не связанных с базой данных, или запуска фоновых задач, которые не зависят напрямую от готовности базы данных.

    INFO

    Примечание:** Доступность базы данных (self.db) не гарантируется на данном этапе, так как настройка базы данных происходит асинхронно. Если вам нужна БД, используйте on_db_ready.

  • async def on_db_ready(self):
    • Вызывается после успешной инициализации базы данных модуля (self.db) и создания таблиц, определенных в self.db_meta (если они не существовали).
    • Это гарантированная точка, в которой вы можете безопасно взаимодействовать с базой данных вашего модуля сразу после запуска.
    • Используйте его для таких задач, как загрузка начального состояния из базы данных, выполнение проверки данных при запуске или инициализация компонентов, зависящих от базы данных.
  • def on_unload(self):
    • Вызывается непосредственно до того, как модуль будет полностью выгружен ModuleLoader. Это происходит во время таких операций, как перезагрузка, обновление или деинсталляция модуля.
    • Важно: Используйте этот метод, чтобы выполнить все необходимые действия по очистке. Это включает в себя:
      • Остановку фоновых задач или циклов asyncio, запущенных модулем.
      • Сохранение любого изменчивого состояния на диск или в базу данных.
      • Закрытие сетевых соединений или освобождение других ресурсов.

      WARNING

      Неправильная очистка в on_unload может привести к утечке ресурсов (памяти, дескрипторов файлов, сетевых сокетов) или появлению "зомби-задач"**.

Пример использования:

python
# main.py (внутри вашего класса BaseModule)
import asyncio

class MyBackgroundTaskModule(BaseModule):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._background_task = None
        self._some_state = {} # Example state

    def on_init(self):
        """Загрузите состояние из файла, возможно, запустите задачу, если DB не нужна."""
        self.logger.info("Модуль инициализирован (on_init). Загружаем состояние...")
        try:
            # Пример: Загрузка состояния из гипотетического файла
            # with open("module_state.json", "r") as f:
            #     self._some_state = json.load(f)
            pass # Замените на реальную логику загрузки
        except FileNotFoundError:
            self.logger.warning("Стейт файл не найден, начинаем заново.")
            self._some_state = {'runs': 0}

        # Если задача не требует немедленного DB, можно начать здесь
        # self._start_task()

    async def on_db_ready(self):
        """БД готова, загрузите состояние БД, запустите зависящие от БД задачи."""
        self.logger.info("База данных готова (on_db_ready).")
        # Пример: Загрузка чего-либо из БД
        # async with self.db.session_maker() as session:
        #     config_value = await session.scalar(...)
        #     self._some_state['db_config'] = config_value

        # Запустите задачу сейчас, если она зависит от БД
        self._start_task()

    def _start_task(self):
        """Помощник для запуска фоновой задачи."""
        if self._background_task is None or self._background_task.done():
            self.logger.info("Запуск фоновой задачи...")
            self._background_task = asyncio.create_task(self._run_background_job())
        else:
             self.logger.warning("Задача уже выполняется.")

    async def _run_background_job(self):
        """Пример асинхронной задачи."""
        try:
            while True:
                self.logger.info(f"Фоновая задача запущена... Количество запусков: {self._some_state.get('runs', 0)}")
                self._some_state['runs'] = self._some_state.get('runs', 0) + 1
                # При необходимости получите доступ к self.db здесь (убедитесь, что он готов через on_db_ready).
                await asyncio.sleep(60) # Выполнять каждую минуту
        except asyncio.CancelledError:
            self.logger.info("Фоновое задание отменено")
        except Exception as e:
            self.logger.error(f"Фоновое задание не выполнено:{e}", exc_info=True)
        finally:
            self.logger.info("Фоновое задание выполнено")


    def on_unload(self):
        """Очистка: Отменить задание, сохранить состояние."""
        # 1. Отмена фонового задания
        if self._background_task and not self._background_task.done():
            self.logger.info("Cancelling background task...")
            self._background_task.cancel()
            # Хорошей практикой является кратковременное ожидание или обработка завершения задачи.
            # но по возможности сохранять on_unload синхронным для простоты во время выключения.
            # Для сложной очистки рассмотрите паттерны, включающие asyncio.gather/wait_for
            # внутри асинхронного помощника, вызываемого отсюда, если это строго необходимо.

        # 2. Сохранить состояние
        # try:
        #     self.logger.info("Сохранение состояния в файл...")
        #     # with open("module_state.json", "w") as f:
        #     #     json.dump(self._some_state, f)
        # except Exception as e:
        #      self.logger.error(f"Не удалось сохранить состояние: {e}")

        self.logger.info("Очистка завершена.")