1
1
from abc import ABC , abstractmethod
2
+ from typing import Generic
3
+ from sinch .core .types import BM
2
4
3
5
4
6
class PageIterator :
5
- def __init__ (self , paginator ):
7
+ def __init__ (self , paginator , yield_first_page = False ):
6
8
self .paginator = paginator
9
+ # If yielding the first page, set started to False
10
+ self .started = not yield_first_page
7
11
8
12
def __iter__ (self ):
9
13
return self
10
14
11
15
def __next__ (self ):
16
+ if not self .started :
17
+ self .started = True
18
+ return self .paginator
19
+
12
20
if self .paginator .has_next_page :
13
- return self .paginator .next_page ()
21
+ self .paginator = self .paginator .next_page ()
22
+ return self .paginator
14
23
else :
15
24
raise StopIteration
16
25
17
26
18
27
class AsyncPageIterator :
19
- def __init__ (self , paginator ):
28
+ def __init__ (self , paginator , yield_first_page = False ):
20
29
self .paginator = paginator
30
+ self .started = not yield_first_page
21
31
22
32
def __aiter__ (self ):
23
33
return self
24
34
25
35
async def __anext__ (self ):
36
+ if not self .started :
37
+ self .started = True
38
+ return self .paginator
39
+
26
40
if self .paginator .has_next_page :
27
- return await self .paginator .next_page ()
41
+ next_paginator = await self .paginator .next_page ()
42
+ if next_paginator :
43
+ self .paginator = next_paginator
44
+ return self .paginator
28
45
else :
29
46
raise StopAsyncIteration
30
47
31
48
32
- class Paginator (ABC ):
49
+ class Paginator (ABC , Generic [ BM ] ):
33
50
"""
34
51
Pagination response object.
35
-
36
- auto_paging_iter method returns an iterator object that can be used for iterator-based page traversing.
37
- For example:
38
- for page in paginated_response.auto_paging_iter():
39
- ...process page object
40
-
41
- For manual pagination use has_next_page property with next_page() method.
42
- For example:
43
- if paginated_response.has_next_page:
44
- paginated_response = paginated_response.next_page()
45
52
"""
46
- def __init__ (self , sinch , endpoint , result ):
53
+ def __init__ (self , sinch , endpoint , result : BM ):
47
54
self ._sinch = sinch
48
55
self .result = result
49
56
self .endpoint = endpoint
@@ -53,8 +60,14 @@ def __init__(self, sinch, endpoint, result):
53
60
def __repr__ (self ):
54
61
return "Paginated response content: " + str (self .result )
55
62
56
- @abstractmethod
57
- def auto_paging_iter (self ):
63
+ # TODO: Make content() method abstract in Parent class as we implement in the other domains:
64
+ # - Refactor pydantic models in other domains to have a content property.
65
+ def content (self ):
66
+ pass
67
+
68
+ # TODO: Make iterator() method abstract in Parent class as we implement in the other domains:
69
+ # - Refactor pydantic models in other domains to have a content property.
70
+ def iterator (self ):
58
71
pass
59
72
60
73
@abstractmethod
@@ -105,51 +118,79 @@ async def next_page(self):
105
118
return self
106
119
107
120
def auto_paging_iter (self ):
108
- return AsyncPageIterator (self )
121
+ return AsyncPageIterator (self , yield_first_page = True )
109
122
110
123
@classmethod
111
124
async def _initialize (cls , sinch , endpoint ):
112
125
result = await sinch .configuration .transport .request (endpoint )
113
126
return cls (sinch , endpoint , result )
114
127
115
128
116
- class TokenBasedPaginator (Paginator ):
117
- __doc__ = Paginator . __doc__
129
+ class TokenBasedPaginator (Paginator [ BM ] ):
130
+ """Base paginator for token-based pagination with explicit page navigation and metadata."""
118
131
119
- def _calculate_next_page (self ):
120
- if self . result . next_page_token :
121
- self . has_next_page = True
122
- else :
123
- self .has_next_page = False
132
+ def __init__ (self , sinch , endpoint , result = None ):
133
+ super (). __init__ ( sinch , endpoint , result or sinch . configuration . transport . request ( endpoint ))
134
+
135
+ def content ( self ) -> list [ BM ] :
136
+ return getattr ( self .result , "content" , [])
124
137
125
138
def next_page (self ):
139
+ """Returns a new paginator instance for the next page."""
140
+ if not self .has_next_page :
141
+ return None
142
+
126
143
self .endpoint .request_data .page_token = self .result .next_page_token
127
144
self .result = self ._sinch .configuration .transport .request (self .endpoint )
128
145
self ._calculate_next_page ()
129
146
return self
130
147
131
- def auto_paging_iter (self ):
132
- return PageIterator (self )
148
+ def iterator (self ):
149
+ """Iterates over individual items across all pages."""
150
+ paginator = self
151
+ while paginator :
152
+ yield from paginator .content ()
153
+
154
+ next_page_instance = paginator .next_page ()
155
+ if not next_page_instance :
156
+ break
157
+ paginator = next_page_instance
158
+
159
+ def _calculate_next_page (self ):
160
+ self .has_next_page = bool (getattr (self .result , "next_page_token" , None ))
133
161
134
162
@classmethod
135
163
def _initialize (cls , sinch , endpoint ):
164
+ """Creates an instance of the paginator skipping first page."""
136
165
result = sinch .configuration .transport .request (endpoint )
137
- return cls (sinch , endpoint , result )
166
+ return cls (sinch , endpoint , result = result )
138
167
139
168
140
169
class AsyncTokenBasedPaginator (TokenBasedPaginator ):
141
- __doc__ = TokenBasedPaginator . __doc__
170
+ """Asynchronous token-based paginator."""
142
171
143
172
async def next_page (self ):
173
+ if not self .has_next_page :
174
+ return None
175
+
144
176
self .endpoint .request_data .page_token = self .result .next_page_token
145
- self .result = await self ._sinch .configuration .transport .request (self .endpoint )
146
- self ._calculate_next_page ()
147
- return self
177
+ next_result = await self ._sinch .configuration .transport .request (self .endpoint )
148
178
149
- def auto_paging_iter (self ):
150
- return AsyncPageIterator (self )
179
+ return self .__class__ (self ._sinch , self .endpoint , result = next_result )
180
+
181
+ async def iterator (self ):
182
+ """Iterates asynchronously over individual items across all pages."""
183
+ paginator = self
184
+ while paginator :
185
+ for item in paginator .content ():
186
+ yield item
187
+
188
+ next_page_instance = await paginator .next_page ()
189
+ if not next_page_instance :
190
+ break
191
+ paginator = next_page_instance
151
192
152
193
@classmethod
153
194
async def _initialize (cls , sinch , endpoint ):
154
195
result = await sinch .configuration .transport .request (endpoint )
155
- return cls (sinch , endpoint , result )
196
+ return cls (sinch , endpoint , result = result )
0 commit comments