mirror of
https://github.com/kuhyx/steam-backlog-enforcer.git
synced 2026-11-11 14:27:33 +01:00
Each keeps its name and serves the moved names through a module __getattr__ (PEP 562) rather than a top-level re-export: every child imports back from its parent, so a plain import would be circular. The lookup goes through importlib.import_module, which is a call rather than an import statement, so it needs no linter suppression. Return type is a _Reexport alias -- mypy rejects `object` as not callable at the call sites, ruff rejects a bare Any. Also fixes two leaks where tests wrote to the user's real config, both caused by a redirected constant moving to a new module while conftest kept patching the old one: - APPROVED_EXCEPTIONS_FILE / EXCEPTION_AUDIT_LOG moved to _whitelist_locking; the suite had appended 27 fixture entries to the live exception_audit.log. - _OWNED_IDS_CACHE_FILE was never redirected at all: it is derived from CONFIG_DIR at import time, so it captured the real path before the fixture could patch it, and the suite overwrote the user's owned-games cache. Both are now verified by md5summing ~/.config/steam_backlog_enforcer before and after a full run, which is the only check that actually catches this -- the tests pass either way. 1347 tests, 100.00% coverage. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016A1EYiv7e7HGhET84oa9M3
96 lines
2.8 KiB
Python
96 lines
2.8 KiB
Python
"""Cached batch lookup of HowLongToBeat times.
|
|
|
|
Split out of :mod:`steam_backlog_enforcer.hltb` to keep both files
|
|
under the 250-line cap.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import time
|
|
|
|
from steam_backlog_enforcer._hltb_types import (
|
|
ProgressCb,
|
|
_HLTBExtras,
|
|
load_hltb_cache,
|
|
load_hltb_count_comp_cache,
|
|
load_hltb_leisure_100h_cache,
|
|
load_hltb_polls_cache,
|
|
load_hltb_rush_cache,
|
|
save_hltb_cache,
|
|
)
|
|
from steam_backlog_enforcer.hltb import fetch_hltb_times
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
# ──────────────────────────────────────────────────────────────
|
|
# Confidence-only batch fetch (no leisure/DLC detail pages)
|
|
# ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
def fetch_hltb_times_cached(
|
|
games: list[tuple[int, str]],
|
|
progress_cb: ProgressCb | None = None,
|
|
) -> dict[int, float]:
|
|
"""Fetch HLTB times, using disk cache for already-known games.
|
|
|
|
Args:
|
|
games: list of (app_id, name) tuples to look up.
|
|
progress_cb: optional callback(done, total, found, game_name).
|
|
|
|
Returns: dict mapping app_id -> completionist_hours.
|
|
"""
|
|
cache = load_hltb_cache()
|
|
polls = load_hltb_polls_cache()
|
|
extras = _HLTBExtras(
|
|
count_comp=load_hltb_count_comp_cache(),
|
|
rush=load_hltb_rush_cache(),
|
|
leisure_100h=load_hltb_leisure_100h_cache(),
|
|
)
|
|
uncached = [(app_id, name) for app_id, name in games if app_id not in cache]
|
|
|
|
if uncached:
|
|
logger.info(
|
|
"Fetching HLTB data for %d uncached games (%d cached)...",
|
|
len(uncached),
|
|
len(games) - len(uncached),
|
|
)
|
|
t0 = time.monotonic()
|
|
fetch_hltb_times(
|
|
uncached,
|
|
cache=cache,
|
|
polls=polls,
|
|
progress_cb=progress_cb,
|
|
extras=extras,
|
|
)
|
|
elapsed = time.monotonic() - t0
|
|
|
|
# Final save.
|
|
save_hltb_cache(cache, polls, extras)
|
|
|
|
found = sum(1 for aid, _ in uncached if cache.get(aid, -1) > 0)
|
|
rate = len(uncached) / elapsed if elapsed > 0 else 0
|
|
logger.info(
|
|
"HLTB fetch done: %d/%d found in %.1fs (%.0f games/s)",
|
|
found,
|
|
len(uncached),
|
|
elapsed,
|
|
rate,
|
|
)
|
|
else:
|
|
logger.info("All %d games found in HLTB cache.", len(games))
|
|
|
|
return cache
|
|
|
|
|
|
_REEXPORTED_TYPES = frozenset({"HLTB_BASE_URL"})
|
|
|
|
_MOVED_TO_HLTB_CONFIDENCE = frozenset(
|
|
{
|
|
"fetch_hltb_confidence_cached",
|
|
"fetch_hltb_detail_missing",
|
|
"get_hltb_submit_url",
|
|
}
|
|
)
|