跳转至

pagination

core.pagination

分页与换一批核心组件定义.

ResponseAdapter

ResponseAdapter(
    has_more_flag: str
    | Callable[[Any], bool]
    | None = None,
    total: str | Callable[[Any], int] | None = None,
    cursor: str | Callable[[Any], Any] | None = None,
    count: str | Callable[[Any], int] | None = None,
)

响应提取器, 负责从响应中提取迭代所需的核心数据.

初始化响应提取器.

PARAMETER DESCRIPTION
has_more_flag

是否还有更多数据的标志位提取方式.

TYPE: str | Callable[[Any], bool] | None DEFAULT: None

total

总数提取方式.

TYPE: str | Callable[[Any], int] | None DEFAULT: None

cursor

下一页游标或下一批刷新参数提取方式.

TYPE: str | Callable[[Any], Any] | None DEFAULT: None

count

当前页实际返回数量提取方式.

TYPE: str | Callable[[Any], int] | None DEFAULT: None

Source code in qqmusic_api/core/pagination.py
def __init__(
    self,
    has_more_flag: str | Callable[[Any], bool] | None = None,
    total: str | Callable[[Any], int] | None = None,
    cursor: str | Callable[[Any], Any] | None = None,
    count: str | Callable[[Any], int] | None = None,
) -> None:
    """初始化响应提取器.

    Args:
        has_more_flag: 是否还有更多数据的标志位提取方式.
        total: 总数提取方式.
        cursor: 下一页游标或下一批刷新参数提取方式.
        count: 当前页实际返回数量提取方式.
    """
    self._has_more_flag = has_more_flag
    self._total = total
    self._cursor = cursor
    self._count = count

get_has_more_flag

get_has_more_flag(response: Any) -> bool | None

提取显式的 has_more 标志.

Source code in qqmusic_api/core/pagination.py
def get_has_more_flag(self, response: Any) -> bool | None:
    """提取显式的 has_more 标志."""
    return self._extract(response, self._has_more_flag)

get_total

get_total(response: Any) -> int | None

提取数据总数.

Source code in qqmusic_api/core/pagination.py
def get_total(self, response: Any) -> int | None:
    """提取数据总数."""
    total = self._extract(response, self._total)
    return total if isinstance(total, int) else None

get_cursor

get_cursor(response: Any) -> Any | None

提取下一页游标或下一批刷新参数.

Source code in qqmusic_api/core/pagination.py
def get_cursor(self, response: Any) -> Any | None:
    """提取下一页游标或下一批刷新参数."""
    return self._extract(response, self._cursor)

get_count

get_count(response: Any) -> int | None

提取当前页实际返回数量.

Source code in qqmusic_api/core/pagination.py
def get_count(self, response: Any) -> int | None:
    """提取当前页实际返回数量."""
    count = self._extract(response, self._count)
    return count if isinstance(count, int) else None

BaseIteratorStrategy

Bases: ABC

迭代策略基类.

has_next abstractmethod

has_next(
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> bool

判断是否还能继续迭代.

PARAMETER DESCRIPTION
params

当前请求参数.

TYPE: PaginationParams

response

当前响应数据.

TYPE: Any

adapter

响应适配器.

TYPE: ResponseAdapter

Source code in qqmusic_api/core/pagination.py
@abstractmethod
def has_next(self, params: PaginationParams, response: Any, adapter: ResponseAdapter) -> bool:
    """判断是否还能继续迭代.

    Args:
        params: 当前请求参数.
        response: 当前响应数据.
        adapter: 响应适配器.
    """

next_params abstractmethod

next_params(
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> PaginationParams

计算并返回下一次请求使用的全新参数字典.

PARAMETER DESCRIPTION
params

当前请求参数.

TYPE: PaginationParams

response

当前响应数据.

TYPE: Any

adapter

响应适配器.

TYPE: ResponseAdapter

Source code in qqmusic_api/core/pagination.py
@abstractmethod
def next_params(
    self,
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> PaginationParams:
    """计算并返回下一次请求使用的全新参数字典.

    Args:
        params: 当前请求参数.
        response: 当前响应数据.
        adapter: 响应适配器.
    """

PagerStrategy

Bases: BaseIteratorStrategy

连续翻页策略基类.

RefresherStrategy

Bases: BaseIteratorStrategy

换一批策略基类.

PageStrategy

PageStrategy(
    page_key: str | int,
    page_size: int | None = None,
    start_page: int = 1,
)

Bases: PagerStrategy

基于页码的翻页策略.

初始化页码策略.

PARAMETER DESCRIPTION
page_key

页码参数名.

TYPE: str | int

page_size

每页条数。仅在需要根据总数推导下一页时必填。

TYPE: int | None DEFAULT: None

start_page

起始页码.

TYPE: int DEFAULT: 1

Source code in qqmusic_api/core/pagination.py
def __init__(self, page_key: str | int, page_size: int | None = None, start_page: int = 1) -> None:
    """初始化页码策略.

    Args:
        page_key: 页码参数名.
        page_size: 每页条数。仅在需要根据总数推导下一页时必填。
        start_page: 起始页码.
    """
    self.page_key = page_key
    self.page_size = page_size
    self.start_page = start_page

has_next

has_next(
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> bool

判断是否还有下一页.

Source code in qqmusic_api/core/pagination.py
def has_next(self, params: PaginationParams, response: Any, adapter: ResponseAdapter) -> bool:
    """判断是否还有下一页."""
    explicit_flag = adapter.get_has_more_flag(response)
    if explicit_flag is not None:
        return bool(explicit_flag)

    total = adapter.get_total(response)
    if total is None or self.page_size is None:
        return False

    current_params = cast("dict[Any, Any]", params)
    current_page = current_params.get(self.page_key, self.start_page)
    if not isinstance(current_page, int):
        raise TypeError("分页请求缺少有效的页码参数, 无法判断是否存在下一页")
    consumed_pages = current_page - self.start_page + 1
    return consumed_pages * self.page_size < total

next_params

next_params(
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> PaginationParams

计算下一页参数.

Source code in qqmusic_api/core/pagination.py
def next_params(
    self,
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> PaginationParams:
    """计算下一页参数."""
    new_params = cast("dict[Any, Any]", copy.deepcopy(params))
    current_page = new_params.get(self.page_key, self.start_page)
    if not isinstance(current_page, int):
        raise TypeError("分页请求缺少有效的页码参数, 无法计算下一页")
    new_params[self.page_key] = current_page + 1
    return new_params

OffsetStrategy

OffsetStrategy(
    offset_key: str | int,
    *,
    page_size_key: str | int | None = None,
    page_size: int | None = None,
    start_offset: int = 0,
)

Bases: PagerStrategy

基于偏移量窗口的翻页策略.

初始化偏移量策略.

PARAMETER DESCRIPTION
offset_key

偏移量参数名.

TYPE: str | int

page_size_key

每页条数参数名.

TYPE: str | int | None DEFAULT: None

page_size

固定每页条数.

TYPE: int | None DEFAULT: None

start_offset

起始偏移量.

TYPE: int DEFAULT: 0

RAISES DESCRIPTION
ValueError

当 page_size_key 和 page_size 同时缺失时抛出.

Source code in qqmusic_api/core/pagination.py
def __init__(
    self,
    offset_key: str | int,
    *,
    page_size_key: str | int | None = None,
    page_size: int | None = None,
    start_offset: int = 0,
) -> None:
    """初始化偏移量策略.

    Args:
        offset_key: 偏移量参数名.
        page_size_key: 每页条数参数名.
        page_size: 固定每页条数.
        start_offset: 起始偏移量.

    Raises:
        ValueError: 当 page_size_key 和 page_size 同时缺失时抛出.
    """
    if page_size_key is None and page_size is None:
        raise ValueError("OffsetStrategy 需要 page_size_key 或 page_size")
    self.offset_key = offset_key
    self.page_size_key = page_size_key
    self.page_size = page_size
    self.start_offset = start_offset

has_next

has_next(
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> bool

判断是否还有下一页.

Source code in qqmusic_api/core/pagination.py
def has_next(self, params: PaginationParams, response: Any, adapter: ResponseAdapter) -> bool:
    """判断是否还有下一页."""
    explicit_flag = adapter.get_has_more_flag(response)
    if explicit_flag is not None:
        return bool(explicit_flag)

    total = adapter.get_total(response)
    if total is None:
        raise ValueError("分页响应未提供 has_more_flag 或 total, 无法判断是否存在下一页")

    current_params = cast("dict[Any, Any]", params)
    current_offset = current_params.get(self.offset_key, self.start_offset)
    if current_offset is None:
        raise ValueError("分页请求缺少有效的 offset 参数, 无法计算下一页")
    step = self._resolve_step(params, response, adapter)
    if step <= 0:
        return False
    return current_offset + step < total

next_params

next_params(
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> PaginationParams

计算下一页参数.

Source code in qqmusic_api/core/pagination.py
def next_params(
    self,
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> PaginationParams:
    """计算下一页参数."""
    new_params = cast("dict[Any, Any]", copy.deepcopy(params))
    current_offset = new_params.get(self.offset_key, self.start_offset)
    if current_offset is None:
        raise ValueError("分页请求缺少有效的 offset 参数, 无法计算下一页")
    step = self._resolve_step(params, response, adapter)
    if step <= 0:
        raise ValueError("分页响应未提供有效的当前页数量, 无法计算下一页偏移量")
    new_params[self.offset_key] = current_offset + step
    return new_params

BatchRefreshStrategy

BatchRefreshStrategy(refresh_key: str | int)

Bases: RefresherStrategy

基于上一批结果标记换一批内容的策略.

初始化换一批策略.

PARAMETER DESCRIPTION
refresh_key

下一次请求需要替换的参数名。

TYPE: str | int

Source code in qqmusic_api/core/pagination.py
def __init__(self, refresh_key: str | int) -> None:
    """初始化换一批策略.

    Args:
        refresh_key: 下一次请求需要替换的参数名。
    """
    self.refresh_key = refresh_key

has_next

has_next(
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> bool

判断是否还能继续换一批.

Source code in qqmusic_api/core/pagination.py
def has_next(self, params: PaginationParams, response: Any, adapter: ResponseAdapter) -> bool:
    """判断是否还能继续换一批."""
    explicit_flag = adapter.get_has_more_flag(response)
    if not explicit_flag:
        return False
    next_refresh_value = self._extract_refresh_value(response, adapter)
    current_params = cast("dict[Any, Any]", params)
    return current_params.get(self.refresh_key) != next_refresh_value

next_params

next_params(
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> PaginationParams

计算下一批请求参数.

Source code in qqmusic_api/core/pagination.py
def next_params(
    self,
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> PaginationParams:
    """计算下一批请求参数."""
    new_params = cast("dict[Any, Any]", copy.deepcopy(params))
    new_params[self.refresh_key] = self._extract_refresh_value(response, adapter)
    return new_params

CursorStrategy

CursorStrategy(cursor_key: str | int)

Bases: PagerStrategy

基于响应游标回写的翻页策略.

初始化游标策略.

PARAMETER DESCRIPTION
cursor_key

下一页游标写回的请求参数名.

TYPE: str | int

Source code in qqmusic_api/core/pagination.py
def __init__(self, cursor_key: str | int) -> None:
    """初始化游标策略.

    Args:
        cursor_key: 下一页游标写回的请求参数名.
    """
    self.cursor_key = cursor_key

has_next

has_next(
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> bool

判断是否还有下一页.

Source code in qqmusic_api/core/pagination.py
def has_next(self, params: PaginationParams, response: Any, adapter: ResponseAdapter) -> bool:
    """判断是否还有下一页."""
    explicit_flag = adapter.get_has_more_flag(response)
    if explicit_flag is not None and not bool(explicit_flag):
        return False

    next_cursor = self._extract_cursor(response, adapter)
    current_params = cast("dict[Any, Any]", params)
    return current_params.get(self.cursor_key) != next_cursor

next_params

next_params(
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> PaginationParams

计算下一页参数.

Source code in qqmusic_api/core/pagination.py
def next_params(
    self,
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> PaginationParams:
    """计算下一页参数."""
    new_params = cast("dict[Any, Any]", copy.deepcopy(params))
    new_params[self.cursor_key] = self._extract_cursor(response, adapter)
    return new_params

MultiFieldContinuationStrategy

MultiFieldContinuationStrategy(
    build_next_params: NextParamsBuilder,
    *,
    context_name: str = "continuation",
)

Bases: PagerStrategy

基于多字段 continuation 更新的翻页策略.

初始化多字段 continuation 策略.

PARAMETER DESCRIPTION
build_next_params

根据当前请求与响应构造下一页完整参数的函数.

TYPE: NextParamsBuilder

context_name

错误上下文中的策略名称.

TYPE: str DEFAULT: 'continuation'

Source code in qqmusic_api/core/pagination.py
def __init__(self, build_next_params: NextParamsBuilder, *, context_name: str = "continuation") -> None:
    """初始化多字段 continuation 策略.

    Args:
        build_next_params: 根据当前请求与响应构造下一页完整参数的函数.
        context_name: 错误上下文中的策略名称.
    """
    self._build_next_params = build_next_params
    self.context_name = context_name

has_next

has_next(
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> bool

判断是否还有下一页.

Source code in qqmusic_api/core/pagination.py
def has_next(self, params: PaginationParams, response: Any, adapter: ResponseAdapter) -> bool:
    """判断是否还有下一页."""
    explicit_flag = adapter.get_has_more_flag(response)
    if explicit_flag is False:
        return False
    return self._build_next_params_candidate(params, response, adapter) is not None

next_params

next_params(
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> PaginationParams

计算下一页参数.

Source code in qqmusic_api/core/pagination.py
def next_params(
    self,
    params: PaginationParams,
    response: Any,
    adapter: ResponseAdapter,
) -> PaginationParams:
    """计算下一页参数."""
    return self._resolve_next_params(params, response, adapter)

PagerMeta dataclass

PagerMeta(
    strategy: PagerStrategy, adapter: ResponseAdapter
)

连续翻页元数据声明.

RefreshMeta dataclass

RefreshMeta(
    strategy: RefresherStrategy, adapter: ResponseAdapter
)

换一批元数据声明.

ResponsePager

ResponsePager(
    initial_request: PaginatedRequest[RequestResultT],
    limit: int | None = None,
)

Bases: _BaseResponseAdvancer[RequestResultT], AsyncIterator[RequestResultT]

按页消费请求结果的异步分页器.

初始化分页器.

PARAMETER DESCRIPTION
initial_request

已声明连续翻页元数据的初始请求对象.

TYPE: PaginatedRequest[RequestResultT]

limit

最多返回的页数。传 None 表示按上游分页信号一直获取。

TYPE: int | None DEFAULT: None

Source code in qqmusic_api/core/pagination.py
def __init__(self, initial_request: "PaginatedRequest[RequestResultT]", limit: int | None = None) -> None:
    """初始化分页器.

    Args:
        initial_request: 已声明连续翻页元数据的初始请求对象.
        limit: 最多返回的页数。传 `None` 表示按上游分页信号一直获取。
    """
    super().__init__(initial_request)
    self._limit = limit
    self._yielded_count = 0

next async

next() -> RequestResultT

获取并返回下一页响应.

Source code in qqmusic_api/core/pagination.py
async def next(self) -> RequestResultT:
    """获取并返回下一页响应."""
    return await self.__anext__()

has_more

has_more() -> bool

返回当前分页器是否还能继续产出下一页.

Source code in qqmusic_api/core/pagination.py
def has_more(self) -> bool:
    """返回当前分页器是否还能继续产出下一页."""
    return self._can_advance()

ResponseRefresher

ResponseRefresher(
    initial_request: RefreshableRequest[RequestResultT],
)

Bases: _BaseResponseAdvancer[RequestResultT]

按需请求下一批结果的换一批器.

初始化换一批器.

PARAMETER DESCRIPTION
initial_request

已声明换一批元数据的初始请求对象。

TYPE: RefreshableRequest[RequestResultT]

Source code in qqmusic_api/core/pagination.py
def __init__(self, initial_request: "RefreshableRequest[RequestResultT]") -> None:
    """初始化换一批器.

    Args:
        initial_request: 已声明换一批元数据的初始请求对象。
    """
    super().__init__(initial_request)
    self._first_response: RequestResultT | None = None

first async

first() -> RequestResultT

请求并返回当前批结果.

Source code in qqmusic_api/core/pagination.py
async def first(self) -> RequestResultT:
    """请求并返回当前批结果."""
    if self._first_response is None:
        self._first_response = await self._advance()
    return self._first_response

refresh async

refresh() -> RequestResultT

请求并返回下一批结果.

Source code in qqmusic_api/core/pagination.py
async def refresh(self) -> RequestResultT:
    """请求并返回下一批结果."""
    if self._first_response is None:
        await self.first()
    return await self._advance()