mirror of
https://github.com/Horizontal42/Sort_YM.git
synced 2026-11-10 15:42:02 +03:00
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.
50 lines
1.9 KiB
Python
50 lines
1.9 KiB
Python
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
|