From 1871bc29712d4198ceecdacec5033562d212d430 Mon Sep 17 00:00:00 2001 From: agent_coder Date: Tue, 7 Jul 2026 16:58:42 +0300 Subject: [PATCH] =?UTF-8?q?refactor(render):=20=D1=8D=D1=82=D0=B0=D0=BF=20?= =?UTF-8?q?6=20=E2=80=94=20=D0=BA=D0=BE=D1=81=D0=BC=D0=B5=D1=82=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=20(#33,=20#34)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Финальный этап render-pipeline рефактора. Чисто косметика, вывод байт-в-байт неизменен (golden-оракул нетронут, 0 изменений в tests/test_data/). 1. `_wrap_post_html(body, footer)` — единая обёртка поста вместо трёх байт-идентичных копий (_format_html + обе ветки _render_messages_groups). 2. Инлайн-стили 400px/200px/max-width → именованные константы. 3. `_trim_messages_groups` заинлайнен как `[:limit]`; импорты в test_stage4_eventloop.py/test_group_ids.py перевязаны на _render_pipeline без потери покрытия тримминга. 4. Устаревшие комментарии «stage-4»/«4.4» переписаны под текущую границу санитайза. 5. Фильтр exclude_flags → list-comprehension (эквивалент по де Моргану); фильтр exclude_text и его excluded_post-debug-лог сохранены дословно. Гейт: pytest 443 passed (без дельты); test_stage0_golden зелёный под PYTHONHASHSEED 0/1; ни одной golden-фикстуры не изменено. Co-Authored-By: Claude Opus 4.8 (1M context) --- post_parser.py | 62 +++++++++++++++++++++------------- rss_generator.py | 46 +++++++------------------ tests/test_group_ids.py | 2 +- tests/test_stage4_eventloop.py | 7 ++-- 4 files changed, 56 insertions(+), 61 deletions(-) diff --git a/post_parser.py b/post_parser.py index 1b306e3..ad1e61e 100644 --- a/post_parser.py +++ b/post_parser.py @@ -175,6 +175,23 @@ MEDIA_SOURCES: Dict[Any, Callable[[Message], Tuple[Any, Optional[str]]]] = { } +# Inline media-sizing style literals, named so the renderers below read as intent +# rather than magic numbers. Values are substituted verbatim into the style strings, +# so the emitted bytes are unchanged. +MEDIA_MAX_HEIGHT_PX = "400px" # standard image/video max-height +MEDIA_MAX_HEIGHT_SMALL_PX = "200px" # looping/sticker media max-height +MEDIA_MAX_WIDTH_PX = "400px" # audio player max-width + + +def _wrap_post_html(body: str, footer: str) -> str: + """Shared post-HTML shell: the message-body + message-footer div pair joined by a + single newline. Used by PostParser._format_html (single/debug post) and by both + branches of rss_generator._render_messages_groups (single message and merged + group), so the emitted byte structure stays identical across all three call sites.""" + return (f'
{body}
\n' + f'') + + @dataclass class RenderCtx: """Everything a renderer needs. _generate_html_media assembles it (URL signing, @@ -193,34 +210,34 @@ class RenderCtx: # byte-for-byte fidelity is the stage-5a contract. def _render_img_400(ctx: 'RenderCtx') -> List[str]: return [f''] + f'max-height:{MEDIA_MAX_HEIGHT_PX}; object-fit:contain;">'] def _render_video_400(ctx: 'RenderCtx') -> List[str]: return [f''] + f'height:auto; max-height:{MEDIA_MAX_HEIGHT_PX};">'] def _render_audio(ctx: 'RenderCtx') -> List[str]: - return [f'