gitea.utils.pagination
pagination
Helpers for walking paginated Gitea listings.
A listing is walked by asking for page 1, then page 2, and so on until a page says the listing has ended. What a page says is the whole difficulty: the endpoints wrapped here return a bare JSON array, so "this was the last one" has to be inferred, and an instance that infers wrongly is asked for page 3, page 4 and page 5 forever.
Four things end a walk. _end_of_listing applies all four and is the only place
any of them is written down; a walker asks it about each page and does what the
answer says, so the synchronous and the asynchronous walk cannot drift apart and
a fifth rule is added in one place rather than two.
- The page repeats the one before it. Asked first, and the one ending whose
page is not part of the listing: an instance that ignores the
pageparameter answers every request with the same items, so no page is ever empty, short, or different from its predecessor and nothing else below ever fires. Two consecutive identical pages cannot happen in a listing that is really being paged - the same items would have to be served twice - so the repeat is read as the listing having ended at the page before it, and its items are not handed back a second time.
The other three end the walk with the page that ended it included, because its items do belong to the listing:
- The page is empty, or shorter than the first page. The oldest signal and the one that ends almost every real listing. The length of the first page is the yardstick rather than the requested limit, because an instance may cap the page size below what was asked for.
- The response says so.
page_countandhas_morein a page's metadata are honoured when a caller reports them - Gitea sends the equivalent as headers on every page - and ignored when it does not, so this costs nothing to the callers that report neither. A page count has to be positive to be read as one: zero describes no listing at all, and taking it at its word would end a walk on a first page that came back full. - The page limit. A backstop for an instance that does none of the above:
one cycling through pages, say, so that no two consecutive ones match.
MAX_PAGESis far above any listing these endpoints return, so reaching it means something is wrong rather than that a listing is large, and it is logged rather than quietly truncating the result.
Only the empty-or-short rule is a judgement about the data; the rest are about the instance being wrong, which is why they live here and not in each caller - every paginated command is walked through these, so none of them can be the one that still hangs.
Functions:
gitea.utils.pagination.iter_pages
iter_pages(
fetch_page: Callable[
[int], tuple[list[dict[str, Any]], dict[str, Any]]
],
) -> Iterator[tuple[list[dict[str, Any]], dict[str, Any]]]
Yield the pages of a paginated listing, one request at a time.
Pages are requested lazily, so a caller that stops early stops the requests with it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fetch_page
|
Callable[[int], tuple[list[dict[str, Any]], dict[str, Any]]]
|
Callable returning the items and metadata of the given page number. |
required |
Yields:
| Type | Description |
|---|---|
tuple[list[dict[str, Any]], dict[str, Any]]
|
A tuple containing the items and the metadata of each page, in order. |
Source code in src/gitea/utils/pagination.py
gitea.utils.pagination.iter_async_pages
async
iter_async_pages(
fetch_page: Callable[
[int],
Awaitable[
tuple[list[dict[str, Any]], dict[str, Any]]
],
],
) -> AsyncIterator[
tuple[list[dict[str, Any]], dict[str, Any]]
]
Yield the pages of a paginated listing, one request at a time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fetch_page
|
Callable[[int], Awaitable[tuple[list[dict[str, Any]], dict[str, Any]]]]
|
Callable returning the items and metadata of the given page number. |
required |
Yields:
| Type | Description |
|---|---|
AsyncIterator[tuple[list[dict[str, Any]], dict[str, Any]]]
|
A tuple containing the items and the metadata of each page, in order. |
Source code in src/gitea/utils/pagination.py
gitea.utils.pagination.collect_all_pages
collect_all_pages(
fetch_page: Callable[
[int], tuple[list[dict[str, Any]], dict[str, Any]]
],
) -> tuple[list[dict[str, Any]], dict[str, Any]]
Fetch every page of a paginated listing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fetch_page
|
Callable[[int], tuple[list[dict[str, Any]], dict[str, Any]]]
|
Callable returning the items and metadata of the given page number. |
required |
Returns:
| Type | Description |
|---|---|
tuple[list[dict[str, Any]], dict[str, Any]]
|
A tuple containing every item across all pages and the metadata of the last response. |