Elexam Core является библиотекой для микро сервисов с базовым функционалом и форматированием.
EventDict указан как фактический тип параметра из аннотации, MutableMapping[str, Any] оставлен как пояснение эквивалентности. |
||
|---|---|---|
| src/elexam_core | ||
| .gitignore | ||
| .python-version | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
elexam-core
Общая библиотека платформы Elexam — единый источник правды для envelope ответа, trace_id, structlog, каталога ошибок, ULID и http-клиента. Все сервисы зависят от конкретной версии этой библиотеки, чтобы поведение было гарантированно одинаковым.
Что внутри
| Модуль | Назначение |
|---|---|
elexam_core.context |
ContextVar[str] для хранения trace_id в пределах async-запроса (async-safe). Используется остальными модулями внутри. |
elexam_core.middleware |
TraceMiddleware для FastAPI/Starlette — генерирует trace_id (ULID) или подхватывает его из входящего заголовка X-Trace-Id, кладёт в ContextVar. |
elexam_core.envelope |
success() / error() — сборка стандартного конверта ответа API с trace_id, event, processed_at. |
elexam_core.errors |
ErrorCode — Enum-каталог кодов ошибок вида exam.not_found; ERROR_META — HTTP-статус и severity для каждого кода. |
elexam_core.logging |
Настройка structlog: JSON в stdout, trace_id автоматически в каждой строке лога. |
elexam_core.ulid |
Утилита генерации ULID — монотонный, URL-safe, лексикографически сортируемый идентификатор. Используется везде вместо UUID. |
elexam_core.http_client |
httpx-обёртка для межсервисных вызовов: автоматически пробрасывает текущий trace_id из ContextVar в заголовок X-Trace-Id исходящего запроса. |
Установка
Через uv с приватным Gitea PyPI-индексом
Сначала добавь индекс в pyproject.toml своего сервиса (или убедись, что он уже объявлен):
[[tool.uv.index]]
name = "elexam"
url = "https://git.dregomor.ru/api/packages/Returner_org/pypi/simple"
Затем установи пакет:
uv add elexam-core==0.1.0
Напрямую из git по тегу
До того, как пакет залит в registry, или для точного закрепления на конкретном коммите:
uv add "elexam-core @ git+https://git.dregomor.ru/Returner_org/elexam-core.git@v0.1.0"
Быстрый пример
from fastapi import FastAPI
from fastapi.responses import JSONResponse
import structlog
import elexam_core.logging # инициализирует structlog один раз при импорте
from elexam_core.middleware import TraceMiddleware
from elexam_core.envelope import success, error
from elexam_core.errors import ErrorCode, ERROR_META
app = FastAPI()
app.add_middleware(TraceMiddleware) # с этого момента trace_id есть в каждом запросе
log = structlog.get_logger()
@app.get("/exams/{exam_id}")
async def get_exam(exam_id: str):
exam = None # здесь был бы запрос к БД
if exam is None:
log.warning("exam not found", exam_id=exam_id)
# structlog выведет в stdout:
# {"level": "warning", "trace_id": "01J7XR...", "exam_id": "...", "event": "exam not found", ...}
meta = ERROR_META[ErrorCode.EXAM_NOT_FOUND]
return JSONResponse(
status_code=meta["http"],
content=error(
message="Экзамен не найден",
errors=[{"field": "exam_id", "code": ErrorCode.EXAM_NOT_FOUND}],
event_type="exam.get",
),
)
return success(data=exam, event_type="exam.get")
Ответ при успехе:
{
"status": "success",
"data": { "id": "01J7XR...", "title": "Математика" },
"message": "OK",
"errors": null,
"meta": null,
"trace_id": "01J7XRQP2E...",
"event": { "id": "01J7XRQP2F...", "type": "exam.get" },
"processed_at": "2026-06-27T12:34:56.789Z"
}
Один и тот же trace_id попадает в заголовок X-Trace-Id ответа, в тело JSON, в каждую строку лога. Найти по нему всю цепочку запроса через все сервисы можно одним грепом по stdout.