Рефакторинг рендера [этап 1/6]: sanitizer.py + санитайз per-post внутри _render_pipeline #28
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), раздел «Этап 1». Зависимость: этап 0 (#27) — golden-эталоны должны существовать ДО этого этапа. Заменяет закрытое #22 (писалось по v1 спеки). Line-refs действительны дляf9550d8.Цель
Один конфиг bleach на весь проект, один санитайз на пост, ноль thread-hop'ов в циклах. Сейчас конфиг скопирован трижды и разъехался, RSS-путь делает
asyncio.to_thread+ новыйCSSSanitizer+ вложенную функцию НА КАЖДЫЙ пост — при том, что_render_pipelineуже выполняется в worker-треде.Пункты реестра §3, фиксируемые этим этапом
s,delразрешены и в фидах (сейчас зачёркивание выживает только в одиночном посте).<hr class="post-divider">реально виден в HTML-фиде (сейчас вырезается: добавляется до санитайза,hrне в whitelist, strip=True).html_sanitization_error/rss_html_sanitization_error/html_final_sanitization_error) объединяются в ОДНО —html_sanitization_error+ log_context; grep-правила мониторинга обновить при деплое.Новый модуль
sanitizer.pyПравки
post_parser.py::_sanitize_html(branch:702–737) → делегат вsanitizer.sanitize_html. Fail-open веткаreturn html_rawисчезает (§3.2). Метод оставить — его зовётprocess_message.rss_generator.py::_render_pipeline(branch:324–344): добавить параметрchannel(только для логов); после_render_messages_groups(т.е. ПОСЛЕ фильтров — не тратить bleach на отфильтрованное):p['html'] = sanitize_html(p['html'], log_context=f"channel {channel}, message_id {p['message_id']}"). ВАЖНО: близнецы ещё не слиты —channelдобавить в ОБА вызоваto_thread(_render_pipeline, …)(branch:433 и 657)._sanitize_sync+to_thread;fe.content(content=post['html'], type='CDATA')напрямую.fe.description(post['text']...)не трогать — feedgen сам XML-эскейпит._concat_html/_sanitize_sync+ обаto_thread;html = '\n<hr class="post-divider">\n'.join(...)— join ПОСЛЕ санитайза.rss_module.HTMLSanitizer— перенацелить на модульsanitizer(пометка «API relocation, no behavior change»).get_post(одиночный пост) НЕ трогать.Тесты
tests/test_sanitizer.py:s/delвыживают;script/onerrorВЫРЕЗАЮТСЯ (не эскейпятся в текст — проверка strip=True);tg://href выживает; fail-closed при исключении (mock bleach → exception → эскейпнутый результат, лог с именемhtml_sanitization_error);<hr class="post-divider">присутствует в выводеgenerate_channel_html.DoD
Одно определение allowed_tags в репо; в rss_generator нет прямых вызовов bleach; pytest + golden зелёные (golden-дифф — только пп.1/3/4 с комментарием).
Общие правила эпика
Ветка
refactor/render-pipelineот f9550d8; один этап = один коммит; комментарии — English; точечные правки; правки тестов — только по реестру §3 или как API relocation с пометкой.