@@ -87,28 +87,32 @@ def next_page(
8787 def flatten_to_list (self ) -> List [Any ]:
8888 """Fetches all pages and returns all items as a single list."""
8989 all_items = []
90- current_items , pagination = self .first_page ()
91-
92- if current_items :
90+ for current_items in self ._walk ():
9391 all_items .extend (current_items )
9492
95- while pagination and pagination .has_next_page and pagination .next_page_cursor :
96- current_items , pagination = self .next_page (pagination .next_page_cursor )
97- if current_items :
98- all_items .extend (current_items )
99-
10093 return all_items
10194
10295 def flatten (self ) -> Generator [Any , None , None ]:
10396 """Fetches all pages and yields items one by one using a generator."""
104- current_items , pagination = self .first_page ()
105- if current_items :
97+ for current_items in self ._walk ():
10698 yield from current_items
10799
100+ def _walk (self ) -> Generator [List [Any ], None , None ]:
101+ """Yields each page once, stopping if the server repeats a cursor."""
102+ current_items , pagination = self .first_page ()
103+ yield current_items or []
104+
105+ seen_cursors = set ()
106+
108107 while pagination and pagination .has_next_page and pagination .next_page_cursor :
109- current_items , pagination = self .next_page (pagination .next_page_cursor )
110- if current_items :
111- yield from current_items
108+ cursor = pagination .next_page_cursor
109+
110+ if cursor in seen_cursors :
111+ return
112+ seen_cursors .add (cursor )
113+
114+ current_items , pagination = self .next_page (cursor )
115+ yield current_items or []
112116
113117
114118class AsyncSeamPaginator :
@@ -161,29 +165,30 @@ async def next_page(
161165 async def flatten_to_list (self ) -> List [Any ]:
162166 """Fetches all pages and returns all items as a single list."""
163167 all_items = []
164- current_items , pagination = await self .first_page ()
165-
166- if current_items :
168+ async for current_items in self ._walk ():
167169 all_items .extend (current_items )
168170
169- while pagination and pagination .has_next_page and pagination .next_page_cursor :
170- current_items , pagination = await self .next_page (
171- pagination .next_page_cursor
172- )
173- if current_items :
174- all_items .extend (current_items )
175-
176171 return all_items
177172
178173 async def flatten (self ) -> AsyncGenerator [Any , None ]:
179174 """Fetches all pages and yields items one by one using an async generator."""
175+ async for current_items in self ._walk ():
176+ for item in current_items :
177+ yield item
178+
179+ async def _walk (self ) -> AsyncGenerator [List [Any ], None ]:
180+ """Yields each page once, stopping if the server repeats a cursor."""
180181 current_items , pagination = await self .first_page ()
181- for item in current_items or []:
182- yield item
182+ yield current_items or []
183+
184+ seen_cursors = set ()
183185
184186 while pagination and pagination .has_next_page and pagination .next_page_cursor :
185- current_items , pagination = await self .next_page (
186- pagination .next_page_cursor
187- )
188- for item in current_items or []:
189- yield item
187+ cursor = pagination .next_page_cursor
188+
189+ if cursor in seen_cursors :
190+ return
191+ seen_cursors .add (cursor )
192+
193+ current_items , pagination = await self .next_page (cursor )
194+ yield current_items or []
0 commit comments