Рефакторинг рендера [этап 5/6]: таблица медиа-типов в post_parser (два коммита: 5a байт-в-байт + 5b фиксы) #32
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Часть эпика «Рефакторинг пайплайна рендера». Спека:
plans/2026-07-05-render-pipeline-refactor.md(v5), раздел «Этап 5». Зависимость: этап 3 (#30). Независим от этапа 4 — можно параллельно. РОВНО ДВА КОММИТА: 5a (рефакторинг байт-в-байт) и 5b (зарегистрированные фиксы). Line-refs действительны дляf9550d8.Цель
В post_parser три параллельные «лестницы» выбора media-объекта, синхронизируемые вручную:
_get_file_unique_id(dict-маппинг, branch:1258–1284),_save_media_file_ids(if/elif, branch:1304–1359),_generate_html_media(elif-каскад, branch:838–959). Одна точка правды: таблицаMEDIA_SOURCES(селектор + render kind) + renderer-функции.Коммит 5a — рефакторинг байт-в-байт
Шаг 0 (обязательный, до правок): fragment-level снапшоты
_generate_html_media(ДО санитайза) для всех типов: PHOTO, VIDEO, ANIMATION, VIDEO_NOTE, AUDIO, VOICE, STICKER img/video, DOCUMENT pdf/обычный, LIVE_PHOTO, STORY video/photo, POLL с description_media, PAID_MEDIA, WEB_PAGE с фото (даёт ПУСТОЙ media-div — воспроизводить), и edge-ветки: WEB_PAGE без фото (незакрытый div — в 5a воспроизводится БУКВАЛЬНО), file_unique_id is None, channel_username is None, гейт webpage-превью «text ≤ 10». Это отдельный слой эталонов, НЕ смешивать с feed-golden этапа 0.PDF идёт ЧЕРЕЗ таблицу:
_select_documentвозвращает'pdf'дляapplication/pdf;RENDERERS['pdf']используетctx.tg_link(сборкаt.me/c/...vst.me/...— в_generate_html_media, как сейчас, branch:887–893).Потребители:
_get_file_unique_id,_save_media_file_ids,_generate_html_media— все через селектор. НЕ трогать: PAID_MEDIA-блок, NO_IMAGE-гейт, POLL-гейт, guard channel_username, webpage-превью.Ограничение:
flags.append(...)НЕ выносить из_extract_flags— эндпоинт/flagsпарсит его исходник черезinspect.getsource(branch:152–165). Тест:get_all_possible_flags()непуст и содержит известные флаги.Межмодульный инвариант-тест: для каждого типа из MEDIA_SOURCES объект, выбранный селектором, находится
api_server.find_file_id_in_messageпо своемуfile_unique_id(иначе новый entry таблицы даст URL, который /media не разрешит → 404). Ограничение: обе стороны на моках — багов класса «обе ждут атрибут, которого нет у реального kurigram-объекта» тест не ловит; сверка атрибутов моков сpyrogram.types— опциональный best-effort. САМУfind_file_id_in_messageНЕ сливать с таблицей: её контракт шире (ищет любой скачиваемый объект независимо от message.media, включая explanation_media, который рендер сознательно игнорирует).Коммит 5b — зарегистрированные фиксы
message.video), в_save_media_file_ids.</div class="message-media">во всех ветках (сейчас приfile_unique_id is Nonediv не закрывается).DoD
Прямые атрибутные цепочки вида
m.photo.file_unique_id— только в селекторах таблицы, хелперах_poll_media_object/_story_media_object(реализация селекторов) и_format_webpage. Потребители извлекают uid единообразно —getattr(selected_obj, 'file_unique_id', None)от объекта, возвращённого селектором; санкционированные точки:_get_file_unique_id,_save_media_file_ids, инвариант-тест. Три лестницы удалены; снапшоты обоих уровней + pytest зелёные; инвариант-тест зелёный.Общие правила эпика
Ветка
refactor/render-pipelineот f9550d8; комментарии — English; точечные правки; 5a обязан дать байт-идентичные fragment-снапшоты.Дополнение v6 (обязательное): источник сообщений для fragment-снапшотов шага 0 — ЗАПИСАННЫЙ КОРПУС этапа 0 (#27), а не моки, для всех типов, которые в нём есть. По инвентарю корпуса доступны: PHOTO, VIDEO, ANIMATION, VIDEO_NOTE, AUDIO, VOICE, STICKER, DOCUMENT, POLL, WEB_PAGE (с фото и без), GIVEAWAY, GIVEAWAY_WINNERS. Моки — ТОЛЬКО для отсутствующих в корпусе типов: PAID_MEDIA, STORY, LIVE_PHOTO, CHECKLIST, CONTACT/LOCATION/VENUE/DICE/GAME/INVOICE/UNSUPPORTED. Ограничение инвариант-теста «ложная зелень на моках» тем самым сужается до этих mock-only типов. Детали — спека v6, этап 5a.