testsAndMisc/python_pkg/wsg_grabber/_worker_base.py
Krzysztof kuhy Rudnicki 324ec0c9b5 Split wsg_grabber/downloader.py under the 250-line cap
Worker did two separable jobs. Both become mixins, because the tests drive
their methods as bound attributes (worker._download_one(), worker._fetch_thread)
and free functions would have broken that:

  _scan.py    ScanMixin      -- threads.json, the archive, one changed thread
  _fetch.py   DownloadMixin  -- claim a file, fetch it, record the outcome

_worker_base.py holds WorkerBase(ABC) with the state both halves touch and the
three methods Worker supplies. Two earlier shapes were abandoned rather than
suppressed: NotImplementedError stubs are uncovered lines under the 100% gate,
and `if TYPE_CHECKING` stubs satisfy mypy but leave pylint reporting no-member.
The ABC satisfies both with no disable comment anywhere.

_downloaded needs a real class-level default: _fetch does `self._downloaded += 1`
and pylint only accepts an attribute as assignable when there is something to
assign to. Worker.__init__ still owns the instance value.

downloader.py 420 -> 238, _scan.py 134, _fetch.py 98, _worker_base.py 52.
277 tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-16 16:01:11 +02:00

53 lines
1.9 KiB
Python

"""The state and collaborators the worker's two halves both rely on.
:class:`~python_pkg.wsg_grabber.downloader.Worker` is assembled from two mixins
-- :mod:`._scan` and :mod:`._fetch` -- so that no single module exceeds the
250-line cap. Each half touches bookkeeping the other half owns, so both
inherit from here rather than declaring the same attributes twice.
The annotations carry no values: ``Worker.__init__`` remains the only place
this state is created. The two methods are abstract because ``Worker`` supplies
them; declaring them gives mypy and pylint something real to resolve against,
which a bare mixin does not.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from python_pkg.wsg_grabber.downloader import WorkerDeps
from python_pkg.wsg_grabber.models import (
DownloadEvent,
JsonResponse,
ThreadRef,
)
class WorkerBase(ABC):
"""State and collaborators shared by the worker's two halves."""
_deps: WorkerDeps
_pending_threads: list[ThreadRef]
_catalog_fresh: bool
_finished_announced: bool
# Given a real class-level default rather than a bare annotation: _fetch
# increments it (`self._downloaded += 1`), and pylint only treats an
# attribute as existing for assignment when there is something to assign
# to. Worker.__init__ still sets the instance value on construction.
_downloaded: int = 0
@abstractmethod
def _get_json(self, url: str, stamp: str | None = None) -> JsonResponse:
"""Fetch a JSON endpoint, pacing requests and counting the last one."""
@abstractmethod
def _publish(self, event: DownloadEvent) -> None:
"""Hand an event to the UI thread."""
@abstractmethod
def _wait_for_slot(self) -> None:
"""Block until the polite inter-request delay has elapsed."""