16
16
17
17
import requests
18
18
19
+ import time
20
+
19
21
from slacker .utils import get_item_id_by_name
20
22
21
23
22
24
__version__ = '0.9.60'
23
25
24
26
API_BASE_URL = 'https://slack.com/api/{api}'
25
27
DEFAULT_TIMEOUT = 10
26
-
28
+ DEFAULT_RETRIES = 0
29
+ # seconds to wait after a 429 error if Slack's API doesn't provide one
30
+ DEFAULT_WAIT = 20
27
31
28
32
__all__ = ['Error' , 'Response' , 'BaseAPI' , 'API' , 'Auth' , 'Users' , 'Groups' ,
29
33
'Channels' , 'Chat' , 'IM' , 'IncomingWebhook' , 'Search' , 'Files' ,
@@ -50,22 +54,45 @@ def __str__(self):
50
54
51
55
class BaseAPI (object ):
52
56
def __init__ (self , token = None , timeout = DEFAULT_TIMEOUT , proxies = None ,
53
- session = None ):
57
+ session = None , rate_limit_retries = DEFAULT_RETRIES ):
54
58
self .token = token
55
59
self .timeout = timeout
56
60
self .proxies = proxies
57
61
self .session = session
62
+ self .rate_limit_retries = rate_limit_retries
58
63
59
64
def _request (self , method , api , ** kwargs ):
60
65
if self .token :
61
66
kwargs .setdefault ('params' , {})['token' ] = self .token
62
67
63
- response = method (API_BASE_URL .format (api = api ),
64
- timeout = self .timeout ,
65
- proxies = self .proxies ,
66
- ** kwargs )
68
+ # while we have rate limit retries left, fetch the resource and back
69
+ # off as Slack's HTTP response suggests
70
+ for retry_num in range (self .rate_limit_retries ):
71
+ response = method (API_BASE_URL .format (api = api ),
72
+ timeout = self .timeout ,
73
+ proxies = self .proxies ,
74
+ ** kwargs )
75
+
76
+ if response .status_code == requests .codes .ok :
77
+ break
67
78
68
- response .raise_for_status ()
79
+ # handle HTTP 429 as documented at
80
+ # https://api.slack.com/docs/rate-limits
81
+ elif response .status_code == requests .codes .too_many : # HTTP 429
82
+ time .sleep (int (response .headers .get ('retry-after' , DEFAULT_WAIT )))
83
+ continue
84
+
85
+ else :
86
+ response .raise_for_status ()
87
+
88
+ else :
89
+ # with no retries left, make one final attempt to fetch the resource,
90
+ # but do not handle too_many status differently
91
+ response = method (API_BASE_URL .format (api = api ),
92
+ timeout = self .timeout ,
93
+ proxies = self .proxies ,
94
+ ** kwargs )
95
+ response .raise_for_status ()
69
96
70
97
response = Response (response .text )
71
98
if not response .successful :
@@ -977,14 +1004,15 @@ class Slacker(object):
977
1004
978
1005
def __init__ (self , token , incoming_webhook_url = None ,
979
1006
timeout = DEFAULT_TIMEOUT , http_proxy = None , https_proxy = None ,
980
- session = None ):
1007
+ session = None , rate_limit_retries = DEFAULT_RETRIES ):
981
1008
982
1009
proxies = self .__create_proxies (http_proxy , https_proxy )
983
1010
api_args = {
984
1011
'token' : token ,
985
1012
'timeout' : timeout ,
986
1013
'proxies' : proxies ,
987
1014
'session' : session ,
1015
+ 'rate_limit_retries' : rate_limit_retries ,
988
1016
}
989
1017
self .im = IM (** api_args )
990
1018
self .api = API (** api_args )
0 commit comments