Andrey Epifancev 1eef422b6e feat(parser): каскад стратегий иерархии строк (outline → indent → account_pattern → sums)
hierarchy.py: BodyRow, Levels, стратегии outline (сдвиг к 0), indent (ранги
различных отступов — единицы alignment.indent и пробелов не смешиваются),
account_pattern (ранг кода + жирность, ASSUMPTION в docstring), sums
(жадно, без рекурсии); is_consistent, parents (по позициям и уровням,
Review Focus 3), resolve_levels: «Итого» в стратегии не передаётся и
получает уровень 0; меньше двух строк кроме «Итого» — дерево «trivial».
deadline.py: кооперативный Deadline (R-3). Unit-тесты; fixtures-тест — в
Задаче 8 (R-2). Отметки плана и tasks.md 2.3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 13:31:59 +04:00

27 lines
1.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Кооперативный таймаут разбора (design D-8, ruling R-3).
Разбор идёт в `asyncio.to_thread`, поток нельзя прервать снаружи, поэтому шаги парсера сами вызывают
`Deadline.check(<шаг>)` между этапами; превышение — `ParseTimeoutError` (код контракта `TIMEOUT`).
"""
import time
from collections.abc import Callable
from src.parser.errors import ParseTimeoutError
__all__ = ["Deadline"]
class Deadline:
"""Лимит времени, отсчитываемый от создания; часы подменяются в тестах."""
def __init__(self, seconds: float, clock: Callable[[], float] = time.monotonic) -> None:
self._seconds = seconds
self._clock = clock
self._expires_at = clock() + seconds
def check(self, where: str) -> None:
"""Бросает `ParseTimeoutError`, если лимит превышен; `where` — имя шага для сообщения."""
if self._clock() > self._expires_at:
raise ParseTimeoutError(f"превышен лимит {self._seconds} с на шаге {where}")