1- import  re , urllib , requests 
1+ import  re , urllib , requests ,  logging ,  functools ,  inspect ,  asyncio 
22from  datetime  import  datetime 
33from  requests  import  Response 
44from  requests .compat  import  urljoin 
@@ -83,6 +83,8 @@ def normalized_string(s):
8383
8484
8585    def  decorator (wrapped ):
86+ 
87+         @functools .wraps (wrapped ) 
8688        async  def  wrapper (* inner_args , ** inner_kwargs ):
8789
8890            normalized  =  {}
@@ -113,7 +115,8 @@ async def wrapper(*inner_args, **inner_kwargs):
113115
114116
115117
116- async  def  page_generator (coro , array_element = None , offset_param = 'offset' , offset_init_value = 0 , offset_is_by_count = True , ** kwargs ):
118+ async  def  page_generator (coro , array_element = None , offset_param = 'offset' , offset_init_value = 0 , offset_is_by_count = True , 
119+                          page_retries_max = 5 , page_retry_delay_s = 3 , ** kwargs ):
117120    """ 
118121    An async generator function that is used to automatically fetch the next page 
119122    of results from the API when the API supports result paging. 
@@ -128,24 +131,42 @@ async def page_generator(coro, array_element=None, offset_param='offset', offset
128131        offset_is_by_count - Set to true (default) if the API next offset is indicated by count of elements retrieved. 
129132                             If set to false, the offset is incremented by one to indicate a page offset where the count 
130133                             of results per page is set by other parameters. 
134+         page_retries_max - The number of retries to fetch a page in the event of an error.  Defaults to 5. 
135+         page_retry_delay_s - The number of seconds to delay the next page fetch retry.  Defaults to 3 seconds. 
136+          
131137        kwargs - Keyword args passed to the coroutine at the time the coroutine is executed. 
132138    """ 
139+     _log  =  logging .getLogger (f"page_generator:{ inspect .unwrap (coro ).__name__ }  )
140+ 
133141    offset  =  offset_init_value 
134142    buf  =  []
143+     retries  =  0 
144+ 
135145
136146    while  True :
137147        if  len (buf ) ==  0 :
138-             kwargs [offset_param ] =  offset 
139-             json  =  (await  coro (** kwargs )).json ()
140-             buf  =  json [array_element ] if  array_element  is  not None  else  json 
141- 
142-             if  buf  is  None  or  len (buf ) ==  0 :
143-                 return 
144- 
145-             if  offset_is_by_count :
146-                 offset  =  offset  +  len (buf )
147-             else :
148-                 offset  +=  1 
148+             try :
149+                 kwargs [offset_param ] =  offset 
150+                 json  =  (await  coro (** kwargs )).json ()
151+                 buf  =  json [array_element ] if  array_element  is  not None  else  json 
152+                 retries  =  0 
153+ 
154+                 if  buf  is  None  or  len (buf ) ==  0 :
155+                     return 
156+ 
157+                 if  offset_is_by_count :
158+                     offset  =  offset  +  len (buf )
159+                 else :
160+                     offset  +=  1 
161+             except  BaseException  as  ex :
162+                 if  retries  <  page_retries_max :
163+                     _log .debug (f"Exception fetching next page, will retry: { ex }  )
164+                     await  asyncio .sleep (page_retry_delay_s )
165+                     retries  +=  1 
166+                     continue 
167+                 else :
168+                     _log .debug (f"Abort after { retries }  , ex )
169+                     raise 
149170
150171        yield  buf .pop ()
151172
0 commit comments