Sort_YM/sort_ym/ymclient.py
Andrew Pimenov 970fd551e5 tighten retry scope, RU-only lyrics text cache, fix theme pattern and dedupe log
with_retries caught bare Exception with a type whitelist, masking real bugs
as network errors under retry/backoff - narrowed back to NetworkError.

fetch_api_languages was writing full lyrics text for every language into
lyrics_text.json via its track_supplement side channel, breaking the
documented RU-only invariant and letting analyze feed foreign-language
lyrics through the Russian-only prompt - now gated on normalize_api_language.

dedupe_playlists logged the found duplicate count as "removed" even when
a mid-loop network/API error cut deletion short.

THEME_PATTERN's relaxed regex allowed 2-char themes, contradicting its own
test; current_themes was hoisted out of the analyze loop so later prompts
never saw themes found earlier in the same run. Both reverted to intended
behavior, docs updated to match.
2026-08-19 04:14:29 +03:00

50 lines
1.9 KiB
Python
Raw Permalink 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.

from __future__ import annotations
import time
from collections.abc import Callable, Iterator, Sequence
from typing import TypeVar
from yandex_music import Client
from yandex_music.exceptions import BadRequestError, NetworkError, NotFoundError
T = TypeVar("T")
def make_client(token: str, request_timeout: float = 20) -> Client:
client = Client(token=token)
client.request.set_timeout(request_timeout)
with_retries(lambda: client.init())
return client
def chunked(items: Sequence[T], size: int) -> Iterator[Sequence[T]]:
if size <= 0:
raise ValueError("size must be > 0")
for i in range(0, len(items), size):
yield items[i : i + size]
def with_retries(fn: Callable[[], T], max_attempts: int = 4, base_delay: float = 2.0) -> T:
"""Повторяет вызов при временных сетевых сбоях (таймауты и т.п.) с экспоненциальной паузой.
NotFoundError и BadRequestError не повторяются - это окончательные ответы сервера
(данные точно не найдены / запрос точно невалиден), а не сбои сети, хотя формально
оба тоже унаследованы от NetworkError. Повтор такого запроса даст тот же результат.
"""
if max_attempts <= 0:
max_attempts = 1
last_exc: NetworkError | None = None
for attempt in range(max_attempts):
try:
return fn()
except (NotFoundError, BadRequestError):
raise
except NetworkError as e:
last_exc = e
if attempt < max_attempts - 1:
wait = base_delay * (2**attempt)
print(f" сетевая ошибка ({type(e).__name__}: {e}), повтор через {wait:.0f}с...")
time.sleep(wait)
assert last_exc is not None
raise last_exc