22import json
33import time
44import os
5+ import sys
56import argparse
67import glob
78import re
1415HEADER_ROWS = 5
1516HEADER_COLS = "W"
1617
18+ # Retry settings
19+ REQUEST_RETRY_TIMES = 3
20+ REQUEST_RETRY_DELAY = 10
21+
1722class FeishuSheetHandler :
1823 """Feishu Sheet Handler for retrieving and writing sheet data"""
1924
@@ -25,13 +30,36 @@ def __init__(self, app_id, app_secret):
2530 self .token_expire_time = 0
2631 self .get_access_token ()
2732
33+ def _request_with_timeout_retry (self , request_func , request_name ):
34+ """Retry request when ReadTimeout happens."""
35+ for attempt in range (REQUEST_RETRY_TIMES ):
36+ try :
37+ return request_func ()
38+ except requests .exceptions .ReadTimeout :
39+ if attempt == REQUEST_RETRY_TIMES - 1 :
40+ print (
41+ f"FATAL: HTTP timeout after { REQUEST_RETRY_TIMES } attempts while handling "
42+ f"{ request_name } . Please manually revert the Feishu sheet to a previous version."
43+ )
44+ sys .exit (1 )
45+ print (
46+ f"{ request_name } timed out on attempt "
47+ f"{ attempt + 1 } /{ REQUEST_RETRY_TIMES } , retry after { REQUEST_RETRY_DELAY } s"
48+ )
49+ time .sleep (REQUEST_RETRY_DELAY )
50+
2851 def get_access_token (self ):
2952 """Get and cache tenant_access_token"""
3053 if self .access_token and time .time () < self .token_expire_time :
3154 return self .access_token
3255
3356 url = f"{ self .base_url } /auth/v3/tenant_access_token/internal"
34- resp = requests .post (url , json = {"app_id" : self .app_id , "app_secret" : self .app_secret }, timeout = 10 )
57+ resp = self ._request_with_timeout_retry (
58+ lambda : requests .post (url , json = {"app_id" : self .app_id , "app_secret" : self .app_secret }, timeout = 10 ),
59+ "Get access token"
60+ )
61+ if resp is None :
62+ return None
3563 if resp .status_code != 200 :
3664 print ("Failed to get token: HTTP error" , resp .status_code )
3765 return None
@@ -57,7 +85,12 @@ def _feishu_request(self, method, endpoint, **kwargs):
5785 }
5886
5987 url = f"{ self .base_url } { endpoint } "
60- resp = requests .request (method , url , headers = headers , timeout = 15 , ** kwargs )
88+ resp = self ._request_with_timeout_retry (
89+ lambda : requests .request (method , url , headers = headers , timeout = 15 , ** kwargs ),
90+ f"{ method } { endpoint } "
91+ )
92+ if resp is None :
93+ return None
6194
6295 if resp .status_code != 200 :
6396 print (f"Request failed: HTTP { resp .status_code } " )
0 commit comments