Skip to content

Pagination

deezify.paginator

Pagination helpers.

List endpoints answer with an envelope like::

{"data": [...], "total": 123, "prev": "...", "next": "..."}

:class:PaginatedList is a small immutable view over that envelope: list() gives the items, .total the advertised total, and :meth:iter_all_pages walks next links until exhaustion.

PaginatedList

Bases: Generic[T]

A single page of a Deezer collection plus navigation helpers.

Source code in deezify/paginator.py
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
class PaginatedList(Generic[T]):
    """A single page of a Deezer collection plus navigation helpers."""

    def __init__(
        self,
        payload: dict[str, Any] | list[Any],
        *,
        fetch: Callable[[str], dict[str, Any]] | None = None,
    ) -> None:
        if isinstance(payload, list):
            payload = {"data": payload, "total": len(payload)}
        self._payload = payload
        self._fetch = fetch

    # -- page content ----------------------------------------------------
    @property
    def data(self) -> list[T]:
        items = self._payload.get("data", [])
        return list(items) if isinstance(items, list) else []

    @property
    def total(self) -> int | None:
        total = self._payload.get("total")
        return int(total) if isinstance(total, (int, float)) else None

    @property
    def next_url(self) -> str | None:
        nxt = self._payload.get("next")
        return str(nxt) if nxt else None

    @property
    def prev_url(self) -> str | None:
        prev = self._payload.get("prev")
        return str(prev) if prev else None

    @property
    def checksum(self) -> str | None:
        checksum = self._payload.get("checksum")
        return str(checksum) if checksum else None

    @property
    def raw(self) -> dict[str, Any]:
        return self._payload

    # -- dunder sugar ----------------------------------------------------
    def __iter__(self) -> Iterator[T]:
        return iter(self.data)

    def __len__(self) -> int:
        return len(self.data)

    def __getitem__(self, index: int) -> T:
        return self.data[index]

    def __repr__(self) -> str:  # pragma: no cover - cosmetic
        return f"PaginatedList(data={len(self)}, total={self.total})"

    # -- navigation ------------------------------------------------------
    def _require_fetch(self) -> Callable[[str], dict[str, Any]]:
        if self._fetch is None:
            raise TypeError("This PaginatedList has no fetcher; cannot walk pages.")
        return self._fetch

    def iter_all_pages(self) -> Iterator["PaginatedList[T]"]:
        """Yield this page, then every following page via ``next`` links."""
        fetch = self._require_fetch()
        page: PaginatedList[T] | None = self
        while page is not None:
            yield page
            nxt = page.next_url
            page = PaginatedList(fetch(nxt), fetch=fetch) if nxt else None

    def iter_all_items(self) -> Iterator[T]:
        """Yield every item across this and all following pages."""
        for page in self.iter_all_pages():
            yield from page

    def fetch_next(self) -> "PaginatedList[T] | None":
        """Fetch the page pointed at by ``next`` (``None`` when absent)."""
        nxt = self.next_url
        if not nxt:
            return None
        return PaginatedList(self._require_fetch()(nxt), fetch=self._fetch)

    def fetch_prev(self) -> "PaginatedList[T] | None":
        prev = self.prev_url
        if not prev:
            return None
        return PaginatedList(self._require_fetch()(prev), fetch=self._fetch)

iter_all_pages()

Yield this page, then every following page via next links.

Source code in deezify/paginator.py
83
84
85
86
87
88
89
90
def iter_all_pages(self) -> Iterator["PaginatedList[T]"]:
    """Yield this page, then every following page via ``next`` links."""
    fetch = self._require_fetch()
    page: PaginatedList[T] | None = self
    while page is not None:
        yield page
        nxt = page.next_url
        page = PaginatedList(fetch(nxt), fetch=fetch) if nxt else None

iter_all_items()

Yield every item across this and all following pages.

Source code in deezify/paginator.py
92
93
94
95
def iter_all_items(self) -> Iterator[T]:
    """Yield every item across this and all following pages."""
    for page in self.iter_all_pages():
        yield from page

fetch_next()

Fetch the page pointed at by next (None when absent).

Source code in deezify/paginator.py
 97
 98
 99
100
101
102
def fetch_next(self) -> "PaginatedList[T] | None":
    """Fetch the page pointed at by ``next`` (``None`` when absent)."""
    nxt = self.next_url
    if not nxt:
        return None
    return PaginatedList(self._require_fetch()(nxt), fetch=self._fetch)