Большой рефакторинг с кодексом
This commit is contained in:
89
README.md
89
README.md
@@ -1,11 +1,12 @@
|
||||
# Config Manager
|
||||
## Описание
|
||||
Пакет предназначен для запуска приложений.
|
||||
Класс ConfigManager реализует точку входа программы и предоставляет актуальную конфигурацию приложения, а также упрощает настройку логирования.
|
||||
Пакет предназначен для запуска приложений с периодическим выполнением логики, перезагрузкой конфига и управлением по HTTP API.
|
||||
|
||||
## ConfigManager v2: устройство и взаимосвязи
|
||||
**Контракт:** приложение наследует **ConfigManagerV2**, переопределяет **execute()** (периодическая работа). Управление (старт/стоп, health) — через каналы, которые создаются снаружи и передаются в конструктор в **control_channels** (в т.ч. HttpControlChannel для API).
|
||||
|
||||
**ConfigManager v2** — точка входа приложения. Он наследует внутреннюю логику от **\_RuntimeController** (циклы воркера и обновления конфига, запуск/остановка каналов управления).
|
||||
## ConfigManager: устройство и взаимосвязи
|
||||
|
||||
**ConfigManager** (класс ConfigManagerV2) — точка входа приложения. Он наследует внутреннюю логику от **\_RuntimeController** (циклы воркера и обновления конфига, запуск/остановка каналов управления).
|
||||
|
||||
**Ядро (core):**
|
||||
- **ConfigLoader** — читает конфиг из файла (YAML/JSON), считает хеш и отдаёт конфиг только при изменении; при ошибке парсинга возвращает последний валидный конфиг.
|
||||
@@ -19,29 +20,72 @@
|
||||
- **HttpControlChannel** — HTTP API (`/health`, `/actions/start`, `/actions/stop`, `/actions/status`); использует **UvicornServerRunner**; для `/health` вызывает **HealthAggregator.collect()**, для действий — переданные обработчики из **ControlChannelBridge**.
|
||||
- **TelegramControlChannel** — реализация через long polling Telegram; команды `/start`, `/stop`, `/status` вызывают переданные обработчики.
|
||||
|
||||
**Поток работы:** при `start()` менеджер собирает список каналов: при `management.enabled: true` в **config.yaml** (секция `management`) добавляется **HttpControlChannel**, плюс опционально **control_channel** / **control_channels** в конструкторе. Все каналы поднимаются с одним **ControlChannelBridge**, затем запускаются два цикла: **WorkerLoop** и периодическое обновление конфига через **ConfigLoader**. Остановка по halt (через любой канал) завершает оба цикла; в конце останавливаются все каналы. Настройки HTTP-канала (host, port, timeout, health_timeout) задаются в config.yaml в секции `management`.
|
||||
**Поток работы:** при `start()` менеджер поднимает каналы из **control_channels** (заданные снаружи), затем запускает два цикла: **WorkerLoop** и периодическое обновление конфига через **ConfigLoader**. Управление по API: `/health`, `/actions/start`, `/actions/stop` — если в control_channels передан **HttpControlChannel**. Остановка по halt завершает оба цикла; в конце останавливаются все каналы.
|
||||
|
||||
## Диаграмма классов (v1 и v2)
|
||||
## Запуск приложения с ConfigManagerV2 и HttpControlChannel
|
||||
|
||||
1. **Наследуйте ConfigManagerV2** и реализуйте метод `execute()` (в нём — ваша периодическая работа). При необходимости переопределите `get_health_status()` для кастомного ответа `/health`.
|
||||
|
||||
2. **Создайте каналы снаружи и передайте в конструктор.** Для HTTP API создайте **HttpControlChannel**; для health нужен колбэк менеджера — передайте **control_channels** как фабрику (lambda, получающую менеджер):
|
||||
```python
|
||||
from config_manager.v2.control import HttpControlChannel
|
||||
|
||||
app = MyApp(
|
||||
str(path_to_config),
|
||||
control_channels=lambda m: [
|
||||
HttpControlChannel(
|
||||
host="0.0.0.0",
|
||||
port=8000,
|
||||
timeout=3,
|
||||
health_provider=m.get_health_provider(),
|
||||
)
|
||||
],
|
||||
)
|
||||
```
|
||||
Либо передайте готовый список каналов: `control_channels=[channel1, channel2]`.
|
||||
|
||||
3. **Запустите из async-контекста:** `await app.start()` или `asyncio.create_task(app.start())` для фона. Остановка: `await app.stop()` или запрос `/actions/stop` по HTTP.
|
||||
|
||||
**Минимальный пример с HTTP API:**
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from config_manager import ConfigManager
|
||||
from config_manager.v2.control import HttpControlChannel
|
||||
|
||||
class MyApp(ConfigManager):
|
||||
def execute(self) -> None:
|
||||
pass # ваша периодическая работа
|
||||
|
||||
async def main() -> None:
|
||||
app = MyApp(
|
||||
str(Path(__file__).parent / "config.yaml"),
|
||||
control_channels=lambda m: [
|
||||
HttpControlChannel(
|
||||
host="0.0.0.0", port=8000, timeout=3,
|
||||
health_provider=m.get_health_provider(),
|
||||
)
|
||||
],
|
||||
)
|
||||
asyncio.create_task(app.start())
|
||||
await asyncio.sleep(3600)
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
Готовый пример: `tests/test_app.py`.
|
||||
|
||||
## Диаграмма классов
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
direction TB
|
||||
|
||||
class ConfigManager {
|
||||
+str path
|
||||
+Any config
|
||||
+float update_interval
|
||||
+float work_interval
|
||||
-Event _halt
|
||||
-Task _task
|
||||
+start() async
|
||||
+stop() async
|
||||
+execute()*
|
||||
-_worker_loop() async
|
||||
-_periodic_update_loop() async
|
||||
-_update_config() async
|
||||
}
|
||||
|
||||
class ConfigManagerV2 {
|
||||
+str path
|
||||
+Any config
|
||||
@@ -135,7 +179,6 @@ classDiagram
|
||||
+int port
|
||||
}
|
||||
|
||||
ConfigManager --> LogManager : использует
|
||||
ConfigManagerV2 --|> _RuntimeController : наследует
|
||||
ConfigManagerV2 --> ConfigLoader : использует
|
||||
ConfigManagerV2 --> LogManager : использует
|
||||
@@ -150,7 +193,7 @@ classDiagram
|
||||
ControlChannelBridge ..> ControlChannel : on_start, on_stop, on_status
|
||||
```
|
||||
|
||||
## Логирование (v2)
|
||||
## Логирование
|
||||
Логирование настраивается из конфигурационного файла только если в нём есть секция **`log`** в формате [dictConfig](https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig). Если секции `log` нет, менеджер пишет предупреждение в лог, а уровень Python по умолчанию (WARNING) сохраняется — сообщения INFO/DEBUG могут не отображаться.
|
||||
|
||||
**Как проверить, что конфигурация логирования применилась:**
|
||||
|
||||
Reference in New Issue
Block a user