Sign in to run and submit your work
Reading is open to everyone. Running code and saving drafts need an account so your work is yours and comes back on your next visit.
or
CODE WORKSPACE
A partner API returns a page of rows and a cursor for the next one. Following it is a four-line loop, and every production incident with one is about how it ends: a cursor that points backwards, a page that has gone, or a chain longer than anybody budgeted for.
Write collect_pages(pages, start_cursor, max_pages). Return the rows, how many pages were read, and why the walk stopped.
Function to write
collect_pages(pages: list[dict], start_cursor: str, max_pages: int) -> dictA dict with the collected `rows`, the number of `pages` read, and the reason it `stopped`.
How to approach it
Run the looping chain. Count the rows, then say why there are that many.
Sample cases
+ 2 held back until you submit
two pages to the end
The ordinary chain: both pages read, rows in order, and the reason recorded as end.
Input
Argument 1
[
{
'cursor': 'c0',
'rows': [
1,
2
],
'next': 'c1'
},
{
'cursor': 'c1',
'rows': [
3
],
'next': None
}
]Argument 2
'c0'Argument 3
10Returns
{
'rows': [
1,
2,
3
],
'pages': 2,
'stopped': 'end'
}the API loops back on itself
A cursor pointing back at one already followed must end the walk rather than run forever.
Input
Argument 1
[
{
'cursor': 'c0',
'rows': [
1
],
'next': 'c1'
},
{
'cursor': 'c1',
'rows': [
2
],
'next': 'c0'
}
]Argument 2
'c0'Argument 3
10Returns
{
'rows': [
1,
2
],
'pages': 2,
'stopped': 'cycle'
}no pages at all
The starting cursor resolves to nothing, which is a stop reason and not a KeyError.
Input
Argument 1
[] (empty list)
Argument 2
'c0'Argument 3
10Returns
{
'rows': [],
'pages': 0,
'stopped': 'missing page'
}Constraints
{'rows': [...], 'pages': <count>, 'stopped': <reason>}.next from start_cursor, collecting rows in page order, until next is None. That is the reason end.cycle before reading it a second time.missing page, rather than raising.max_pages pages. Reaching it stops with page limit.pages counts the pages actually read, so it never exceeds max_pages.Worked example
A two-page chain from c0 collects rows 1, 2 and 3 across two pages and stops with end.
Now point c1 back at c0. The starter's while cursor is not None never becomes false, so it collects rows forever until the memory limit kills it — and the symptom in production is not a crash, it is a job that runs for six hours and fills a disk. A seen set turns that into a two-page walk with a reason attached.
What this tests
That a loop over data somebody else controls needs its own termination guarantees, and that reporting why it stopped is what separates a boundary you can operate from one that silently returns half a dataset.
collect_pages(pages: list[dict], start_cursor: str, max_pages: int) -> dictSubmit for review to find out what your query gets right, what it gets wrong, and how it compares with the best working query for this exercise.
This scenario runs a full workspace — editor, canvas and results side by side. It needs a laptop or desktop to be usable. Open this page on a bigger screen to start building.